]> git.tdb.fi Git - libs/game.git/commitdiff
Add a transform component and propagation system
authorMikko Rasa <tdb@tdb.fi>
Sat, 22 Oct 2022 19:52:04 +0000 (22:52 +0300)
committerMikko Rasa <tdb@tdb.fi>
Sat, 22 Oct 2022 19:52:04 +0000 (22:52 +0300)
source/game/entity.cpp
source/game/entity.h
source/game/root.h
source/game/transform.cpp [new file with mode: 0644]
source/game/transform.h [new file with mode: 0644]
source/game/transformpropagator.cpp [new file with mode: 0644]
source/game/transformpropagator.h [new file with mode: 0644]

index 055c8d63d4dde384c6f3e4cead73b770a76b8c79..8f56f670cb50de28c68876c64f12a9405c6c3278 100644 (file)
@@ -1,15 +1,27 @@
 #include "entity.h"
 #include "component.h"
 #include "root.h"
+#include "transform.h"
 
 using namespace std;
 
 namespace Msp::Game {
 
-Entity::Entity(Handle<Entity> p):
+Entity::Entity(Handle<Entity> p, TransformTag):
        parent(p)
 { }
 
+Entity::Entity(Handle<Entity> p, const TransformValues &tv):
+       parent(p),
+       transform(*this)
+{
+       transform->set_values(tv);
+}
+
+// Hide ~Owned<Transform> from the header
+Entity::~Entity()
+{ }
+
 void Entity::add_component(Handle<Component> comp)
 {
        if(comp->get_entity().get()!=this)
index 7c7ee89fe85cf2c117fccd7f259540dafe3b93b6..c28cbe5075b94567709ba562f33698097e959e29 100644 (file)
@@ -2,11 +2,14 @@
 #define MSP_GAME_ENTITY_H_
 
 #include "handle.h"
+#include "owned.h"
 
 namespace Msp::Game {
 
 class Component;
 class Stage;
+class Transform;
+struct TransformValues;
 
 class hierarchy_error: public std::logic_error
 {
@@ -16,13 +19,18 @@ public:
 
 class Entity
 {
+public:
+       enum TransformTag { NO_TRANSFORM };
+
 private:
        Handle<Entity> parent;
        std::vector<Handle<Component>> components;
        std::vector<Handle<Entity>> children;
+       Owned<Transform> transform;
 
 public:
-       Entity(Handle<Entity>);
+       Entity(Handle<Entity>, TransformTag);
+       Entity(Handle<Entity>, const TransformValues &);
        virtual ~Entity();
 
        void add_component(Handle<Component>);
@@ -33,7 +41,9 @@ public:
 
        Handle<Entity> get_parent() const { return parent; }
        Handle<Entity> get_root();
+       const std::vector<Handle<Entity>> &get_children() const { return children; }
        Stage &get_stage();
+       Handle<Transform> get_transform() const { return transform; }
 };
 
 
index 4f7ea76101c4b831d2be304b922a047e508b4f19..726ca32693bb385c4b9a46c9f5974594fe9ed0c4 100644 (file)
@@ -13,7 +13,7 @@ private:
        Stage &stage;
 
 public:
-       Root(Stage &s): Entity(nullptr), stage(s) { }
+       Root(Stage &s): Entity(nullptr, NO_TRANSFORM), stage(s) { }
 
        Stage &get_stage() const { return stage; }
 };
diff --git a/source/game/transform.cpp b/source/game/transform.cpp
new file mode 100644 (file)
index 0000000..6af24c0
--- /dev/null
@@ -0,0 +1,27 @@
+#include "transform.h"
+#include <msp/geometry/affinetransform.h>
+
+namespace Msp::Game {
+
+Transform::Transform(Handle<Entity> e):
+       Component(e)
+{ }
+
+void Transform::set_values(const TransformValues &v)
+{
+       values = v;
+}
+
+void Transform::update_world_matrix(const Transform *parent)
+{
+       using Affine = Geometry::AffineTransform<float, 3>;
+
+       local_matrix = Affine::translation(values.position)*
+               Affine::rotation(values.rotation)*Affine::scaling(values.scale);
+       if(parent)
+               world_matrix = parent->get_world_matrix()*local_matrix;
+       else
+               world_matrix = local_matrix;
+}
+
+} // namespace Msp::Game
diff --git a/source/game/transform.h b/source/game/transform.h
new file mode 100644 (file)
index 0000000..d698499
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef MSP_GAME_TRANSFORM_H_
+#define MSP_GAME_TRANSFORM_H_
+
+#include <msp/geometry/quaternion.h>
+#include <msp/linal/matrix.h>
+#include <msp/linal/vector.h>
+#include "component.h"
+
+namespace Msp::Game {
+
+struct TransformValues
+{
+       LinAl::Vector<float, 3> position;
+       Geometry::Quaternion<float> rotation = Geometry::Quaternion<float>::one();
+       LinAl::Vector<float, 3> scale = { 1.0f, 1.0f, 1.0f };
+};
+
+class Transform: public Component
+{
+private:
+       TransformValues values;
+       LinAl::Matrix<float, 4, 4> local_matrix;
+       LinAl::Matrix<float, 4, 4> world_matrix;
+
+public:
+       Transform(Handle<Entity>);
+
+       void set_values(const TransformValues &);
+       const TransformValues &get_values() const { return values; }
+       const LinAl::Vector<float, 3> &get_position() const { return values.position; }
+       const Geometry::Quaternion<float> &get_rotation() const { return values.rotation; }
+       const LinAl::Vector<float, 3> &get_scale() const { return values.scale; }
+       const LinAl::Matrix<float, 4, 4> &get_world_matrix() const { return world_matrix; }
+
+       void update_world_matrix(const Transform *);
+};
+
+} // namespace Msp::Game
+
+#endif
diff --git a/source/game/transformpropagator.cpp b/source/game/transformpropagator.cpp
new file mode 100644 (file)
index 0000000..73ecf64
--- /dev/null
@@ -0,0 +1,47 @@
+#include "transformpropagator.h"
+#include "events.h"
+#include "root.h"
+#include "stage.h"
+#include "transform.h"
+
+using namespace std;
+
+namespace Msp::Game {
+
+TransformPropagator::TransformPropagator(Stage &s):
+       System(s),
+       observer(s.get_event_bus())
+{
+       observer.observe<Events::EntityCreated>([this](const Events::EntityCreated &){ transforms_dirty = true; });
+       observer.observe<Events::EntityDestroyed>([this](const Events::EntityDestroyed &){ transforms_dirty = true; });
+}
+
+void TransformPropagator::tick(Time::TimeDelta)
+{
+       if(transforms_dirty)
+               rebuild_transform_order();
+
+       for(const ParentedTransform &t: transforms)
+               t.transform->update_world_matrix(t.parent.get());
+}
+
+void TransformPropagator::rebuild_transform_order()
+{
+       transforms.clear();
+       for(Handle<Entity> c: stage.get_root()->get_children())
+               rebuild_transform_order(c, nullptr);
+}
+
+void TransformPropagator::rebuild_transform_order(Handle<Entity> entity, Handle<Transform> parent_trans)
+{
+       if(Handle<Transform> trans = entity->get_transform())
+       {
+               transforms.emplace_back(trans, parent_trans);
+               parent_trans = trans;
+       }
+
+       for(Handle<Entity> c: entity->get_children())
+               rebuild_transform_order(c, parent_trans);
+}
+
+} // namespace Msp::Game
diff --git a/source/game/transformpropagator.h b/source/game/transformpropagator.h
new file mode 100644 (file)
index 0000000..3ac291b
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef MSP_GAME_TRANSFORMPROPAGATOR_H_
+#define MSP_GAME_TRANSFORMPROPAGATOR_H_
+
+#include "eventobserver.h"
+#include "handle.h"
+#include "system.h"
+
+namespace Msp::Game {
+
+class Entity;
+class Transform;
+
+class TransformPropagator: public System
+{
+private:
+       struct ParentedTransform
+       {
+               Handle<Transform> transform;
+               Handle<Transform> parent;
+       };
+
+       EventObserver observer;
+       std::vector<ParentedTransform> transforms;
+       bool transforms_dirty = true;
+
+public:
+       TransformPropagator(Stage &);
+
+       void tick(Time::TimeDelta) override;
+
+private:
+       void rebuild_transform_order();
+       void rebuild_transform_order(Handle<Entity>, Handle<Transform>);
+};
+
+} // namespace Msp::Game
+
+#endif