summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2024-02-24 15:57:43 +0100
committerStanislaw Halik <sthalik@misaki.pl>2024-02-24 15:57:43 +0100
commit16f6ec061a2c6b1122974fe149b7645d8ad46e2b (patch)
tree07cede31134ef3bb1e3ae621c3ac81e23aad8e66 /src
parentfabb82b7850d9a332579b5d05c5a787ce8a24ce8 (diff)
move walkable region calculation to class chunk
Diffstat (limited to 'src')
-rw-r--r--src/chunk-region.cpp179
-rw-r--r--src/chunk-region.hpp13
-rw-r--r--src/chunk.cpp8
-rw-r--r--src/chunk.hpp24
-rw-r--r--src/object.cpp4
-rw-r--r--src/path-search.hpp7
6 files changed, 223 insertions, 12 deletions
diff --git a/src/chunk-region.cpp b/src/chunk-region.cpp
new file mode 100644
index 00000000..2c290dee
--- /dev/null
+++ b/src/chunk-region.cpp
@@ -0,0 +1,179 @@
+#include "chunk-region.hpp"
+#include "path-search-bbox.hpp"
+#include "world.hpp"
+#include <Corrade/Containers/GrowableArray.h>
+#include <Magnum/Math/Functions.h>
+
+namespace floormat {
+
+namespace {
+
+using detail_astar::bbox;
+using detail_astar::div_factor;
+using detail_astar::div_size;
+using detail_astar::div_count;
+
+static_assert(div_count.x() == div_count.y());
+static_assert((iTILE_SIZE2 % div_size).isZero());
+
+constexpr auto chunk_bits = div_count.product(),
+ visited_bits = div_count.product()*4*4;
+constexpr auto div_min = -iTILE_SIZE2/2 + div_size/2,
+ div_max = iTILE_SIZE2 * TILE_MAX_DIM - iTILE_SIZE2/2 - div_size + div_size/2;
+
+constexpr bbox<Int> bbox_from_pos1(Vector2i center)
+{
+ constexpr auto half = div_size/2;
+ auto start = center - half;
+ return { start, start + div_size };
+}
+
+constexpr bbox<Int> bbox_from_pos2(Vector2i pt, Vector2i from)
+{
+ auto bb0 = bbox_from_pos1(from);
+ auto bb = bbox_from_pos1(pt);
+ auto min = Math::min(bb0.min, bb.min);
+ auto max = Math::max(bb0.max, bb.max);
+ return { min, max };
+}
+
+constexpr bbox<Int> make_pos(Vector2i ij, Vector2i from)
+{
+ auto pos = div_min + div_size * ij;
+ auto pos0 = pos + from*div_size;
+ return bbox_from_pos2(pos, pos0);
+}
+
+bool check_pos(chunk& c, const std::array<chunk*, 8>& nbs, Vector2i ij, Vector2i from)
+{
+ auto pos = make_pos(ij, from);
+ bool ret = path_search::is_passable_(&c, nbs, Vector2(pos.min), Vector2(pos.max), 0);
+ //if (ret) Debug{} << "check" << ij << ij/div_factor << ij % div_factor << pos.min << pos.max << ret;
+ //Debug{} << "check" << ij << pos.min << pos.max << ret;
+ return ret;
+}
+
+struct node_s
+{
+ Vector2i pos;
+};
+
+struct tmp_s
+{
+ Array<node_s> stack;
+ std::bitset<visited_bits> visited;
+
+ static uint32_t get_index(Vector2i pos);
+ void append(std::bitset<chunk_bits>& passable, Vector2i pos);
+ [[nodiscard]] bool check_visited(std::bitset<chunk_bits>& passable, Vector2i pos, int from);
+ void clear();
+};
+
+uint32_t tmp_s::get_index(Vector2i pos)
+{
+ return (uint32_t)pos.y() * (uint32_t)div_count.x() + (uint32_t)pos.x();
+}
+
+void tmp_s::append(std::bitset<chunk_bits>& passable, Vector2i pos)
+{
+ auto i = get_index(pos);
+ passable[i] = true;
+ arrayAppend(stack, {pos});
+}
+
+bool tmp_s::check_visited(std::bitset<chunk_bits>& passable, Vector2i pos, int from)
+{
+ auto i = get_index(pos);
+ //fm_debug_assert(i < passable.size());
+ if (passable[i])
+ return {};
+ auto v = i*4 + (uint32_t)from;
+ //fm_debug_assert(v < visited.size());
+ if (visited[v])
+ return {};
+ visited[v] = true;
+ return true;
+}
+
+void tmp_s::clear()
+{
+ arrayResize(stack, 0);
+ visited = {};
+}
+
+tmp_s& get_tmp()
+{
+ static Pointer<tmp_s> tmp = [] {
+ Pointer<tmp_s> p{InPlace};
+ arrayReserve(p->stack, 4*div_count.product());
+ return p;
+ }();
+ arrayResize(tmp->stack, 0);
+ tmp->visited = {};
+ return *tmp;
+}
+
+} // namespace
+
+void chunk::delete_pass_region(pass_region*& ptr)
+{
+ if (ptr)
+ {
+ delete ptr;
+ ptr = nullptr;
+ }
+}
+
+auto chunk::get_pass_region() -> const pass_region*
+{
+ if (!_region_modified)
+ {
+ fm_debug_assert(_region != nullptr);
+ return _region;
+ }
+
+ if (!_region)
+ _region = new pass_region;
+ else
+ _region->bits = {};
+
+ make_pass_region(*_region);
+ return _region;
+}
+
+bool chunk::is_region_modified() const noexcept { return _region_modified; }
+void chunk::mark_region_modified() noexcept { _region_modified = true; }
+
+void chunk::make_pass_region(pass_region& ret)
+{
+ auto& tmp = get_tmp();
+ const auto nbs = _world->neighbors(_coord);
+
+ constexpr Vector2i fours[4] = { {0, 1}, {0, -1}, {1, 0}, {-1, 0} };
+ constexpr auto last = div_count - Vector2i{1};
+ //if (Vector2i pos{0, 0}; check_pos(*c, nbs, pos, fours[1])) tmp.append(pos, 1); // top
+
+ for (int i = 0; i < div_count.x(); i++)
+ {
+ if (Vector2i pos{i, last.y()}; check_pos(*this, nbs, pos, fours[0])) tmp.append(ret.bits, pos); // bottom
+ if (Vector2i pos{i, 0}; check_pos(*this, nbs, pos, fours[1])) tmp.append(ret.bits, pos); // top
+ if (Vector2i pos{last.x(), i}; check_pos(*this, nbs, pos, fours[2])) tmp.append(ret.bits, pos); // right
+ if (Vector2i pos{0, i}; check_pos(*this, nbs, pos, fours[3])) tmp.append(ret.bits, pos); // left
+ }
+
+ while (!tmp.stack.isEmpty())
+ {
+ auto p = tmp.stack.back().pos;
+ arrayRemoveSuffix(tmp.stack);
+ for (int i = 0; i < 4; i++)
+ {
+ Vector2i from = fours[i], pos{p - from};
+ if ((uint32_t)pos.x() >= div_count.x() || (uint32_t)pos.y() >= div_count.y()) [[unlikely]]
+ continue;
+ if (tmp.check_visited(ret.bits, pos, i) && check_pos(*this, nbs, pos, from))
+ tmp.append(ret.bits, pos);
+ }
+ }
+}
+
+} // namespace floormat
diff --git a/src/chunk-region.hpp b/src/chunk-region.hpp
new file mode 100644
index 00000000..5720e93e
--- /dev/null
+++ b/src/chunk-region.hpp
@@ -0,0 +1,13 @@
+#pragma once
+#include "chunk.hpp"
+#include "path-search.hpp"
+#include <bitset>
+
+namespace floormat {
+
+struct chunk::pass_region
+{
+ std::bitset<detail_astar::div_count.product()> bits;
+};
+
+} // namespace floormat
diff --git a/src/chunk.cpp b/src/chunk.cpp
index 4963a9e7..1a727599 100644
--- a/src/chunk.cpp
+++ b/src/chunk.cpp
@@ -152,6 +152,7 @@ chunk::~chunk() noexcept
arrayResize(_objects, 0);
arrayShrink(_objects);
_rtree->RemoveAll();
+ delete_pass_region(_region);
}
chunk::chunk(chunk&&) noexcept = default;
@@ -162,10 +163,12 @@ bool chunk::bbox::operator==(const bbox& other) const noexcept = default;
void chunk::add_object_unsorted(const std::shared_ptr<object>& e)
{
_objects_sorted = false;
+ _region_modified = true;
if (!e->is_dynamic())
mark_scenery_modified();
if (bbox bb; _bbox_for_scenery(*e, bb))
_add_bbox(bb);
+ arrayReserve(_objects, 8);
arrayAppend(_objects, e);
}
@@ -173,10 +176,8 @@ void chunk::sort_objects()
{
if (_objects_sorted)
return;
-
_objects_sorted = true;
mark_scenery_modified();
-
std::sort(_objects.begin(), _objects.end(), [](const auto& a, const auto& b) {
return a->id < b->id;
});
@@ -185,10 +186,12 @@ void chunk::sort_objects()
void chunk::add_object(const std::shared_ptr<object>& e)
{
fm_assert(_objects_sorted);
+ _region_modified = true;
if (!e->is_dynamic())
mark_scenery_modified();
if (bbox bb; _bbox_for_scenery(*e, bb))
_add_bbox(bb);
+ arrayReserve(_objects, 8);
auto& es = _objects;
auto it = std::lower_bound(es.cbegin(), es.cend(), e, object_id_lessp);
arrayInsert(es, (size_t)std::distance(es.cbegin(), it), e);
@@ -197,6 +200,7 @@ void chunk::add_object(const std::shared_ptr<object>& e)
void chunk::remove_object(size_t i)
{
fm_assert(_objects_sorted);
+ _region_modified = true;
auto& es = _objects;
fm_debug_assert(i < es.size());
const auto e = es[i];
diff --git a/src/chunk.hpp b/src/chunk.hpp
index 3e142abf..5404d25b 100644
--- a/src/chunk.hpp
+++ b/src/chunk.hpp
@@ -27,6 +27,7 @@ public:
friend struct tile_ref;
friend struct object;
friend class world;
+ struct pass_region;
tile_ref operator[](size_t idx) noexcept;
tile_proto operator[](size_t idx) const noexcept;
@@ -62,10 +63,12 @@ public:
void mark_walls_modified() noexcept;
void mark_scenery_modified() noexcept;
void mark_passability_modified() noexcept;
+ void mark_region_modified() noexcept;
void mark_modified() noexcept;
bool is_passability_modified() const noexcept;
bool is_scenery_modified() const noexcept;
+ bool is_region_modified() const noexcept;
struct ground_mesh_tuple final {
GL::Mesh& mesh;
@@ -116,6 +119,9 @@ public:
static constexpr size_t max_wall_quad_count =
TILE_COUNT*Wall::Direction_COUNT*(Wall::Group_COUNT+4);
+ const pass_region* get_pass_region();
+ void make_pass_region(pass_region& ret);
+
private:
struct ground_stuff
{
@@ -133,23 +139,27 @@ private:
Pointer<ground_stuff> _ground;
Pointer<wall_stuff> _walls;
+ pass_region* _region = nullptr;
Array<std::shared_ptr<object>> _objects;
class world* _world;
GL::Mesh ground_mesh{NoCreate}, wall_mesh{NoCreate}, scenery_mesh{NoCreate};
Pointer<RTree> _rtree;
chunk_coords_ _coord;
- mutable bool _maybe_empty : 1 = true,
- _ground_modified : 1 = true,
- _walls_modified : 1 = true,
- _scenery_modified : 1 = true,
- _pass_modified : 1 = true,
- _teardown : 1 = false,
- _objects_sorted : 1 = true;
+ mutable bool _maybe_empty : 1 = true,
+ _ground_modified : 1 = true,
+ _walls_modified : 1 = true,
+ _scenery_modified : 1 = true,
+ _pass_modified : 1 = true,
+ _region_modified : 1 = true,
+ _teardown : 1 = false,
+ _objects_sorted : 1 = true;
void ensure_scenery_buffers(scenery_scratch_buffers bufs);
static topo_sort_data make_topo_sort_data(object& e, uint32_t mesh_idx);
+ static void delete_pass_region(pass_region*& ptr);
+
struct bbox final // NOLINT(cppcoreguidelines-pro-type-member-init)
{
object_id id;
diff --git a/src/object.cpp b/src/object.cpp
index 52244121..34659aa5 100644
--- a/src/object.cpp
+++ b/src/object.cpp
@@ -72,6 +72,7 @@ object::~object() noexcept
if (chunk::bbox bb; c->_bbox_for_scenery(*this, bb))
c->_remove_bbox(bb);
c->_world->do_kill_object(id);
+ c->mark_region_modified();
const_cast<object_id&>(id) = 0;
}
@@ -244,6 +245,7 @@ void object::move_to(size_t& i, Vector2i delta, rotation new_r)
if (coord_.chunk() == coord.chunk())
{
c->_replace_bbox(bb0, bb1, b0, b1);
+ c->mark_region_modified();
const_cast<global_coords&>(coord) = coord_;
set_bbox_(offset_, bb_offset, bb_size, pass);
const_cast<rotation&>(r) = new_r;
@@ -255,6 +257,7 @@ void object::move_to(size_t& i, Vector2i delta, rotation new_r)
c2.mark_scenery_modified();
c2._add_bbox(bb1);
c->remove_object(i);
+ c->mark_region_modified();
auto& es = c2._objects;
auto it = std::lower_bound(es.cbegin(), es.cend(), e_, object_id_lessp);
const_cast<global_coords&>(coord) = coord_;
@@ -306,6 +309,7 @@ void object::set_bbox(Vector2b offset_, Vector2b bb_offset_, Vector2ub bb_size_,
set_bbox_(offset_, bb_offset_, bb_size_, pass);
const bool b = c->_bbox_for_scenery(*this, bb);
c->_replace_bbox(bb0, bb, b0, b);
+ c->mark_region_modified();
}
bool object::can_activate(size_t) const { return false; }
diff --git a/src/path-search.hpp b/src/path-search.hpp
index c48b5532..daf3379d 100644
--- a/src/path-search.hpp
+++ b/src/path-search.hpp
@@ -20,9 +20,10 @@ namespace floormat::detail_astar {
template<typename T> struct bbox;
struct cache;
struct chunk_cache;
-static constexpr int div_factor = 4;
-static constexpr auto div_size = iTILE_SIZE2 / div_factor;
-static constexpr auto min_size = Vector2ui(div_size * 2);
+constexpr inline int div_factor = 4;
+constexpr inline auto div_size = iTILE_SIZE2 / div_factor;
+constexpr inline auto min_size = Vector2ui(div_size * 2);
+constexpr inline auto div_count = iTILE_SIZE2 * TILE_MAX_DIM / detail_astar::div_size;
} // namespace floormat::detail_astar