Browse Source

Interpolating transforms for animation.Implemented Primitives as variants to avoid RTTI and virtual function calls. Now targeting C++17.

Michael 7 years ago
parent
commit
3397937d79

+ 12 - 0
Build/CMakeLists.txt

@@ -275,6 +275,9 @@ foreach(library ${LIBRARIES})
                            VERSION ${PROJECT_VERSION}
                            SOVERSION ${LIBROCKET_VERSION_MAJOR}
     )
+	
+	set_property(TARGET ${NAME} PROPERTY CXX_STANDARD 17)
+	set_property(TARGET ${NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
     
     install(TARGETS ${NAME}
             EXPORT libRocketTargets
@@ -591,6 +594,9 @@ endif(NOT BUILD_FRAMEWORK)
     # Build and install the basic samples
     foreach(sample ${samples})
         bl_sample(${sample} ${sample_LIBRARIES})
+		
+		set_property(TARGET ${sample} PROPERTY CXX_STANDARD 17)
+		set_property(TARGET ${sample} PROPERTY CXX_STANDARD_REQUIRED ON)
 
         # The samples always set this as their current working directory
         install(DIRECTORY DESTINATION ${SAMPLES_DIR}/basic/${sample})
@@ -708,6 +714,8 @@ endif(NOT BUILD_FRAMEWORK)
 
     # Build and install invaders sample
     bl_sample(invaders ${sample_LIBRARIES})
+	set_property(TARGET invaders PROPERTY CXX_STANDARD 17)
+	set_property(TARGET invaders PROPERTY CXX_STANDARD_REQUIRED ON)
     install(DIRECTORY DESTINATION ${SAMPLES_DIR}/invaders)
     install(TARGETS invaders 
     	RUNTIME DESTINATION ${SAMPLES_DIR}/invaders
@@ -716,6 +724,8 @@ endif(NOT BUILD_FRAMEWORK)
 	if(BUILD_PYTHON_BINDINGS)
 	    # Build and install pyinvaders sample
 	    bl_sample(pyinvaders ${sample_LIBRARIES} ${PYTHON_LIBRARIES} ${PY_BINDINGS_LINK_LIBS})
+		set_property(TARGET pyinvaders PROPERTY CXX_STANDARD 17)
+		set_property(TARGET pyinvaders PROPERTY CXX_STANDARD_REQUIRED ON)
 	    install(DIRECTORY DESTINATION ${SAMPLES_DIR}/pyinvaders)
 	    install(TARGETS pyinvaders
 	    	RUNTIME DESTINATION ${SAMPLES_DIR}/pyinvaders
@@ -724,6 +734,8 @@ endif(NOT BUILD_FRAMEWORK)
     
     if(BUILD_LUA_BINDINGS)
         bl_sample(luainvaders RocketCoreLua RocketControlsLua ${sample_LIBRARIES} ${LUA_BINDINGS_LINK_LIBS})
+		set_property(TARGET luainvaders PROPERTY CXX_STANDARD 17)
+		set_property(TARGET luainvaders PROPERTY CXX_STANDARD_REQUIRED ON)
         install(DIRECTORY DESTINATION ${SAMPLES_DIR}/luainvaders)
         install(TARGETS luainvaders 
         	RUNTIME DESTINATION ${SAMPLES_DIR}/luainvaders

+ 2 - 2
Include/Rocket/Core/Matrix4.h

@@ -315,9 +315,9 @@ class Matrix4
 		static const ThisType FromColumnMajor(const ComponentType* components) throw();
 
 		// Convert to raw values; keep the storage mode in mind.
-		inline operator Component*() throw()
+		inline Component* data() throw()
 			{ return &vectors[0][0]; }
-		inline operator const Component*() const throw()
+		inline const Component* data() const throw()
 			{ return &vectors[0][0]; }
 
 		/// Get the i-th row

+ 2 - 2
Include/Rocket/Core/Matrix4.inl

@@ -180,8 +180,8 @@ template< typename Component, class Storage >
 bool Matrix4< Component, Storage >::Invert() throw()
 {
 	Matrix4< Component, Storage >::ThisType result;
-	Component *dst = result;
-	const Component *src = *this;
+	Component *dst = result.data();
+	const Component *src = data();
 
 	dst[0] = src[5]  * src[10] * src[15] -
 		src[5]  * src[11] * src[14] -

+ 0 - 56
Include/Rocket/Core/Reference.h

@@ -37,9 +37,6 @@ namespace Core {
 template< class ReferenceCountable >
 class ROCKETCORE_API SharedReference;
 
-template< class ReferenceCountable >
-class ROCKETCORE_API SharedConstReference;
-
 /**
 	A smart pointer class template that manages ReferenceCountables.
 	SharedReference allows unrestricted access to the shared object via every reference.
@@ -52,7 +49,6 @@ class ROCKETCORE_API SharedReference
 public:
 	typedef ReferenceCountable ReferenceType;
 	typedef SharedReference< ReferenceCountable > ThisType;
-	typedef SharedConstReference< ReferenceCountable > ReadOnlyType;
 
 	/// Constructor. Does not increase the object's reference count.
 	/// @param[in] object The object to refer to.
@@ -87,62 +83,10 @@ public:
 	ReferenceType* operator->() throw()
 		{ return object; }
 
-	friend ReadOnlyType;
-
 private:
 	mutable ReferenceType *object;
 };
 
-/**
-	A smart pointer class template that manages ReferenceCountables.
-	SharedConstReference allows read-only access to the shared object via every reference.
-	@author Markus Schöngart
-	@see ReferenceCountable
-*/
-template< class ReferenceCountable >
-class ROCKETCORE_API SharedConstReference
-{
-public:
-	typedef ReferenceCountable ReferenceType;
-	typedef SharedConstReference< ReferenceCountable > ThisType;
-	typedef SharedReference< ReferenceCountable > ReadWriteType;
-
-	/// Constructor. Does not increase the object's reference count.
-	/// @param[in] object The object to refer to.
-	SharedConstReference(ReferenceType* object = 0) : object(object)
-		{ /*if (object) object->AddReference();*/ }
-	/// Copy constructor. Increases the object's reference count.
-	/// @param[in] other The other Reference.
-	SharedConstReference(const ThisType& other) : object(other.object)
-		{ if (object) object->AddReference(); }
-	/// Constructor from read-write reference. Increases the object's reference count.
-	/// @param[in] other The other Reference.
-	SharedConstReference(const ReadWriteType& other) : object(other.object)
-		{ if (object) object->AddReference(); }
-	/// Destructor. Decrements the stored object's reference count.
-	~SharedConstReference()
-		{ if (object) object->RemoveReference(); }
-
-	/// Swaps the contents of two References.
-	void Swap(ThisType& other) throw()
-		{ std::swap(object, other.object); }
-
-	/// Assign another referenced object to this smart pointer
-	const ThisType& operator=(ReferenceType* ref)
-		{ ThisType tmp(ref); Swap(tmp); return *this; }
-	/// Assign another referenced object to this smart pointer
-	const ThisType& operator=(const ThisType& other)
-		{ ThisType tmp(other); Swap(tmp); return *this; }
-
-	const ReferenceType& operator*() const throw()
-		{ return *object; }
-
-	const ReferenceType* operator->() const throw()
-		{ return object; }
-
-private:
-	mutable ReferenceType *object;
-};
 
 }
 }

+ 11 - 6
Include/Rocket/Core/Transform.h

@@ -35,7 +35,7 @@ namespace Rocket {
 namespace Core {
 
 class ViewState;
-namespace Transforms { class Primitive; }
+namespace Transforms { struct Primitive; }
 
 /**
 	The Transform class holds the information parsed from an element's
@@ -51,6 +51,8 @@ namespace Transforms { class Primitive; }
 class ROCKETCORE_API Transform : public ReferenceCountable
 {
 public:
+	typedef std::vector< Transforms::Primitive > Primitives;
+
 	/// Default constructor, initializes an identity transform
 	Transform();
 
@@ -68,21 +70,24 @@ public:
 
 	/// Remove all Primitives from this Transform
 	void ClearPrimitives();
+
 	/// Add a Primitive to this Transform
 	void AddPrimitive(const Transforms::Primitive& p);
+
 	/// Return the number of Primitives in this Transform
-	int GetNumPrimitives() const throw()
-		{ return (int)primitives.size(); }
+	int GetNumPrimitives() const throw();
+
 	/// Return the i-th Primitive in this Transform
-	const Transforms::Primitive& GetPrimitive(int i) const throw()
-		{ return *primitives[i]; }
+	const Transforms::Primitive& GetPrimitive(int i) const throw();
+
+	Primitives& GetPrimitives() throw() { return primitives; }
+	const Primitives& GetPrimitives() const throw() { return primitives; }
 
 protected:
 	void OnReferenceDeactivate()
 		{ delete this; }
 
 private:
-	typedef std::vector< Transforms::Primitive * > Primitives;
 	Primitives primitives;
 };
 

+ 99 - 202
Include/Rocket/Core/TransformPrimitive.h

@@ -31,10 +31,12 @@
 #include "Header.h"
 #include "Types.h"
 #include "Property.h"
+#include <variant>
 
 namespace Rocket {
 namespace Core {
 namespace Transforms {
+	
 
 struct NumericValue
 {
@@ -56,282 +58,177 @@ struct NumericValue
 	Rocket::Core::Property::Unit unit;
 };
 
-/**
-	The Primitive class is the base class of geometric transforms such as rotations, scalings and translations.
-	Instances of this class are added to a Rocket::Core::Transform instance
-	by the Rocket::Core::PropertyParserTransform, which is responsible for
-	parsing the `transform' property.
 
-	@author Markus Schöngart
-	@see Rocket::Core::Transform
-	@see Rocket::Core::PropertyParserTransform
- */
-class Primitive
-{
-	public:
-		virtual ~Primitive() { }
-
-		virtual Primitive* Clone() const = 0;
-
-		/// Resolve the transformation matrix encoded by the primitive.
-		/// @param m The transformation matrix to resolve the Primitive to.
-		/// @param e The Element which to resolve the Primitive for.
-		/// @return true if the Primitive encodes a transformation.
-		virtual bool ResolveTransform(Matrix4f& m, Element& e) const throw()
-			{ return false; }
-		/// Resolve the perspective value encoded by the primitive.
-		/// @param p The perspective value to resolve the Primitive to.
-		/// @param e The Element which to resolve the Primitive for.
-		/// @return true if the Primitive encodes a perspective value.
-		virtual bool ResolvePerspective(float &p, Element& e) const throw()
-			{ return false; }
-};
 
 template< size_t N >
-class ResolvedValuesPrimitive : public Primitive
-{
-	public:
-		ResolvedValuesPrimitive(const NumericValue* values) throw()
-			{ for (size_t i = 0; i < N; ++i) this->values[i] = values[i].number; }
-
-		void InterpolateValues(const ResolvedValuesPrimitive<N>& other, float alpha)
-		{
-			for (size_t i = 0; i < N; i++)
-				values[i] = values[i]*(1.0f - alpha) + other.values[i] * alpha;
-		}
-
-	protected:
-		float values[N];
+struct ResolvedValuesPrimitive
+{
+	ResolvedValuesPrimitive(const float* values) throw()
+	{
+		for (size_t i = 0; i < N; ++i)
+			this->values[i] = values[i];
+	}
+	ResolvedValuesPrimitive(const NumericValue* values) throw()
+	{
+		for (size_t i = 0; i < N; ++i) 
+			this->values[i] = values[i].number;
+	}
+	
+	float values[N];
 };
 
 template< size_t N >
-class UnresolvedValuesPrimitive : public Primitive
+struct UnresolvedValuesPrimitive
 {
-	public:
-		UnresolvedValuesPrimitive(const NumericValue* values) throw()
-			{ memcpy(this->values, values, sizeof(this->values)); }
-
-		void InterpolateValues(const UnresolvedValuesPrimitive<N>& other, float alpha)
-		{
-			// TODO: Convert between different units?
-			for (size_t i = 0; i < N; i++)
-				values[i].number = values[i].number*(1.0f - alpha) + other.values[i].number * alpha;
-		}
-
-	protected:
-		NumericValue values[N];
+	UnresolvedValuesPrimitive(const NumericValue* values) throw()
+	{
+		memcpy(this->values, values, sizeof(this->values));
+	}
+
+	NumericValue values[N];
 };
 
-class Matrix2D : public ResolvedValuesPrimitive< 6 >
-{
-	public:
-		Matrix2D(const NumericValue* values) throw()
-			: ResolvedValuesPrimitive(values) { }
 
-		inline Primitive* Clone() const { return new Matrix2D(*this); }
-		bool ResolveTransform(Matrix4f& m, Element& e) const throw();
-};
 
-class Matrix3D : public ResolvedValuesPrimitive< 16 >
-{
-	public:
-		Matrix3D(const NumericValue* values) throw()
-			: ResolvedValuesPrimitive(values) { }
 
-		inline Primitive* Clone() const { return new Matrix3D(*this); }
-		bool ResolveTransform(Matrix4f& m, Element& e) const throw();
-};
 
-class TranslateX : public UnresolvedValuesPrimitive< 1 >
+struct Matrix2D : public ResolvedValuesPrimitive< 6 >
 {
-	public:
-		TranslateX(const NumericValue* values) throw()
-			: UnresolvedValuesPrimitive(values) { }
-
-		inline Primitive* Clone() const { return new TranslateX(*this); }
-		bool ResolveTransform(Matrix4f& m, Element& e) const throw();
+	Matrix2D(const NumericValue* values) throw() : ResolvedValuesPrimitive(values) { }
 };
 
-class TranslateY : public UnresolvedValuesPrimitive< 1 >
+struct Matrix3D : public ResolvedValuesPrimitive< 16 >
 {
-	public:
-		TranslateY(const NumericValue* values) throw()
-			: UnresolvedValuesPrimitive(values) { }
-
-		inline Primitive* Clone() const { return new TranslateY(*this); }
-		bool ResolveTransform(Matrix4f& m, Element& e) const throw();
+	Matrix3D(const Matrix4f& matrix) throw() : ResolvedValuesPrimitive(matrix.data()) { }
+	Matrix3D(const NumericValue* values) throw() : ResolvedValuesPrimitive(values) { }
 };
 
-class TranslateZ : public UnresolvedValuesPrimitive< 1 >
+struct TranslateX : public UnresolvedValuesPrimitive< 1 >
 {
-	public:
-		TranslateZ(const NumericValue* values) throw()
-			: UnresolvedValuesPrimitive(values) { }
-
-		inline Primitive* Clone() const { return new TranslateZ(*this); }
-		bool ResolveTransform(Matrix4f& m, Element& e) const throw();
+	TranslateX(const NumericValue* values) throw() : UnresolvedValuesPrimitive(values) { }
 };
 
-class Translate2D : public UnresolvedValuesPrimitive< 2 >
+struct TranslateY : public UnresolvedValuesPrimitive< 1 >
 {
-	public:
-		Translate2D(const NumericValue* values) throw()
-			: UnresolvedValuesPrimitive(values) { }
-
-		inline Primitive* Clone() const { return new Translate2D(*this); }
-		bool ResolveTransform(Matrix4f& m, Element& e) const throw();
+	TranslateY(const NumericValue* values) throw() : UnresolvedValuesPrimitive(values) { }
 };
 
-class Translate3D : public UnresolvedValuesPrimitive< 3 >
+struct TranslateZ : public UnresolvedValuesPrimitive< 1 >
 {
-	public:
-		Translate3D(const NumericValue* values) throw()
-			: UnresolvedValuesPrimitive(values) { }
-
-		inline Primitive* Clone() const { return new Translate3D(*this); }
-		bool ResolveTransform(Matrix4f& m, Element& e) const throw();
+	TranslateZ(const NumericValue* values) throw() : UnresolvedValuesPrimitive(values) { }
 };
 
-class ScaleX : public ResolvedValuesPrimitive< 1 >
+struct Translate2D : public UnresolvedValuesPrimitive< 2 >
 {
-	public:
-		ScaleX(const NumericValue* values) throw()
-			: ResolvedValuesPrimitive(values) { }
-
-		inline Primitive* Clone() const { return new ScaleX(*this); }
-		bool ResolveTransform(Matrix4f& m, Element& e) const throw();
+	Translate2D(const NumericValue* values) throw() : UnresolvedValuesPrimitive(values) { }
 };
 
-class ScaleY : public ResolvedValuesPrimitive< 1 >
+struct Translate3D : public UnresolvedValuesPrimitive< 3 >
 {
-	public:
-		ScaleY(const NumericValue* values) throw()
-			: ResolvedValuesPrimitive(values) { }
-
-		inline Primitive* Clone() const { return new ScaleY(*this); }
-		bool ResolveTransform(Matrix4f& m, Element& e) const throw();
+	Translate3D(const NumericValue* values) throw() : UnresolvedValuesPrimitive(values) { }
 };
 
-class ScaleZ : public ResolvedValuesPrimitive< 1 >
+struct ScaleX : public ResolvedValuesPrimitive< 1 >
 {
-	public:
-		ScaleZ(const NumericValue* values) throw()
-			: ResolvedValuesPrimitive(values) { }
-
-		inline Primitive* Clone() const { return new ScaleZ(*this); }
-		bool ResolveTransform(Matrix4f& m, Element& e) const throw();
+	ScaleX(const NumericValue* values) throw() : ResolvedValuesPrimitive(values) { }
 };
 
-class Scale2D : public ResolvedValuesPrimitive< 2 >
+struct ScaleY : public ResolvedValuesPrimitive< 1 >
 {
-	public:
-		Scale2D(const NumericValue* values) throw()
-			: ResolvedValuesPrimitive(values) { }
-
-		inline Primitive* Clone() const { return new Scale2D(*this); }
-		bool ResolveTransform(Matrix4f& m, Element& e) const throw();
+	ScaleY(const NumericValue* values) throw() : ResolvedValuesPrimitive(values) { }
 };
 
-class Scale3D : public ResolvedValuesPrimitive< 3 >
+struct ScaleZ : public ResolvedValuesPrimitive< 1 >
 {
-	public:
-		Scale3D(const NumericValue* values) throw()
-			: ResolvedValuesPrimitive(values) { }
-
-		inline Primitive* Clone() const { return new Scale3D(*this); }
-		bool ResolveTransform(Matrix4f& m, Element& e) const throw();
+	ScaleZ(const NumericValue* values) throw() : ResolvedValuesPrimitive(values) { }
 };
 
-class RotateX : public ResolvedValuesPrimitive< 1 >
+struct Scale2D : public ResolvedValuesPrimitive< 2 >
 {
-	public:
-		RotateX(const NumericValue* values) throw()
-			: ResolvedValuesPrimitive(values) { }
-
-		inline Primitive* Clone() const { return new RotateX(*this); }
-		bool ResolveTransform(Matrix4f& m, Element& e) const throw();
+	Scale2D(const NumericValue* values) throw() : ResolvedValuesPrimitive(values) { }
 };
 
-class RotateY : public ResolvedValuesPrimitive< 1 >
+struct Scale3D : public ResolvedValuesPrimitive< 3 >
 {
-	public:
-		RotateY(const NumericValue* values) throw()
-			: ResolvedValuesPrimitive(values) { }
-
-		inline Primitive* Clone() const { return new RotateY(*this); }
-		bool ResolveTransform(Matrix4f& m, Element& e) const throw();
+	Scale3D(const NumericValue* values) throw() : ResolvedValuesPrimitive(values) { }
 };
 
-class RotateZ : public ResolvedValuesPrimitive< 1 >
+struct RotateX : public ResolvedValuesPrimitive< 1 >
 {
-	public:
-		RotateZ(const NumericValue* values) throw()
-			: ResolvedValuesPrimitive(values) { }
-
-		inline Primitive* Clone() const { return new RotateZ(*this); }
-		bool ResolveTransform(Matrix4f& m, Element& e) const throw();
+	RotateX(const NumericValue* values) throw() : ResolvedValuesPrimitive(values) { }
 };
 
-class Rotate2D : public ResolvedValuesPrimitive< 1 >
+struct RotateY : public ResolvedValuesPrimitive< 1 >
 {
-	public:
-		Rotate2D(const NumericValue* values) throw()
-			: ResolvedValuesPrimitive(values) { }
-
-		inline Primitive* Clone() const { return new Rotate2D(*this); }
-		bool ResolveTransform(Matrix4f& m, Element& e) const throw();
+	RotateY(const NumericValue* values) throw() : ResolvedValuesPrimitive(values) {}
 };
 
-class Rotate3D : public ResolvedValuesPrimitive< 4 >
+struct RotateZ : public ResolvedValuesPrimitive< 1 >
 {
-	public:
-		Rotate3D(const NumericValue* values) throw()
-			: ResolvedValuesPrimitive(values) { }
-
-		inline Primitive* Clone() const { return new Rotate3D(*this); }
-		bool ResolveTransform(Matrix4f& m, Element& e) const throw();
+	RotateZ(const NumericValue* values) throw() : ResolvedValuesPrimitive(values) { }
 };
 
-class SkewX : public ResolvedValuesPrimitive< 1 >
+struct Rotate2D : public ResolvedValuesPrimitive< 1 >
 {
-	public:
-		SkewX(const NumericValue* values) throw()
-			: ResolvedValuesPrimitive(values) { }
+	Rotate2D(const NumericValue* values) throw() : ResolvedValuesPrimitive(values) { }
+};
 
-		inline Primitive* Clone() const { return new SkewX(*this); }
-		bool ResolveTransform(Matrix4f& m, Element& e) const throw();
+struct Rotate3D : public ResolvedValuesPrimitive< 4 >
+{
+	Rotate3D(const NumericValue* values) throw() : ResolvedValuesPrimitive(values) { }
 };
 
-class SkewY : public ResolvedValuesPrimitive< 1 >
+struct SkewX : public ResolvedValuesPrimitive< 1 >
 {
-	public:
-		SkewY(const NumericValue* values) throw()
-			: ResolvedValuesPrimitive(values) { }
+	SkewX(const NumericValue* values) throw() : ResolvedValuesPrimitive(values) { }
+};
 
-		inline Primitive* Clone() const { return new SkewY(*this); }
-		bool ResolveTransform(Matrix4f& m, Element& e) const throw();
+struct SkewY : public ResolvedValuesPrimitive< 1 >
+{
+	SkewY(const NumericValue* values) throw() : ResolvedValuesPrimitive(values) { }
 };
 
-class Skew2D : public ResolvedValuesPrimitive< 2 >
+struct Skew2D : public ResolvedValuesPrimitive< 2 >
 {
-	public:
-		Skew2D(const NumericValue* values) throw()
-			: ResolvedValuesPrimitive(values) { }
+	Skew2D(const NumericValue* values) throw() : ResolvedValuesPrimitive(values) { }
+};
 
-		inline Primitive* Clone() const { return new Skew2D(*this); }
-		bool ResolveTransform(Matrix4f& m, Element& e) const throw();
+struct Perspective : public UnresolvedValuesPrimitive< 1 >
+{
+	Perspective(const NumericValue* values) throw() : UnresolvedValuesPrimitive(values) { }
 };
 
-class Perspective : public UnresolvedValuesPrimitive< 1 >
+
+using PrimitiveVariant = std::variant<
+	Matrix2D, Matrix3D,
+	TranslateX, TranslateY, TranslateZ, Translate2D, Translate3D,
+	ScaleX, ScaleY, ScaleZ, Scale2D, Scale3D,
+	RotateX, RotateY, RotateZ, Rotate2D, Rotate3D,
+	SkewX, SkewY, Skew2D,
+	Perspective>;
+
+
+/**
+	The Primitive struct is the base struct of geometric transforms such as rotations, scalings and translations.
+	Instances of this struct are added to a Rocket::Core::Transform instance
+	by the Rocket::Core::PropertyParserTransform, which is responsible for
+	parsing the `transform' property.
+
+	@author Markus Schöngart
+	@see Rocket::Core::Transform
+	@see Rocket::Core::PropertyParserTransform
+ */
+struct Primitive
 {
-	public:
-		Perspective(const NumericValue* values) throw()
-			: UnresolvedValuesPrimitive(values) { }
+	PrimitiveVariant primitive;
 
-		inline Primitive* Clone() const { return new Perspective(*this); }
-		bool ResolvePerspective(float& p, Element& e) const throw();
+	void SetIdentity() throw();
+	bool ResolveTransform(Matrix4f& m, Element& e) const throw();
+	bool ResolvePerspective(float &p, Element& e) const throw();
+	bool InterpolateWith(const Primitive& other, float alpha) throw();
 };
 
+
 }
 }
 }

+ 1 - 1
Include/Rocket/Core/Types.h

@@ -114,7 +114,7 @@ namespace Rocket {
 namespace Core {
 
 // Reference types
-typedef SharedConstReference< Transform > TransformRef;
+typedef SharedReference< Transform > TransformRef;
 
 }
 }

+ 1 - 1
Samples/basic/animation/data/animation.rml

@@ -23,7 +23,7 @@
 		#start_game 
 		{
 			opacity: 0.5;
-			transform: rotate(10);
+			transform: rotate(10) translateX(100) scale(0.5);
 			transform-origin: 30% 80% 0;
 		}
 

+ 18 - 5
Samples/basic/animation/src/main.cpp

@@ -35,6 +35,17 @@
 #include <cmath>
 #include <sstream>
 
+
+// Animations TODO:
+//  - Jittering on slow margin-left updates
+//  - Proper interpolation of full transform matrices (split into translate/rotate/skew/scale). Also, support interpolation of primitive derivatives without going to full matrices.
+//  - Conversion between different units (resolve matrix units, e.g. to px / deg, when adding animation key).
+//  - Better error reporting when submitting invalid animations, check validity on add. Remove animation if invalid.
+//  - Tweening
+//  - RCSS support? Both @keyframes and transition, maybe.
+//  - Profiling
+//  - [offtopic] Improve performance of transform parser (hashtable)
+
 class DemoWindow
 {
 public:
@@ -57,11 +68,13 @@ public:
 			el->Animate("margin-left", Property(100.f, Property::PX), 8.0f, -1, true);
 
 			el = document->GetElementById("start_game");
-			auto transform = new Transform;
-			auto value = Transforms::NumericValue(370.f, Property::DEG);
-			transform->AddPrimitive(Transforms::Rotate2D{ &value });
-			TransformRef tref{ transform };
-			el->Animate("transform", Property(tref, Property::TRANSFORM), 0.8f, -1, false);
+			auto t0 = TransformRef{ new Transform };
+			auto v0 = Transforms::NumericValue(370.f, Property::DEG);
+			t0->AddPrimitive({ Transforms::Rotate2D{ &v0 } });
+			//auto t1 = TransformRef{ new Transform };
+			//auto v1 = Transforms::NumericValue(370.f, Property::DEG);
+			//t1->AddPrimitive({ Transforms::Rotate2D{ &v1 } });
+			el->Animate("transform", Property(t0, Property::TRANSFORM), 1.3f, -1, true);
 
 			el = document->GetElementById("help");
 			el->Animate("image-color", Property(Colourb(128, 255, 255, 255), Property::COLOUR), 0.3f, -1, false);

+ 2 - 2
Samples/shell/src/ShellRenderInterfaceOpenGL.cpp

@@ -285,13 +285,13 @@ void ShellRenderInterfaceOpenGL::ReleaseTexture(Rocket::Core::TextureHandle text
 void ShellRenderInterfaceOpenGL::PushTransform(const Rocket::Core::RowMajorMatrix4f& transform)
 {
 	glPushMatrix();
-	glLoadMatrixf(transform.Transpose());
+	glLoadMatrixf(transform.Transpose().data());
 	++m_transforms;
 }
 void ShellRenderInterfaceOpenGL::PushTransform(const Rocket::Core::ColumnMajorMatrix4f& transform)
 {
 	glPushMatrix();
-	glLoadMatrixf(transform);
+	glLoadMatrixf(transform.data());
 	++m_transforms;
 }
 

+ 2 - 2
Samples/shell/src/win32/ShellRenderInterfaceExtensionsOpenGL_Win32.cpp

@@ -48,10 +48,10 @@ void ShellRenderInterfaceOpenGL::SetViewport(int width, int height)
 		glViewport(0, 0, width, height);
 		projection = Rocket::Core::Matrix4f::ProjectOrtho(0, (float)width, (float)height, 0, -1, 1);
 		glMatrixMode(GL_PROJECTION);
-		glLoadMatrixf(projection);
+		glLoadMatrixf(projection.data());
 		view = Rocket::Core::Matrix4f::Identity();
 		glMatrixMode(GL_MODELVIEW);
-		glLoadMatrixf(view);
+		glLoadMatrixf(view.data());
 
 		if(m_rocket_context != NULL)
 		{

+ 12 - 12
Source/Core/Dictionary.cpp

@@ -222,12 +222,12 @@ Dictionary::~Dictionary()
  */
 Dictionary::DictionaryEntry* Dictionary::Retrieve(const String& key, Hash hash) const
 {
-	register Hash i = 0;
-	register unsigned int perturb;
-	register DictionaryEntry *freeslot;
-	register unsigned int mask = this->mask;
+	Hash i = 0;
+	unsigned int perturb;
+	DictionaryEntry *freeslot;
+	unsigned int mask = this->mask;
 	DictionaryEntry *ep0 = table;
-	register DictionaryEntry *ep;
+	DictionaryEntry *ep;
 	
 	DICTIONARY_DEBUG_CODE( Log::Message(LC_CORE, Log::LT_ALWAYS, "Dictionary::Retreive %s", key); )
 		/* Make sure this function doesn't have to handle non-string keys,
@@ -272,7 +272,7 @@ Dictionary::DictionaryEntry* Dictionary::Retrieve(const String& key, Hash hash)
  */
 void Dictionary::Insert(const String& key, Hash hash, const Variant& value)
 {	
-	register DictionaryEntry *ep;	
+	DictionaryEntry *ep;	
 	
 	DICTIONARY_DEBUG_CODE( Log::Message(LC_CORE, Log::LT_ALWAYS, "Dictionary::Insert %s", key); );
 	ep = Retrieve(key, hash);
@@ -426,8 +426,8 @@ void Dictionary::Set(const String& key, const Variant &value)
 		return;
 	}
 
-	register Hash hash;
-	register unsigned int n_used;  
+	Hash hash;
+	unsigned int n_used;  
 	
 	DICTIONARY_DEBUG_CODE( Log::Message(LC_CORE, Log::LT_ALWAYS, "Dictionary::Set %s", key); );
 	hash = StringUtilities::FNVHash( key.CString() );
@@ -454,8 +454,8 @@ void Dictionary::Set(const String& key, const Variant &value)
 
 bool Dictionary::Remove(const String& key)
 {
-	register Hash hash;
-	register DictionaryEntry *ep;  
+	Hash hash;
+	DictionaryEntry *ep;  
 	
 	DICTIONARY_DEBUG_CODE( Log::Message(LC_CORE, Log::LT_ALWAYS, "Dictionary::Remove %s", key) );
 	hash = StringUtilities::FNVHash( key.CString() );
@@ -540,7 +540,7 @@ void Dictionary::Merge(const Dictionary& dict)
 */
 bool Dictionary::Iterate(int &pos, String& key, Variant* &value) const
 {
-	register unsigned int i;
+	unsigned int i;
 
 	DICTIONARY_DEBUG_CODE( Log::Message(LC_CORE, Log::LT_ALWAYS, "Dictionary::Next %d", pos); )
 		i = pos;
@@ -562,7 +562,7 @@ void Dictionary::operator=(const Dictionary &dict) {
 }
 
 void Dictionary::Copy(const Dictionary &dict) {
-	register unsigned int i;
+	unsigned int i;
 	
 	DICTIONARY_DEBUG_CODE( Log::Message(LC_CORE, Log::LT_ALWAYS, "Dictionary::Copy Dict (Size: %d)", dict.num_used); )
 

+ 1 - 1
Source/Core/Element.cpp

@@ -920,7 +920,7 @@ void Element::Animate(const String & property_name, const Property & target_valu
 		animation->SetDuration(target_time);
 	}
 
-	animation->AddKey(target_time, target_value);
+	animation->AddKey(target_time, target_value, *this);
 }
 
 // Iterates over the properties defined on this element.

+ 169 - 69
Source/Core/ElementAnimation.cpp

@@ -27,7 +27,6 @@
 
 #include "precompiled.h"
 #include "ElementAnimation.h"
-#include "../../Include/Rocket/Core/Element.h"
 #include "../../Include/Rocket/Core/TransformPrimitive.h"
 
 namespace Rocket {
@@ -55,36 +54,6 @@ static Colourb ColourFromLinearSpace(Colourf c)
 	return result;
 }
 
-template<size_t N>
-static bool TryInterpolatePrimitive(Rocket::Core::Transforms::Primitive* p_inout, const Rocket::Core::Transforms::Primitive* p1, float alpha)
-{
-	using namespace Rocket::Core::Transforms;
-	bool result = false;
-	if (auto values0 = dynamic_cast<UnresolvedValuesPrimitive< N >*>(p_inout))
-	{
-		auto values1 = dynamic_cast<const UnresolvedValuesPrimitive< N >*>(p1);
-		if (values1) {
-			values0->InterpolateValues(*values1, alpha);
-			result = true;
-		}
-	}
-	return result;
-}
-template<size_t N>
-static bool TryInterpolateResolvedPrimitive(Rocket::Core::Transforms::Primitive* p_inout, const Rocket::Core::Transforms::Primitive* p1, float alpha)
-{
-	using namespace Rocket::Core::Transforms;
-	bool result = false;
-	if (auto values0 = dynamic_cast<ResolvedValuesPrimitive< N >*>(p_inout))
-	{
-		auto values1 = dynamic_cast<const ResolvedValuesPrimitive< N >*>(p1);
-		if (values1) {
-			values0->InterpolateValues(*values1, alpha);
-			result = true;
-		}
-	}
-	return result;
-}
 
 static Variant InterpolateValues(const Variant & v0, const Variant & v1, float alpha)
 {
@@ -116,48 +85,34 @@ static Variant InterpolateValues(const Variant & v0, const Variant & v1, float a
 	{
 		using namespace Rocket::Core::Transforms;
 
+		// Build the new, interpolating transform
+		auto t = TransformRef{ new Transform };
+
 		auto t0 = v0.Get<TransformRef>();
 		auto t1 = v1.Get<TransformRef>();
-		Primitive* p0 = t0->GetPrimitive(0).Clone();
-		const Primitive* p1 = &t1->GetPrimitive(0);
-
-
-		bool success = false;
-
-		// Todo: Check carefully for memory leaks
-		// Todo: Lots of dynamic dispatch, not good!
-		if (TryInterpolateResolvedPrimitive<1>(p0, p1, alpha))
-			success = true;
-		else if (TryInterpolateResolvedPrimitive<2>(p0, p1, alpha))
-			success = true;
-		else if (TryInterpolateResolvedPrimitive<3>(p0, p1, alpha))
-			success = true;
-		else if (TryInterpolateResolvedPrimitive<4>(p0, p1, alpha))
-			success = true;
-		else if (TryInterpolateResolvedPrimitive<6>(p0, p1, alpha))
-			success = true;
-		else if (TryInterpolateResolvedPrimitive<16>(p0, p1, alpha))
-			success = true;
-		else if (TryInterpolatePrimitive<1>(p0, p1, alpha))
-			success = true;
-		else if (TryInterpolatePrimitive<2>(p0, p1, alpha))
-			success = true;
-		else if (TryInterpolatePrimitive<3>(p0, p1, alpha))
-			success = true;
-
-
-		if(success)
+
+		const auto& p0 = t0->GetPrimitives();
+		const auto& p1 = t1->GetPrimitives();
+
+		if (p0.size() != p1.size())
 		{
-			auto transform = new Transform;
-			transform->AddPrimitive(*p0);
-			TransformRef tref{ transform };
-			delete p0;
-			return Variant(tref);
+			Log::Message(Log::LT_WARNING, "Transform primitives not of same size during interpolation.");
+			return Variant{ t0 };
 		}
-		else
+
+		for (size_t i = 0; i < p0.size(); i++)
 		{
-			delete p0;
+			Primitive p = p0[i];
+			if (!p.InterpolateWith(p1[i], alpha))
+			{
+				Log::Message(Log::LT_WARNING, "Transform primitives not of same type during interpolation.");
+				return Variant{ t0 };
+			}
+			t->AddPrimitive(p);
 		}
+
+		return Variant(t);
+
 		Log::Message(Log::LT_WARNING, "Could not decode transform for interpolation.");
 	}
 	}
@@ -169,13 +124,158 @@ static Variant InterpolateValues(const Variant & v0, const Variant & v1, float a
 
 
 
-bool ElementAnimation::AddKey(float time, const Property & property)
+enum class PrepareTransformResult { Unchanged = 0, ChangedT0 = 1, ChangedT1 = 2, ChangedT0andT1 = 3, Invalid = 4 };
+
+static PrepareTransformResult PrepareTransformPair(Transform& t0, Transform& t1, Element& element)
+{
+	using namespace Transforms;
+
+	// Insert missing primitives into transform
+	// See e.g. https://drafts.csswg.org/css-transforms-1/#interpolation-of-transforms for inspiration
+
+
+	auto& prims0 = t0.GetPrimitives();
+	auto& prims1 = t1.GetPrimitives();
+
+	// Check for trivial case where they contain the same primitives
+	if (prims0.size() == prims1.size())
+	{
+		bool same_primitives = true;
+		for (size_t i = 0; i < prims0.size(); i++)
+		{
+			if (prims0[i].primitive.index() != prims1[i].primitive.index())
+			{
+				same_primitives = false;
+				break;
+			}
+		}
+		if (same_primitives)
+			return PrepareTransformResult::Unchanged;
+	}
+
+	if (prims0.size() != prims1.size())
+	{
+		// Try to match the smallest set of primitives to the larger set, set missing keys in the small set to identity.
+		// Requirement: The small set must match types in the same order they appear in the big set.
+		// Example: (letter indicates type, number represent values)
+		// big:       a0 b0 c0 b1
+		//               ^     ^ 
+		// small:     b2 b3   
+		//            ^  ^
+		// new small: a1 b2 c1 b3   
+		bool prims0_smallest = (prims0.size() < prims1.size());
+
+		auto& small = (prims0_smallest ? prims0 : prims1);
+		auto& big = (prims0_smallest ? prims1 : prims0);
+
+		std::vector<size_t> matching_indices; // Indices into 'big' for matching types
+		matching_indices.reserve(small.size() + 1);
+
+		size_t big_index = 0;
+		bool match_success = true;
+
+		// Iterate through the small set to see if its types fit into the big set
+		for (size_t i = 0; i < small.size(); i++)
+		{
+			auto small_type = small[i].primitive.index();
+			match_success = false;
+
+			for (; big_index < big.size(); big_index++)
+			{
+				auto big_type = big[big_index].primitive.index();
+
+				if (small_type == big_type)
+				{
+					matching_indices.push_back(big_index);
+					match_success = true;
+					big_index += 1;
+					break;
+				}
+			}
+
+			if (!match_success)
+				break;
+		}
+
+
+		if (match_success)
+		{
+			// Success, insert the missing primitives into the small set
+			matching_indices.push_back(big.size()); // Needed to copy elements behind the last matching primitive
+			small.reserve(big.size());
+			size_t i0 = 0;
+			for (size_t match_index : matching_indices)
+			{
+				for (size_t i = i0; i < match_index; i++)
+				{
+					Primitive p = big[i];
+					p.SetIdentity();
+					small.insert(small.begin() + i, p);
+				}
+
+				// Next value to copy is one-past the matching primitive
+				i0 = match_index + 1;
+			}
+
+			return (prims0_smallest ? PrepareTransformResult::ChangedT0 : PrepareTransformResult::ChangedT1);
+		}
+	}
+
+
+	// If we get here, things get tricky. Need to do full matrix interpolation.
+	// We resolve the full transform here. This is not entirely correct if the elements box size changes
+	// during the animation. Ideally, we would resolve it during each iteration.
+	// For performance: We could also consider breaking up the transforms into their interpolating primitives (translate, rotate, skew, scale) here,
+	// instead of doing this every animation tick.
+	for(Transform* t : {&t0, &t1})
+	{
+		Matrix4f transform_value = Matrix4f::Identity();
+		for (const auto& primitive : t->GetPrimitives())
+		{
+			Matrix4f m;
+			if (primitive.ResolveTransform(m, element))
+				transform_value *= m;
+		}
+		t->ClearPrimitives();
+		t->AddPrimitive({ Matrix3D{transform_value} });
+	}
+
+	return PrepareTransformResult::ChangedT0andT1;
+}
+
+
+static bool PrepareTransforms(std::vector<AnimationKey>& keys, Element& element)
+{
+	for (int i = 1; i < (int)keys.size();)
+	{
+		auto& ref0 = keys[i - 1].value.Get<TransformRef>();
+		auto& ref1 = keys[i].value.Get<TransformRef>();
+
+		auto result = PrepareTransformPair(*ref0, *ref1, element);
+
+		bool changed_t0 = (result == PrepareTransformResult::ChangedT0 || result == PrepareTransformResult::ChangedT0andT1);
+		if (changed_t0 && i > 1)
+			--i;
+		else
+			++i;
+	}
+	return true;
+}
+
+
+
+bool ElementAnimation::AddKey(float time, const Property & property, Element& element)
 {
 	if (property.unit != property_unit)
 		return false;
 
 	keys.push_back({ time, property.value });
 
+	if (property.unit == Property::TRANSFORM)
+	{
+		PrepareTransforms(keys, element);
+	}
+
 	return true;
 }
 
@@ -189,7 +289,7 @@ Property ElementAnimation::UpdateAndGetProperty(float time)
 	if (animation_complete || time - last_update_time <= 0.0f)
 		return result;
 
-	const float dt = 0.01f;// time - last_update_time;
+	const float dt = time - last_update_time;
 
 	last_update_time = time;
 	time_since_iteration_start += dt;

+ 1 - 1
Source/Core/ElementAnimation.h

@@ -70,7 +70,7 @@ public:
 		last_update_time(time), time_since_iteration_start(0.0f), current_iteration(0), reverse_direction(false), animation_complete(false) 
 	{}
 
-	bool AddKey(float time, const Property& property);
+	bool AddKey(float time, const Property& property, Element& element);
 
 	Property UpdateAndGetProperty(float time);
 

+ 23 - 23
Source/Core/PropertyParserTransform.cpp

@@ -63,99 +63,99 @@ bool PropertyParserTransform::ParseValue(Property& property, const String& value
 	const PropertyParser* number3[] = { &number, &number, &number };
 	const PropertyParser* abs_numbers6[] = { &abs_number, &abs_number, &abs_number, &abs_number, &abs_number, &abs_number };
 	const PropertyParser* abs_numbers16[] = { &abs_number, &abs_number, &abs_number, &abs_number, &abs_number, &abs_number, &abs_number, &abs_number, &abs_number, &abs_number, &abs_number, &abs_number, &abs_number, &abs_number, &abs_number, &abs_number };
-
 	while (strlen(next))
 	{
+		using namespace Transforms;
 		int bytes_read = 0;
 
 		if ((bytes_read = Scan(next, "perspective", length1, args, 1)))
 		{
-			transform->AddPrimitive(Transforms::Perspective(args));
+			transform->AddPrimitive({ Perspective(args) });
 		}
 		else if ((bytes_read = Scan(next, "matrix", abs_numbers6, args, 6)))
 		{
-			transform->AddPrimitive(Transforms::Matrix2D(args));
+			transform->AddPrimitive({ Matrix2D(args) });
 		}
 		else if ((bytes_read = Scan(next, "matrix3d", abs_numbers16, args, 16)))
 		{
-			transform->AddPrimitive(Transforms::Matrix3D(args));
+			transform->AddPrimitive({ Matrix3D(args) });
 		}
 		else if ((bytes_read = Scan(next, "translateX", length1, args, 1)))
 		{
-			transform->AddPrimitive(Transforms::TranslateX(args));
+			transform->AddPrimitive({ TranslateX(args) });
 		}
 		else if ((bytes_read = Scan(next, "translateY", length1, args, 1)))
 		{
-			transform->AddPrimitive(Transforms::TranslateY(args));
+			transform->AddPrimitive({ TranslateY(args) });
 		}
 		else if ((bytes_read = Scan(next, "translateZ", length1, args, 1)))
 		{
-			transform->AddPrimitive(Transforms::TranslateZ(args));
+			transform->AddPrimitive({ TranslateZ(args) });
 		}
 		else if ((bytes_read = Scan(next, "translate", length2, args, 2)))
 		{
-			transform->AddPrimitive(Transforms::Translate2D(args));
+			transform->AddPrimitive({ Translate2D(args) });
 		}
 		else if ((bytes_read = Scan(next, "translate3d", length3, args, 3)))
 		{
-			transform->AddPrimitive(Transforms::Translate3D(args));
+			transform->AddPrimitive({ Translate3D(args) });
 		}
 		else if ((bytes_read = Scan(next, "scaleX", number1, args, 1)))
 		{
-			transform->AddPrimitive(Transforms::ScaleX(args));
+			transform->AddPrimitive({ ScaleX(args) });
 		}
 		else if ((bytes_read = Scan(next, "scaleY", number1, args, 1)))
 		{
-			transform->AddPrimitive(Transforms::ScaleY(args));
+			transform->AddPrimitive({ ScaleY(args) });
 		}
 		else if ((bytes_read = Scan(next, "scaleZ", number1, args, 1)))
 		{
-			transform->AddPrimitive(Transforms::ScaleZ(args));
+			transform->AddPrimitive({ ScaleZ(args) });
 		}
 		else if ((bytes_read = Scan(next, "scale", number2, args, 2)))
 		{
-			transform->AddPrimitive(Transforms::Scale2D(args));
+			transform->AddPrimitive({ Scale2D(args) });
 		}
 		else if ((bytes_read = Scan(next, "scale", number1, args, 1)))
 		{
 			args[1] = args[0];
-			transform->AddPrimitive(Transforms::Scale2D(args));
+			transform->AddPrimitive({ Scale2D(args) });
 		}
 		else if ((bytes_read = Scan(next, "scale3d", number3, args, 3)))
 		{
-			transform->AddPrimitive(Transforms::Scale3D(args));
+			transform->AddPrimitive({ Scale3D(args) });
 		}
 		else if ((bytes_read = Scan(next, "rotateX", angle1, args, 1)))
 		{
-			transform->AddPrimitive(Transforms::RotateX(args));
+			transform->AddPrimitive({ RotateX(args) });
 		}
 		else if ((bytes_read = Scan(next, "rotateY", angle1, args, 1)))
 		{
-			transform->AddPrimitive(Transforms::RotateY(args));
+			transform->AddPrimitive({ RotateY(args) });
 		}
 		else if ((bytes_read = Scan(next, "rotateZ", angle1, args, 1)))
 		{
-			transform->AddPrimitive(Transforms::RotateZ(args));
+			transform->AddPrimitive({ RotateZ(args) });
 		}
 		else if ((bytes_read = Scan(next, "rotate", angle1, args, 1)))
 		{
-			transform->AddPrimitive(Transforms::Rotate2D(args));
+			transform->AddPrimitive({ Rotate2D(args) });
 		}
 		else if ((bytes_read = Scan(next, "rotate3d", length3angle1, args, 4)))
 		{
-			transform->AddPrimitive(Transforms::Rotate3D(args));
+			transform->AddPrimitive({ Rotate3D(args) });
 		}
 		else if ((bytes_read = Scan(next, "skewX", angle1, args, 1)))
 		{
-			transform->AddPrimitive(Transforms::SkewX(args));
+			transform->AddPrimitive({ SkewX(args) });
 		}
 		else if ((bytes_read = Scan(next, "skewY", angle1, args, 1)))
 		{
-			transform->AddPrimitive(Transforms::SkewY(args));
+			transform->AddPrimitive({ SkewY(args) });
 		}
 		else if ((bytes_read = Scan(next, "skew", angle2, args, 2)))
 		{
-			transform->AddPrimitive(Transforms::Skew2D(args));
+			transform->AddPrimitive({ Skew2D(args) });
 		}
 
 		if (bytes_read > 0)

+ 15 - 50
Source/Core/Transform.cpp

@@ -40,37 +40,20 @@ Transform::Transform()
 }
 
 Transform::Transform(const Transform& other)
-	: primitives()
 {
-	primitives.reserve(other.primitives.size());
-	Primitives::const_iterator i = other.primitives.begin();
-	Primitives::const_iterator end = other.primitives.end();
-	for (; i != end; ++i)
-	{
-		try
-		{
-			AddPrimitive(**i);
-		}
-		catch(...)
-		{
-			ClearPrimitives();
-			throw;
-		}
-	}
+	primitives = other.primitives;
 }
 
 Transform::~Transform()
 {
-	ClearPrimitives();
+	primitives.clear();
 }
 
-// Swap the content of two Transfrom instances
 void Transform::Swap(Transform& other)
 {
 	primitives.swap(other.primitives);
 }
 
-// Assignment operato
 const Transform& Transform::operator=(const Transform& other)
 {
 	Transform result(other);
@@ -78,42 +61,24 @@ const Transform& Transform::operator=(const Transform& other)
 	return *this;
 }
 
-// Remove all Primitives from this Transform
-void Transform::ClearPrimitives()
+void Transform::ClearPrimitives() 
 {
-	Primitives::iterator i = primitives.begin();
-	Primitives::iterator end = primitives.end();
-	for (; i != end; ++i)
-	{
-		try
-		{
-			delete *i;
-			*i = 0;
-		}
-		catch(...)
-		{
-		}
-	}
 	primitives.clear();
 }
 
-// Add a Primitive to this Transform
-void Transform::AddPrimitive(const Transforms::Primitive& p)
+void Transform::AddPrimitive(const Transforms::Primitive & p)
+{
+	primitives.push_back(p);
+}
+
+int Transform::GetNumPrimitives() const throw() 
+{
+	return (int)primitives.size();
+}
+
+const Transforms::Primitive & Transform::GetPrimitive(int i) const throw() 
 {
-	Transforms::Primitive* q = p.Clone();
-	if (!q)
-	{
-		throw std::bad_alloc();
-	}
-	try
-	{
-		primitives.push_back(q);
-	}
-	catch (...)
-	{
-		delete q;
-		throw;
-	}
+	return primitives[i];
 }
 
 }

+ 250 - 111
Source/Core/TransformPrimitive.cpp

@@ -67,150 +67,289 @@ float NumericValue::ResolveDepth(Element& e) const throw()
 	return Resolve(e, Math::Max(size.x, size.y));
 }
 
-bool Matrix2D::ResolveTransform(Matrix4f& m, Element& e) const throw()
-{
-	m = Matrix4f::FromRows(
-		Vector4f(values[0], values[1], 0, values[2]),
-		Vector4f(values[3], values[4], 0, values[5]),
-		Vector4f(        0,         0, 1,         0),
-		Vector4f(        0,         0, 0,         1)
-	);
-	return true;
-}
 
-bool Matrix3D::ResolveTransform(Matrix4f& m, Element& e) const throw()
-{
-	m = Matrix4f::FromRows(
-		Vector4f(values[ 0], values[ 1], values[ 2], values[ 3]),
-		Vector4f(values[ 4], values[ 5], values[ 6], values[ 7]),
-		Vector4f(values[ 8], values[ 9], values[10], values[11]),
-		Vector4f(values[12], values[13], values[14], values[15])
-	);
-	return true;
-}
 
-bool TranslateX::ResolveTransform(Matrix4f& m, Element& e) const throw()
-{
-	m = Matrix4f::TranslateX(values[0].ResolveWidth(e));
-	return true;
-}
 
-bool TranslateY::ResolveTransform(Matrix4f& m, Element& e) const throw()
-{
-	m = Matrix4f::TranslateY(values[0].ResolveHeight(e));
-	return true;
-}
 
-bool TranslateZ::ResolveTransform(Matrix4f& m, Element& e) const throw()
-{
-	m = Matrix4f::TranslateZ(values[0].ResolveDepth(e));
-	return true;
-}
 
-bool Translate2D::ResolveTransform(Matrix4f& m, Element& e) const throw()
-{
-	m = Matrix4f::Translate(
-		values[0].ResolveWidth(e),
-		values[1].ResolveHeight(e),
-		0
-	);
-	return true;
-}
 
-bool Translate3D::ResolveTransform(Matrix4f& m, Element& e) const throw()
-{
-	m = Matrix4f::Translate(
-		values[0].ResolveWidth(e),
-		values[1].ResolveHeight(e),
-		values[2].ResolveDepth(e)
-	);
-	return true;
-}
 
-bool ScaleX::ResolveTransform(Matrix4f& m, Element& e) const throw()
-{
-	m = Matrix4f::ScaleX(values[0]);
-	return true;
-}
 
-bool ScaleY::ResolveTransform(Matrix4f& m, Element& e) const throw()
-{
-	m = Matrix4f::ScaleY(values[0]);
-	return true;
-}
 
-bool ScaleZ::ResolveTransform(Matrix4f& m, Element& e) const throw()
+struct ResolveTransformVisitor
 {
-	m = Matrix4f::ScaleZ(values[0]);
-	return true;
-}
+	Matrix4f& m;
+	Element& e;
 
-bool Scale2D::ResolveTransform(Matrix4f& m, Element& e) const throw()
-{
-	m = Matrix4f::Scale(values[0], values[1], 1);
-	return true;
-}
+	bool operator()(const Matrix2D& p)
+	{
+		m = Matrix4f::FromRows(
+			Vector4f(p.values[0], p.values[2], 0, p.values[4]),
+			Vector4f(p.values[1], p.values[3], 0, p.values[5]),
+			Vector4f(0, 0, 1, 0),
+			Vector4f(0, 0, 0, 1)
+		);
+		return true;
+	}
 
-bool Scale3D::ResolveTransform(Matrix4f& m, Element& e) const throw()
-{
-	m = Matrix4f::Scale(values[0], values[1], values[2]);
-	return true;
-}
+	bool operator()(const Matrix3D& p)
+	{
+		m = Matrix4f::FromRows(
+			Vector4f(p.values[0], p.values[1], p.values[2], p.values[3]),
+			Vector4f(p.values[4], p.values[5], p.values[6], p.values[7]),
+			Vector4f(p.values[8], p.values[9], p.values[10], p.values[11]),
+			Vector4f(p.values[12], p.values[13], p.values[14], p.values[15])
+		);
+		return true;
+	}
 
-bool RotateX::ResolveTransform(Matrix4f& m, Element& e) const throw()
-{
-	m = Matrix4f::RotateX(values[0]);
-	return true;
-}
+	bool operator()(const TranslateX& p)
+	{
+		m = Matrix4f::TranslateX(p.values[0].ResolveWidth(e));
+		return true;
+	}
 
-bool RotateY::ResolveTransform(Matrix4f& m, Element& e) const throw()
-{
-	m = Matrix4f::RotateY(values[0]);
-	return true;
-}
+	bool operator()(const TranslateY& p)
+	{
+		m = Matrix4f::TranslateY(p.values[0].ResolveHeight(e));
+		return true;
+	}
 
-bool RotateZ::ResolveTransform(Matrix4f& m, Element& e) const throw()
-{
-	m = Matrix4f::RotateZ(values[0]);
-	return true;
-}
+	bool operator()(const TranslateZ& p)
+	{
+		m = Matrix4f::TranslateZ(p.values[0].ResolveDepth(e));
+		return true;
+	}
+
+	bool operator()(const Translate2D& p)
+	{
+		m = Matrix4f::Translate(
+			p.values[0].ResolveWidth(e),
+			p.values[1].ResolveHeight(e),
+			0
+		);
+		return true;
+	}
+
+	bool operator()(const Translate3D& p)
+	{
+		m = Matrix4f::Translate(
+			p.values[0].ResolveWidth(e),
+			p.values[1].ResolveHeight(e),
+			p.values[2].ResolveDepth(e)
+		);
+		return true;
+	}
+
+	bool operator()(const ScaleX& p)
+	{
+		m = Matrix4f::ScaleX(p.values[0]);
+		return true;
+	}
+
+	bool operator()(const ScaleY& p)
+	{
+		m = Matrix4f::ScaleY(p.values[0]);
+		return true;
+	}
+
+	bool operator()(const ScaleZ& p)
+	{
+		m = Matrix4f::ScaleZ(p.values[0]);
+		return true;
+	}
+
+	bool operator()(const Scale2D& p)
+	{
+		m = Matrix4f::Scale(p.values[0], p.values[1], 1);
+		return true;
+	}
+
+	bool operator()(const Scale3D& p)
+	{
+		m = Matrix4f::Scale(p.values[0], p.values[1], p.values[2]);
+		return true;
+	}
+
+	bool operator()(const RotateX& p)
+	{
+		m = Matrix4f::RotateX(p.values[0]);
+		return true;
+	}
+
+	bool operator()(const RotateY& p)
+	{
+		m = Matrix4f::RotateY(p.values[0]);
+		return true;
+	}
+
+	bool operator()(const RotateZ& p)
+	{
+		m = Matrix4f::RotateZ(p.values[0]);
+		return true;
+	}
 
-bool Rotate2D::ResolveTransform(Matrix4f& m, Element& e) const throw()
+	bool operator()(const Rotate2D& p)
+	{
+		m = Matrix4f::RotateZ(p.values[0]);
+		return true;
+	}
+
+	bool operator()(const Rotate3D& p)
+	{
+		m = Matrix4f::Rotate(Vector3f(p.values[0], p.values[1], p.values[2]), p.values[3]);
+		return true;
+	}
+
+	bool operator()(const SkewX& p)
+	{
+		m = Matrix4f::SkewX(p.values[0]);
+		return true;
+	}
+
+	bool operator()(const SkewY& p)
+	{
+		m = Matrix4f::SkewY(p.values[0]);
+		return true;
+	}
+
+	bool operator()(const Skew2D& p)
+	{
+		m = Matrix4f::Skew(p.values[0], p.values[1]);
+		return true;
+	}
+
+	bool operator()(const Perspective& p)
+	{
+		return false;
+	}
+
+};
+
+
+
+struct SetIdentityVisitor
 {
-	m = Matrix4f::RotateZ(values[0]);
-	return true;
-}
+	template <size_t N>
+	void operator()(ResolvedValuesPrimitive<N>& p)
+	{
+		for (auto& value : p.values)
+			value = 0.0f;
+	}
+	template <size_t N>
+	void operator()(UnresolvedValuesPrimitive<N>& p)
+	{
+		for (auto& value : p.values)
+			value.number = 0.0f;
+	}
+	void operator()(Matrix2D& p)
+	{
+		for (int i = 0; i < 6; i++)
+			p.values[i] = ((i == 0 || i == 3) ? 1.0f : 0.0f);
+	}
+	void operator()(Matrix3D& p)
+	{
+		for (int i = 0; i < 16; i++)
+			p.values[i] = ((i % 5) == 0 ? 1.0f : 0.0f);
+	}
+	void operator()(ScaleX& p)
+	{
+		p.values[0] = 1;
+	}
+	void operator()(ScaleY& p)
+	{
+		p.values[0] = 1;
+	}
+	void operator()(ScaleZ& p)
+	{
+		p.values[0] = 1;
+	}
+	void operator()(Scale2D& p)
+	{
+		p.values[0] = p.values[1] = 1;
+	}
+	void operator()(Scale3D& p)
+	{
+		p.values[0] = p.values[1] = p.values[2] = 1;
+	}
+};
 
-bool Rotate3D::ResolveTransform(Matrix4f& m, Element& e) const throw()
+
+void Primitive::SetIdentity() throw()
 {
-	m = Matrix4f::Rotate(Vector3f(values[0], values[1], values[2]), values[3]);
-	return true;
+	std::visit(SetIdentityVisitor{}, primitive);
 }
 
-bool SkewX::ResolveTransform(Matrix4f& m, Element& e) const throw()
+
+bool Primitive::ResolveTransform(Matrix4f & m, Element & e) const throw()
 {
-	m = Matrix4f::SkewX(values[0]);
-	return true;
+	ResolveTransformVisitor visitor{ m, e };
+
+	bool result = std::visit(visitor, primitive);
+
+	return result;
 }
 
-bool SkewY::ResolveTransform(Matrix4f& m, Element& e) const throw()
+bool Primitive::ResolvePerspective(float & p, Element & e) const throw()
 {
-	m = Matrix4f::SkewY(values[0]);
-	return true;
+	bool result = false;
+
+	if (const Perspective* perspective = std::get_if<Perspective>(&primitive))
+	{
+		p = perspective->values[0].ResolveDepth(e);
+		result = true;
+	}
+
+	return result;
 }
 
-bool Skew2D::ResolveTransform(Matrix4f& m, Element& e) const throw()
+
+
+struct InterpolateVisitor
 {
-	m = Matrix4f::Skew(values[0], values[1]);
-	return true;
-}
+	const PrimitiveVariant& other_variant;
+	float alpha;
+
+	template <size_t N>
+	void Interpolate(ResolvedValuesPrimitive<N>& p0, const ResolvedValuesPrimitive<N>& p1)
+	{
+		for (size_t i = 0; i < N; i++)
+			p0.values[i] = p0.values[i] * (1.0f - alpha) + p1.values[i] * alpha;
+	}
+	template <size_t N>
+	void Interpolate(UnresolvedValuesPrimitive<N>& p0, const UnresolvedValuesPrimitive<N>& p1)
+	{
+		// TODO: While we have the same type and dimension, we may have different units. Should convert?
+		for (size_t i = 0; i < N; i++)
+			p0.values[i].number = p0.values[i].number*(1.0f - alpha) + p1.values[i].number * alpha;
+	}
+	//void Interpolate(Matrix3D& p0, Matrix3D& p1)
+	//{
+	//	// Special interpolation for full matrices TODO
+	//}
+
+	template <typename T>
+	void operator()(T& p0)
+	{
+		auto& p1 = std::get<T>(other_variant);
+		Interpolate(p0, p1);
+	}
+};
+
 
-bool Perspective::ResolvePerspective(float& p, Element& e) const throw()
+
+bool Primitive::InterpolateWith(const Primitive & other, float alpha) throw()
 {
-	p = values[0].ResolveDepth(e);
+	if (primitive.index() != other.primitive.index())
+		return false;
+
+	std::visit(InterpolateVisitor{ other.primitive, alpha }, primitive);
+
 	return true;
 }
 
+
+
 }
 }
 }