diff options
-rw-r--r-- | doc/saves/quicksave - Copy (50).dat | bin | 0 -> 7450 bytes | |||
-rw-r--r-- | loader/anim-info.hpp | 15 | ||||
-rw-r--r-- | loader/anim.cpp | 126 | ||||
-rw-r--r-- | loader/impl.cpp | 1 | ||||
-rw-r--r-- | loader/impl.hpp | 17 | ||||
-rw-r--r-- | loader/json.cpp | 13 | ||||
-rw-r--r-- | loader/loader.cpp | 7 | ||||
-rw-r--r-- | loader/loader.hpp | 24 | ||||
-rw-r--r-- | test/dijkstra.cpp | 15 | ||||
-rw-r--r-- | test/json.cpp | 7 | ||||
-rw-r--r-- | test/loader.cpp | 20 | ||||
-rw-r--r-- | test/path-search.cpp | 11 | ||||
-rw-r--r-- | test/raycast.cpp | 6 | ||||
-rw-r--r-- | test/save/quicksave - Copy (0034).dat | bin | 0 -> 3014 bytes | |||
-rw-r--r-- | test/save/quicksave - Copy (0035).dat | bin | 0 -> 7450 bytes |
15 files changed, 212 insertions, 50 deletions
diff --git a/doc/saves/quicksave - Copy (50).dat b/doc/saves/quicksave - Copy (50).dat Binary files differnew file mode 100644 index 00000000..e548dc75 --- /dev/null +++ b/doc/saves/quicksave - Copy (50).dat diff --git a/loader/anim-info.hpp b/loader/anim-info.hpp new file mode 100644 index 00000000..97ba5954 --- /dev/null +++ b/loader/anim-info.hpp @@ -0,0 +1,15 @@ +#pragma once +#include <memory> +#include <Corrade/Containers/String.h> + +namespace floormat { + +class anim_atlas; + +struct anim_info +{ + String name; + std::shared_ptr<anim_atlas> atlas; +}; + +} // namespace floormat diff --git a/loader/anim.cpp b/loader/anim.cpp new file mode 100644 index 00000000..25b5e292 --- /dev/null +++ b/loader/anim.cpp @@ -0,0 +1,126 @@ +#include "impl.hpp" +#include "loader/anim-info.hpp" +#include "compat/exception.hpp" +#include "src/anim-atlas.hpp" +#include <Corrade/Containers/Array.h> +#include <Corrade/Containers/StridedArrayView.h> +#include <Corrade/Utility/Path.h> +#include <Magnum/Trade/ImageData.h> + +namespace floormat { + +std::shared_ptr<class anim_atlas> +loader_::get_anim_atlas(StringView path) noexcept(false) +{ + auto anim_info = deserialize_anim_def(path + ".json"); + + for (anim_group& group : anim_info.groups) + { + if (!group.mirror_from.isEmpty()) + { + auto it = std::find_if(anim_info.groups.cbegin(), anim_info.groups.cend(), + [&](const anim_group& x) { return x.name == group.mirror_from; }); + if (it == anim_info.groups.cend()) + fm_throw("can't find group '{}' to mirror from '{}'"_cf, group.mirror_from, group.name); + group.frames = array(arrayView(it->frames)); + for (anim_frame& f : group.frames) + f.ground = Vector2i((Int)f.size[0] - f.ground[0], f.ground[1]); + } + } + + auto tex = texture(""_s, path); + + fm_soft_assert(!anim_info.object_name.isEmpty()); + fm_soft_assert(anim_info.pixel_size.product() > 0); + fm_soft_assert(!anim_info.groups.isEmpty()); + fm_soft_assert(anim_info.nframes > 0); + fm_soft_assert(anim_info.nframes == 1 || anim_info.fps > 0); + const auto size = tex.pixels().size(); + const auto width = size[1], height = size[0]; + fm_soft_assert(anim_info.pixel_size[0] == width && anim_info.pixel_size[1] == height); + + auto atlas = std::make_shared<class anim_atlas>(path, tex, std::move(anim_info)); + return atlas; +} + +} // namespace floormat + +namespace floormat::loader_detail { + +ArrayView<const String> loader_impl::anim_atlas_list() +{ + if (anim_atlases.empty()) + get_anim_atlas_list(); + fm_assert(!anim_atlases.empty()); + return { anim_atlases.data(), anim_atlases.size() }; +} + +std::shared_ptr<anim_atlas> loader_impl::anim_atlas(StringView name, StringView dir, loader_policy policy) noexcept(false) +{ + if (name == INVALID) return make_invalid_anim_atlas().atlas; // todo! hack + fm_soft_assert(check_atlas_name(name)); + fm_soft_assert(!dir || dir[dir.size()-1] == '/'); + char buf[FILENAME_MAX]; + auto path = make_atlas_path(buf, dir, name); + + if (auto it = anim_atlas_map.find(path); it != anim_atlas_map.end()) + return it->second; + else + { + auto atlas = get_anim_atlas(path); + return anim_atlas_map[atlas->name()] = atlas; + } +} + +void loader_impl::get_anim_atlas_list() +{ + anim_atlases.clear(); + using f = Path::ListFlag; + constexpr auto flags = f::SkipDirectories | f::SkipDotAndDotDot | f::SkipSpecial | f::SortAscending; + if (const auto list = Path::list(ANIM_PATH, flags); list) + { + anim_atlases.reserve(list->size()); + constexpr auto suffix = ".json"_s; + for (StringView str : *list) + if (str.hasSuffix(suffix)) + anim_atlases.emplace_back(str.exceptSuffix(suffix.size())); + } + anim_atlases.shrink_to_fit(); + fm_assert(!anim_atlases.empty()); +} + +const anim_info& loader_impl::make_invalid_anim_atlas() +{ + if (invalid_anim_atlas) [[likely]] + return *invalid_anim_atlas; + + constexpr auto size = Vector2ui{16}; + + auto frame = anim_frame { + .ground = Vector2i(size/2), + .offset = {}, + .size = size, + }; + auto groups = Array<anim_group>{ValueInit, 1}; + groups[0] = anim_group { + .name = "n"_s, + .frames = array({ frame }), + }; + auto def = anim_def { + .object_name = INVALID, + .anim_name = INVALID, + .groups = Utility::move(groups), + .pixel_size = size, + .scale = anim_scale::fixed{size.x(), true}, + .nframes = 1, + }; + auto atlas = std::make_shared<class anim_atlas>(INVALID, make_error_texture(size), std::move(def)); + auto info = anim_info { + .name = INVALID, + .atlas = atlas, + }; + invalid_anim_atlas = Pointer<anim_info>{ InPlace, std::move(info) }; + return *invalid_anim_atlas; +} + +} // namespace floormat::loader_detail diff --git a/loader/impl.cpp b/loader/impl.cpp index f2f3c669..7da85f3f 100644 --- a/loader/impl.cpp +++ b/loader/impl.cpp @@ -2,6 +2,7 @@ #include "compat/assert.hpp" #include "loader/scenery.hpp" #include "loader/wall-info.hpp" +#include "loader/anim-info.hpp" #include "src/ground-atlas.hpp" #include <Corrade/Containers/Pair.h> #include <Magnum/Trade/ImageData.h> diff --git a/loader/impl.hpp b/loader/impl.hpp index b376503b..5c8a542a 100644 --- a/loader/impl.hpp +++ b/loader/impl.hpp @@ -10,12 +10,6 @@ #include <Corrade/PluginManager/PluginManager.h> #include <Magnum/Trade/AbstractImporter.h> -namespace floormat { -struct anim_def; -struct wall_info; -struct ground_info; -} - namespace floormat::loader_detail { struct loader_impl final : loader_ @@ -53,9 +47,9 @@ struct loader_impl final : loader_ std::shared_ptr<class wall_atlas> wall_atlas(StringView name, loader_policy policy) override; ArrayView<const wall_info> wall_atlas_list() override; void get_wall_atlas_list(); - const wall_info& make_invalid_wall_atlas(); + const wall_info& make_invalid_wall_atlas() override; - // >-----> tile >-----> + // >-----> ground >-----> tsl::robin_map<StringView, ground_info*> ground_atlas_map; std::vector<ground_info> ground_atlas_array; std::vector<String> missing_ground_atlases; @@ -63,15 +57,16 @@ struct loader_impl final : loader_ std::shared_ptr<class ground_atlas> ground_atlas(StringView filename, loader_policy policy) noexcept(false) override; ArrayView<const ground_info> ground_atlas_list() noexcept(false) override; void get_ground_atlas_list(); - const ground_info& make_invalid_ground_atlas(); + const ground_info& make_invalid_ground_atlas() override; // >-----> anim >-----> tsl::robin_map<StringView, std::shared_ptr<class anim_atlas>> anim_atlas_map; std::vector<String> anim_atlases; + Pointer<anim_info> invalid_anim_atlas; ArrayView<const String> anim_atlas_list() override; - std::shared_ptr<class anim_atlas> anim_atlas(StringView name, StringView dir) noexcept(false) override; - static anim_def deserialize_anim(StringView filename); + std::shared_ptr<class anim_atlas> anim_atlas(StringView name, StringView dir, loader_policy policy) noexcept(false) override; void get_anim_atlas_list(); + const anim_info& make_invalid_anim_atlas() override; // >-----> scenery >-----> std::vector<serialized_scenery> sceneries_array; diff --git a/loader/json.cpp b/loader/json.cpp index 756f8151..5c7eecb5 100644 --- a/loader/json.cpp +++ b/loader/json.cpp @@ -6,6 +6,7 @@ #include "serialize/anim.hpp" #include "serialize/scenery.hpp" #include "loader/scenery.hpp" +#include "loader/anim-info.hpp" #include <Corrade/Containers/ArrayViewStl.h> #include <Corrade/Utility/Path.h> @@ -24,14 +25,26 @@ void loader_impl::get_scenery_list() { sceneries_array.clear(); sceneries_array = json_helper::from_json<std::vector<serialized_scenery>>(Path::join(SCENERY_PATH, "scenery.json")); + + if constexpr(false) + { + auto proto = scenery_proto{}; + proto.atlas = make_invalid_anim_atlas().atlas; + proto.bbox_size = Vector2ub{20}; + proto.subtype = generic_scenery_proto{false, true}; + sceneries_array.push_back({ .name = INVALID, .proto = proto }); + } + sceneries_map.clear(); sceneries_map.reserve(sceneries_array.size() * 2); + for (const serialized_scenery& s : sceneries_array) { if (sceneries_map.contains(s.name)) fm_abort("duplicate scenery name '%s'", s.name.data()); sceneries_map[s.name] = &s; } + fm_assert(!sceneries_map.empty()); } diff --git a/loader/loader.cpp b/loader/loader.cpp index af7b053f..531d731a 100644 --- a/loader/loader.cpp +++ b/loader/loader.cpp @@ -1,6 +1,7 @@ #include "impl.hpp" #include "ground-info.hpp" #include "wall-info.hpp" +#include "anim-info.hpp" #include "scenery.hpp" namespace floormat::loader_detail { @@ -19,8 +20,10 @@ void loader_impl::destroy() anim_atlas_map.clear(); anim_atlases.clear(); + invalid_anim_atlas = nullptr; sceneries_map.clear(); sceneries_array.clear(); + vobj_atlas_map.clear(); vobjs.clear(); } @@ -45,8 +48,6 @@ loader_::~loader_() noexcept = default; StringView loader_::strip_prefix(StringView name) { - if (name.hasPrefix(IMAGE_PATH_)) - return name.exceptPrefix(IMAGE_PATH_.size()); if (name.hasPrefix(ANIM_PATH)) return name.exceptPrefix(ANIM_PATH.size()); if (name.hasPrefix(SCENERY_PATH)) @@ -60,8 +61,6 @@ StringView loader_::strip_prefix(StringView name) return name; } -const StringView loader_::INVALID = "<invalid>"_s; // todo use it -const StringView loader_::IMAGE_PATH_ = "images/"_s; const StringView loader_::ANIM_PATH = "anim/"_s; const StringView loader_::SCENERY_PATH = "scenery/"_s; const StringView loader_::TEMP_PATH = "../../../"_s; diff --git a/loader/loader.hpp b/loader/loader.hpp index a9b1d262..96ab57ed 100644 --- a/loader/loader.hpp +++ b/loader/loader.hpp @@ -2,7 +2,7 @@ #include "compat/defs.hpp" #include "src/pass-mode.hpp" #include "loader/policy.hpp" -#include <stdio.h> +#include <stdio.h> // NOLINT(*-deprecated-headers) #include <memory> #include <Corrade/Containers/String.h> @@ -17,12 +17,14 @@ namespace floormat { struct anim_def; class anim_atlas; +struct anim_info; struct scenery_proto; struct vobj_info; class ground_atlas; struct ground_info; struct wall_info; class wall_atlas; +struct scenery_proto; struct vobj_info final { @@ -36,12 +38,12 @@ struct loader_ virtual Trade::ImageData2D texture(StringView prefix, StringView filename) noexcept(false) = 0; virtual std::shared_ptr<class ground_atlas> ground_atlas(StringView filename, loader_policy policy = loader_policy::DEFAULT) noexcept(false) = 0; virtual ArrayView<const String> anim_atlas_list() = 0; - virtual std::shared_ptr<class anim_atlas> anim_atlas(StringView name, StringView dir = ANIM_PATH) noexcept(false) = 0; + virtual std::shared_ptr<class anim_atlas> anim_atlas(StringView name, StringView dir = ANIM_PATH, loader_policy policy = loader_policy::DEFAULT) noexcept(false) = 0; virtual std::shared_ptr<class wall_atlas> wall_atlas(StringView name, loader_policy policy = loader_policy::DEFAULT) noexcept(false) = 0; virtual ArrayView<const wall_info> wall_atlas_list() = 0; virtual void destroy() = 0; static loader_& default_loader() noexcept; - virtual ArrayView<const ground_info> ground_atlas_list() noexcept(false) = 0; + virtual ArrayView<const ground_info> ground_atlas_list() noexcept(false) = 0; // todo maybe try returning virtual ArrayView<const serialized_scenery> sceneries() = 0; virtual const scenery_proto& scenery(StringView name) noexcept(false) = 0; virtual StringView startup_directory() noexcept = 0; @@ -51,16 +53,22 @@ struct loader_ static StringView make_atlas_path(char(&buf)[FILENAME_MAX], StringView dir, StringView name); [[nodiscard]] static bool check_atlas_name(StringView name) noexcept; - /** \deprecated{internal use only}*/ [[nodiscard]] std::shared_ptr<class ground_atlas> get_ground_atlas(StringView name, Vector2ub size, pass_mode pass) noexcept(false); - /** \deprecated{internal use only}*/ [[nodiscard]] std::shared_ptr<class wall_atlas> get_wall_atlas(StringView name) noexcept(false); - /** \deprecated{internal use only}*/ [[nodiscard]] std::shared_ptr<class anim_atlas> get_anim_atlas(StringView path) noexcept(false); + virtual const wall_info& make_invalid_wall_atlas() = 0; + virtual const ground_info& make_invalid_ground_atlas() = 0; + virtual const anim_info& make_invalid_anim_atlas() = 0; + + /** \deprecated{internal use only}*/ [[nodiscard]] + std::shared_ptr<class ground_atlas> get_ground_atlas(StringView name, Vector2ub size, pass_mode pass) noexcept(false); + /** \deprecated{internal use only}*/ [[nodiscard]] + std::shared_ptr<class wall_atlas> get_wall_atlas(StringView name) noexcept(false); + /** \deprecated{internal use only}*/ [[nodiscard]] + std::shared_ptr<class anim_atlas> get_anim_atlas(StringView path) noexcept(false); virtual ~loader_() noexcept; fm_DECLARE_DELETED_COPY_ASSIGNMENT(loader_); fm_DECLARE_DELETED_MOVE_ASSIGNMENT(loader_); - static const StringView INVALID; - static const StringView IMAGE_PATH_; + static constexpr StringView INVALID = "<invalid>"_s; static const StringView ANIM_PATH; static const StringView SCENERY_PATH; static const StringView TEMP_PATH; diff --git a/test/dijkstra.cpp b/test/dijkstra.cpp index d5412559..8a702a52 100644 --- a/test/dijkstra.cpp +++ b/test/dijkstra.cpp @@ -1,6 +1,7 @@ #include "app.hpp" #include "src/path-search.hpp" #include "loader/loader.hpp" +#include "loader/wall-info.hpp" #include <Magnum/Math/Functions.h> namespace floormat { @@ -19,7 +20,7 @@ void test_app::test_dijkstra() constexpr auto wpos = global_coords{wch, wt}; auto& ch = w[wch]; - auto metal2 = wall_image_proto{loader.wall_atlas("empty", loader_policy::warn), 0}; + auto wall = wall_image_proto{loader.make_invalid_wall_atlas().atlas, 0}; for (int16_t j = wcy - 1; j <= wcy + 1; j++) for (int16_t i = wcx - 1; i <= wcx + 1; i++) @@ -27,15 +28,15 @@ void test_app::test_dijkstra() auto &c = w[chunk_coords_{i, j, 0}]; for (int k : { 3, 4, 5, 6, 11, 12, 13, 14, 15, }) { - c[{ k, k }].wall_north() = metal2; - c[{ k, k }].wall_west() = metal2; + c[{ k, k }].wall_north() = wall; + c[{ k, k }].wall_west() = wall; } } - ch[{ wtx, wty }].wall_west() = metal2; - ch[{ wtx, wty }].wall_north() = metal2; - ch[{ wtx+1, wty }].wall_west() = metal2; - ch[{ wtx, wty +1}].wall_north() = metal2; + ch[{ wtx, wty }].wall_west() = wall; + ch[{ wtx, wty }].wall_north() = wall; + ch[{ wtx+1, wty }].wall_west() = wall; + ch[{ wtx, wty +1}].wall_north() = wall; for (int16_t j = wcy - 1; j <= wcy + 1; j++) for (int16_t i = wcx - 1; i <= wcx + 1; i++) diff --git a/test/json.cpp b/test/json.cpp index d9be8ff3..70d8a80c 100644 --- a/test/json.cpp +++ b/test/json.cpp @@ -1,6 +1,7 @@ #include "app.hpp" #include "serialize/tile.hpp" #include "serialize/ground-atlas.hpp" +#include "serialize/wall-atlas.hpp" #include "serialize/magnum-vector.hpp" #include "serialize/json-helper.hpp" #include "compat/assert.hpp" @@ -10,6 +11,8 @@ #include "src/chunk.hpp" #include "src/world.hpp" #include "loader/loader.hpp" +#include "loader/wall-info.hpp" +#include <memory> #include <Corrade/Containers/StringView.h> #include <Corrade/Utility/Path.h> @@ -20,10 +23,6 @@ void test_app::test_json() // NOLINT(readability-convert-member-functions-to-sta fm_assert(Path::exists(Path::join(loader.TEMP_PATH, "CMakeCache.txt"))); const auto output_dir = Path::join(loader.TEMP_PATH, "test/."_s); { - auto atlas = loader.ground_atlas("metal1"); - json_helper::to_json(atlas, Path::join(output_dir, "atlas.json")); - } - { Magnum::Math::Vector<2, int> v2i_1{1, 2}; Vector2i v2i_2{2, 3}; json_helper::to_json(v2i_1, Path::join(output_dir, "vec2i_1.json")); diff --git a/test/loader.cpp b/test/loader.cpp index 2716d238..3b04459a 100644 --- a/test/loader.cpp +++ b/test/loader.cpp @@ -1,8 +1,8 @@ #include "app.hpp" #include "compat/assert.hpp" #include "loader/loader.hpp" -#include "src/ground-atlas.hpp" #include "loader/wall-info.hpp" +#include "src/ground-atlas.hpp" namespace floormat { @@ -37,6 +37,14 @@ constexpr const char* anim_atlases[] = { void test_app::test_loader() { + fm_assert(loader.make_invalid_ground_atlas().atlas); + fm_assert(&loader.make_invalid_ground_atlas().atlas == &loader.make_invalid_ground_atlas().atlas); + fm_assert(loader.make_invalid_ground_atlas().name == loader.INVALID); + + fm_assert(loader.make_invalid_wall_atlas().atlas); + fm_assert(&loader.make_invalid_wall_atlas().atlas == &loader.make_invalid_wall_atlas().atlas); + fm_assert(loader.make_invalid_wall_atlas().name == loader.INVALID); + for (const auto& str : anim_atlases) (void)loader.get_anim_atlas(str); for (const auto& x : ground_atlases) @@ -51,16 +59,6 @@ void test_app::test_loader() loader.sceneries(); for (StringView name : loader.anim_atlas_list()) loader.anim_atlas(name); - - { auto walls = loader.wall_atlas_list(); - fm_assert(!walls.isEmpty()); - fm_assert(loader.wall_atlas("test1"_s)); - fm_assert(loader.wall_atlas(loader.INVALID, loader_policy::ignore)); - fm_assert(loader.wall_atlas("test1"_s) == loader.wall_atlas("test1"_s)); - fm_assert(loader.wall_atlas("test1"_s) != loader.wall_atlas(loader.INVALID, loader_policy::ignore)); - } - for (const auto& info : loader.wall_atlas_list()) - fm_assert(loader.wall_atlas(info.name)); } } // namespace floormat diff --git a/test/path-search.cpp b/test/path-search.cpp index 1345f091..5a1dc825 100644 --- a/test/path-search.cpp +++ b/test/path-search.cpp @@ -2,6 +2,7 @@ #include "compat/assert.hpp" #include "compat/function2.hpp" #include "loader/loader.hpp" +#include "loader/wall-info.hpp" #include "src/world.hpp" #include "src/scenery.hpp" #include "src/path-search.hpp" @@ -178,7 +179,7 @@ void test_bbox() return neighbor_tiles(w, { ch, pos }, {}, (object_id)-1, path_search::never_continue()); }; - const auto metal2 = loader.wall_atlas("empty", loader_policy::warn); + const auto wall = loader.make_invalid_wall_atlas().atlas; const auto table = loader.scenery("table1"); { @@ -190,7 +191,7 @@ void test_bbox() auto w = world(); [[maybe_unused]] auto& c12 = w[coord2]; [[maybe_unused]] auto& c11 = w[coord1]; - c12[{0, 0}].wall_north() = {metal2, 0}; + c12[{0, 0}].wall_north() = { wall, 0}; fm_assert( !is_passable_1(c12, bbox({}, N)) ); fm_assert( is_passable_1(c12, bbox({}, E)) ); @@ -211,8 +212,8 @@ void test_bbox() auto w = world(); auto& c = w[ch]; - c[{8, 7}].wall_north() = {metal2,0}; - c[{8, 9}].wall_north() = {metal2,0}; + c[{8, 7}].wall_north() = { wall,0}; + c[{8, 9}].wall_north() = { wall,0}; fm_assert( is_passable_1(c, bbox({8, 6}, N)) ); fm_assert( !is_passable_1(c, bbox({8, 6}, S)) ); fm_assert( !is_passable_1(c, bbox({8, 7}, N)) ); @@ -224,7 +225,7 @@ void test_bbox() fm_assert(neighbors(w, ch, {8, 8}).size == 3); - c[{8, 8}].wall_north() = {metal2,0}; + c[{8, 8}].wall_north() = { wall,0}; c.mark_passability_modified(); fm_assert( is_passable_1(c, bbox({8, 8}, C)) ); fm_assert( !is_passable_1(c, bbox({8, 7}, S)) ); diff --git a/test/raycast.cpp b/test/raycast.cpp index 23b03926..cb76333f 100644 --- a/test/raycast.cpp +++ b/test/raycast.cpp @@ -3,6 +3,7 @@ #include "src/raycast-diag.hpp" #include "src/world.hpp" #include "loader/loader.hpp" +#include "loader/wall-info.hpp" #include <Magnum/Math/Functions.h> namespace floormat { @@ -12,8 +13,13 @@ namespace { world make_world() { constexpr auto var = (variant_t)-1; +#if 1 + auto wall1_ = loader.make_invalid_wall_atlas().atlas; + auto wall2_ = loader.make_invalid_wall_atlas().atlas; +#else auto wall1_ = loader.wall_atlas("test1"_s); auto wall2_ = loader.wall_atlas("concrete1"_s); +#endif auto wall1 = wall_image_proto{wall1_, var}; auto wall2 = wall_image_proto{wall2_, var}; diff --git a/test/save/quicksave - Copy (0034).dat b/test/save/quicksave - Copy (0034).dat Binary files differnew file mode 100644 index 00000000..20d38b8a --- /dev/null +++ b/test/save/quicksave - Copy (0034).dat diff --git a/test/save/quicksave - Copy (0035).dat b/test/save/quicksave - Copy (0035).dat Binary files differnew file mode 100644 index 00000000..e548dc75 --- /dev/null +++ b/test/save/quicksave - Copy (0035).dat |