summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--editor/imgui-inspect.cpp3
-rw-r--r--editor/inspect-types.cpp25
-rw-r--r--editor/update.cpp12
-rw-r--r--src/chunk-collision.cpp92
-rw-r--r--src/chunk.cpp3
-rw-r--r--src/chunk.hpp49
-rw-r--r--src/scenery.cpp12
-rw-r--r--src/scenery.hpp3
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);