diff options
-rw-r--r-- | editor/app.hpp | 2 | ||||
-rw-r--r-- | editor/draw.cpp | 33 | ||||
-rw-r--r-- | editor/events.cpp | 2 | ||||
-rw-r--r-- | editor/imgui.cpp | 17 | ||||
-rw-r--r-- | editor/keys.hpp | 3 | ||||
-rw-r--r-- | editor/update.cpp | 9 | ||||
-rw-r--r-- | src/RTree.cpp | 2 | ||||
-rw-r--r-- | src/RTree.h | 13 | ||||
-rw-r--r-- | src/RTree.hpp | 36 | ||||
-rw-r--r-- | src/chunk-collision.cpp | 101 | ||||
-rw-r--r-- | src/chunk.hpp | 16 |
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, |