diff options
-rw-r--r-- | draw/anim.hpp | 2 | ||||
-rw-r--r-- | editor/app.hpp | 4 | ||||
-rw-r--r-- | editor/editor.hpp | 4 | ||||
-rw-r--r-- | editor/scenery-editor.hpp | 2 | ||||
-rw-r--r-- | editor/vobj-editor.hpp | 2 | ||||
-rw-r--r-- | floormat/main.hpp | 2 | ||||
-rw-r--r-- | loader/atlas.cpp | 10 | ||||
-rw-r--r-- | loader/impl.cpp | 1 | ||||
-rw-r--r-- | loader/impl.hpp | 42 | ||||
-rw-r--r-- | loader/json.cpp | 5 | ||||
-rw-r--r-- | loader/loader.cpp | 2 | ||||
-rw-r--r-- | loader/loader.hpp | 17 | ||||
-rw-r--r-- | loader/vobj-info.hpp | 2 | ||||
-rw-r--r-- | loader/vobj.cpp | 6 | ||||
-rw-r--r-- | loader/wall-atlas.cpp | 99 | ||||
-rw-r--r-- | loader/wall-info.hpp | 15 | ||||
-rw-r--r-- | main/main-impl.hpp | 2 | ||||
-rw-r--r-- | serialize/wall-atlas.cpp | 110 | ||||
-rw-r--r-- | serialize/wall-atlas.hpp | 18 | ||||
-rw-r--r-- | src/anim-atlas.hpp | 31 | ||||
-rw-r--r-- | src/chunk.hpp | 2 | ||||
-rw-r--r-- | src/critter.hpp | 2 | ||||
-rw-r--r-- | src/object.hpp | 2 | ||||
-rw-r--r-- | src/scenery.hpp | 2 | ||||
-rw-r--r-- | src/tile-atlas.hpp | 25 | ||||
-rw-r--r-- | src/tile-image.hpp | 2 | ||||
-rw-r--r-- | src/tile.hpp | 2 | ||||
-rw-r--r-- | src/wall-atlas.cpp | 16 | ||||
-rw-r--r-- | src/wall-atlas.hpp | 32 | ||||
-rw-r--r-- | test/json/wall-atlas-01_header.json | 4 | ||||
-rw-r--r-- | test/json/wall-atlas-02_groups.json | 4 | ||||
-rw-r--r-- | test/wall-atlas.cpp | 123 |
32 files changed, 359 insertions, 233 deletions
diff --git a/draw/anim.hpp b/draw/anim.hpp index dcfbfe68..03bb10c4 100644 --- a/draw/anim.hpp +++ b/draw/anim.hpp @@ -17,7 +17,7 @@ namespace floormat { struct tile_shader; -struct anim_atlas; +class anim_atlas; struct chunk; struct clickable; struct object; diff --git a/editor/app.hpp b/editor/app.hpp index 7cf983bc..04857771 100644 --- a/editor/app.hpp +++ b/editor/app.hpp @@ -21,10 +21,10 @@ namespace floormat { struct chunk; struct floormat_main; -struct tile_atlas; +class tile_atlas; struct tile_editor; struct fm_settings; -struct anim_atlas; +class anim_atlas; struct critter; struct point; diff --git a/editor/editor.hpp b/editor/editor.hpp index 1ae983de..d877288d 100644 --- a/editor/editor.hpp +++ b/editor/editor.hpp @@ -15,8 +15,8 @@ namespace floormat { struct world; -struct anim_atlas; -struct tile_atlas; +class anim_atlas; +class tile_atlas; struct app; struct editor final diff --git a/editor/scenery-editor.hpp b/editor/scenery-editor.hpp index f3c3780b..cf2a1681 100644 --- a/editor/scenery-editor.hpp +++ b/editor/scenery-editor.hpp @@ -6,7 +6,7 @@ namespace floormat { -struct anim_atlas; +class anim_atlas; struct global_coords; struct world; struct app; diff --git a/editor/vobj-editor.hpp b/editor/vobj-editor.hpp index e26289d4..e92c6abe 100644 --- a/editor/vobj-editor.hpp +++ b/editor/vobj-editor.hpp @@ -10,7 +10,7 @@ namespace floormat { struct world; struct global_coords; struct object; -struct anim_atlas; +class anim_atlas; struct vobj_info; struct app; diff --git a/floormat/main.hpp b/floormat/main.hpp index 11a42bd7..1604ad22 100644 --- a/floormat/main.hpp +++ b/floormat/main.hpp @@ -15,7 +15,7 @@ struct tile_shader; struct lightmap_shader; struct world; struct scenery; -struct anim_atlas; +class anim_atlas; struct clickable; struct floor_mesh; struct wall_mesh; diff --git a/loader/atlas.cpp b/loader/atlas.cpp index 2afd4d2e..3fd30e80 100644 --- a/loader/atlas.cpp +++ b/loader/atlas.cpp @@ -55,12 +55,12 @@ std::shared_ptr<tile_atlas> loader_impl::tile_atlas(StringView name, Vector2ub s char buf[FILENAME_MAX]; auto path = make_atlas_path(buf, IMAGE_PATH, name); - auto atlas = std::make_shared<struct tile_atlas>(path, name, texture(""_s, path), size, pass); + auto atlas = std::make_shared<class tile_atlas>(path, name, texture(""_s, path), size, pass); tile_atlas_map[atlas->name()] = atlas; return atlas; } -std::shared_ptr<struct tile_atlas> loader_impl::tile_atlas(StringView filename) noexcept(false) +std::shared_ptr<class tile_atlas> loader_impl::tile_atlas(StringView filename) noexcept(false) { fm_assert(!tile_atlas_map.empty()); auto it = tile_atlas_map.find(filename); @@ -120,7 +120,7 @@ std::shared_ptr<anim_atlas> loader_impl::anim_atlas(StringView name, StringView 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<struct anim_atlas>(path, tex, std::move(anim_info)); + auto atlas = std::make_shared<class anim_atlas>(path, tex, std::move(anim_info)); return anim_atlas_map[atlas->name()] = atlas; } } @@ -132,11 +132,13 @@ void loader_impl::get_anim_atlas_list() 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()*2); + anim_atlases.reserve(list->size()); for (StringView str : *list) if (str.hasSuffix(".json")) anim_atlases.emplace_back(str.exceptSuffix(std::size(".json")-1)); } + anim_atlases.shrink_to_fit(); + fm_assert(!anim_atlases.empty()); } } // namespace floormat::loader_detail diff --git a/loader/impl.cpp b/loader/impl.cpp index 02493089..029f96a6 100644 --- a/loader/impl.cpp +++ b/loader/impl.cpp @@ -2,6 +2,7 @@ #include "compat/assert.hpp" #include "loader/scenery.hpp" #include "loader/vobj-info.hpp" +#include "loader/wall-info.hpp" #include <Corrade/Containers/Pair.h> #include <Magnum/Trade/ImageData.h> diff --git a/loader/impl.hpp b/loader/impl.hpp index f5975d0e..525a5ecc 100644 --- a/loader/impl.hpp +++ b/loader/impl.hpp @@ -10,22 +10,36 @@ #include <Corrade/PluginManager/PluginManager.h> #include <Magnum/Trade/AbstractImporter.h> -namespace floormat { struct anim_def; } +namespace floormat { +struct anim_def; +struct wall_info; +} namespace floormat::loader_detail { struct loader_impl final : loader_ { +// <-----< resources <-----< Optional<Utility::Resource> shader_res; Optional<PluginManager::Manager<Trade::AbstractImporter>> importer_plugins; Containers::Pointer<Trade::AbstractImporter> image_importer; Containers::Pointer<Trade::AbstractImporter> tga_importer; +// >-----> resources >-----> - tsl::robin_map<StringView, std::shared_ptr<struct tile_atlas>> tile_atlas_map; - tsl::robin_map<StringView, std::shared_ptr<struct anim_atlas>> anim_atlas_map; - tsl::robin_map<StringView, const struct vobj_info*> vobj_atlas_map; +// <-----< walls <-----< + struct wall_index { uint32_t val = (uint32_t)-1; }; + tsl::robin_map<StringView, const wall_info*> wall_atlas_map; + std::vector<wall_info> wall_atlas_array; + + const wall_info& wall_atlas(StringView name, StringView dir) override; + ArrayView<const wall_info> wall_atlas_list() override; + void get_wall_atlas_list(); + std::shared_ptr<class wall_atlas> get_wall_atlas(StringView pathname); +// >-----> walls >-----> + + tsl::robin_map<StringView, std::shared_ptr<class tile_atlas>> tile_atlas_map; + tsl::robin_map<StringView, std::shared_ptr<class anim_atlas>> anim_atlas_map; std::vector<String> anim_atlases; - std::vector<struct vobj_info> vobjs; std::vector<serialized_scenery> sceneries_array; tsl::robin_map<StringView, const serialized_scenery*> sceneries_map; @@ -34,21 +48,27 @@ struct loader_impl final : loader_ StringView shader(StringView filename) noexcept override; Trade::ImageData2D texture(StringView prefix, StringView filename) noexcept(false) override; - std::shared_ptr<struct tile_atlas> tile_atlas(StringView filename, Vector2ub size, Optional<pass_mode> pass) noexcept(false) override; - std::shared_ptr<struct tile_atlas> tile_atlas(StringView filename) noexcept(false) override; + std::shared_ptr<class tile_atlas> tile_atlas(StringView filename, Vector2ub size, Optional<pass_mode> pass) noexcept(false) override; + std::shared_ptr<class tile_atlas> tile_atlas(StringView filename) noexcept(false) override; ArrayView<const String> anim_atlas_list() override; - std::shared_ptr<struct anim_atlas> anim_atlas(StringView name, StringView dir) noexcept(false) override; + std::shared_ptr<class anim_atlas> anim_atlas(StringView name, StringView dir) noexcept(false) override; const std::vector<serialized_scenery>& sceneries() override; const scenery_proto& scenery(StringView name) noexcept(false) override; void get_anim_atlas_list(); void get_scenery_list(); - static anim_def deserialize_anim(StringView filename); - void get_vobj_list(); - std::shared_ptr<struct anim_atlas> make_vobj_anim_atlas(StringView name, StringView image_filename); +// <-----< vobjs <-----< + tsl::robin_map<StringView, const struct vobj_info*> vobj_atlas_map; + std::vector<struct vobj_info> vobjs; + + std::shared_ptr<class anim_atlas> make_vobj_anim_atlas(StringView name, StringView image_filename); const struct vobj_info& vobj(StringView name) override; ArrayView<const struct vobj_info> vobj_list() override; + void get_vobj_list(); +// >-----> vobjs >-----> + + static anim_def deserialize_anim(StringView filename); void set_application_working_directory(); StringView startup_directory() noexcept override; diff --git a/loader/json.cpp b/loader/json.cpp index 7e6aae68..007bafeb 100644 --- a/loader/json.cpp +++ b/loader/json.cpp @@ -29,6 +29,7 @@ void loader_impl::get_scenery_list() fm_abort("duplicate scenery name '%s'", s.name.data()); sceneries_map[s.name] = &s; } + fm_assert(!sceneries_map.empty()); } const std::vector<serialized_scenery>& loader_impl::sceneries() @@ -53,9 +54,9 @@ const scenery_proto& loader_impl::scenery(StringView name) noexcept(false) namespace floormat { -std::vector<std::shared_ptr<struct tile_atlas>> loader_::tile_atlases(StringView filename, pass_mode p) +std::vector<std::shared_ptr<class tile_atlas>> loader_::tile_atlases(StringView filename, pass_mode p) { - auto vec = json_helper::from_json<std::vector<std::shared_ptr<struct tile_atlas>>>( + auto vec = json_helper::from_json<std::vector<std::shared_ptr<class tile_atlas>>>( Path::join(loader_::IMAGE_PATH, filename)); for (auto& x : vec) if (!x->pass_mode()) diff --git a/loader/loader.cpp b/loader/loader.cpp index 7d95da0e..a89cadab 100644 --- a/loader/loader.cpp +++ b/loader/loader.cpp @@ -32,6 +32,8 @@ StringView loader_::strip_prefix(StringView name) return name.exceptPrefix(SCENERY_PATH.size()); if (name.hasPrefix(VOBJ_PATH)) return name.exceptPrefix(VOBJ_PATH.size()); + if (name.hasPrefix(WALL_TILESET_PATH)) + return name.exceptPrefix(WALL_TILESET_PATH.size()); return name; } diff --git a/loader/loader.hpp b/loader/loader.hpp index 181688fc..390f020c 100644 --- a/loader/loader.hpp +++ b/loader/loader.hpp @@ -17,23 +17,28 @@ using ImageData2D = ImageData<2>; namespace floormat { -struct tile_atlas; -struct anim_atlas; +class tile_atlas; +class anim_atlas; +class wall_atlas; struct scenery_proto; struct vobj_info; +struct wall_info; struct loader_ { virtual StringView shader(StringView filename) noexcept = 0; virtual Trade::ImageData2D texture(StringView prefix, StringView filename) noexcept(false) = 0; // todo remove Optional when wall_atlas is fully implemented -sh 20231122 - virtual std::shared_ptr<struct tile_atlas> tile_atlas(StringView filename, Vector2ub size, Optional<pass_mode> pass) noexcept(false) = 0; - virtual std::shared_ptr<struct tile_atlas> tile_atlas(StringView filename) noexcept(false) = 0; + virtual std::shared_ptr<class tile_atlas> tile_atlas(StringView filename, Vector2ub size, Optional<pass_mode> pass) noexcept(false) = 0; + virtual std::shared_ptr<class tile_atlas> tile_atlas(StringView filename) noexcept(false) = 0; virtual ArrayView<const String> anim_atlas_list() = 0; - virtual std::shared_ptr<struct 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) noexcept(false) = 0; + virtual const wall_info& wall_atlas(StringView name, StringView dir = WALL_TILESET_PATH) = 0; + virtual ArrayView<const wall_info> wall_atlas_list() = 0; static void destroy(); static loader_& default_loader() noexcept; - static std::vector<std::shared_ptr<struct tile_atlas>> tile_atlases(StringView filename, pass_mode p); + // todo move to ArrayView later, make non-static, and remove pass_mode + static std::vector<std::shared_ptr<class tile_atlas>> tile_atlases(StringView filename, pass_mode p); virtual const std::vector<serialized_scenery>& sceneries() = 0; virtual const scenery_proto& scenery(StringView name) noexcept(false) = 0; virtual StringView startup_directory() noexcept = 0; diff --git a/loader/vobj-info.hpp b/loader/vobj-info.hpp index 8bc7d189..75ace811 100644 --- a/loader/vobj-info.hpp +++ b/loader/vobj-info.hpp @@ -4,7 +4,7 @@ namespace floormat { -struct anim_atlas; +class anim_atlas; struct vobj_info final { diff --git a/loader/vobj.cpp b/loader/vobj.cpp index 1f8f5207..aa3be207 100644 --- a/loader/vobj.cpp +++ b/loader/vobj.cpp @@ -48,7 +48,7 @@ namespace floormat::loader_detail { #pragma GCC diagnostic ignored "-Wmissing-field-initializers" #endif -std::shared_ptr<struct anim_atlas> loader_impl::make_vobj_anim_atlas(StringView name, StringView image_filename) +std::shared_ptr<class anim_atlas> loader_impl::make_vobj_anim_atlas(StringView name, StringView image_filename) { auto tex = texture(VOBJ_PATH, image_filename); anim_def def; @@ -65,7 +65,7 @@ std::shared_ptr<struct anim_atlas> loader_impl::make_vobj_anim_atlas(StringView .size = def.pixel_size }} }}; - auto atlas = std::make_shared<struct anim_atlas>(name, tex, std::move(def)); + auto atlas = std::make_shared<class anim_atlas>(name, tex, std::move(def)); return atlas; } @@ -87,6 +87,8 @@ void loader_impl::get_vobj_list() const auto& x = vobjs.back(); vobj_atlas_map[x.atlas->name()] = &x; } + + fm_assert(!vobjs.empty()); } ArrayView<const vobj_info> loader_impl::vobj_list() diff --git a/loader/wall-atlas.cpp b/loader/wall-atlas.cpp new file mode 100644 index 00000000..10f3524d --- /dev/null +++ b/loader/wall-atlas.cpp @@ -0,0 +1,99 @@ +#include "loader/impl.hpp" +#include "wall-info.hpp" +#include "compat/assert.hpp" +#include "compat/exception.hpp" +#include "src/wall-atlas.hpp" +#include "serialize/wall-atlas.hpp" +#include "wall-info.hpp" +#include "serialize/json-helper.hpp" +#include "serialize/corrade-string.hpp" +#include <Corrade/Containers/Array.h> +#include <Corrade/Containers/ArrayViewStl.h> +#include <Corrade/Utility/Path.h> +#include <Magnum/Trade/ImageData.h> +#include <vector> + +namespace floormat { +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(wall_info, name, descr) +} // namespace floormat + +namespace floormat::loader_detail { + +std::shared_ptr<wall_atlas> loader_impl::get_wall_atlas(StringView filename) +{ + using namespace floormat::Wall; + using namespace floormat::Wall::detail; + + const auto jroot = json_helper::from_json_(filename); + auto header = read_info_header(jroot); + auto frames = read_all_frames(jroot); + + size_t direction_count = 0; + for (const auto& [str, curdir] : wall_atlas::directions) + if (jroot.contains(std::string_view{str.data(), str.size()})) + direction_count++; + fm_soft_assert(direction_count > 0); + fm_debug_assert(direction_count <= (size_t)Direction_::COUNT); + + auto directions = Array<Direction>{direction_count}; + std::array<DirArrayIndex, 4> dir_array_indexes{}; + + uint8_t dir_idx = 0; + for (const auto& [str, curdir] : wall_atlas::directions) + { + if (!jroot.contains(std::string_view{str.data(), str.size()})) + continue; + auto i = (size_t)curdir; + fm_debug_assert(dir_idx < direction_count); + dir_array_indexes[i] = { .val = dir_idx }; + directions[dir_idx++] = read_direction_metadata(jroot, curdir); + } + fm_debug_assert(dir_idx == direction_count); + + auto atlas = std::make_shared<class wall_atlas>(); + return atlas; +} + +const wall_info& loader_impl::wall_atlas(StringView name, StringView dir) +{ + fm_soft_assert(check_atlas_name(name)); + char buf[FILENAME_MAX]; + auto path = make_atlas_path(buf, dir, name); + + auto it = wall_atlas_map.find(path); + if (it == wall_atlas_map.end()) + fm_throw("no such wall atlas '{}'"_cf, fmt::string_view{path.data(), path.size()}); + fm_assert(it->second != nullptr); + if (!it->second->atlas) + { + const_cast<wall_info*>(it->second)->atlas = get_wall_atlas(path); + } + return *it->second; +} + +void loader_impl::get_wall_atlas_list() +{ + wall_atlas_array = json_helper::from_json<std::vector<wall_info>>(Path::join(WALL_TILESET_PATH, "walls.json"_s)); + wall_atlas_array.shrink_to_fit(); + wall_atlas_map.clear(); + wall_atlas_map.reserve(wall_atlas_array.size()*2); + + for (const auto& x : wall_atlas_array) + { + fm_soft_assert(check_atlas_name(x.name)); + StringView name = x.name; + wall_atlas_map[name] = &x; + fm_debug_assert(name.data() == wall_atlas_map[name]->name.data()); + } + + fm_assert(!wall_atlas_map.empty()); +} + +ArrayView<const wall_info> loader_impl::wall_atlas_list() +{ + if (wall_atlas_map.empty()) + get_wall_atlas_list(); + return wall_atlas_array; +} + +} // namespace floormat::loader_detail diff --git a/loader/wall-info.hpp b/loader/wall-info.hpp new file mode 100644 index 00000000..570f436d --- /dev/null +++ b/loader/wall-info.hpp @@ -0,0 +1,15 @@ +#pragma once +#include <memory> +#include <Corrade/Containers/String.h> + +namespace floormat { + +class wall_atlas; + +struct wall_info +{ + String name, descr; + std::shared_ptr<wall_atlas> atlas; +}; + +} // namespace floormat diff --git a/main/main-impl.hpp b/main/main-impl.hpp index 5619dda6..f66b0248 100644 --- a/main/main-impl.hpp +++ b/main/main-impl.hpp @@ -29,7 +29,7 @@ namespace floormat { struct floormat_app; struct scenery; -struct anim_atlas; +class anim_atlas; struct clickable; struct main_impl final : Platform::Sdl2Application, floormat_main diff --git a/serialize/wall-atlas.cpp b/serialize/wall-atlas.cpp index 6d711d6b..79abe6fa 100644 --- a/serialize/wall-atlas.cpp +++ b/serialize/wall-atlas.cpp @@ -5,6 +5,7 @@ #include "compat/exception.hpp" #include "loader/loader.hpp" #include "pass-mode.hpp" +#include "json-helper.hpp" #include <utility> #include <string_view> #include <Corrade/Containers/PairStl.h> @@ -15,6 +16,79 @@ // todo add test on dummy files that generates 100% coverage on the j.contains() blocks! +namespace floormat { + +using namespace floormat::Wall::detail; + +bool wall_atlas_def::operator==(const wall_atlas_def& other) const noexcept +{ + if (header != other.header) + return false; + if (direction_array.size() != other.direction_array.size()) + return false; + for (uint8_t i = 0; i < std::size(direction_to_Direction_array_index); i++) + { + auto i1 = direction_to_Direction_array_index[i], + i2 = other.direction_to_Direction_array_index[i]; + if (!i1 != !i2) + return false; + if (i1) + { + fm_assert(i1.val < direction_array.size()); + fm_assert(i2.val < other.direction_array.size()); + if (direction_array[i1.val] != other.direction_array[i2.val]) + return false; + } + } + if (frames.size() != other.frames.size()) + return false; + for (auto i = 0uz; i < frames.size(); i++) + if (frames[i] != other.frames[i]) + return false; + return true; +} + +wall_atlas_def wall_atlas_def::deserialize(StringView filename) +{ + wall_atlas_def atlas; + + const auto jroot = json_helper::from_json_(filename); + atlas.header = read_info_header(jroot); + atlas.frames = read_all_frames(jroot); + auto [dirs, dir_indexes] = read_all_directions(jroot); + fm_soft_assert(!dirs.isEmpty()); + fm_soft_assert(dir_indexes != std::array<Wall::DirArrayIndex, 4>{}); + atlas.direction_array = std::move(dirs); + atlas.direction_to_Direction_array_index = dir_indexes; + + return atlas; +} + +void wall_atlas_def::serialize(StringView filename) const +{ + auto jroot = json{}; + + write_info_header(jroot, header); + write_all_frames(jroot, frames); + + for (const auto [name_, dir] : wall_atlas::directions) + { + if (auto idx = direction_to_Direction_array_index[(size_t)dir]) + { + const auto& dir = direction_array[idx.val]; + if (!dir.is_empty()) + { + std::string_view name = {name_.data(), name_.size()}; + write_direction_metadata(jroot[name], dir); + } + } + } + + json_helper::to_json_(jroot, filename); +} + +} // namespace floormat + namespace floormat::Wall::detail { uint8_t direction_index_from_name(StringView s) @@ -141,10 +215,8 @@ Info read_info_header(const json& jroot) { fm_soft_assert(jroot.contains(("name"))); fm_soft_assert(jroot.contains(("depth"))); - Info val = {std::string{jroot["name"]}, {}, jroot["depth"]}; + Info val = {std::string{jroot["name"]}, jroot["depth"]}; fm_soft_assert(val.depth > 0); - if (jroot.contains("description")) - val.description = std::string{jroot["description"]}; return val; } @@ -214,9 +286,8 @@ void write_all_directions(json& jroot, const wall_atlas& a) } void write_info_header(json& jroot, const Info& info) -{ jroot["name"] = info.name; - if (info.description) - jroot["description"] = info.description; +{ + jroot["name"] = info.name; jroot["depth"] = info.depth; } @@ -225,38 +296,15 @@ void write_info_header(json& jroot, const Info& info) namespace floormat::Wall { NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Frame, offset) -NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Info, name, description, depth) +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Info, name, depth) } // namespace floormat::Wall namespace nlohmann { -using namespace floormat; -using namespace floormat::Wall; -using namespace floormat::Wall::detail; +using floormat::Wall::Frame; void adl_serializer<Frame>::to_json(nlohmann::json& j, const Frame& x) { using nlohmann::to_json; to_json(j, x); } void adl_serializer<Frame>::from_json(const json& j, Frame& x) { using nlohmann::from_json; from_json(j, x); } -void adl_serializer<std::shared_ptr<wall_atlas>>::to_json(json& j, const std::shared_ptr<const wall_atlas>& x) -{ - fm_assert(x != nullptr); - write_info_header(j, x->info()); - write_all_directions(j, *x); - write_all_frames(j, x->raw_frame_array()); -} - -void adl_serializer<std::shared_ptr<wall_atlas>>::from_json(const json& j, std::shared_ptr<wall_atlas>& x) -{ - auto info = read_info_header(j); - fm_assert(loader.check_atlas_name(info.name)); - auto [dirs, map] = read_all_directions(j); - Array<Frame> frames; - auto img = loader.texture(loader.WALL_TILESET_PATH, info.name); - if (j.contains("frames")) - frames = read_all_frames(j); - - x = std::make_shared<wall_atlas>(std::move(info), img, std::move(frames), std::move(dirs), map); -} - } // namespace nlohmann diff --git a/serialize/wall-atlas.hpp b/serialize/wall-atlas.hpp index f531a48e..b69f7e96 100644 --- a/serialize/wall-atlas.hpp +++ b/serialize/wall-atlas.hpp @@ -3,24 +3,12 @@ #include <memory> #include <nlohmann/json_fwd.hpp> -namespace nlohmann { - template<> -struct adl_serializer<floormat::Wall::Frame> -{ - static void to_json(json& j, const floormat::Wall::Frame& x); - static void from_json(const json& j, floormat::Wall::Frame& x); +struct nlohmann::adl_serializer<floormat::Wall::Frame> { + static void to_json(json& j, const floormat::Wall::Frame& val); + static void from_json(const json& j, floormat::Wall::Frame& val); }; -template<> -struct adl_serializer<std::shared_ptr<floormat::wall_atlas>> -{ - static void to_json(json& j, const std::shared_ptr<const floormat::wall_atlas>& x); - static void from_json(const json& j, std::shared_ptr<floormat::wall_atlas>& x); -}; - -} // namespace nlohmann - namespace floormat::Wall::detail { using nlohmann::json; diff --git a/src/anim-atlas.hpp b/src/anim-atlas.hpp index 22df30eb..2803566f 100644 --- a/src/anim-atlas.hpp +++ b/src/anim-atlas.hpp @@ -2,6 +2,7 @@ #include "compat/defs.hpp" #include "rotation.hpp" #include "anim.hpp" +#include "src/quads.hpp" #include <array> #include <Corrade/Containers/BitArray.h> #include <Corrade/Containers/String.h> @@ -11,11 +12,23 @@ namespace floormat { -struct anim_atlas final +class anim_atlas final { - using texcoords = std::array<Vector2, 4>; - using quad = std::array<Vector3, 4>; + using texcoords = Quads::texcoords; + using quad = Quads::quad; + String _name; + BitArray _bitmask; + anim_def _info; + std::array<uint8_t, (size_t)rotation_COUNT> _group_indices = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }; + GL::Texture2D _tex; + + static decltype(_group_indices) make_group_indices(const anim_def& anim) noexcept; + static uint8_t rotation_to_index(StringView name); + +public: anim_atlas() noexcept; anim_atlas(String name, const ImageView2D& tex, anim_def info); ~anim_atlas() noexcept; @@ -43,18 +56,6 @@ struct anim_atlas final static void make_bitmask_(const ImageView2D& tex, BitArray& array); static BitArray make_bitmask(const ImageView2D& tex); - -private: - String _name; - BitArray _bitmask; - anim_def _info; - std::array<uint8_t, (size_t)rotation_COUNT> _group_indices = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - }; - GL::Texture2D _tex; - - static decltype(_group_indices) make_group_indices(const anim_def& anim) noexcept; - static uint8_t rotation_to_index(StringView name); }; } // namespace floormat diff --git a/src/chunk.hpp b/src/chunk.hpp index e964fca0..a24957b1 100644 --- a/src/chunk.hpp +++ b/src/chunk.hpp @@ -11,7 +11,7 @@ namespace floormat { -struct anim_atlas; +class anim_atlas; struct object; struct object_proto; class tile_iterator; diff --git a/src/critter.hpp b/src/critter.hpp index 81c8b601..724e2b28 100644 --- a/src/critter.hpp +++ b/src/critter.hpp @@ -6,7 +6,7 @@ namespace floormat { -struct anim_atlas; +class anim_atlas; struct world; struct critter_proto : object_proto diff --git a/src/object.hpp b/src/object.hpp index c02e4d07..50512419 100644 --- a/src/object.hpp +++ b/src/object.hpp @@ -12,7 +12,7 @@ namespace floormat { template<typename T> struct object_type_; -struct anim_atlas; +class anim_atlas; struct world; struct chunk; diff --git a/src/scenery.hpp b/src/scenery.hpp index 70b7733c..9135b548 100644 --- a/src/scenery.hpp +++ b/src/scenery.hpp @@ -10,7 +10,7 @@ namespace floormat { struct chunk; -struct anim_atlas; +class anim_atlas; struct world; enum class scenery_type : unsigned char { diff --git a/src/tile-atlas.hpp b/src/tile-atlas.hpp index 45251c6d..1a06db4a 100644 --- a/src/tile-atlas.hpp +++ b/src/tile-atlas.hpp @@ -1,5 +1,6 @@ #pragma once #include "src/pass-mode.hpp" +#include "src/quads.hpp" #include <array> #include <memory> #include <Corrade/Containers/Optional.h> @@ -10,11 +11,22 @@ namespace floormat { -struct tile_atlas final +class tile_atlas final { using quad = std::array<Vector3, 4>; using texcoords = std::array<Vector2, 4>; + static std::unique_ptr<const texcoords[]> make_texcoords_array(Vector2ui pixel_size, Vector2ub tile_count); + static texcoords make_texcoords(Vector2ui pixel_size, Vector2ub tile_count, size_t i); + + std::unique_ptr<const texcoords[]> texcoords_; + GL::Texture2D tex_; + String path_, name_; + Vector2ui size_; + Vector2ub dims_; + Optional<enum pass_mode> passability; + +public: // todo remove Optional when wall atlases are fully implemented -sh 20231122 tile_atlas(StringView path, StringView name, const ImageView2D& img, Vector2ub tile_count, Optional<enum pass_mode> pass_mode); @@ -28,17 +40,6 @@ struct tile_atlas final Optional<enum pass_mode> pass_mode() const; // todo remove later enum pass_mode pass_mode(enum pass_mode p) const; void set_pass_mode(enum pass_mode p); // todo remove later - -private: - static std::unique_ptr<const texcoords[]> make_texcoords_array(Vector2ui pixel_size, Vector2ub tile_count); - static texcoords make_texcoords(Vector2ui pixel_size, Vector2ub tile_count, size_t i); - - std::unique_ptr<const texcoords[]> texcoords_; - GL::Texture2D tex_; - String path_, name_; - Vector2ui size_; - Vector2ub dims_; - Optional<enum pass_mode> passability; }; diff --git a/src/tile-image.hpp b/src/tile-image.hpp index 907a465c..c93cc778 100644 --- a/src/tile-image.hpp +++ b/src/tile-image.hpp @@ -3,7 +3,7 @@ namespace floormat { -struct tile_atlas; +class tile_atlas; using variant_t = uint8_t; diff --git a/src/tile.hpp b/src/tile.hpp index f9d6adc8..fbe2e93d 100644 --- a/src/tile.hpp +++ b/src/tile.hpp @@ -4,7 +4,7 @@ namespace floormat { struct chunk; -struct anim_atlas; +class anim_atlas; struct tile_proto final { diff --git a/src/wall-atlas.cpp b/src/wall-atlas.cpp index 31659cca..3c2ac3cf 100644 --- a/src/wall-atlas.cpp +++ b/src/wall-atlas.cpp @@ -70,15 +70,13 @@ Vector2i wall_atlas::expected_size(int depth, Tag group) } } -wall_atlas::wall_atlas(Info info, const ImageView2D& img, - Array<Frame> frames, - Array<Direction> directions, - std::array<DirArrayIndex, 4> direction_to_DirArrayIndex) - : _dir_array{ std::move(directions) }, _frame_array{ std::move(frames) }, - _info{ std::move(info) }, - _direction_to_Direction_array_index{ direction_to_DirArrayIndex } +wall_atlas::wall_atlas(wall_atlas_def def, String path, const ImageView2D& img) + : _dir_array{std::move(def.direction_array)}, + _frame_array{std::move(def.frames)}, + _info{std::move(def.header)}, _path{std::move(path)}, + _direction_to_Direction_array_index{def.direction_to_Direction_array_index} { - _texture.setLabel(_info.name) + _texture.setLabel(_path) .setWrapping(GL::SamplerWrapping::ClampToEdge) .setMagnificationFilter(GL::SamplerFilter::Nearest) .setMinificationFilter(GL::SamplerFilter::Linear) @@ -149,9 +147,7 @@ auto wall_atlas::direction(size_t dir) const -> const Direction* uint8_t wall_atlas::direction_count() const { return (uint8_t)_dir_array.size(); } auto wall_atlas::raw_frame_array() const -> ArrayView<const Frame> { return _frame_array; } -auto wall_atlas::info() const -> const Info& { return _info; } GL::Texture2D& wall_atlas::texture() { fm_debug_assert(_texture.id()); return _texture; } -StringView wall_atlas::name() const { return _info.name; } size_t wall_atlas::enum_to_index(enum rotation r) { diff --git a/src/wall-atlas.hpp b/src/wall-atlas.hpp index 6a0abf15..e3470a56 100644 --- a/src/wall-atlas.hpp +++ b/src/wall-atlas.hpp @@ -67,7 +67,7 @@ struct Direction struct Info { - String name = "(unnamed)"_s, description = {}; + String name; unsigned depth = 0; bool operator==(const Info&) const noexcept; @@ -75,7 +75,7 @@ struct Info struct DirArrayIndex { std::uint8_t val = (uint8_t)-1; - operator bool() const { return val == (uint8_t)-1; } + operator bool() const { return val != (uint8_t)-1; } bool operator==(const DirArrayIndex&) const noexcept; }; @@ -84,6 +84,19 @@ struct DirArrayIndex { namespace floormat { +struct wall_atlas_def final +{ + bool operator==(const wall_atlas_def&) const noexcept; + + Wall::Info header; + Array<Wall::Frame> frames; + Array<Wall::Direction> direction_array; + std::array<Wall::DirArrayIndex, 4> direction_to_Direction_array_index; + + static wall_atlas_def deserialize(StringView filename); + void serialize(StringView filename) const; +}; + class wall_atlas final { using Frame = Wall::Frame; @@ -97,6 +110,7 @@ class wall_atlas final Array<Direction> _dir_array; Array<Frame> _frame_array; Info _info; + String _path; GL::Texture2D _texture{NoCreate}; std::array<DirArrayIndex, 4> _direction_to_Direction_array_index; @@ -106,19 +120,21 @@ public: fm_DECLARE_DELETED_MOVE_ASSIGNMENT(wall_atlas); wall_atlas() noexcept; ~wall_atlas() noexcept; - wall_atlas(Info info, const ImageView2D& img, - Array<Frame> frames, Array<Direction> directions, - std::array<DirArrayIndex, 4> direction_to_DirArrayIndex); - StringView name() const; - uint8_t direction_count() const; + wall_atlas(wall_atlas_def def, String path, const ImageView2D& img); + void serialize(StringView filename) const; const Group* group(size_t dir, Tag tag) const; const Group* group(const Direction& dir, Tag tag) const; const Group* group(const Direction* dir, Tag tag) const; const Direction* direction(size_t dir) const; + uint8_t direction_count() const; ArrayView<const Frame> frames(const Group& a) const; ArrayView<const Frame> raw_frame_array() const; - const Info& info() const; + + const Info& info() const { return _info; } + StringView name() const { return _info.name; } + //StringView path() const { return _path; } + GL::Texture2D& texture(); static size_t enum_to_index(enum rotation x); diff --git a/test/json/wall-atlas-01_header.json b/test/json/wall-atlas-01_header.json index 6859c6da..7d0bb3b4 100644 --- a/test/json/wall-atlas-01_header.json +++ b/test/json/wall-atlas-01_header.json @@ -1,7 +1,5 @@ { "name": "foo", "depth": 42, - "frames": [], - "n": {}, - "w": {} + "frames": [] } diff --git a/test/json/wall-atlas-02_groups.json b/test/json/wall-atlas-02_groups.json index e3ff3fb3..196085bd 100644 --- a/test/json/wall-atlas-02_groups.json +++ b/test/json/wall-atlas-02_groups.json @@ -18,6 +18,10 @@ } ], "n": { + "wall": { + "offset": 0, + "count": 1 + } }, "w": { "pass-mode": "shoot-through", diff --git a/test/wall-atlas.cpp b/test/wall-atlas.cpp index 7cd922ae..5bf143d8 100644 --- a/test/wall-atlas.cpp +++ b/test/wall-atlas.cpp @@ -3,6 +3,7 @@ #include "serialize/wall-atlas.hpp" #include "serialize/json-helper.hpp" #include "loader/loader.hpp" +#include "compat/exception.hpp" #include <Corrade/Containers/StringStl.h> #include <Corrade/Utility/Path.h> @@ -37,21 +38,6 @@ void test_read_header(StringView filename) fm_assert(info.depth == 42); } -void test_read_empty_directions(StringView filename) -{ - const auto jroot = json_helper::from_json_(Path::join(json_path(), filename)); - test_read_header(filename); - fm_assert(!jroot.empty()); - - fm_assert( jroot.contains("n") ); - fm_assert(!jroot.contains("e") ); - fm_assert(!jroot.contains("s") ); - fm_assert( jroot.contains("w") ); - - fm_assert(jroot["n"].is_object() && jroot["n"].empty()); - fm_assert(jroot["w"].is_object() && jroot["w"].empty()); -} - void test_read_groups(StringView filename) { constexpr Group group_defaults; @@ -60,16 +46,15 @@ void test_read_groups(StringView filename) auto info = read_info_header(jroot); fm_assert(info.name == "foo"_s); fm_assert(info.depth == 42); - fm_assert(info.description == ""_s); fm_assert(jroot["depth"] == 42); fm_assert( jroot.contains("n") ); fm_assert(!jroot.contains("e") ); fm_assert(!jroot.contains("s") ); fm_assert( jroot.contains("w") ); - fm_assert(jroot["n"].is_object() && jroot["n"].empty()); + fm_assert(jroot["n"].is_object() && !jroot["n"].empty()); fm_assert(jroot["w"].is_object() && !jroot["w"].empty()); - fm_assert(read_direction_metadata(jroot, Direction_::N).is_empty()); + fm_assert(!read_direction_metadata(jroot, Direction_::N).is_empty()); fm_assert(read_direction_metadata(jroot, Direction_::E).is_empty()); fm_assert(read_direction_metadata(jroot, Direction_::S).is_empty()); @@ -87,89 +72,33 @@ void test_read_groups(StringView filename) fm_assert(dir.overlay.mirrored == true ); } -struct wall_atlas_ -{ - bool operator==(const wall_atlas_&) const noexcept; - - Info header; - std::array<Direction, 4> directions = {}; - Array<Frame> frames; -}; - -[[nodiscard]] wall_atlas_ read_from_file(StringView filename, bool do_checks = true) +[[nodiscard]] wall_atlas_def read_and_check(StringView filename) { - wall_atlas_ atlas; + auto atlas = wall_atlas_def::deserialize(filename); - const auto jroot = json_helper::from_json_(filename); - atlas.header = read_info_header(jroot); + const Info header_defaults; + fm_assert(atlas.header.name != header_defaults.name); + fm_assert(atlas.header.depth != header_defaults.depth); - if (do_checks) - { - const Info header_defaults; - fm_assert(atlas.header.name != header_defaults.name); - fm_assert(atlas.header.depth != header_defaults.depth); - } + constexpr Frame frame_defaults; + constexpr Group group_defaults; - bool got_any_directions = false; - for (const auto& [_, curdir] : wall_atlas::directions) - { - auto i = (size_t)curdir; - atlas.directions[i] = read_direction_metadata(jroot, curdir); - got_any_directions = got_any_directions || atlas.directions[i]; - } - if (do_checks) - fm_assert(got_any_directions); - - atlas.frames = read_all_frames(jroot); - - if (do_checks) - { - constexpr Frame frame_defaults; - constexpr Group group_defaults; - - fm_assert(!atlas.frames.isEmpty()); - fm_assert(atlas.frames[0].offset != frame_defaults.offset); - const auto& dir = atlas.directions[(size_t)Direction_::W]; - fm_assert(dir.side.pixel_size != group_defaults.pixel_size); - fm_assert(dir.side.from_rotation == group_defaults.from_rotation); - fm_assert(dir.corner_L.from_rotation != group_defaults.from_rotation); - fm_assert(dir.corner_L.from_rotation == (uint8_t)Direction_::N); - } + fm_assert(!atlas.frames.isEmpty()); + fm_assert(atlas.frames[0].offset != frame_defaults.offset); + auto dir_index = atlas.direction_to_Direction_array_index[(size_t)Direction_::W]; + fm_assert(dir_index); + const auto& dir = atlas.direction_array[dir_index.val]; + fm_assert(dir.side.pixel_size != group_defaults.pixel_size); + fm_assert(dir.side.from_rotation == group_defaults.from_rotation); + fm_assert(dir.corner_L.from_rotation != group_defaults.from_rotation); + fm_assert(dir.corner_L.from_rotation == (uint8_t)Direction_::N); return atlas; } -void write_to_temp_file(const wall_atlas_& atlas) +void write_to_temp_file(const wall_atlas_def& atlas) { - auto jroot = json{}; - - write_info_header(jroot, atlas.header); - write_all_frames(jroot, atlas.frames); - - for (const auto [name_, dir] : wall_atlas::directions) - { - std::string_view name = {name_.data(), name_.size()}; - auto i = (size_t)dir; - if (atlas.directions[i]) - write_direction_metadata(jroot[name], atlas.directions[i]); - } - - const auto filename = temp_filename(); - json_helper::to_json_(jroot, filename); -} - -bool wall_atlas_::operator==(const wall_atlas_& other) const noexcept -{ - if (header != other.header) - return false; - if (directions != other.directions) - return false; - if (frames.size() != other.frames.size()) - return false; - for (auto i = 0uz; i < frames.size(); i++) - if (frames[i] != other.frames[i]) - return false; - return true; + atlas.serialize(temp_filename()); } } // namespace @@ -182,17 +111,15 @@ void floormat::test_app::test_wall_atlas() constexpr auto S_01_header_json = "wall-atlas-01_header.json"_s, S_02_groups_json = "wall-atlas-02_groups.json"_s; - { test_read_header(S_01_header_json); - test_read_empty_directions(S_01_header_json); - } + test_read_header(S_01_header_json); { test_read_header(S_02_groups_json); test_read_groups(S_02_groups_json); } - { auto a = read_from_file(Path::join(json_path(), S_02_groups_json)); - write_to_temp_file(a); - auto b = read_from_file(temp_filename()); + { auto a = read_and_check(Path::join(json_path(), S_02_groups_json)); + a.serialize(temp_filename()); + auto b = read_and_check(temp_filename()); fm_assert(a == b); } } |