summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--compat/LooseQuadtree-impl.h137
-rw-r--r--compat/LooseQuadtree.cpp140
-rw-r--r--compat/LooseQuadtree.h6
-rw-r--r--serialize/world-impl.hpp12
-rw-r--r--serialize/world-reader.cpp4
-rw-r--r--serialize/world-writer.cpp4
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/chunk-render.cpp107
-rw-r--r--src/chunk.cpp183
-rw-r--r--src/chunk.hpp22
-rw-r--r--src/pass-mode.hpp5
-rw-r--r--src/scenery.hpp2
-rw-r--r--src/tile-atlas.cpp1
-rw-r--r--src/tile-atlas.hpp1
-rw-r--r--src/tile.cpp34
-rw-r--r--src/tile.hpp17
-rw-r--r--test/app.hpp5
-rw-r--r--test/bbox.cpp28
-rw-r--r--test/main.cpp1
-rw-r--r--test/serializer.cpp2
20 files changed, 394 insertions, 319 deletions
diff --git a/compat/LooseQuadtree-impl.h b/compat/LooseQuadtree-impl.h
index 3ad64033..4d37bcd0 100644
--- a/compat/LooseQuadtree-impl.h
+++ b/compat/LooseQuadtree-impl.h
@@ -113,138 +113,6 @@ struct BlocksAllocatorAdaptor {
};
-BlocksAllocator::BlocksAllocator() {}
-
-
-BlocksAllocator::~BlocksAllocator() {
- for (auto& size_block_pair : size_to_blocks_) {
- BlocksHead& blocks_head = size_block_pair.second;
- for (auto& address_empty_pair : blocks_head.address_to_empty_slot_number) {
- assert(address_empty_pair.second == blocks_head.slots_in_a_block_);
- Block* block = address_empty_pair.first;
- delete block;
- }
- }
-}
-
-
-void* BlocksAllocator::Allocate(std::size_t object_size) {
-#ifdef LQT_USE_OWN_ALLOCATOR
- if (object_size < sizeof(void*)) object_size = sizeof(void*);
- assert(object_size <= kMaxAllowedAlloc);
- auto size_blocks_it = size_to_blocks_.find(object_size);
- if (size_blocks_it == size_to_blocks_.end()) {
- size_blocks_it = size_to_blocks_.emplace(object_size, object_size).first;
- }
- BlocksHead& blocks_head = size_blocks_it->second;
- assert(blocks_head.slots_in_a_block_ == kBlockSize / object_size);
- if (blocks_head.first_empty_slot == nullptr) {
- Block* new_block = new Block();
- std::size_t empties = blocks_head.slots_in_a_block_;
- blocks_head.address_to_empty_slot_number.emplace(new_block, empties);
- blocks_head.first_empty_slot = reinterpret_cast<void*>(new_block);
- void* current_slot = reinterpret_cast<void*>(new_block);
- empties--;
- while (empties > 0) {
- void* next_slot =
- reinterpret_cast<void*>(reinterpret_cast<char*>(current_slot)
- + object_size);
- *reinterpret_cast<void**>(current_slot) = next_slot;
- current_slot = next_slot;
- empties--;
- }
- *reinterpret_cast<void**>(current_slot) = nullptr;
- }
- assert(blocks_head.first_empty_slot != nullptr);
- void* slot = blocks_head.first_empty_slot;
- blocks_head.first_empty_slot = *reinterpret_cast<void**>(slot);
- auto address_empties_it =
- blocks_head.address_to_empty_slot_number.upper_bound(reinterpret_cast<Block*>(slot));
- assert(address_empties_it != blocks_head.address_to_empty_slot_number.begin());
- address_empties_it--;
- assert(address_empties_it->first <= reinterpret_cast<Block*>(slot) &&
- (std::size_t)(reinterpret_cast<char*>(slot) -
- reinterpret_cast<char*>(address_empties_it->first)) < kBlockSize);
- assert((std::size_t)(reinterpret_cast<char*>(slot) -
- reinterpret_cast<char*>(address_empties_it->first)) % object_size == 0);
- assert(address_empties_it->second > 0 &&
- address_empties_it->second <= blocks_head.slots_in_a_block_);
- address_empties_it->second--;
- return slot;
-#else
- return reinterpret_cast<void*>(new char[object_size]);
-#endif
-}
-
-
-void BlocksAllocator::Deallocate(void* p, std::size_t object_size) {
-#ifdef LQT_USE_OWN_ALLOCATOR
- if (object_size < sizeof(void*)) object_size = sizeof(void*);
- assert(object_size <= kMaxAllowedAlloc);
- auto size_blocks_it = size_to_blocks_.find(object_size);
- assert(size_blocks_it != size_to_blocks_.end());
- BlocksHead& blocks_head = size_blocks_it->second;
- assert(blocks_head.slots_in_a_block_ == kBlockSize / object_size);
- auto address_empties_it =
- blocks_head.address_to_empty_slot_number.upper_bound(reinterpret_cast<Block*>(p));
- assert(address_empties_it != blocks_head.address_to_empty_slot_number.begin());
- address_empties_it--;
- assert(address_empties_it->first <= reinterpret_cast<Block*>(p) &&
- (std::size_t)(reinterpret_cast<char*>(p) -
- reinterpret_cast<char*>(address_empties_it->first)) < kBlockSize);
- assert((std::size_t)(reinterpret_cast<char*>(p) -
- reinterpret_cast<char*>(address_empties_it->first)) % object_size == 0);
- assert(address_empties_it->second < blocks_head.slots_in_a_block_);
- void* slot = p;
- *reinterpret_cast<void**>(slot) = blocks_head.first_empty_slot;
- blocks_head.first_empty_slot = slot;
- address_empties_it->second++;
- assert(address_empties_it->second > 0 &&
- address_empties_it->second <= blocks_head.slots_in_a_block_);
-#else
- (void)object_size;
- delete[] reinterpret_cast<char*>(p);
-#endif
-}
-
-
-void BlocksAllocator::ReleaseFreeBlocks() {
- for (auto& size_block_pair : size_to_blocks_) {
- BlocksHead& blocks_head = size_block_pair.second;
- void** current = &blocks_head.first_empty_slot;
- while (*current != nullptr) {
- auto address_empties_it =
- blocks_head.address_to_empty_slot_number.upper_bound(reinterpret_cast<Block*>(*current));
- assert(address_empties_it != blocks_head.address_to_empty_slot_number.begin());
- address_empties_it--;
- assert(address_empties_it->first <= reinterpret_cast<Block*>(*current) &&
- (std::size_t)(reinterpret_cast<char*>(*current) -
- reinterpret_cast<char*>(address_empties_it->first)) < kBlockSize);
- assert((std::size_t)(reinterpret_cast<char*>(*current) -
- reinterpret_cast<char*>(address_empties_it->first)) % size_block_pair.first == 0);
- assert(address_empties_it->second > 0 &&
- address_empties_it->second <= blocks_head.slots_in_a_block_);
- if (address_empties_it->second >= blocks_head.slots_in_a_block_) {
- *current = **(void***)current;
- }
- else {
- current = *(void***)current;
- }
- }
- auto address_empties_it = blocks_head.address_to_empty_slot_number.begin();
- while (address_empties_it != blocks_head.address_to_empty_slot_number.end()) {
- if (address_empties_it->second >= blocks_head.slots_in_a_block_) {
- auto prev_address_empties_it = address_empties_it;
- address_empties_it++;
- blocks_head.address_to_empty_slot_number.erase(prev_address_empties_it);
- }
- else {
- address_empties_it++;
- }
- }
- }
-}
-
template <typename T, typename... Args>
T* BlocksAllocator::New(Args&&... args) {
@@ -494,9 +362,6 @@ private:
-
-
-
template <typename NumberT>
bool BoundingBox<NumberT>::Intersects(const BoundingBox<Number>& other) const {
return !(
@@ -520,7 +385,7 @@ bool BoundingBox<NumberT>::Contains(Number x, Number y) const {
template <typename NumberT, typename ObjectT>
- detail::ForwardTreeTraversal<NumberT, ObjectT>::ForwardTreeTraversal() :
+detail::ForwardTreeTraversal<NumberT, ObjectT>::ForwardTreeTraversal() :
position_(BoundingBox<Number>(0, 0, 0, 0), nullptr), depth_(0) {
}
diff --git a/compat/LooseQuadtree.cpp b/compat/LooseQuadtree.cpp
new file mode 100644
index 00000000..7017b0ac
--- /dev/null
+++ b/compat/LooseQuadtree.cpp
@@ -0,0 +1,140 @@
+#include "LooseQuadtree-impl.h"
+#include "compat/assert.hpp"
+#undef assert
+#define assert fm_assert
+
+namespace loose_quadtree::detail {
+
+BlocksAllocator::BlocksAllocator() {}
+
+
+BlocksAllocator::~BlocksAllocator() {
+ for (auto& size_block_pair : size_to_blocks_) {
+ BlocksHead& blocks_head = size_block_pair.second;
+ for (auto& address_empty_pair : blocks_head.address_to_empty_slot_number) {
+ assert(address_empty_pair.second == blocks_head.slots_in_a_block_);
+ Block* block = address_empty_pair.first;
+ delete block;
+ }
+ }
+}
+
+
+void* BlocksAllocator::Allocate(std::size_t object_size) {
+#ifdef LQT_USE_OWN_ALLOCATOR
+ if (object_size < sizeof(void*)) object_size = sizeof(void*);
+ assert(object_size <= kMaxAllowedAlloc);
+ auto size_blocks_it = size_to_blocks_.find(object_size);
+ if (size_blocks_it == size_to_blocks_.end()) {
+ size_blocks_it = size_to_blocks_.emplace(object_size, object_size).first;
+ }
+ BlocksHead& blocks_head = size_blocks_it->second;
+ assert(blocks_head.slots_in_a_block_ == kBlockSize / object_size);
+ if (blocks_head.first_empty_slot == nullptr) {
+ Block* new_block = new Block();
+ std::size_t empties = blocks_head.slots_in_a_block_;
+ blocks_head.address_to_empty_slot_number.emplace(new_block, empties);
+ blocks_head.first_empty_slot = reinterpret_cast<void*>(new_block);
+ void* current_slot = reinterpret_cast<void*>(new_block);
+ empties--;
+ while (empties > 0) {
+ void* next_slot =
+ reinterpret_cast<void*>(reinterpret_cast<char*>(current_slot)
+ + object_size);
+ *reinterpret_cast<void**>(current_slot) = next_slot;
+ current_slot = next_slot;
+ empties--;
+ }
+ *reinterpret_cast<void**>(current_slot) = nullptr;
+ }
+ assert(blocks_head.first_empty_slot != nullptr);
+ void* slot = blocks_head.first_empty_slot;
+ blocks_head.first_empty_slot = *reinterpret_cast<void**>(slot);
+ auto address_empties_it =
+ blocks_head.address_to_empty_slot_number.upper_bound(reinterpret_cast<Block*>(slot));
+ assert(address_empties_it != blocks_head.address_to_empty_slot_number.begin());
+ address_empties_it--;
+ assert(address_empties_it->first <= reinterpret_cast<Block*>(slot) &&
+ (std::size_t)(reinterpret_cast<char*>(slot) -
+ reinterpret_cast<char*>(address_empties_it->first)) < kBlockSize);
+ assert((std::size_t)(reinterpret_cast<char*>(slot) -
+ reinterpret_cast<char*>(address_empties_it->first)) % object_size == 0);
+ assert(address_empties_it->second > 0 &&
+ address_empties_it->second <= blocks_head.slots_in_a_block_);
+ address_empties_it->second--;
+ return slot;
+#else
+ return reinterpret_cast<void*>(new char[object_size]);
+#endif
+}
+
+
+void BlocksAllocator::Deallocate(void* p, std::size_t object_size) {
+#ifdef LQT_USE_OWN_ALLOCATOR
+ if (object_size < sizeof(void*)) object_size = sizeof(void*);
+ assert(object_size <= kMaxAllowedAlloc);
+ auto size_blocks_it = size_to_blocks_.find(object_size);
+ assert(size_blocks_it != size_to_blocks_.end());
+ BlocksHead& blocks_head = size_blocks_it->second;
+ assert(blocks_head.slots_in_a_block_ == kBlockSize / object_size);
+ auto address_empties_it =
+ blocks_head.address_to_empty_slot_number.upper_bound(reinterpret_cast<Block*>(p));
+ assert(address_empties_it != blocks_head.address_to_empty_slot_number.begin());
+ address_empties_it--;
+ assert(address_empties_it->first <= reinterpret_cast<Block*>(p) &&
+ (std::size_t)(reinterpret_cast<char*>(p) -
+ reinterpret_cast<char*>(address_empties_it->first)) < kBlockSize);
+ assert((std::size_t)(reinterpret_cast<char*>(p) -
+ reinterpret_cast<char*>(address_empties_it->first)) % object_size == 0);
+ assert(address_empties_it->second < blocks_head.slots_in_a_block_);
+ void* slot = p;
+ *reinterpret_cast<void**>(slot) = blocks_head.first_empty_slot;
+ blocks_head.first_empty_slot = slot;
+ address_empties_it->second++;
+ assert(address_empties_it->second > 0 &&
+ address_empties_it->second <= blocks_head.slots_in_a_block_);
+#else
+ (void)object_size;
+ delete[] reinterpret_cast<char*>(p);
+#endif
+}
+
+
+void BlocksAllocator::ReleaseFreeBlocks() {
+ for (auto& size_block_pair : size_to_blocks_) {
+ BlocksHead& blocks_head = size_block_pair.second;
+ void** current = &blocks_head.first_empty_slot;
+ while (*current != nullptr) {
+ auto address_empties_it =
+ blocks_head.address_to_empty_slot_number.upper_bound(reinterpret_cast<Block*>(*current));
+ assert(address_empties_it != blocks_head.address_to_empty_slot_number.begin());
+ address_empties_it--;
+ assert(address_empties_it->first <= reinterpret_cast<Block*>(*current) &&
+ (std::size_t)(reinterpret_cast<char*>(*current) -
+ reinterpret_cast<char*>(address_empties_it->first)) < kBlockSize);
+ assert((std::size_t)(reinterpret_cast<char*>(*current) -
+ reinterpret_cast<char*>(address_empties_it->first)) % size_block_pair.first == 0);
+ assert(address_empties_it->second > 0 &&
+ address_empties_it->second <= blocks_head.slots_in_a_block_);
+ if (address_empties_it->second >= blocks_head.slots_in_a_block_) {
+ *current = **(void***)current;
+ }
+ else {
+ current = *(void***)current;
+ }
+ }
+ auto address_empties_it = blocks_head.address_to_empty_slot_number.begin();
+ while (address_empties_it != blocks_head.address_to_empty_slot_number.end()) {
+ if (address_empties_it->second >= blocks_head.slots_in_a_block_) {
+ auto prev_address_empties_it = address_empties_it;
+ address_empties_it++;
+ blocks_head.address_to_empty_slot_number.erase(prev_address_empties_it);
+ }
+ else {
+ address_empties_it++;
+ }
+ }
+ }
+}
+
+} // namespace loose_quadtree::detail
diff --git a/compat/LooseQuadtree.h b/compat/LooseQuadtree.h
index 3f52457a..a8544346 100644
--- a/compat/LooseQuadtree.h
+++ b/compat/LooseQuadtree.h
@@ -38,11 +38,13 @@ template <typename NumberT>
struct BoundingBox {
using Number = NumberT;
- BoundingBox(Number _left, Number _top, Number _width, Number _height) :
+ constexpr BoundingBox(Number _left, Number _top, Number _width, Number _height) :
left(_left), top(_top), width(_width), height(_height) {}
bool Intersects(const BoundingBox<Number>& other) const ;
bool Contains(const BoundingBox<Number>& other) const;
bool Contains(Number x, Number y) const;
+ BoundingBox& operator=(const BoundingBox&) noexcept = default;
+ BoundingBox(const BoundingBox&) noexcept = default;
Number left;
Number top;
@@ -107,6 +109,4 @@ private:
Impl impl_;
};
-
-
} //loose_quadtree
diff --git a/serialize/world-impl.hpp b/serialize/world-impl.hpp
index 99db4cc0..e74ebda2 100644
--- a/serialize/world-impl.hpp
+++ b/serialize/world-impl.hpp
@@ -54,12 +54,12 @@ constexpr inline atlasid scenery_id_max = int_max<atlasid> & ~scenery_id_flag_ma
} // namespace
enum : tilemeta {
- meta_ground = 1 << (pass_bits + 0),
- meta_wall_n = 1 << (pass_bits + 1),
- meta_wall_w = 1 << (pass_bits + 2),
- meta_short_atlasid = 1 << (pass_bits + 3),
- meta_short_variant_ = 1 << (pass_bits + 4),
- meta_scenery = 1 << (pass_bits + 5),
+ meta_ground = 1 << 2,
+ meta_wall_n = 1 << 3,
+ meta_wall_w = 1 << 4,
+ meta_short_atlasid = 1 << 5,
+ meta_short_variant_ = 1 << 6,
+ meta_scenery = 1 << 7,
};
} // namespace floormat::Serialize
diff --git a/serialize/world-reader.cpp b/serialize/world-reader.cpp
index 98704599..f5459203 100644
--- a/serialize/world-reader.cpp
+++ b/serialize/world-reader.cpp
@@ -149,7 +149,7 @@ void reader_state::read_chunks(reader_t& s)
return { atlas, v };
};
- t.passability() = pass_mode(flags & pass_mask);
+ //t.passability() = pass_mode(flags & pass_mask);
if (flags & meta_ground)
t.ground() = make_atlas();
if (flags & meta_wall_n)
@@ -178,6 +178,7 @@ void reader_state::read_chunks(reader_t& s)
t.scenery() = sc;
}
+#if 0
switch (auto x = pass_mode(flags & pass_mask))
{
case pass_mode::shoot_through:
@@ -188,6 +189,7 @@ void reader_state::read_chunks(reader_t& s)
default: [[unlikely]]
fm_throw("bad pass mode '{}' for tile {}"_cf, i, pass_mode_(x));
}
+#endif
}
}
}
diff --git a/serialize/world-writer.cpp b/serialize/world-writer.cpp
index 0ca84e7b..21fba2b3 100644
--- a/serialize/world-writer.cpp
+++ b/serialize/world-writer.cpp
@@ -319,8 +319,8 @@ void writer_state::serialize_chunk(const chunk& c, chunk_coords coord)
if (flags != 0 && ashortp(img_g) && ashortp(img_n) && ashortp(img_w))
flags |= meta_short_atlasid;
- fm_debug_assert((pass_mode_(x.passability) & pass_mask) == pass_mode_(x.passability));
- flags |= pass_mode_(x.passability);
+ //fm_debug_assert((pass_mode_(x.passability) & pass_mask) == pass_mode_(x.passability));
+ //flags |= pass_mode_(x.passability);
s << flags;
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index d30bf88d..b7ef8bd6 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,5 +1,5 @@
set(self floormat)
-file(GLOB sources "*.cpp" "../shaders/*.cpp" CONFIGURE_ARGS)
+file(GLOB sources *.cpp ../shaders/*.cpp ../compat/*.cpp CONFIGURE_ARGS)
add_library(${self} OBJECT "${sources}")
target_link_libraries(
${self} PUBLIC
diff --git a/src/chunk-render.cpp b/src/chunk-render.cpp
new file mode 100644
index 00000000..37440af9
--- /dev/null
+++ b/src/chunk-render.cpp
@@ -0,0 +1,107 @@
+#include "chunk.hpp"
+#include "tile-atlas.hpp"
+#include "shaders/tile.hpp"
+#include <algorithm>
+#include <Magnum/GL/Buffer.h>
+#include <Corrade/Containers/ArrayViewStl.h>
+
+namespace floormat {
+
+static auto make_index_array(std::size_t offset)
+{
+ std::array<std::array<UnsignedShort, 6>, TILE_COUNT> array; // NOLINT(cppcoreguidelines-pro-type-member-init)
+ for (std::size_t i = 0; i < TILE_COUNT; i++)
+ array[i] = tile_atlas::indices(i + offset);
+ return array;
+}
+
+struct vertex {
+ Vector3 position;
+ Vector2 texcoords;
+ float depth = -1;
+};
+
+auto chunk::ensure_ground_mesh() noexcept -> ground_mesh_tuple
+{
+ if (!_ground_modified)
+ return { ground_mesh, ground_indexes };
+ _ground_modified = false;
+
+ for (std::size_t i = 0; i < TILE_COUNT; i++)
+ ground_indexes[i] = std::uint8_t(i);
+ std::sort(ground_indexes.begin(), ground_indexes.end(), [this](std::uint8_t a, std::uint8_t b) {
+ return _ground_atlases[a].get() < _ground_atlases[b].get();
+ });
+
+ std::array<std::array<vertex, 4>, TILE_COUNT> vertexes;
+ for (std::size_t k = 0; k < TILE_COUNT; k++)
+ {
+ const std::uint8_t i = ground_indexes[k];
+ if (auto atlas = _ground_atlases[i]; !atlas)
+ vertexes[k] = {};
+ else
+ {
+ const local_coords pos{i};
+ const auto quad = atlas->floor_quad(Vector3(pos.x, pos.y, 0) * TILE_SIZE, TILE_SIZE2);
+ const auto texcoords = atlas->texcoords_for_id(_ground_variants[i]);
+ const float depth = tile_shader::depth_value(pos);
+ auto& v = vertexes[k];
+ for (std::size_t j = 0; j < 4; j++)
+ v[j] = { quad[j], texcoords[j], depth };
+ }
+ }
+ const auto indexes = make_index_array(0);
+
+ GL::Mesh mesh{GL::MeshPrimitive::Triangles};
+ mesh.addVertexBuffer(GL::Buffer{vertexes}, 0, tile_shader::Position{}, tile_shader::TextureCoordinates{}, tile_shader::Depth{})
+ .setIndexBuffer(GL::Buffer{indexes}, 0, GL::MeshIndexType::UnsignedShort)
+ .setCount(6 * TILE_COUNT);
+ ground_mesh = Utility::move(mesh);
+ return { ground_mesh, ground_indexes };
+}
+
+auto chunk::ensure_wall_mesh() noexcept -> wall_mesh_tuple
+{
+ if (!_walls_modified)
+ return { wall_mesh, wall_indexes };
+ _walls_modified = false;
+
+ for (std::size_t i = 0; i < TILE_COUNT*2; i++)
+ wall_indexes[i] = std::uint16_t(i);
+
+ std::sort(wall_indexes.begin(), wall_indexes.end(), [this](std::uint16_t a, std::uint16_t b) {
+ return _wall_atlases[a] < _wall_atlases[b];
+ });
+
+ std::array<std::array<vertex, 4>, TILE_COUNT*2> vertexes;
+ for (std::size_t k = 0; k < TILE_COUNT*2; k++)
+ {
+ const std::uint16_t i = wall_indexes[k];
+ if (const auto& atlas = _wall_atlases[i]; !atlas)
+ vertexes[k] = {};
+ else
+ {
+ const auto& variant = _wall_variants[i];
+ const local_coords pos{i / 2u};
+ const auto center = Vector3(pos.x, pos.y, 0) * TILE_SIZE;
+ const auto quad = i & 1 ? atlas->wall_quad_W(center, TILE_SIZE) : atlas->wall_quad_N(center, TILE_SIZE);
+ const float depth = tile_shader::depth_value(pos);
+ const auto texcoords = atlas->texcoords_for_id(variant);
+ auto& v = vertexes[k];
+ for (std::size_t j = 0; j < 4; j++)
+ v[j] = { quad[j], texcoords[j], depth, };
+ }
+ }
+
+ using index_t = std::array<std::array<UnsignedShort, 6>, TILE_COUNT>;
+ const index_t indexes[2] = { make_index_array(0), make_index_array(TILE_COUNT) };
+
+ GL::Mesh mesh{GL::MeshPrimitive::Triangles};
+ mesh.addVertexBuffer(GL::Buffer{vertexes}, 0, tile_shader::Position{}, tile_shader::TextureCoordinates{}, tile_shader::Depth{})
+ .setIndexBuffer(GL::Buffer{indexes}, 0, GL::MeshIndexType::UnsignedShort)
+ .setCount(6 * TILE_COUNT);
+ wall_mesh = Utility::move(mesh);
+ return { wall_mesh, wall_indexes };
+}
+
+} // namespace floormat
diff --git a/src/chunk.cpp b/src/chunk.cpp
index 55afe1b9..e66b193a 100644
--- a/src/chunk.cpp
+++ b/src/chunk.cpp
@@ -1,9 +1,6 @@
#include "chunk.hpp"
-#include "tile-atlas.hpp"
-#include "shaders/tile.hpp"
-#include <algorithm>
-#include <Corrade/Containers/ArrayViewStl.h>
-#include <Magnum/GL/Buffer.h>
+#include "compat/LooseQuadtree-impl.h"
+#include "src/tile-atlas.hpp"
namespace floormat {
@@ -27,133 +24,97 @@ bool chunk::empty(bool force) const noexcept
tile_atlas* chunk::ground_atlas_at(std::size_t i) const noexcept { return _ground_atlases[i].get(); }
tile_atlas* chunk::wall_atlas_at(std::size_t i) const noexcept { return _wall_atlases[i].get(); }
-static auto make_index_array(std::size_t offset)
-{
- std::array<std::array<UnsignedShort, 6>, TILE_COUNT> array; // NOLINT(cppcoreguidelines-pro-type-member-init)
- for (std::size_t i = 0; i < TILE_COUNT; i++)
- array[i] = tile_atlas::indices(i + offset);
- return array;
-}
+tile_ref chunk::operator[](std::size_t idx) noexcept { return { *this, std::uint8_t(idx) }; }
+tile_proto chunk::operator[](std::size_t idx) const noexcept { return tile_proto(tile_ref { *const_cast<chunk*>(this), std::uint8_t(idx) }); }
+tile_ref chunk::operator[](local_coords xy) noexcept { return operator[](xy.to_index()); }
+tile_proto chunk::operator[](local_coords xy) const noexcept { return operator[](xy.to_index()); }
+
+auto chunk::begin() noexcept -> iterator { return iterator { *this, 0 }; }
+auto chunk::end() noexcept -> iterator { return iterator { *this, TILE_COUNT }; }
+auto chunk::cbegin() const noexcept -> const_iterator { return const_iterator { *this, 0 }; }
+auto chunk::cend() const noexcept -> const_iterator { return const_iterator { *this, TILE_COUNT }; }
+auto chunk::begin() const noexcept -> const_iterator { return cbegin(); }
+auto chunk::end() const noexcept -> const_iterator { return cend(); }
-struct vertex {
- Vector3 position;
- Vector2 texcoords;
- float depth = -1;
-};
+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; }
-auto chunk::ensure_ground_mesh() noexcept -> ground_mesh_tuple
+void chunk::mark_modified() noexcept
{
- if (!_ground_modified)
- return { ground_mesh, ground_indexes };
- _ground_modified = false;
+ mark_ground_modified();
+ mark_walls_modified();
+ mark_scenery_modified();
+}
- for (std::size_t i = 0; i < TILE_COUNT; i++)
- ground_indexes[i] = std::uint8_t(i);
- std::sort(ground_indexes.begin(), ground_indexes.end(), [this](std::uint8_t a, std::uint8_t b) {
- return _ground_atlases[a].get() < _ground_atlases[b].get();
- });
+static constexpr auto tile_size2us = Vector2us(iTILE_SIZE2);
- std::array<std::array<vertex, 4>, TILE_COUNT> vertexes;
- for (std::size_t k = 0; k < TILE_COUNT; k++)
- {
- const std::uint8_t i = ground_indexes[k];
- if (auto atlas = _ground_atlases[i]; !atlas)
- vertexes[k] = {};
- else
- {
- const local_coords pos{i};
- const auto quad = atlas->floor_quad(Vector3(pos.x, pos.y, 0) * TILE_SIZE, TILE_SIZE2);
- const auto texcoords = atlas->texcoords_for_id(_ground_variants[i]);
- const float depth = tile_shader::depth_value(pos);
- auto& v = vertexes[k];
- for (std::size_t j = 0; j < 4; j++)
- v[j] = { quad[j], texcoords[j], depth };
- }
- }
- const auto indexes = make_index_array(0);
-
- GL::Mesh mesh{GL::MeshPrimitive::Triangles};
- mesh.addVertexBuffer(GL::Buffer{vertexes}, 0, tile_shader::Position{}, tile_shader::TextureCoordinates{}, tile_shader::Depth{})
- .setIndexBuffer(GL::Buffer{indexes}, 0, GL::MeshIndexType::UnsignedShort)
- .setCount(6 * TILE_COUNT);
- ground_mesh = Utility::move(mesh);
- return { ground_mesh, ground_indexes };
+static constexpr Vector2s tile_start(std::size_t k)
+{
+ const auto i = std::uint8_t(k);
+ const local_coords coord{i};
+ constexpr auto tile_size2s = Vector2s(tile_size2us), half = tile_size2s/2;
+ return tile_size2s * Vector2s(coord.x, coord.y) - half;
}
-auto chunk::ensure_wall_mesh() noexcept -> wall_mesh_tuple
+auto chunk::ensure_passability() noexcept -> lqt&
{
- if (!_walls_modified)
- return { wall_mesh, wall_indexes };
- _walls_modified = false;
+ auto& qt = *_static_lqt;
+
+ if (!_pass_modified)
+ return qt;
+ _pass_modified = false;
- for (std::size_t i = 0; i < TILE_COUNT*2; i++)
- wall_indexes[i] = std::uint16_t(i);
+ qt.Clear();
+ _lqt_bboxes.clear();
+ _lqt_bboxes.reserve(32);
- std::sort(wall_indexes.begin(), wall_indexes.end(), [this](std::uint16_t a, std::uint16_t b) {
- return _wall_atlases[a] < _wall_atlases[b];
- });
+ constexpr auto whole_tile = [](std::size_t k, pass_mode p) constexpr -> bbox {
+ auto start = tile_start(k);
+ return { start[0], start[1], tile_size2us[0], tile_size2us[1], p };
+ };
- std::array<std::array<vertex, 4>, TILE_COUNT*2> vertexes;
- for (std::size_t k = 0; k < TILE_COUNT*2; k++)
+ constexpr auto wall_north = [](std::size_t k, pass_mode p) constexpr -> bbox {
+ auto start = tile_start(k) - Vector2s(0, 1);
+ return { start[0], start[1], tile_size2us[0], 2, p };
+ };
+
+ constexpr auto wall_west = [](std::size_t k, pass_mode p) constexpr -> bbox {
+ auto start = tile_start(k) - Vector2s(1, 0);
+ return { start[0], start[1], 2, tile_size2us[1], p };
+ };
+
+ for (std::size_t i = 0; i < TILE_COUNT; i++)
{
- const std::uint16_t i = wall_indexes[k];
- if (const auto& atlas = _wall_atlases[i]; !atlas)
- vertexes[k] = {};
- else
- {
- const auto& variant = _wall_variants[i];
- const local_coords pos{i / 2u};
- const auto center = Vector3(pos.x, pos.y, 0) * TILE_SIZE;
- const auto quad = i & 1 ? atlas->wall_quad_W(center, TILE_SIZE) : atlas->wall_quad_N(center, TILE_SIZE);
- const float depth = tile_shader::depth_value(pos);
- const auto texcoords = atlas->texcoords_for_id(variant);
- auto& v = vertexes[k];
- for (std::size_t j = 0; j < 4; j++)
- v[j] = { quad[j], texcoords[j], depth, };
- }
+ const auto tile = const_cast<chunk&>(*this)[i];
+ if (auto s = tile.scenery())
+ if (auto p = s.frame.passability; p != pass_mode::pass)
+ _lqt_bboxes.push_back(whole_tile(i, p));
+ if (auto atlas = tile.ground_atlas())
+ if (auto p = atlas->pass_mode(pass_mode::pass); p != pass_mode::pass)
+ _lqt_bboxes.push_back(whole_tile(i, p));
+ if (auto atlas = tile.wall_north_atlas())
+ if (auto p = atlas->pass_mode(pass_mode::blocked); p != pass_mode::pass)
+ _lqt_bboxes.push_back(wall_north(i, p));
+ if (auto atlas = tile.wall_west_atlas())
+ if (auto p = atlas->pass_mode(pass_mode::blocked); p != pass_mode::pass)
+ _lqt_bboxes.push_back(wall_west(i, p));
}
- using index_t = std::array<std::array<UnsignedShort, 6>, TILE_COUNT>;
- const index_t indexes[2] = { make_index_array(0), make_index_array(TILE_COUNT) };
+ for (auto& bbox : _lqt_bboxes)
+ qt.Insert(&bbox);
- GL::Mesh mesh{GL::MeshPrimitive::Triangles};
- mesh.addVertexBuffer(GL::Buffer{vertexes}, 0, tile_shader::Position{}, tile_shader::TextureCoordinates{}, tile_shader::Depth{})
- .setIndexBuffer(GL::Buffer{indexes}, 0, GL::MeshIndexType::UnsignedShort)
- .setCount(6 * TILE_COUNT);
- wall_mesh = Utility::move(mesh);
- return { wall_mesh, wall_indexes };
+ return qt;
}
-fm_noinline
-chunk::chunk() noexcept // NOLINT(modernize-use-equals-default)
+void chunk::bb_extractor::ExtractBoundingBox(const chunk::bbox* x, BB* bbox)
{
- //fm_debug("chunk ctor");
+ *bbox = { x->left, x->top, std::int16_t(x->width), std::int16_t(x->height) };
}
-tile_ref chunk::operator[](std::size_t idx) noexcept { return { *this, std::uint8_t(idx) }; }
-tile_proto chunk::operator[](std::size_t idx) const noexcept { return tile_proto(tile_ref { *const_cast<chunk*>(this), std::uint8_t(idx) }); }
-tile_ref chunk::operator[](local_coords xy) noexcept { return operator[](xy.to_index()); }
-tile_proto chunk::operator[](local_coords xy) const noexcept { return operator[](xy.to_index()); }
-
-auto chunk::begin() noexcept -> iterator { return iterator { *this, 0 }; }
-auto chunk::end() noexcept -> iterator { return iterator { *this, TILE_COUNT }; }
-auto chunk::cbegin() const noexcept -> const_iterator { return const_iterator { *this, 0 }; }
-auto chunk::cend() const noexcept -> const_iterator { return const_iterator { *this, TILE_COUNT }; }
-auto chunk::begin() const noexcept -> const_iterator { return cbegin(); }
-auto chunk::end() const noexcept -> const_iterator { return cend(); }
-
+chunk::chunk() noexcept : _static_lqt { std::make_unique<lqt>() } {}
+chunk::~chunk() noexcept = default;
chunk::chunk(chunk&&) noexcept = default;
chunk& chunk::operator=(chunk&&) noexcept = default;
-void chunk::mark_ground_modified() noexcept { _ground_modified = true; }
-void chunk::mark_walls_modified() noexcept { _walls_modified = true; }
-void chunk::mark_scenery_modified() noexcept { _scenery_modified = true; }
-
-void chunk::mark_modified() noexcept
-{
- mark_ground_modified();
- mark_walls_modified();
- mark_scenery_modified();
-}
-
} // namespace floormat
diff --git a/src/chunk.hpp b/src/chunk.hpp
index 9acd5f12..d66dfdc8 100644
--- a/src/chunk.hpp
+++ b/src/chunk.hpp
@@ -4,8 +4,12 @@
#include "scenery.hpp"
#include <type_traits>
#include <array>
-#include <bitset>
+#include <vector>
+#include <memory>
#include <Magnum/GL/Mesh.h>
+#include "compat/LooseQuadtree.h"
+
+namespace loose_quadtree { template<typename NumberT, typename ObjectT, typename BoundingBoxExtractorT> class LooseQuadtree; }
namespace floormat {
@@ -14,7 +18,6 @@ struct anim_atlas;
struct chunk final
{
friend struct tile_ref;
- friend struct pass_mode_ref;
tile_ref operator[](std::size_t idx) noexcept;
tile_proto operator[](std::size_t idx) const noexcept;
@@ -34,6 +37,7 @@ struct chunk final
bool empty(bool force = false) const noexcept;
chunk() noexcept;
+ ~chunk() noexcept;
chunk(const chunk&) = delete;
chunk& operator=(const chunk&) = delete;
chunk(chunk&&) noexcept;
@@ -59,6 +63,12 @@ struct chunk final
wall_mesh_tuple ensure_wall_mesh() noexcept;
tile_atlas* wall_atlas_at(std::size_t i) const noexcept;
+ struct bbox final { std::int16_t left, top; std::uint16_t width, height; enum pass_mode pass_mode; };
+ using BB = loose_quadtree::BoundingBox<std::int16_t>;
+ struct bb_extractor { static void ExtractBoundingBox(const bbox* object, BB* bbox); };
+ using lqt = loose_quadtree::LooseQuadtree<std::int16_t, bbox, bb_extractor>;
+ lqt& ensure_passability() noexcept;
+
private:
std::array<std::shared_ptr<tile_atlas>, TILE_COUNT> _ground_atlases;
std::array<std::uint8_t, TILE_COUNT> ground_indexes = {};
@@ -68,12 +78,16 @@ private:
std::array<variant_t, TILE_COUNT*2> _wall_variants = {};
std::array<std::shared_ptr<anim_atlas>, TILE_COUNT> _scenery_atlases;
std::array<scenery, TILE_COUNT> _scenery_variants = {};
- std::bitset<TILE_COUNT*2> _passability = {};
+
+ std::unique_ptr<lqt> _static_lqt;
+ std::vector<bbox> _lqt_bboxes;
+
GL::Mesh ground_mesh{NoCreate}, wall_mesh{NoCreate};
mutable bool _maybe_empty : 1 = true,
_ground_modified : 1 = true,
_walls_modified : 1 = true,
- _scenery_modified : 1 = true;
+ _scenery_modified : 1 = true,
+ _pass_modified : 1 = true;
};
} // namespace floormat
diff --git a/src/pass-mode.hpp b/src/pass-mode.hpp
index a60a8188..112aafcc 100644
--- a/src/pass-mode.hpp
+++ b/src/pass-mode.hpp
@@ -3,8 +3,7 @@
namespace floormat {
-enum class pass_mode : std::uint8_t { shoot_through, pass, blocked, see_through };
-constexpr inline std::uint8_t pass_mode_COUNT = std::uint8_t(pass_mode::see_through) + 1;
-static_assert(pass_mode_COUNT == 4);
+enum class pass_mode : std::uint8_t { blocked, see_through, shoot_through, pass, };
+constexpr inline std::uint8_t pass_mode_COUNT = 4;
} // namespace floormat
diff --git a/src/scenery.hpp b/src/scenery.hpp
index 5cfe3827..04fcf984 100644
--- a/src/scenery.hpp
+++ b/src/scenery.hpp
@@ -36,7 +36,7 @@ struct scenery final
frame_t frame = 0;
rotation r : 3 = rotation::N;
scenery_type type : 3 = scenery_type::none;
- pass_mode passability : 2 = pass_mode{0};
+ pass_mode passability : 2 = pass_mode::pass;
std::uint8_t active : 1 = false;
std::uint8_t closing : 1 = false;
std::uint8_t interactive : 1 = false;
diff --git a/src/tile-atlas.cpp b/src/tile-atlas.cpp
index 6f9e832f..b13b9ea8 100644
--- a/src/tile-atlas.cpp
+++ b/src/tile-atlas.cpp
@@ -57,6 +57,7 @@ auto tile_atlas::make_texcoords_array(Vector2ui pixel_size, Vector2ub tile_count
std::size_t tile_atlas::num_tiles() const { return Vector2ui{dims_}.product(); }
Optional<enum pass_mode> tile_atlas::pass_mode() const { return passability; }
+enum pass_mode tile_atlas::pass_mode(enum pass_mode p) const { return passability ? *passability : p; }
void tile_atlas::set_pass_mode(enum pass_mode p)
{
diff --git a/src/tile-atlas.hpp b/src/tile-atlas.hpp
index 61f528ef..e4ac8412 100644
--- a/src/tile-atlas.hpp
+++ b/src/tile-atlas.hpp
@@ -28,6 +28,7 @@ struct tile_atlas final
GL::Texture2D& texture() { return tex_; }
StringView name() const { return name_; }
Optional<enum pass_mode> pass_mode() const;
+ enum pass_mode pass_mode(enum pass_mode p) const;
void set_pass_mode(enum pass_mode p);
private:
diff --git a/src/tile.cpp b/src/tile.cpp
index 419e59a3..34d2a6e1 100644
--- a/src/tile.cpp
+++ b/src/tile.cpp
@@ -3,33 +3,6 @@
namespace floormat {
-pass_mode_ref::pass_mode_ref(chunk& c, std::uint8_t i) noexcept : _chunk{&c}, i{i}
-{
-}
-
-pass_mode_ref& pass_mode_ref::operator=(pass_mode val) noexcept
-{
- auto x = std::underlying_type_t<pass_mode>(val) & pass_mode_COUNT;
- auto& bitset = _chunk->_passability;
- bitset[i*2 + 0] = x & 1;
- bitset[i*2 + 1] = x >> 1 & 1;
- return *this;
-}
-
-pass_mode_ref& pass_mode_ref::operator=(const pass_mode_ref& x) noexcept
-{
- return operator=(pass_mode(x)); // NOLINT(misc-unconventional-assign-operator)
-}
-
-pass_mode_ref::operator pass_mode() const noexcept
-{
- auto& bitset = _chunk->_passability;
- std::uint8_t ret = 0;
- ret |= (std::uint8_t)bitset[i*2 + 1];
- ret |= (std::uint8_t)bitset[i*2 + 0] << 1;
- return pass_mode(ret);
-}
-
bool operator==(const tile_proto& a, const tile_proto& b) noexcept {
return a.ground() == b.ground() &&
a.wall_north() == b.wall_north() &&
@@ -64,15 +37,11 @@ tile_image_proto tile_ref::wall_north() const noexcept { return { _chunk->_wall_
tile_image_proto tile_ref::wall_west() const noexcept { return { _chunk->_wall_atlases[i*2+1], _chunk->_wall_variants[i*2+1] }; }
scenery_proto tile_ref::scenery() const noexcept { return { _chunk->_scenery_atlases[i], _chunk->_scenery_variants[i] }; }
-pass_mode_ref tile_ref::passability() noexcept { return { *_chunk, i }; }
-pass_mode tile_ref::passability() const noexcept { return pass_mode_ref { *const_cast<struct chunk*>(_chunk), i }; }
-
tile_ref::operator tile_proto() const noexcept
{
return {
_chunk->_ground_atlases[i], _chunk->_wall_atlases[i*2+0], _chunk->_wall_atlases[i*2+1], _chunk->_scenery_atlases[i],
_chunk->_ground_variants[i], _chunk->_wall_variants[i*2+0], _chunk->_wall_variants[i*2+1], _chunk->_scenery_variants[i],
- passability(),
};
}
@@ -84,8 +53,7 @@ bool operator==(const tile_ref& a, const tile_ref& b) noexcept
return a.ground() == b.ground() &&
a.wall_north() == b.wall_north() &&
a.wall_west() == b.wall_west() &&
- a.scenery() == b.scenery() &&
- a.passability() == b.passability();
+ a.scenery() == b.scenery();
}
} // namespace floormat
diff --git a/src/tile.hpp b/src/tile.hpp
index 543b70b6..7a6604dd 100644
--- a/src/tile.hpp
+++ b/src/tile.hpp
@@ -1,32 +1,18 @@
#pragma once
#include "tile-image.hpp"
#include "scenery.hpp"
-#include "pass-mode.hpp"
namespace floormat {
struct chunk;
struct anim_atlas;
-struct pass_mode_ref final
-{
- pass_mode_ref(chunk& c, std::uint8_t i) noexcept;
- pass_mode_ref& operator=(pass_mode x) noexcept;
- pass_mode_ref& operator=(const pass_mode_ref& x) noexcept;
- operator pass_mode() const noexcept;
-
-private:
- chunk* _chunk;
- std::uint8_t i;
-};
-
struct tile_proto final
{
std::shared_ptr<tile_atlas> ground_atlas, wall_north_atlas, wall_west_atlas;
std::shared_ptr<anim_atlas> scenery_atlas;
variant_t ground_variant = 0, wall_north_variant = 0, wall_west_variant = 0;
struct scenery scenery_frame;
- pass_mode passability = pass_mode{0};
tile_image_proto ground() const noexcept;
tile_image_proto wall_north() const noexcept;
@@ -60,9 +46,6 @@ struct tile_ref final
std::shared_ptr<const tile_atlas> wall_west_atlas() const noexcept;
std::shared_ptr<const anim_atlas> scenery_atlas() const noexcept;
- pass_mode_ref passability() noexcept;
- pass_mode passability() const noexcept;
-
explicit operator tile_proto() const noexcept;
struct chunk& chunk() noexcept { return *_chunk; }
diff --git a/test/app.hpp b/test/app.hpp
index 17ca2b6e..46ea3fc0 100644
--- a/test/app.hpp
+++ b/test/app.hpp
@@ -13,12 +13,16 @@
#endif
namespace floormat {
+
+struct chunk;
+
struct test_app final : private FM_APPLICATION
{
using Application = FM_APPLICATION;
explicit test_app(const Arguments& arguments);
~test_app();
int exec() override;
+ static chunk make_test_chunk();
static void test_json();
static void test_tile_iter();
static void test_const_math();
@@ -26,5 +30,6 @@ struct test_app final : private FM_APPLICATION
static void test_entity();
static void test_quadtree();
static void test_loader();
+ static void test_bbox();
};
} // namespace floormat
diff --git a/test/bbox.cpp b/test/bbox.cpp
new file mode 100644
index 00000000..c045d858
--- /dev/null
+++ b/test/bbox.cpp
@@ -0,0 +1,28 @@
+#include "app.hpp"
+#include "src/chunk.hpp"
+#include "compat/LooseQuadtree-impl.h"
+#include <Magnum/Math/Vector2.h>
+
+namespace floormat {
+
+void test_app::test_bbox()
+{
+ auto c = make_test_chunk();
+ auto& qt = c.ensure_passability();
+ fm_assert(qt.GetSize() >= 2);
+
+ using namespace loose_quadtree;
+ using bbox = BoundingBox<std::int16_t>;
+ constexpr auto pos1 = Vector2s(iTILE_SIZE2 * (TILE_MAX_DIM/2) - Vector2i(0, iTILE_SIZE[1]/2)),
+ size = Vector2s(iTILE_SIZE2);
+ constexpr auto b1 = bbox{pos1[0], pos1[1], size[0], size[1]};
+ constexpr auto pos2 = Vector2s(iTILE_SIZE2 * (Vector2i(TILE_MAX_DIM/2) - Vector2i(-1, -1)) - Vector2i(0, iTILE_SIZE[1]/2));
+ auto q1 = qt.QueryIntersectsRegion(b1);
+ fm_assert(!q1.EndOfQuery());
+ do q1.Next(); while (!q1.EndOfQuery());
+ constexpr auto b2 = bbox{pos2[0], pos2[1], size[0], size[1]};
+ auto q2 = qt.QueryIntersectsRegion(b2);
+ fm_assert(q2.EndOfQuery());
+}
+
+} // namespace floormat
diff --git a/test/main.cpp b/test/main.cpp
index ec09f55f..305c4926 100644
--- a/test/main.cpp
+++ b/test/main.cpp
@@ -25,6 +25,7 @@ int test_app::exec()
test_serializer();
test_entity();
test_loader();
+ test_bbox();
test_quadtree();
return 0;
}
diff --git a/test/serializer.cpp b/test/serializer.cpp
index 9f382602..69b45575 100644
--- a/test/serializer.cpp
+++ b/test/serializer.cpp
@@ -9,7 +9,7 @@ namespace floormat {
namespace Path = Corrade::Utility::Path;
-static chunk make_test_chunk()
+chunk test_app::make_test_chunk()
{
auto metal1 = loader.tile_atlas("metal1", {2, 2}, pass_mode::pass),
metal2 = loader.tile_atlas("metal2", {2, 2}, pass_mode::blocked),