diff options
-rw-r--r-- | editor/imgui-inspect.cpp | 3 | ||||
-rw-r--r-- | editor/inspect-types.cpp | 25 | ||||
-rw-r--r-- | editor/update.cpp | 12 | ||||
-rw-r--r-- | src/chunk-collision.cpp | 92 | ||||
-rw-r--r-- | src/chunk.cpp | 3 | ||||
-rw-r--r-- | src/chunk.hpp | 49 | ||||
-rw-r--r-- | src/scenery.cpp | 12 | ||||
-rw-r--r-- | src/scenery.hpp | 3 |
8 files changed, 151 insertions, 48 deletions
diff --git a/editor/imgui-inspect.cpp b/editor/imgui-inspect.cpp index d776e032..529ef1a9 100644 --- a/editor/imgui-inspect.cpp +++ b/editor/imgui-inspect.cpp @@ -26,8 +26,7 @@ void app::draw_inspector() auto dpi = M->dpi_scale(); ImGui::SetNextWindowSize({300*dpi[0], 0}); auto b2 = begin_window("inspector"_s); - if (entities::inspect_type(s)) - c.mark_scenery_modified(); + c.with_scenery_bbox_update(s.index(), [&] { entities::inspect_type(s); }); } } } diff --git a/editor/inspect-types.cpp b/editor/inspect-types.cpp index 9a59118b..1168a88a 100644 --- a/editor/inspect-types.cpp +++ b/editor/inspect-types.cpp @@ -6,6 +6,7 @@ #include "entity/types.hpp" #include "inspect.hpp" #include "loader/loader.hpp" +#include "chunk.hpp" #include <Corrade/Containers/ArrayViewStl.h> //#define TEST_STR @@ -46,16 +47,32 @@ struct entity_accessors<scenery_ref> { }, entity::type<pass_mode>::field{"pass-mode"_s, [](const scenery_ref& x) { return x.frame.passability; }, - [](scenery_ref& x, pass_mode value) { x.frame.passability = value; } + [](scenery_ref& x, pass_mode value) { + x.chunk().with_scenery_bbox_update(x.index(), [&] { + x.frame.passability = value; + }); + }, }, entity::type<Vector2b>::field{"bbox-offset"_s, [](const scenery_ref& x) { return x.frame.bbox_offset; }, - [](scenery_ref& x, Vector2b value) { x.frame.bbox_offset = value; }, - [](const scenery_ref& x) { return x.frame.passability == pass_mode::pass ? field_status::readonly : field_status::enabled; }, + [](scenery_ref& x, Vector2b value) { + x.chunk().with_scenery_bbox_update(x.index(), [&] { + x.frame.bbox_offset = value; + }); + }, + [](const scenery_ref& x) { + return x.frame.passability == pass_mode::pass + ? field_status::readonly + : field_status::enabled; + }, }, entity::type<Vector2ub>::field{"bbox-size"_s, [](const scenery_ref& x) { return x.frame.bbox_size; }, - [](scenery_ref& x, Vector2ub value) { x.frame.bbox_size = value; }, + [](scenery_ref& x, Vector2ub value) { + x.chunk().with_scenery_bbox_update(x.index(), [&] { + x.frame.bbox_size = value; + }); + }, [](const scenery_ref& x) { return x.frame.passability == pass_mode::pass ? field_status::readonly : field_status::enabled; }, }, entity::type<bool>::field{"interactive"_s, diff --git a/editor/update.cpp b/editor/update.cpp index f9afa6c9..a907117a 100644 --- a/editor/update.cpp +++ b/editor/update.cpp @@ -165,17 +165,7 @@ void app::update_world(float dt) for (std::int16_t x = minx; x <= maxx; x++) for (auto& c = world[chunk_coords{x, y}]; auto [x, k, pt] : c) if (auto sc = x.scenery()) - { - auto [atlas, s] = x.scenery(); - auto pass0 = s.passability; - auto offset0 = s.offset; - auto bb_offset0 = s.bbox_offset; - auto bb_size0 = s.bbox_size; - sc.update(dt); - if (pass0 != s.passability || offset0 != s.offset || - bb_offset0 != s.bbox_offset || bb_size0 != s.bbox_size) - c.mark_scenery_modified(); - } + c.with_scenery_bbox_update(sc.index(), [&] { sc.update(dt); }); } void app::set_cursor() diff --git a/src/chunk-collision.cpp b/src/chunk-collision.cpp index 5b85de1e..bd761b03 100644 --- a/src/chunk-collision.cpp +++ b/src/chunk-collision.cpp @@ -18,36 +18,36 @@ constexpr Vector2 tile_start(std::size_t k) return TILE_SIZE2 * Vector2(coord) - half_tile; } -constexpr Pair<Vector2, Vector2> scenery_tile(std::size_t k, const scenery& sc) +constexpr Pair<Vector2i, Vector2i> scenery_tile(std::size_t k, const scenery& sc) { const local_coords coord{k}; - auto center = TILE_SIZE2 * Vector2(coord) + Vector2(sc.offset) + Vector2(sc.bbox_offset); - auto start = center - Vector2(sc.bbox_size); - auto size = Vector2(sc.bbox_size)*2; + auto center = iTILE_SIZE2 * Vector2i(coord) + Vector2i(sc.offset) + Vector2i(sc.bbox_offset); + auto start = center - Vector2i(sc.bbox_size); + auto size = Vector2i(sc.bbox_size)*2; return { start, start + size, }; -}; +} constexpr Pair<Vector2, Vector2> whole_tile(std::size_t k) { auto start = tile_start(k); return { start, start + TILE_SIZE2, }; -}; +} constexpr Pair<Vector2, Vector2> wall_north(std::size_t k) { auto start = tile_start(k) - Vector2(0, 1); return { start, start + Vector2(TILE_SIZE2[0], 2), }; -}; +} constexpr Pair<Vector2, Vector2> wall_west(std::size_t k) { auto start = tile_start(k) - Vector2(1, 0); return { start, start + Vector2(2, TILE_SIZE2[1]), }; -}; +} -constexpr std::uint64_t make_id(collision_type type, std::uint64_t id) +constexpr std::uint64_t make_id(collision_type type, pass_mode p, std::uint64_t id) { - return std::bit_cast<std::uint64_t>(collision_data { (std::uint64_t)type, id }); + return std::bit_cast<std::uint64_t>(collision_data { (std::uint64_t)type, (std::uint64_t)p, id }); } } // namespace @@ -62,22 +62,20 @@ void chunk::ensure_passability() noexcept for (auto i = 0_uz; i < TILE_COUNT; i++) { - auto tile = operator[](i); - if (auto s = tile.scenery()) - if (s.frame.passability != pass_mode::pass && Vector2ui(s.frame.bbox_size).product() > 0) - { - auto [start, end] = scenery_tile(i, s.frame); - auto id = make_id(collision_type::scenery, i); - _rtree.Insert(start.data(), end.data(), id); - } + if (auto s = operator[](i).scenery()) + { + bbox box; + if (_bbox_for_scenery(i, box)) + _add_bbox(box); + } } for (auto i = 0_uz; i < TILE_COUNT; i++) { if (const auto* atlas = ground_atlas_at(i)) - if (atlas->pass_mode(pass_mode::pass) != pass_mode::pass) + if (auto p = atlas->pass_mode(pass_mode::pass); p != pass_mode::pass) { auto [start, end] = whole_tile(i); - auto id = make_id(collision_type::geometry, i); + auto id = make_id(collision_type::geometry, p, i); _rtree.Insert(start.data(), end.data(), id); } } @@ -85,20 +83,66 @@ void chunk::ensure_passability() noexcept { auto tile = operator[](i); if (const auto* atlas = tile.wall_north_atlas().get()) - if (atlas->pass_mode(pass_mode::blocked) != pass_mode::pass) + if (auto p = atlas->pass_mode(pass_mode::blocked); p != pass_mode::pass) { auto [start, end] = wall_north(i); - auto id = make_id(collision_type::geometry, i); + auto id = make_id(collision_type::geometry, p, i); _rtree.Insert(start.data(), end.data(), id); } if (const auto* atlas = tile.wall_west_atlas().get()) - if (atlas->pass_mode(pass_mode::blocked) != pass_mode::pass) + if (auto p = atlas->pass_mode(pass_mode::blocked); p != pass_mode::pass) { auto [start, end] = wall_west(i); - auto id = make_id(collision_type::geometry, i); + auto id = make_id(collision_type::geometry, p, i); _rtree.Insert(start.data(), end.data(), id); } } } +bool chunk::_bbox_for_scenery(std::size_t i, bbox& value) noexcept +{ + fm_debug_assert(i < TILE_COUNT); + auto [atlas, s] = operator[](i).scenery(); + auto [start, end] = scenery_tile(i, s); + auto id = make_id(collision_type::scenery, s.passability, i); + value = { .id = id, .start = start, .end = end }; + return atlas && s.passability != pass_mode::pass && Vector2i(s.bbox_size).product() > 0; +} + +void chunk::_remove_bbox(const bbox& x) +{ + auto start = Vector2(x.start), end = Vector2(x.end); + fm_assert(_rtree.Remove(start.data(), end.data(), x.id)); +} + +void chunk::_add_bbox(const bbox& x) +{ + auto start = Vector2(x.start), end = Vector2(x.end); + _rtree.Insert(start.data(), end.data(), x.id); +} + +void chunk::_replace_bbox(const bbox& x0, const bbox& x1, bool b0, bool b1) +{ + unsigned i = (unsigned)b1 << 1 | (unsigned)(b0 ? 1 : 0) << 0; + CORRADE_ASSUME(i < 4u); + + switch (i) // NOLINT(hicpp-multiway-paths-covered) + { + case 1 << 1 | 1 << 0: + if (x1 == x0) + return; + _remove_bbox(x0); + [[fallthrough]]; + case 1 << 1 | 0 << 0: + _add_bbox(x1); + return; + case 0 << 1 | 1 << 0: + _remove_bbox(x0); + return; + case 0 << 1 | 0 << 0: + return; + } + CORRADE_ASSUME(false); +} + } // namespace floormat diff --git a/src/chunk.cpp b/src/chunk.cpp index b7f54e4d..8310c756 100644 --- a/src/chunk.cpp +++ b/src/chunk.cpp @@ -34,6 +34,7 @@ auto chunk::end() const noexcept -> const_iterator { return cend(); } void chunk::mark_ground_modified() noexcept { _ground_modified = true; _pass_modified = true; } void chunk::mark_walls_modified() noexcept { _walls_modified = true; _pass_modified = true; } void chunk::mark_scenery_modified() noexcept { _scenery_modified = true; _pass_modified = true; } +bool chunk::is_passability_modified() const noexcept { return _pass_modified; } void chunk::mark_modified() noexcept { @@ -48,4 +49,6 @@ chunk::~chunk() noexcept = default; chunk::chunk(chunk&&) noexcept = default; chunk& chunk::operator=(chunk&&) noexcept = default; +bool chunk::bbox::operator==(const bbox& other) const noexcept = default; + } // namespace floormat diff --git a/src/chunk.hpp b/src/chunk.hpp index 8d6f5a6b..54f59e2d 100644 --- a/src/chunk.hpp +++ b/src/chunk.hpp @@ -2,6 +2,7 @@ #include "tile.hpp" #include "tile-iterator.hpp" #include "scenery.hpp" +#include <concepts> #include <type_traits> #include <array> #include <memory> @@ -22,7 +23,8 @@ enum class collision_type : std::uint8_t { struct collision_data final { std::uint64_t tag : 2; - std::uint64_t data : 62; + std::uint64_t pass : 2; + std::uint64_t data : 60; }; struct chunk final @@ -56,6 +58,7 @@ struct chunk final void mark_ground_modified() noexcept; void mark_walls_modified() noexcept; void mark_scenery_modified() noexcept; + bool is_passability_modified() const noexcept; void mark_modified() noexcept; struct ground_mesh_tuple final { @@ -92,6 +95,9 @@ struct chunk final const RTree* rtree() const noexcept; RTree* rtree() noexcept; + template<std::invocable<tile_ref&> F> void with_scenery_bbox_update(tile_ref t, F&& fun); + template<std::invocable<> F> void with_scenery_bbox_update(std::size_t i, F&& fun); + private: std::array<std::shared_ptr<tile_atlas>, TILE_COUNT> _ground_atlases; std::array<std::uint8_t, TILE_COUNT> ground_indexes = {}; @@ -112,6 +118,47 @@ private: _walls_modified : 1 = true, _scenery_modified : 1 = true, _pass_modified : 1 = true; + + struct bbox final // NOLINT(cppcoreguidelines-pro-type-member-init) + { + std::uint64_t id; + Vector2i start, end; + + bool operator==(const bbox& other) const noexcept; + }; + bool _bbox_for_scenery(std::size_t i, bbox& value) noexcept; + void _remove_bbox(const bbox& x); + void _add_bbox(const bbox& x); + void _replace_bbox(const bbox& x0, const bbox& x, bool b0, bool b); }; +template<std::invocable<tile_ref&> F> +void chunk::with_scenery_bbox_update(tile_ref t, F&& fun) +{ + if (is_passability_modified()) + return fun(t); + else + { + bbox x0, x; + std::size_t i = t.index(); + bool b0 = _bbox_for_scenery(i, x0); + fun(t); + _replace_bbox(x0, x, b0, _bbox_for_scenery(i, x)); + } +} + +template<std::invocable<> F> +void chunk::with_scenery_bbox_update(std::size_t i, F&& fun) +{ + if (is_passability_modified()) + return fun(); + else + { + bbox x0, x; + bool b0 = _bbox_for_scenery(i, x0); + fun(); + _replace_bbox(x0, x, b0, _bbox_for_scenery(i, x)); + } +} + } // namespace floormat diff --git a/src/scenery.cpp b/src/scenery.cpp index 86ebea73..f21f95c2 100644 --- a/src/scenery.cpp +++ b/src/scenery.cpp @@ -66,13 +66,15 @@ scenery::scenery(door_tag_t, const anim_atlas& atlas, rotation r, bool is_open, void scenery_ref::rotate(rotation new_r) { - auto& s = frame; - s.bbox_offset = rotate_point(s.bbox_offset, s.r, new_r); - s.bbox_size = rotate_size(s.bbox_size, s.r, new_r); - s.r = new_r; + c->with_scenery_bbox_update(idx, [&] { + auto& s = frame; + s.bbox_offset = rotate_point(s.bbox_offset, s.r, new_r); + s.bbox_size = rotate_size(s.bbox_size, s.r, new_r); + s.r = new_r; + }); } -bool scenery_ref::can_activate() noexcept +bool scenery_ref::can_activate() const noexcept { return frame.interactive; } diff --git a/src/scenery.hpp b/src/scenery.hpp index 7d2cb502..3a8fb241 100644 --- a/src/scenery.hpp +++ b/src/scenery.hpp @@ -81,6 +81,7 @@ struct scenery_ref final { scenery_ref(struct chunk& c, std::size_t i) noexcept; scenery_ref(const scenery_ref&) noexcept; scenery_ref(scenery_ref&&) noexcept; + scenery_ref& operator=(const scenery_ref&) = delete; scenery_ref& operator=(const scenery_proto& proto) noexcept; operator scenery_proto() const noexcept; @@ -95,7 +96,7 @@ struct scenery_ref final { std::shared_ptr<anim_atlas>& atlas; scenery& frame; - bool can_activate() noexcept; + bool can_activate() const noexcept; bool activate(); void update(float dt); void rotate(rotation r); |