summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--editor/app.hpp2
-rw-r--r--editor/draw.cpp33
-rw-r--r--editor/events.cpp2
-rw-r--r--editor/imgui.cpp17
-rw-r--r--editor/keys.hpp3
-rw-r--r--editor/update.cpp9
-rw-r--r--src/RTree.cpp2
-rw-r--r--src/RTree.h13
-rw-r--r--src/RTree.hpp36
-rw-r--r--src/chunk-collision.cpp101
-rw-r--r--src/chunk.hpp16
11 files changed, 182 insertions, 52 deletions
diff --git a/editor/app.hpp b/editor/app.hpp
index d1d67ac8..64fdb587 100644
--- a/editor/app.hpp
+++ b/editor/app.hpp
@@ -130,7 +130,7 @@ private:
std::array<int, key_set::COUNT> key_modifiers = {};
cursor_state cursor;
Optional<global_coords> inspected_scenery;
- bool _draw_collision_boxes : 1 = false;
+ bool _enable_render_bboxes : 1 = false;
};
} // namespace floormat
diff --git a/editor/draw.cpp b/editor/draw.cpp
index 4e175ab4..783ead39 100644
--- a/editor/draw.cpp
+++ b/editor/draw.cpp
@@ -9,6 +9,7 @@
#include "src/world.hpp"
#include <Magnum/Math/Color.h>
#include <Magnum/Math/Vector3.h>
+#include "src/RTree.hpp"
namespace floormat {
@@ -60,7 +61,6 @@ void app::draw_cursor()
void app::draw_collision_boxes()
{
-#if 0
const auto [minx, maxx, miny, maxy] = M->get_draw_bounds();
const auto sz = M->window_size();
auto& world = M->world();
@@ -76,33 +76,30 @@ void app::draw_collision_boxes()
if (c.empty())
continue;
c.ensure_passability();
- auto* lqt = c.lqt_from_collision_type(collision::move);
- if (!lqt)
- continue;
const with_shifted_camera_offset o{shader, pos, {minx, miny}, {maxx, maxy}};
if (floormat_main::check_chunk_visible(shader.camera_offset(), sz))
{
- auto bb = lqt->GetLooseBoundingBox();
- bb.left -= bb.width; bb.top -= bb.height;
- bb.width *= 2; bb.height *= 2;
- auto q = lqt->QueryInsideRegion(bb);
- using extractor = std::decay_t<decltype(*lqt)>::BoundingBoxExtractor;
- while (!q.EndOfQuery())
- {
- loose_quadtree::BoundingBox<std::int16_t> bb{0, 0, 0, 0};
- extractor::ExtractBoundingBox(q.GetCurrent(), &bb);
- _wireframe_rect.draw(shader, { Vector3(bb.left+bb.width/2.f, bb.top+bb.height/2.f, 0), Vector2(bb.width, bb.height), 3 });
- q.Next();
- }
+ constexpr float maxf = 1 << 24, max2f[] = { maxf, maxf }, min2f[] = { -maxf, -maxf };
+ auto* rtree = c.rtree();
+ using rtree_type = std::decay_t<decltype(*rtree)>;
+ using rect_type = typename rtree_type::Rect;
+ rtree->Search(min2f, max2f, [&](std::uint64_t data, const rect_type& rect) {
+ [[maybe_unused]] auto x = std::bit_cast<collision_data>(data);
+ Vector2 start(rect.m_min[0], rect.m_min[1]), end(rect.m_max[0], rect.m_max[1]);
+ auto size = (end - start);
+ auto center = Vector3(start + size*.5f, 0.f);
+ _wireframe_rect.draw(shader, { center, size, 3 });
+ return true;
+ });
}
}
+
shader.set_tint({1, 1, 1, 1});
-#endif
}
void app::draw()
{
- if (_draw_collision_boxes)
+ if (_enable_render_bboxes)
draw_collision_boxes();
if (_editor.current_tile_editor() || _editor.current_scenery_editor())
draw_cursor();
diff --git a/editor/events.cpp b/editor/events.cpp
index d0a57d8b..bec91890 100644
--- a/editor/events.cpp
+++ b/editor/events.cpp
@@ -123,11 +123,11 @@ auto app::resolve_keybinding(int k_, int mods_) const -> std::tuple<key, int>
case SDLK_2: return { key_mode_floor, mods };
case SDLK_3: return { key_mode_walls, mods };
case SDLK_4: return { key_mode_scenery, mods };
+ case SDLK_c | ALT: return { key_mode_collisions, mods };
case SDLK_F5: return { key_quicksave, mods };
case SDLK_F9: return { key_quickload, mods };
case SDLK_q | CTRL: return { key_quit, mods };
case SDLK_n | CTRL: return { key_new_file, mods };
- case SDLK_b | ALT: return { key_collision_boxes, mods };
}
}
}
diff --git a/editor/imgui.cpp b/editor/imgui.cpp
index 1accd9e5..10dcc7de 100644
--- a/editor/imgui.cpp
+++ b/editor/imgui.cpp
@@ -59,11 +59,15 @@ float app::draw_main_menu()
can_rotate = ed->is_anything_selected();
else if (auto* ed = _editor.current_scenery_editor())
can_rotate = ed->is_anything_selected();
- bool b_none = false, b_floor = false, b_walls = false, b_rotate = false, b_scenery = false;
+ auto mode = _editor.mode();
+ using m = editor_mode;
+ bool b_none = mode == m::none, b_floor = mode == m::floor, b_walls = mode == m::walls,
+ b_rotate = false, b_scenery = mode == m::scenery, b_collisions = _enable_render_bboxes;
ImGui::MenuItem("Select", "1", &b_none);
ImGui::MenuItem("Floor", "2", &b_floor);
ImGui::MenuItem("Walls", "3", &b_walls);
ImGui::MenuItem("Scenery", "4", &b_scenery);
+ ImGui::MenuItem("Show collisions", "Alt+C", &b_collisions);
ImGui::Separator();
ImGui::MenuItem("Rotate", "R", &b_rotate, can_rotate);
if (b_none)
@@ -74,16 +78,11 @@ float app::draw_main_menu()
do_key(key_mode_walls);
else if (b_scenery)
do_key(key_mode_scenery);
+ else if (b_collisions)
+ do_key(key_mode_collisions);
if (b_rotate)
do_key(key_rotate_tile);
}
- if (auto b = begin_menu("View"))
- {
- bool show_collisions = _draw_collision_boxes;
- ImGui::MenuItem("Collision boxes", "Alt+B", &show_collisions);
- if (show_collisions)
- do_key(key_collision_boxes);
- }
main_menu_height = ImGui::GetContentRegionMax().y;
}
@@ -105,7 +104,7 @@ void app::draw_ui()
const float main_menu_height = draw_main_menu();
[[maybe_unused]] auto font = font_saver{ctx.FontSize*dpi};
- if (_editor.mode() != editor_mode::none)
+ if (_editor.current_tile_editor() || _editor.current_scenery_editor())
draw_editor_pane(main_menu_height);
draw_fps();
draw_tile_under_cursor();
diff --git a/editor/keys.hpp b/editor/keys.hpp
index 3311a69c..eb1ed7db 100644
--- a/editor/keys.hpp
+++ b/editor/keys.hpp
@@ -16,8 +16,7 @@ enum key : unsigned {
key_camera_up, key_camera_left, key_camera_right, key_camera_down, key_camera_reset,
key_NO_REPEAT,
key_rotate_tile,
- key_mode_none, key_mode_floor, key_mode_walls, key_mode_scenery,
- key_collision_boxes,
+ key_mode_none, key_mode_floor, key_mode_walls, key_mode_scenery, key_mode_collisions,
key_GLOBAL,
key_new_file,
key_quit,
diff --git a/editor/update.cpp b/editor/update.cpp
index 0a9141f0..2b693526 100644
--- a/editor/update.cpp
+++ b/editor/update.cpp
@@ -56,6 +56,7 @@ void app::do_mouse_up_down(std::uint8_t button, bool is_down, int mods)
switch (_editor.mode())
{
default:
+ break;
case editor_mode::none:
if (button == mouse_button_left)
if (auto* s = find_clickable_scenery(*cursor.pixel); s && s->item.can_activate(s->atlas))
@@ -119,8 +120,8 @@ void app::do_key(key k, int mods)
return _editor.set_mode(editor_mode::walls);
case key_mode_scenery:
return _editor.set_mode(editor_mode::scenery);
- case key_collision_boxes:
- return void(_draw_collision_boxes = !_draw_collision_boxes);
+ case key_mode_collisions:
+ return void(_enable_render_bboxes = !_enable_render_bboxes);
case key_quicksave:
return do_quicksave();
case key_quickload:
@@ -151,8 +152,10 @@ void app::update_world(float dt)
if (auto [atlas, scenery] = x.scenery(); atlas != nullptr)
{
auto pass0 = scenery.passability;
+ auto offset0 = scenery.bbox_offset;
+ auto size0 = scenery.bbox_size;
scenery.update(dt, *atlas);
- if (pass0 != scenery.passability)
+ if (pass0 != scenery.passability || offset0 != scenery.offset || size0 != scenery.bbox_size)
c.mark_scenery_modified();
}
}
diff --git a/src/RTree.cpp b/src/RTree.cpp
index c6f79c77..32323e69 100644
--- a/src/RTree.cpp
+++ b/src/RTree.cpp
@@ -1,3 +1,3 @@
#include "RTree.hpp"
-template class RTree<void*, unsigned, 2, float>;
+template class RTree<std::uint64_t, float, 2, float>;
diff --git a/src/RTree.h b/src/RTree.h
index 50fd0384..f4139450 100644
--- a/src/RTree.h
+++ b/src/RTree.h
@@ -7,7 +7,6 @@
//#define RTREE_STDIO
-#include "compat/assert.hpp"
#ifdef RTREE_STDIO
#include <stdio.h>
#endif
@@ -18,9 +17,6 @@
// RTree.h
//
-#define RTREE_TEMPLATE template<class DATATYPE, class ELEMTYPE, int NUMDIMS, class ELEMTYPEREAL, int TMAXNODES, int TMINNODES>
-#define RTREE_QUAL RTree<DATATYPE, ELEMTYPE, NUMDIMS, ELEMTYPEREAL, TMAXNODES, TMINNODES>
-
#define RTREE_DONT_USE_MEMPOOLS // This version does not contain a fixed memory allocator, fill in lines with EXAMPLE to implement one.
#define RTREE_USE_SPHERICAL_VOLUME // Better split classification, may be slower on some systems
@@ -69,7 +65,8 @@ public:
RTree();
RTree(const RTree& other);
- virtual ~RTree();
+ virtual ~RTree() noexcept;
+ RTree& operator=(const RTree&);
/// Insert entry
/// \param a_min Min of bounding rect
@@ -180,8 +177,6 @@ public:
/// Get object at iterator position
DATATYPE& GetAt(Iterator& a_it) { return *a_it; }
-protected:
-
/// Minimal bounding rectangle (n-dimensional)
struct Rect
{
@@ -189,6 +184,8 @@ protected:
ELEMTYPE m_max[NUMDIMS]; ///< Max dimensions of bounding box
};
+protected:
+
/// May be data or may be another subtree
/// The parents level determines this.
/// If the parents level is 0, then this is data
@@ -281,7 +278,7 @@ public:
void ListTree(std::vector<Rect>& vec, std::vector<Node*>& temp) const;
};
-extern template class RTree<void*, unsigned, 2, float>;
+extern template class RTree<std::uint64_t, float, 2, float>;
//#undef RTREE_TEMPLATE
//#undef RTREE_QUAL
diff --git a/src/RTree.hpp b/src/RTree.hpp
index e421db5a..a8f9e031 100644
--- a/src/RTree.hpp
+++ b/src/RTree.hpp
@@ -1,7 +1,6 @@
#pragma once
#include "RTree.h"
-
#include <math.h>
#include <stdlib.h>
#include <cassert>
@@ -14,6 +13,14 @@
#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
#endif
+#define RTREE_TEMPLATE template<class DATATYPE, class ELEMTYPE, int NUMDIMS, class ELEMTYPEREAL, int TMAXNODES, int TMINNODES>
+#define RTREE_QUAL RTree<DATATYPE, ELEMTYPE, NUMDIMS, ELEMTYPEREAL, TMAXNODES, TMINNODES>
+#undef _DEBUG
+#ifndef FM_NO_DEBUG
+#define _DEBUG
+#endif
+
+#include "compat/assert.hpp"
//#define ASSERT assert // RTree uses ASSERT( condition )
#undef ASSERT
#define ASSERT fm_assert
@@ -134,9 +141,19 @@ RTREE_QUAL::RTree(const RTree& other) : RTree()
CopyRec(m_root, other.m_root);
}
+RTREE_TEMPLATE
+RTREE_QUAL& RTREE_QUAL::operator=(const RTree& other)
+{
+ if (&other != this)
+ {
+ RemoveAll();
+ CopyRec(m_root, other.m_root);
+ }
+ return *this;
+}
RTREE_TEMPLATE
-RTREE_QUAL::~RTree()
+RTREE_QUAL::~RTree() noexcept
{
Reset(); // Free, or reset node memory
}
@@ -1293,13 +1310,10 @@ bool RTREE_QUAL::Search(Node* a_node, Rect* a_rect, int& a_foundCount, F&& callb
{
if(Overlap(a_rect, &a_node->m_branch[index].m_rect))
{
- DATATYPE& id = a_node->m_branch[index].m_data;
++a_foundCount;
-
- if(callback && !callback(id))
- {
- return false; // Don't continue searching
- }
+ const Rect& r = a_node->m_branch[index].m_rect;
+ if(!callback(a_node->m_branch[index].m_data, r))
+ return false; // Don't continue searching
}
}
}
@@ -1460,6 +1474,12 @@ bool RTREE_QUAL::Iterator::FindNextData()
}
}
+#undef _DEBUG
+#undef ASSERT
+
+#undef RTREE_QUAL
+#undef RTREE_TEMPLATE
+
#ifdef __GNUG__
#pragma GCC diagnostic pop
#endif
diff --git a/src/chunk-collision.cpp b/src/chunk-collision.cpp
new file mode 100644
index 00000000..bec90f32
--- /dev/null
+++ b/src/chunk-collision.cpp
@@ -0,0 +1,101 @@
+#include "chunk.hpp"
+#include "RTree.hpp"
+#include "tile-atlas.hpp"
+#include <bit>
+#include <Corrade/Containers/PairStl.h>
+
+namespace floormat {
+
+namespace {
+
+constexpr Vector2 tile_start(std::size_t k)
+{
+ const auto i = std::uint8_t(k);
+ const local_coords coord{i};
+ return TILE_SIZE2 * Vector2(coord.x, coord.y) - TILE_SIZE2*.5f;
+}
+
+constexpr Pair<Vector2, Vector2> scenery_tile(std::size_t k, const scenery& sc)
+{
+ constexpr auto half = Vector2(TILE_SIZE2)/2;
+ auto center = tile_start(k) + Vector2(sc.bbox_offset) + half;
+ auto start = center - Vector2(sc.bbox_size);
+ auto size = Vector2(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)
+{
+ return std::bit_cast<std::uint64_t>(collision_data { (std::uint64_t)type, id });
+}
+
+} // namespace
+
+void chunk::ensure_passability() noexcept
+{
+ if (!_pass_modified)
+ return;
+ _pass_modified = false;
+
+ _rtree.RemoveAll();
+
+ for (std::size_t i = 0; 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);
+ }
+ }
+ for (std::size_t i = 0; i < TILE_COUNT; i++)
+ {
+ if (const auto* atlas = ground_atlas_at(i))
+ if (atlas->pass_mode(pass_mode::pass) != pass_mode::pass)
+ {
+ auto [start, end] = whole_tile(i);
+ auto id = make_id(collision_type::geometry, i);
+ _rtree.Insert(start.data(), end.data(), id);
+ }
+ }
+ for (std::size_t i = 0; i < TILE_COUNT; i++)
+ {
+ auto tile = operator[](i);
+ if (const auto* atlas = tile.wall_north_atlas().get())
+ if (atlas->pass_mode(pass_mode::blocked) != pass_mode::pass)
+ {
+ auto [start, end] = wall_north(i);
+ auto id = make_id(collision_type::geometry, 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)
+ {
+ auto [start, end] = wall_west(i);
+ auto id = make_id(collision_type::geometry, i);
+ _rtree.Insert(start.data(), end.data(), id);
+ }
+ }
+}
+
+} // namespace floormat
diff --git a/src/chunk.hpp b/src/chunk.hpp
index 9dd0f117..f117237c 100644
--- a/src/chunk.hpp
+++ b/src/chunk.hpp
@@ -4,9 +4,9 @@
#include "scenery.hpp"
#include <type_traits>
#include <array>
-#include <vector>
#include <memory>
#include <Magnum/GL/Mesh.h>
+#include "RTree.h"
namespace floormat {
@@ -16,6 +16,15 @@ enum class collision : std::uint8_t {
view, shoot, move,
};
+enum class collision_type : std::uint8_t {
+ none, entity, scenery, geometry,
+};
+
+struct collision_data final {
+ std::uint64_t tag : 2;
+ std::uint64_t data : 62;
+};
+
struct chunk final
{
friend struct tile_ref;
@@ -77,6 +86,8 @@ struct chunk final
void ensure_passability() noexcept;
+ RTree<std::uint64_t, float, 2, float>* rtree() const noexcept { return &const_cast<chunk*>(this)->_rtree; }
+
private:
std::array<std::shared_ptr<tile_atlas>, TILE_COUNT> _ground_atlases;
std::array<std::uint8_t, TILE_COUNT> ground_indexes = {};
@@ -89,6 +100,9 @@ private:
std::array<scenery, TILE_COUNT> _scenery_variants = {};
GL::Mesh ground_mesh{NoCreate}, wall_mesh{NoCreate}, scenery_mesh{NoCreate};
+
+ RTree<std::uint64_t, float, 2, float> _rtree;
+
mutable bool _maybe_empty : 1 = true,
_ground_modified : 1 = true,
_walls_modified : 1 = true,