mirror of
https://github.com/enduro2d/enduro2d.git
synced 2025-12-14 16:09:06 +07:00
node: local and world transforms
This commit is contained in:
@@ -18,6 +18,24 @@ namespace e2d
|
||||
}
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
class node;
|
||||
using node_iptr = intrusive_ptr<node>;
|
||||
using const_node_iptr = intrusive_ptr<const node>;
|
||||
|
||||
class scene;
|
||||
using scene_iptr = intrusive_ptr<scene>;
|
||||
using const_scene_iptr = intrusive_ptr<const scene>;
|
||||
|
||||
class actor;
|
||||
using actor_iptr = intrusive_ptr<actor>;
|
||||
using const_actor_iptr = intrusive_ptr<const actor>;
|
||||
|
||||
class node_children_ilist_tag {};
|
||||
using node_children = intrusive_list<node, node_children_ilist_tag>;
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
class camera;
|
||||
|
||||
@@ -9,13 +9,6 @@
|
||||
#include "../_high.hpp"
|
||||
#include "node.hpp"
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
class actor;
|
||||
using actor_iptr = intrusive_ptr<actor>;
|
||||
using const_actor_iptr = intrusive_ptr<const actor>;
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
class actor final : public node {
|
||||
|
||||
@@ -10,16 +10,6 @@
|
||||
|
||||
#include "../_high.hpp"
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
class node;
|
||||
using node_iptr = intrusive_ptr<node>;
|
||||
using const_node_iptr = intrusive_ptr<const node>;
|
||||
|
||||
class node_children_ilist_tag {};
|
||||
using node_children = intrusive_list<node, node_children_ilist_tag>;
|
||||
}
|
||||
|
||||
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_;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -9,13 +9,6 @@
|
||||
#include "../_high.hpp"
|
||||
#include "node.hpp"
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
class scene;
|
||||
using scene_iptr = intrusive_ptr<scene>;
|
||||
using const_scene_iptr = intrusive_ptr<const scene>;
|
||||
}
|
||||
|
||||
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:
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user