#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)
#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
{
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>);
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; }
};
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; }
};
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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