diff --git a/headers/enduro2d/high/_high.hpp b/headers/enduro2d/high/_high.hpp index 75c0bdbb..63d6b464 100644 --- a/headers/enduro2d/high/_high.hpp +++ b/headers/enduro2d/high/_high.hpp @@ -18,6 +18,24 @@ namespace e2d } } +namespace e2d +{ + class node; + using node_iptr = intrusive_ptr; + using const_node_iptr = intrusive_ptr; + + class scene; + using scene_iptr = intrusive_ptr; + using const_scene_iptr = intrusive_ptr; + + class actor; + using actor_iptr = intrusive_ptr; + using const_actor_iptr = intrusive_ptr; + + class node_children_ilist_tag {}; + using node_children = intrusive_list; +} + namespace e2d { class camera; diff --git a/headers/enduro2d/high/scene/actor.hpp b/headers/enduro2d/high/scene/actor.hpp index d459d178..01a04e6e 100644 --- a/headers/enduro2d/high/scene/actor.hpp +++ b/headers/enduro2d/high/scene/actor.hpp @@ -9,13 +9,6 @@ #include "../_high.hpp" #include "node.hpp" -namespace e2d -{ - class actor; - using actor_iptr = intrusive_ptr; - using const_actor_iptr = intrusive_ptr; -} - namespace e2d { class actor final : public node { diff --git a/headers/enduro2d/high/scene/node.hpp b/headers/enduro2d/high/scene/node.hpp index 4fbc57a2..6d79b5a5 100644 --- a/headers/enduro2d/high/scene/node.hpp +++ b/headers/enduro2d/high/scene/node.hpp @@ -10,16 +10,6 @@ #include "../_high.hpp" -namespace e2d -{ - class node; - using node_iptr = intrusive_ptr; - using const_node_iptr = intrusive_ptr; - - class node_children_ilist_tag {}; - using node_children = intrusive_list; -} - namespace e2d { class node @@ -32,6 +22,20 @@ namespace e2d static node_iptr create(); static node_iptr create(const node_iptr& parent); + // + // transform + // + + void transform(const t3f& transform) noexcept; + const t3f& transform() const noexcept; + + const m4f& local_matrix() const noexcept; + const m4f& world_matrix() const noexcept; + + // + // container + // + node_iptr root() noexcept; const_node_iptr root() const noexcept; @@ -99,8 +103,22 @@ namespace e2d virtual void on_change_parent_() noexcept; virtual void on_change_children_() noexcept; private: + enum flag_masks : u32 { + fm_dirty_local_matrix = 1u << 0, + fm_dirty_world_matrix = 1u << 1, + }; + void mark_dirty_local_matrix_() noexcept; + void mark_dirty_world_matrix_() noexcept; + void update_local_matrix_() const noexcept; + void update_world_matrix_() const noexcept; + private: + t3f transform_; node* parent_{nullptr}; node_children children_; + private: + mutable u32 flags_{0u}; + mutable m4f local_matrix_; + mutable m4f world_matrix_; }; } diff --git a/headers/enduro2d/high/scene/scene.hpp b/headers/enduro2d/high/scene/scene.hpp index 629be628..b6181efd 100644 --- a/headers/enduro2d/high/scene/scene.hpp +++ b/headers/enduro2d/high/scene/scene.hpp @@ -9,13 +9,6 @@ #include "../_high.hpp" #include "node.hpp" -namespace e2d -{ - class scene; - using scene_iptr = intrusive_ptr; - using const_scene_iptr = intrusive_ptr; -} - namespace e2d { class scene @@ -25,7 +18,8 @@ namespace e2d virtual ~scene() noexcept; static scene_iptr create(); - const node_iptr& root() const noexcept; + node_iptr root() noexcept; + const_node_iptr root() const noexcept; protected: scene(); private: diff --git a/sources/enduro2d/high/scene/node.cpp b/sources/enduro2d/high/scene/node.cpp index 2f39bf43..eafcfd14 100644 --- a/sources/enduro2d/high/scene/node.cpp +++ b/sources/enduro2d/high/scene/node.cpp @@ -37,6 +37,29 @@ namespace e2d return child; } + void node::transform(const t3f& transform) noexcept { + transform_ = transform; + mark_dirty_local_matrix_(); + } + + const t3f& node::transform() const noexcept { + return transform_; + } + + const m4f& node::local_matrix() const noexcept { + if ( math::check_and_clear_all_flags(flags_, fm_dirty_local_matrix) ) { + update_local_matrix_(); + } + return local_matrix_; + } + + const m4f& node::world_matrix() const noexcept { + if ( math::check_and_clear_all_flags(flags_, fm_dirty_world_matrix) ) { + update_world_matrix_(); + } + return world_matrix_; + } + node_iptr node::root() noexcept { node* n = this; while ( n->parent_ ) { @@ -368,8 +391,36 @@ namespace e2d namespace e2d { void node::on_change_parent_() noexcept { + mark_dirty_world_matrix_(); } void node::on_change_children_() noexcept { } } + +namespace e2d +{ + void node::mark_dirty_local_matrix_() noexcept { + if ( math::check_and_set_any_flags(flags_, fm_dirty_local_matrix) ) { + mark_dirty_world_matrix_(); + } + } + + void node::mark_dirty_world_matrix_() noexcept { + if ( math::check_and_set_any_flags(flags_, fm_dirty_world_matrix) ) { + for ( node& child : children_ ) { + child.mark_dirty_world_matrix_(); + } + } + } + + void node::update_local_matrix_() const noexcept { + local_matrix_ = math::make_trs_matrix4(transform_); + } + + void node::update_world_matrix_() const noexcept { + world_matrix_ = parent_ + ? parent_->world_matrix() * local_matrix() + : local_matrix(); + } +} diff --git a/sources/enduro2d/high/scene/scene.cpp b/sources/enduro2d/high/scene/scene.cpp index 6ba04a0f..c81d6271 100644 --- a/sources/enduro2d/high/scene/scene.cpp +++ b/sources/enduro2d/high/scene/scene.cpp @@ -15,7 +15,11 @@ namespace e2d return scene_iptr(new scene()); } - const node_iptr& scene::root() const noexcept { + node_iptr scene::root() noexcept { + return root_; + } + + const_node_iptr scene::root() const noexcept { return root_; } } diff --git a/untests/sources/untests_high/scene.cpp b/untests/sources/untests_high/scene.cpp index 56c3a54e..64160c6b 100644 --- a/untests/sources/untests_high/scene.cpp +++ b/untests/sources/untests_high/scene.cpp @@ -803,4 +803,68 @@ TEST_CASE("node") { REQUIRE(fake_node::s_parent_changes == 5); REQUIRE(fake_node::s_children_changes == 4); } + SECTION("local_matrix") { + { + auto p = node::create(); + p->transform(math::make_translation_trs3(v3f{10.f,0.f,0.f})); + + auto n = node::create(p); + n->transform(math::make_translation_trs3(v3f{20.f,0.f,0.f})); + REQUIRE(n->local_matrix() == math::make_translation_matrix4(20.f,0.f,0.f)); + + auto v = v4f(5.f,0.f,0.f,1.f); + REQUIRE(v * n->local_matrix() == v4f{25.f,0.f,0.f,1.f}); + + n->transform(math::make_scale_trs3(v3f(1.f,2.f,3.f))); + REQUIRE(n->local_matrix() == math::make_scale_matrix4(1.f,2.f,3.f)); + } + } + SECTION("world_matrix") { + { + auto p = node::create(); + p->transform(math::make_translation_trs3(v3f{10.f,0.f,0.f})); + + auto n = node::create(p); + n->transform(math::make_translation_trs3(v3f{20.f,0.f,0.f})); + + auto v = v4f(5.f,0.f,0.f,1.f); + REQUIRE(v * n->world_matrix() == v4f{35.f,0.f,0.f,1.f}); + + n->transform(math::make_scale_trs3(v3f(1.f,2.f,3.f))); + REQUIRE(n->world_matrix() == + math::make_scale_matrix4(1.f,2.f,3.f) * + math::make_translation_matrix4(10.f,0.f,0.f)); + } + { + auto n = node::create(); + n->transform(math::make_translation_trs3(v3f{20.f,0.f,0.f})); + REQUIRE(n->world_matrix() == + math::make_translation_matrix4(20.f,0.f,0.f)); + + auto p = node::create(); + p->transform(math::make_translation_trs3(v3f{10.f,0.f,0.f})); + + p->add_child(n); + REQUIRE(n->world_matrix() == + math::make_translation_matrix4(30.f,0.f,0.f)); + } + { + auto p1 = node::create(); + p1->transform(math::make_translation_trs3(v3f{10.f,0.f,0.f})); + + auto p2 = node::create(); + p2->transform(math::make_translation_trs3(v3f{20.f,0.f,0.f})); + + auto n = node::create(p2); + n->transform(math::make_translation_trs3(v3f{30.f,0.f,0.f})); + + REQUIRE(n->world_matrix() == + math::make_translation_matrix4(50.f,0.f,0.f)); + + p1->add_child(p2); + + REQUIRE(n->world_matrix() == + math::make_translation_matrix4(60.f,0.f,0.f)); + } + } }