#include "chunk.hpp" #include "RTree.hpp" #include "tile-atlas.hpp" #include #include namespace floormat { const chunk::RTree* chunk::rtree() const noexcept { return &_rtree; } chunk::RTree* chunk::rtree() noexcept { ensure_passability(); return &_rtree; } namespace { constexpr Vector2 tile_start(std::size_t k) { constexpr auto half_tile = Vector2(TILE_SIZE2)/2; const local_coords coord{k}; return TILE_SIZE2 * Vector2(coord) - half_tile; } Pair scenery_tile(const entity& sc) { auto center = iTILE_SIZE2 * Vector2i(sc.coord.local()) + 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 whole_tile(std::size_t k) { auto start = tile_start(k); return { start, start + TILE_SIZE2, }; } constexpr Pair wall_north(std::size_t k) { auto start = tile_start(k) - Vector2(0, 1); return { start, start + Vector2(TILE_SIZE2[0], 2), }; } constexpr Pair 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, pass_mode p, std::uint64_t id) { return std::bit_cast(collision_data { (std::uint64_t)type, (std::uint64_t)p, id }); } } // namespace void chunk::ensure_passability() noexcept { if (!_pass_modified) return; _pass_modified = false; _rtree.RemoveAll(); for (const std::shared_ptr& s : entities()) { bbox box; if (_bbox_for_scenery(*s, box)) _add_bbox(box); } for (auto i = 0_uz; i < TILE_COUNT; i++) { if (const auto* atlas = ground_atlas_at(i)) 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, p, i); _rtree.Insert(start.data(), end.data(), id); } } for (auto i = 0_uz; i < TILE_COUNT; i++) { auto tile = operator[](i); if (const auto* atlas = tile.wall_north_atlas().get()) 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, p, i); _rtree.Insert(start.data(), end.data(), id); } if (const auto* atlas = tile.wall_west_atlas().get()) 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, p, i); _rtree.Insert(start.data(), end.data(), id); } } } bool chunk::_bbox_for_scenery(const entity& s, bbox& value) noexcept { auto [start, end] = scenery_tile(s); auto id = make_id(collision_type::scenery, s.pass, s.id); value = { .id = id, .start = start, .end = end }; return s.atlas && s.pass != pass_mode::pass; } void chunk::_remove_bbox(const bbox& x) { if (_scenery_modified) return; auto start = Vector2(x.start), end = Vector2(x.end); _rtree.Remove(start.data(), end.data(), x.id); } void chunk::_add_bbox(const bbox& x) { if (_scenery_modified) return; 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); } bool chunk::can_place_entity(const entity_proto& proto, local_coords pos) { (void)ensure_scenery_mesh(); const auto center = Vector2(pos)*TILE_SIZE2 + Vector2(proto.offset) + Vector2(proto.bbox_offset), half_bbox = Vector2(proto.bbox_size)*.5f, min = center - half_bbox, max = center + half_bbox; bool ret = true; _rtree.Search(min.data(), max.data(), [&](auto, const auto&) { return ret = false; }); return ret; } } // namespace floormat