Browse Source

[cpp] More 4.0 porting, all of animation and timelines.

badlogic 4 years ago
parent
commit
7b154fb92f
33 changed files with 1493 additions and 897 deletions
  1. 9 1
      spine-cpp/spine-cpp/include/spine/Animation.h
  2. 12 11
      spine-cpp/spine-cpp/include/spine/AttachmentTimeline.h
  3. 16 0
      spine-cpp/spine-cpp/include/spine/Bone.h
  4. 4 0
      spine-cpp/spine-cpp/include/spine/BoneData.h
  5. 16 0
      spine-cpp/spine-cpp/include/spine/Color.h
  6. 147 30
      spine-cpp/spine-cpp/include/spine/ColorTimeline.h
  7. 5 5
      spine-cpp/spine-cpp/include/spine/CurveTimeline.h
  8. 33 23
      spine-cpp/spine-cpp/include/spine/DeformTimeline.h
  9. 2 7
      spine-cpp/spine-cpp/include/spine/DrawOrderTimeline.h
  10. 2 7
      spine-cpp/spine-cpp/include/spine/EventTimeline.h
  11. 12 19
      spine-cpp/spine-cpp/include/spine/IkConstraintTimeline.h
  12. 11 14
      spine-cpp/spine-cpp/include/spine/PathConstraintMixTimeline.h
  13. 4 11
      spine-cpp/spine-cpp/include/spine/PathConstraintPositionTimeline.h
  14. 7 3
      spine-cpp/spine-cpp/include/spine/PathConstraintSpacingTimeline.h
  15. 1 1
      spine-cpp/spine-cpp/include/spine/Property.h
  16. 49 3
      spine-cpp/spine-cpp/include/spine/ShearTimeline.h
  17. 13 1
      spine-cpp/spine-cpp/include/spine/Skeleton.h
  18. 9 1
      spine-cpp/spine-cpp/include/spine/Slot.h
  19. 9 1
      spine-cpp/spine-cpp/include/spine/SlotData.h
  20. 14 16
      spine-cpp/spine-cpp/include/spine/TransformConstraintTimeline.h
  21. 17 44
      spine-cpp/spine-cpp/src/spine/AttachmentTimeline.cpp
  22. 452 93
      spine-cpp/spine-cpp/src/spine/ColorTimeline.cpp
  23. 1 5
      spine-cpp/spine-cpp/src/spine/CurveTimeline.cpp
  24. 250 217
      spine-cpp/spine-cpp/src/spine/DeformTimeline.cpp
  25. 10 30
      spine-cpp/spine-cpp/src/spine/DrawOrderTimeline.cpp
  26. 18 25
      spine-cpp/spine-cpp/src/spine/EventTimeline.cpp
  27. 59 86
      spine-cpp/spine-cpp/src/spine/IkConstraintTimeline.cpp
  28. 62 59
      spine-cpp/spine-cpp/src/spine/PathConstraintMixTimeline.cpp
  29. 6 33
      spine-cpp/spine-cpp/src/spine/PathConstraintPositionTimeline.cpp
  30. 8 24
      spine-cpp/spine-cpp/src/spine/PathConstraintSpacingTimeline.cpp
  31. 154 54
      spine-cpp/spine-cpp/src/spine/ShearTimeline.cpp
  32. 79 71
      spine-cpp/spine-cpp/src/spine/TransformConstraintTimeline.cpp
  33. 2 2
      spine-ts/core/src/Animation.ts

+ 9 - 1
spine-cpp/spine-cpp/include/spine/Animation.h

@@ -54,7 +54,15 @@ class SP_API Animation : public SpineObject {
 
 	friend class AttachmentTimeline;
 
-	friend class ColorTimeline;
+    friend class RGBATimeline;
+
+    friend class RGBTimeline;
+
+    friend class AlphaTimeline;
+
+    friend class RGBA2Timeline;
+
+    friend class RGB2Timeline;
 
 	friend class DeformTimeline;
 

+ 12 - 11
spine-cpp/spine-cpp/include/spine/AttachmentTimeline.h

@@ -50,23 +50,24 @@ namespace spine {
 		RTTI_DECL
 
 	public:
-		explicit AttachmentTimeline(int frameCount);
+		explicit AttachmentTimeline(size_t frameCount, int slotIndex);
 
-		virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>* pEvents, float alpha, MixBlend blend, MixDirection direction);
+		virtual ~AttachmentTimeline();
 
-		virtual int getPropertyId();
+		virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>* pEvents, float alpha, MixBlend blend, MixDirection direction);
 
 		/// Sets the time and value of the specified keyframe.
-		void setFrame(int frameIndex, float time, const String& attachmentName);
+		void setFrame(int frame, float time, const String& attachmentName);
 
-		size_t getSlotIndex();
-		void setSlotIndex(size_t inValue);
-		Vector<float>& getFrames();
 		Vector<String>& getAttachmentNames();
-		size_t getFrameCount();
-	private:
-		size_t _slotIndex;
-		Vector<float> _frames;
+
+        int getSlotIndex() { return _slotIndex; }
+
+        void setSlotIndex(int inValue) { _slotIndex = inValue; }
+
+    protected:
+        int _slotIndex;
+
 		Vector<String> _attachmentNames;
 
         void setAttachment(Skeleton& skeleton, Slot& slot, String* attachmentName);

+ 16 - 0
spine-cpp/spine-cpp/include/spine/Bone.h

@@ -63,6 +63,18 @@ class SP_API Bone : public Updatable {
 
 	friend class PointAttachment;
 
+	friend class AttachmentTimeline;
+
+    friend class RGBATimeline;
+
+    friend class RGBTimeline;
+
+    friend class AlphaTimeline;
+
+    friend class RGBA2Timeline;
+
+    friend class RGB2Timeline;
+
 	friend class ScaleTimeline;
 
     friend class ScaleXTimeline;
@@ -71,6 +83,10 @@ class SP_API Bone : public Updatable {
 
 	friend class ShearTimeline;
 
+    friend class ShearXTimeline;
+
+    friend class ShearYTimeline;
+
 	friend class TranslateTimeline;
 
     friend class TranslateXTimeline;

+ 4 - 0
spine-cpp/spine-cpp/include/spine/BoneData.h

@@ -52,6 +52,10 @@ class SP_API BoneData : public SpineObject {
 
 	friend class ShearTimeline;
 
+    friend class ShearXTimeline;
+
+    friend class ShearYTimeline;
+
 	friend class TranslateTimeline;
 
     friend class TranslateXTimeline;

+ 16 - 0
spine-cpp/spine-cpp/include/spine/Color.h

@@ -51,6 +51,14 @@ public:
 		return *this;
 	}
 
+    inline Color &set(float _r, float _g, float _b) {
+        this->r = _r;
+        this->g = _g;
+        this->b = _b;
+        clamp();
+        return *this;
+    }
+
 	inline Color &set(const Color &other) {
 		r = other.r;
 		g = other.g;
@@ -69,6 +77,14 @@ public:
 		return *this;
 	}
 
+    inline Color &add(float _r, float _g, float _b) {
+        this->r += _r;
+        this->g += _g;
+        this->b += _b;
+        clamp();
+        return *this;
+    }
+
 	inline Color &add(const Color &other) {
 		r += other.r;
 		g += other.g;

+ 147 - 30
spine-cpp/spine-cpp/include/spine/ColorTimeline.h

@@ -33,48 +33,165 @@
 #include <spine/CurveTimeline.h>
 
 namespace spine {
-class SP_API ColorTimeline : public CurveTimeline {
-	friend class SkeletonBinary;
+    class SP_API RGBATimeline : public CurveTimeline {
+        friend class SkeletonBinary;
 
-	friend class SkeletonJson;
+        friend class SkeletonJson;
 
-RTTI_DECL
+        RTTI_DECL
 
-public:
-	static const int ENTRIES;
+    public:
+        explicit RGBATimeline(size_t frameCount, size_t bezierCount, int slotIndex);
 
-	explicit ColorTimeline(int frameCount);
+        virtual ~RGBATimeline();
 
-	virtual void
-	apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
-		MixDirection direction);
+        virtual void
+        apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
+              MixDirection direction);
 
-	virtual int getPropertyId();
+        /// Sets the time and value of the specified keyframe.
+        void setFrame(int frame, float time, float r, float g, float b, float a);
 
-	/// Sets the time and value of the specified keyframe.
-	void setFrame(int frameIndex, float time, float r, float g, float b, float a);
+        int getSlotIndex() { return _slotIndex; };
 
-	int getSlotIndex();
+        void setSlotIndex(int inValue) { _slotIndex = inValue; }
 
-	void setSlotIndex(int inValue);
+    protected:
+        int _slotIndex;
 
-	Vector<float> &getFrames();
+        static const int ENTRIES = 5;
+        static const int R = 1;
+        static const int G = 2;
+        static const int B = 3;
+        static const int A = 4;
+    };
 
-protected:
-	static const int PREV_TIME;
-	static const int PREV_R;
-	static const int PREV_G;
-	static const int PREV_B;
-	static const int PREV_A;
-	static const int R;
-	static const int G;
-	static const int B;
-	static const int A;
+    class SP_API RGBTimeline : public CurveTimeline {
+        friend class SkeletonBinary;
 
-private:
-	int _slotIndex;
-	Vector<float> _frames;
-};
+        friend class SkeletonJson;
+
+        RTTI_DECL
+
+    public:
+        explicit RGBTimeline(size_t frameCount, size_t bezierCount, int slotIndex);
+
+        virtual ~RGBTimeline();
+
+        virtual void
+        apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
+              MixDirection direction);
+
+        /// Sets the time and value of the specified keyframe.
+        void setFrame(int frame, float time, float r, float g, float b);
+
+        int getSlotIndex() { return _slotIndex; };
+
+        void setSlotIndex(int inValue) { _slotIndex = inValue; }
+
+    protected:
+        int _slotIndex;
+
+        static const int ENTRIES = 4;
+        static const int R = 1;
+        static const int G = 2;
+        static const int B = 3;
+    };
+
+    class SP_API AlphaTimeline : public CurveTimeline1 {
+        friend class SkeletonBinary;
+
+        friend class SkeletonJson;
+
+    RTTI_DECL
+
+    public:
+        explicit AlphaTimeline(size_t frameCount, size_t bezierCount, int slotIndex);
+
+        virtual ~AlphaTimeline();
+
+        virtual void
+        apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
+              MixDirection direction);
+
+        int getSlotIndex() { return _slotIndex; };
+
+        void setSlotIndex(int inValue) { _slotIndex = inValue; }
+
+    protected:
+        int _slotIndex;
+    };
+
+    class SP_API RGBA2Timeline : public CurveTimeline {
+        friend class SkeletonBinary;
+
+        friend class SkeletonJson;
+
+    RTTI_DECL
+
+    public:
+        explicit RGBA2Timeline(size_t frameCount, size_t bezierCount, int slotIndex);
+
+        virtual ~RGBA2Timeline();
+
+        virtual void
+        apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
+              MixDirection direction);
+
+        /// Sets the time and value of the specified keyframe.
+        void setFrame(int frame, float time, float r, float g, float b, float a, float r2, float g2, float b2);
+
+        int getSlotIndex() { return _slotIndex; };
+
+        void setSlotIndex(int inValue) { _slotIndex = inValue; }
+
+    protected:
+        int _slotIndex;
+
+        static const int ENTRIES = 8;
+        static const int R = 1;
+        static const int G = 2;
+        static const int B = 3;
+        static const int A = 4;
+        static const int R2 = 5;
+        static const int G2 = 6;
+        static const int B2 = 7;
+    };
+
+    class SP_API RGB2Timeline : public CurveTimeline {
+        friend class SkeletonBinary;
+
+        friend class SkeletonJson;
+
+    RTTI_DECL
+
+    public:
+        explicit RGB2Timeline(size_t frameCount, size_t bezierCount, int slotIndex);
+
+        virtual ~RGB2Timeline();
+
+        virtual void
+        apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
+              MixDirection direction);
+
+        /// Sets the time and value of the specified keyframe.
+        void setFrame(int frame, float time, float r, float g, float b, float r2, float g2, float b2);
+
+        int getSlotIndex() { return _slotIndex; };
+
+        void setSlotIndex(int inValue) { _slotIndex = inValue; }
+
+    protected:
+        int _slotIndex;
+
+        static const int ENTRIES = 7;
+        static const int R = 1;
+        static const int G = 2;
+        static const int B = 3;
+        static const int R2 = 4;
+        static const int G2 = 5;
+        static const int B2 = 6;
+    };
 }
 
 #endif /* Spine_ColorTimeline_h */

+ 5 - 5
spine-cpp/spine-cpp/include/spine/CurveTimeline.h

@@ -72,8 +72,8 @@ namespace spine {
 
         float getCurveValue(float time);
     protected:
-        static const int ENTRIES;
-        static const int VALUE;
+        static const int ENTRIES = 2;
+        static const int VALUE = 1;
     };
 
     class SP_API CurveTimeline2 : public CurveTimeline {
@@ -88,9 +88,9 @@ namespace spine {
 
         float getCurveValue(float time);
     protected:
-        static const int ENTRIES;
-        static const int VALUE1;
-        static const int VALUE2;
+        static const int ENTRIES = 3;
+        static const int VALUE1 = 1;
+        static const int VALUE2 = 2;
     };
 }
 

+ 33 - 23
spine-cpp/spine-cpp/include/spine/DeformTimeline.h

@@ -33,37 +33,47 @@
 #include <spine/CurveTimeline.h>
 
 namespace spine {
-	class VertexAttachment;
+    class VertexAttachment;
 
-	class SP_API DeformTimeline : public CurveTimeline {
-		friend class SkeletonBinary;
-		friend class SkeletonJson;
+    class SP_API DeformTimeline : public CurveTimeline {
+        friend class SkeletonBinary;
 
-		RTTI_DECL
+        friend class SkeletonJson;
 
-	public:
-		explicit DeformTimeline(int frameCount);
+    RTTI_DECL
 
-		virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>* pEvents, float alpha, MixBlend blend, MixDirection direction);
+    public:
+        explicit DeformTimeline(size_t frameCount, size_t bezierCount, int slotIndex, VertexAttachment *attachment);
 
-		virtual int getPropertyId();
+        virtual void
+        apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
+              MixDirection direction);
 
-		/// Sets the time and value of the specified keyframe.
-		void setFrame(int frameIndex, float time, Vector<float>& vertices);
+        /// Sets the time and value of the specified keyframe.
+        void setFrame(int frameIndex, float time, Vector<float> &vertices);
 
-		int getSlotIndex();
-		void setSlotIndex(int inValue);
-		Vector<float>& getFrames();
-		Vector< Vector<float> >& getVertices();
-		VertexAttachment* getAttachment();
-		void setAttachment(VertexAttachment* inValue);
+        Vector <Vector<float>> &getVertices();
 
-	private:
-		int _slotIndex;
-		Vector<float> _frames;
-		Vector< Vector<float> > _frameVertices;
-		VertexAttachment* _attachment;
-	};
+        VertexAttachment *getAttachment();
+
+        void setAttachment(VertexAttachment *inValue);
+
+        void setBezier(int bezier, int frame, float value, float time1, float value1, float cx1, float cy1,
+                  float cx2, float cy2, float time2, float value2);
+
+        float getCurvePercent(float time, int frame);
+
+        int getSlotIndex() { return _slotIndex; }
+
+        void setSlotIndex(int inValue) { _slotIndex = inValue; }
+
+    protected:
+        int _slotIndex;
+
+        Vector <Vector<float>> _vertices;
+
+        VertexAttachment *_attachment;
+    };
 }
 
 #endif /* Spine_DeformTimeline_h */

+ 2 - 7
spine-cpp/spine-cpp/include/spine/DrawOrderTimeline.h

@@ -40,22 +40,17 @@ namespace spine {
 		RTTI_DECL
 
 	public:
-		explicit DrawOrderTimeline(int frameCount);
+		explicit DrawOrderTimeline(size_t frameCount);
 
 		virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>* pEvents, float alpha, MixBlend blend, MixDirection direction);
 
-		virtual int getPropertyId();
-
 		/// Sets the time and value of the specified keyframe.
 		/// @param drawOrder May be NULL to use bind pose draw order
-		void setFrame(size_t frameIndex, float time, Vector<int>& drawOrder);
+		void setFrame(size_t frame, float time, Vector<int>& drawOrder);
 
-		Vector<float>& getFrames();
 		Vector< Vector<int> >& getDrawOrders();
-		size_t getFrameCount();
 
 	private:
-		Vector<float> _frames;
 		Vector< Vector<int> > _drawOrders;
 	};
 }

+ 2 - 7
spine-cpp/spine-cpp/include/spine/EventTimeline.h

@@ -40,23 +40,18 @@ namespace spine {
 		RTTI_DECL
 
 	public:
-		explicit EventTimeline(int frameCount);
+		explicit EventTimeline(size_t frameCount);
 
 		~EventTimeline();
 
 		virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>* pEvents, float alpha, MixBlend blend, MixDirection direction);
 
-		virtual int getPropertyId();
-
 		/// Sets the time and value of the specified keyframe.
-		void setFrame(size_t frameIndex, Event* event);
+		void setFrame(size_t frame, Event* event);
 
-		Vector<float> getFrames();
 		Vector<Event*>& getEvents();
-		size_t getFrameCount();
 
 	private:
-		Vector<float> _frames;
 		Vector<Event*> _events;
 	};
 }

+ 12 - 19
spine-cpp/spine-cpp/include/spine/IkConstraintTimeline.h

@@ -41,32 +41,25 @@ namespace spine {
 		RTTI_DECL
 
 	public:
-		static const int ENTRIES;
-
-		explicit IkConstraintTimeline(int frameCount);
+		explicit IkConstraintTimeline(size_t frameCount, size_t bezierCount, int ikConstraintIndex);
 
 		virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>* pEvents, float alpha, MixBlend blend, MixDirection direction);
 
-		virtual int getPropertyId();
-
 		/// Sets the time, mix and bend direction of the specified keyframe.
-		void setFrame (int frameIndex, float time, float mix, float softness, int bendDirection, bool compress, bool stretch);
+		void setFrame (int frame, float time, float mix, float softness, int bendDirection, bool compress, bool stretch);
+
+        int getIkConstraintIndex() { return _ikConstraintIndex; }
 
+        void setIkConstraintIndex(int inValue) { _ikConstraintIndex = inValue; }
 	private:
-		static const int PREV_TIME;
-		static const int PREV_MIX;
-		static const int PREV_SOFTNESS;
-		static const int PREV_BEND_DIRECTION;
-		static const int PREV_COMPRESS;
-		static const int PREV_STRETCH;
-		static const int MIX;
-		static const int SOFTNESS;
-		static const int BEND_DIRECTION;
-		static const int COMPRESS;
-		static const int STRETCH;
+        int _ikConstraintIndex;
 
-		Vector<float> _frames;
-		int _ikConstraintIndex;
+	    static const int ENTRIES = 6;
+		static const int MIX = 1;
+		static const int SOFTNESS = 2;
+		static const int BEND_DIRECTION = 3;
+		static const int COMPRESS = 4;
+		static const int STRETCH = 5;
 	};
 }
 

+ 11 - 14
spine-cpp/spine-cpp/include/spine/PathConstraintMixTimeline.h

@@ -42,26 +42,23 @@ namespace spine {
 		RTTI_DECL
 
 	public:
-		static const int ENTRIES;
-
-		explicit PathConstraintMixTimeline(int frameCount);
+		explicit PathConstraintMixTimeline(size_t frameCount, size_t bezierCount, int pathConstraintIndex);
 
 		virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>* pEvents, float alpha, MixBlend blend, MixDirection direction);
 
-		virtual int getPropertyId();
+        /// Sets the time and mixes of the specified keyframe.
+        void setFrame(int frameIndex, float time, float mixRotate, float mixX, float mixY);
 
-	private:
-		static const int PREV_TIME;
-		static const int PREV_ROTATE;
-		static const int PREV_TRANSLATE;
-		static const int ROTATE;
-		static const int TRANSLATE;
+        int getPathConstraintIndex() { return _pathConstraintIndex; }
 
-		Vector<float> _frames;
-		int _pathConstraintIndex;
+        void setPathConstraintIndex(int inValue) { _pathConstraintIndex = inValue; }
+	private:
+        int _pathConstraintIndex;
 
-		/// Sets the time and mixes of the specified keyframe.
-		void setFrame(int frameIndex, float time, float rotateMix, float translateMix);
+		static const int ENTRIES = 4;
+		static const int ROTATE = 1;
+		static const int X = 2;
+        static const int Y = 2;
 	};
 }
 

+ 4 - 11
spine-cpp/spine-cpp/include/spine/PathConstraintPositionTimeline.h

@@ -34,7 +34,7 @@
 
 namespace spine {
 
-	class SP_API PathConstraintPositionTimeline : public CurveTimeline {
+	class SP_API PathConstraintPositionTimeline : public CurveTimeline1 {
 		friend class SkeletonBinary;
 		friend class SkeletonJson;
 
@@ -43,23 +43,16 @@ namespace spine {
 	public:
 		static const int ENTRIES;
 
-		explicit PathConstraintPositionTimeline(int frameCount);
+		explicit PathConstraintPositionTimeline(size_t frameCount, size_t bezierCount, int pathConstraintIndex);
 
 		virtual ~PathConstraintPositionTimeline();
 
 		virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>* pEvents, float alpha, MixBlend blend, MixDirection direction);
 
-		virtual int getPropertyId();
-
-		/// Sets the time and value of the specified keyframe.
-		void setFrame(int frameIndex, float time, float value);
+        int getPathConstraintIndex() { return _pathConstraintIndex; }
 
+        void setPathConstraintIndex(int inValue) { _pathConstraintIndex = inValue; }
 	protected:
-		static const int PREV_TIME;
-		static const int PREV_VALUE;
-		static const int VALUE;
-
-		Vector<float> _frames;
 		int _pathConstraintIndex;
 	};
 }

+ 7 - 3
spine-cpp/spine-cpp/include/spine/PathConstraintSpacingTimeline.h

@@ -33,18 +33,22 @@
 #include <spine/PathConstraintPositionTimeline.h>
 
 namespace spine {
-	class SP_API PathConstraintSpacingTimeline : public PathConstraintPositionTimeline {
+	class SP_API PathConstraintSpacingTimeline : public CurveTimeline1 {
 		friend class SkeletonBinary;
 		friend class SkeletonJson;
 
 		RTTI_DECL
 
 	public:
-		explicit PathConstraintSpacingTimeline(int frameCount);
+		explicit PathConstraintSpacingTimeline(size_t frameCount, size_t bezierCount, int pathConstraintIndex);
 
 		virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>* pEvents, float alpha, MixBlend blend, MixDirection direction);
 
-		virtual int getPropertyId();
+        int getPathConstraintIndex() { return _pathConstraintIndex; }
+
+        void setPathConstraintIndex(int inValue) { _pathConstraintIndex = inValue; }
+    protected:
+        int _pathConstraintIndex;
 	};
 }
 

+ 1 - 1
spine-cpp/spine-cpp/include/spine/Property.h

@@ -51,7 +51,7 @@ enum Property {
     Property_TransformConstraint = 1 << 15,
     Property_PathConstraintPosition = 1 << 16,
     Property_PathConstraintSpacing = 1 << 17,
-    Property_OathConstraintMix = 1 << 18
+    Property_PathConstraintMix = 1 << 18
 };
 }
 

+ 49 - 3
spine-cpp/spine-cpp/include/spine/ShearTimeline.h

@@ -33,19 +33,65 @@
 #include <spine/TranslateTimeline.h>
 
 namespace spine {
-	class SP_API ShearTimeline : public TranslateTimeline {
+	class SP_API ShearTimeline : public CurveTimeline2 {
 		friend class SkeletonBinary;
 		friend class SkeletonJson;
 
 		RTTI_DECL
 
 	public:
-		explicit ShearTimeline(int frameCount);
+		explicit ShearTimeline(size_t frameCount, size_t bezierCount, int boneIndex);
+
+		virtual ~ShearTimeline();
 
 		virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>* pEvents, float alpha, MixBlend blend, MixDirection direction);
 
-		virtual int getPropertyId();
+        int getBoneIndex() { return _boneIndex; }
+
+        void setBoneIndex(int inValue) { _boneIndex = inValue; }
+    private:
+        int _boneIndex;
 	};
+
+    class SP_API ShearXTimeline : public CurveTimeline1 {
+        friend class SkeletonBinary;
+        friend class SkeletonJson;
+
+    RTTI_DECL
+
+    public:
+        explicit ShearXTimeline(size_t frameCount, size_t bezierCount, int boneIndex);
+
+        virtual ~ShearXTimeline();
+
+        virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>* pEvents, float alpha, MixBlend blend, MixDirection direction);
+
+        int getBoneIndex() { return _boneIndex; }
+
+        void setBoneIndex(int inValue) { _boneIndex = inValue; }
+    private:
+        int _boneIndex;
+    };
+
+    class SP_API ShearYTimeline : public CurveTimeline1 {
+        friend class SkeletonBinary;
+        friend class SkeletonJson;
+
+    RTTI_DECL
+
+    public:
+        explicit ShearYTimeline(size_t frameCount, size_t bezierCount, int boneIndex);
+
+        virtual ~ShearYTimeline();
+
+        virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>* pEvents, float alpha, MixBlend blend, MixDirection direction);
+
+        int getBoneIndex() { return _boneIndex; }
+
+        void setBoneIndex(int inValue) { _boneIndex = inValue; }
+    private:
+        int _boneIndex;
+    };
 }
 
 #endif /* Spine_ShearTimeline_h */

+ 13 - 1
spine-cpp/spine-cpp/include/spine/Skeleton.h

@@ -64,7 +64,15 @@ class SP_API Skeleton : public SpineObject {
 
 	friend class AttachmentTimeline;
 
-	friend class ColorTimeline;
+	friend class RGBATimeline;
+
+    friend class RGBTimeline;
+
+    friend class AlphaTimeline;
+
+    friend class RGBA2Timeline;
+
+    friend class RGB2Timeline;
 
 	friend class DeformTimeline;
 
@@ -88,6 +96,10 @@ class SP_API Skeleton : public SpineObject {
 
 	friend class ShearTimeline;
 
+    friend class ShearXTimeline;
+
+    friend class ShearYTimeline;
+
 	friend class TransformConstraintTimeline;
 
     friend class RotateTimeline;

+ 9 - 1
spine-cpp/spine-cpp/include/spine/Slot.h

@@ -54,7 +54,15 @@ class SP_API Slot : public SpineObject {
 
 	friend class AttachmentTimeline;
 
-	friend class ColorTimeline;
+	friend class RGBATimeline;
+
+	friend class RGBTimeline;
+
+	friend class AlphaTimeline;
+
+	friend class RGBA2Timeline;
+
+	friend class RGB2Timeline;
 
 	friend class DeformTimeline;
 

+ 9 - 1
spine-cpp/spine-cpp/include/spine/SlotData.h

@@ -45,7 +45,15 @@ class SP_API SlotData : public SpineObject {
 
 	friend class AttachmentTimeline;
 
-	friend class ColorTimeline;
+    friend class RGBATimeline;
+
+    friend class RGBTimeline;
+
+    friend class AlphaTimeline;
+
+    friend class RGBA2Timeline;
+
+    friend class RGB2Timeline;
 
 	friend class DeformTimeline;
 

+ 14 - 16
spine-cpp/spine-cpp/include/spine/TransformConstraintTimeline.h

@@ -41,29 +41,27 @@ namespace spine {
 		RTTI_DECL
 
 	public:
-		static const int ENTRIES;
-
-		explicit TransformConstraintTimeline(int frameCount);
+		explicit TransformConstraintTimeline(size_t frameCount, size_t bezierCount, int transformConstraintIndex);
 
 		virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>* pEvents, float alpha, MixBlend blend, MixDirection direction);
 
-		virtual int getPropertyId();
+		void setFrame(size_t frameIndex, float time, float mixRotate, float mixX, float mixY, float mixScaleX, float mixScaleY, float mixShearY);
+
+        int getTransformConstraintIndex() { return _transformConstraintIndex; }
 
-		void setFrame(size_t frameIndex, float time, float rotateMix, float translateMix, float scaleMix, float shearMix);
+        void setTransformConstraintIndex(int inValue) { _transformConstraintIndex = inValue; }
+    private:
+        int _transformConstraintIndex;
 
-	private:
-		static const int PREV_TIME;
-		static const int PREV_ROTATE;
-		static const int PREV_TRANSLATE;
-		static const int PREV_SCALE;
-		static const int PREV_SHEAR;
-		static const int ROTATE;
-		static const int TRANSLATE;
-		static const int SCALE;
-		static const int SHEAR;
+        static const int ENTRIES = 7;
+		static const int ROTATE = 1;
+		static const int X = 2;
+        static const int Y = 3;
+		static const int SCALEX = 4;
+        static const int SCALEY = 5;
+		static const int SHEARY = 6;
 
 		Vector<float> _frames;
-		int _transformConstraintIndex;
 	};
 }
 

+ 17 - 44
spine-cpp/spine-cpp/src/spine/AttachmentTimeline.cpp

@@ -46,13 +46,12 @@ using namespace spine;
 
 RTTI_IMPL(AttachmentTimeline, Timeline)
 
-AttachmentTimeline::AttachmentTimeline(int frameCount) : Timeline(), _slotIndex(0) {
-	_frames.ensureCapacity(frameCount);
-	_attachmentNames.ensureCapacity(frameCount);
-
-	_frames.setSize(frameCount, 0);
+AttachmentTimeline::AttachmentTimeline(size_t frameCount, int slotIndex) : Timeline(frameCount, 1), _slotIndex(slotIndex) {
+    PropertyId ids[] = {((PropertyId) Property_Attachment << 32) | slotIndex };
+    setPropertyIds(ids, 1);
 
-	for (int i = 0; i < frameCount; ++i) {
+	_attachmentNames.ensureCapacity(frameCount);
+	for (size_t i = 0; i < frameCount; ++i) {
 		_attachmentNames.add(String());
 	}
 }
@@ -68,63 +67,37 @@ void AttachmentTimeline::apply(Skeleton &skeleton, float lastTime, float time, V
 	SP_UNUSED(pEvents);
 	SP_UNUSED(alpha);
 
-	assert(_slotIndex < skeleton._slots.size());
-
 	String *attachmentName;
-	Slot *slotP = skeleton._slots[_slotIndex];
-	Slot &slot = *slotP;
-	if (!slot._bone.isActive()) return;
+	Slot *slot = skeleton._slots[_slotIndex];
+	if (!slot->_bone._active) return;
 
 	if (direction == MixDirection_Out) {
-	    if (blend == MixBlend_Setup) setAttachment(skeleton, slot, &slot._data._attachmentName);
+	    if (blend == MixBlend_Setup) setAttachment(skeleton, *slot, &slot->_data._attachmentName);
 		return;
 	}
 
 	if (time < _frames[0]) {
 		// Time is before first frame.
 		if (blend == MixBlend_Setup || blend == MixBlend_First) {
-            setAttachment(skeleton, slot, &slot._data._attachmentName);
+            setAttachment(skeleton, *slot, &slot->_data._attachmentName);
 		}
 		return;
 	}
 
 	size_t frameIndex;
-	if (time >= _frames[_frames.size() - 1]) {
-		// Time is after last frame.
-		frameIndex = _frames.size() - 1;
-	} else {
-		frameIndex = Animation::binarySearch(_frames, time, 1) - 1;
-	}
-
-	attachmentName = &_attachmentNames[frameIndex];
-	slot.setAttachment(attachmentName->length() == 0 ? NULL : skeleton.getAttachment(_slotIndex, *attachmentName));
-}
+    if (time < _frames[0]) {
+        if (blend == MixBlend_Setup || blend == MixBlend_First) setAttachment(skeleton, *slot, &slot->_data._attachmentName);
+        return;
+    }
 
-int AttachmentTimeline::getPropertyId() {
-	return ((int) TimelineType_Attachment << 24) + _slotIndex;
+    setAttachment(skeleton, *slot, &_attachmentNames[Animation::search(_frames, time)]);
 }
 
-void AttachmentTimeline::setFrame(int frameIndex, float time, const String &attachmentName) {
-	_frames[frameIndex] = time;
-	_attachmentNames[frameIndex] = attachmentName;
-}
-
-size_t AttachmentTimeline::getSlotIndex() {
-	return _slotIndex;
-}
-
-void AttachmentTimeline::setSlotIndex(size_t inValue) {
-	_slotIndex = inValue;
-}
-
-Vector<float> &AttachmentTimeline::getFrames() {
-	return _frames;
+void AttachmentTimeline::setFrame(int frame, float time, const String &attachmentName) {
+	_frames[frame] = time;
+	_attachmentNames[frame] = attachmentName;
 }
 
 Vector<String> &AttachmentTimeline::getAttachmentNames() {
 	return _attachmentNames;
 }
-
-size_t AttachmentTimeline::getFrameCount() {
-	return _frames.size();
-}

+ 452 - 93
spine-cpp/spine-cpp/src/spine/ColorTimeline.cpp

@@ -44,102 +44,461 @@
 
 using namespace spine;
 
-RTTI_IMPL(ColorTimeline, CurveTimeline)
+RTTI_IMPL(RGBATimeline, CurveTimeline)
 
-const int ColorTimeline::ENTRIES = 5;
-const int ColorTimeline::PREV_TIME = -5;
-const int ColorTimeline::PREV_R = -4;
-const int ColorTimeline::PREV_G = -3;
-const int ColorTimeline::PREV_B = -2;
-const int ColorTimeline::PREV_A = -1;
-const int ColorTimeline::R = 1;
-const int ColorTimeline::G = 2;
-const int ColorTimeline::B = 3;
-const int ColorTimeline::A = 4;
+RGBATimeline::RGBATimeline(size_t frameCount, size_t bezierCount, int slotIndex) : CurveTimeline(frameCount,
+                                                                                                 RGBATimeline::ENTRIES,
+                                                                                                 bezierCount),
+                                                                                   _slotIndex(slotIndex) {
+    PropertyId ids[] = {((PropertyId) Property_Rgb << 32) | slotIndex,
+                        ((PropertyId) Property_Alpha << 32) | slotIndex};
+    setPropertyIds(ids, 2);
+}
+
+RGBATimeline::~RGBATimeline() {
+}
+
+void RGBATimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha,
+                         MixBlend blend, MixDirection direction
+) {
+    SP_UNUSED(lastTime);
+    SP_UNUSED(pEvents);
+    SP_UNUSED(direction);
+
+    Slot *slot = skeleton._slots[_slotIndex];
+    if (!slot->_bone._active) return;
+
+    if (time < _frames[0]) {
+        Color &color = slot->_color, &setup = slot->_data._color;
+        switch (blend) {
+            case MixBlend_Setup:
+                color.set(setup);
+                return;
+            case MixBlend_First:
+                color.add((setup.r - color.r) * alpha, (setup.g - color.g) * alpha, (setup.b - color.b) * alpha,
+                          (setup.a - color.a) * alpha);
+            default: {
+            }
+        }
+        return;
+    }
+
+    float r = 0, g = 0, b = 0, a = 0;
+    int i = Animation::search(_frames, time, RGBATimeline::ENTRIES);
+    int curveType = (int) _curves[i / RGBATimeline::ENTRIES];
+    switch (curveType) {
+        case RGBATimeline::LINEAR: {
+            float before = _frames[i];
+            r = _frames[i + RGBATimeline::R];
+            g = _frames[i + RGBATimeline::G];
+            b = _frames[i + RGBATimeline::B];
+            a = _frames[i + RGBATimeline::A];
+            float t = (time - before) / (_frames[i + RGBATimeline::ENTRIES] - before);
+            r += (_frames[i + RGBATimeline::ENTRIES + RGBATimeline::R] - r) * t;
+            g += (_frames[i + RGBATimeline::ENTRIES + RGBATimeline::G] - g) * t;
+            b += (_frames[i + RGBATimeline::ENTRIES + RGBATimeline::B] - b) * t;
+            a += (_frames[i + RGBATimeline::ENTRIES + RGBATimeline::A] - a) * t;
+            break;
+        }
+        case RGBATimeline::STEPPED: {
+            r = _frames[i + RGBATimeline::R];
+            g = _frames[i + RGBATimeline::G];
+            b = _frames[i + RGBATimeline::B];
+            a = _frames[i + RGBATimeline::A];
+            break;
+        }
+        default: {
+            r = getBezierValue(time, i, RGBATimeline::R, curveType - RGBATimeline::BEZIER);
+            g = getBezierValue(time, i, RGBATimeline::G,
+                               curveType + RGBATimeline::BEZIER_SIZE - RGBATimeline::BEZIER);
+            b = getBezierValue(time, i, RGBATimeline::B,
+                               curveType + RGBATimeline::BEZIER_SIZE * 2 - RGBATimeline::BEZIER);
+            a = getBezierValue(time, i, RGBATimeline::A,
+                               curveType + RGBATimeline::BEZIER_SIZE * 3 - RGBATimeline::BEZIER);
+        }
+    }
+    Color &color = slot->_color;
+    if (alpha == 1)
+        color.set(r, g, b, a);
+    else {
+        if (blend == MixBlend_Setup) color.set(slot->_data._color);
+        color.add((r - color.r) * alpha, (g - color.g) * alpha, (b - color.b) * alpha, (a - color.a) * alpha);
+    }
+}
+
+void RGBATimeline::setFrame(int frame, float time, float r, float g, float b, float a) {
+    frame *= ENTRIES;
+    _frames[frame] = time;
+    _frames[frame + R] = r;
+    _frames[frame + G] = g;
+    _frames[frame + B] = b;
+    _frames[frame + A] = a;
+}
+
+RTTI_IMPL(RGBTimeline, CurveTimeline)
+
+RGBTimeline::RGBTimeline(size_t frameCount, size_t bezierCount, int slotIndex) : CurveTimeline(frameCount,
+                                                                                               RGBTimeline::ENTRIES,
+                                                                                               bezierCount),
+                                                                                 _slotIndex(slotIndex) {
+    PropertyId ids[] = {((PropertyId) Property_Rgb << 32) | slotIndex};
+    setPropertyIds(ids, 1);
+}
+
+RGBTimeline::~RGBTimeline() {
+}
+
+void RGBTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha,
+                        MixBlend blend, MixDirection direction
+) {
+    SP_UNUSED(lastTime);
+    SP_UNUSED(pEvents);
+    SP_UNUSED(direction);
+
+    Slot *slot = skeleton._slots[_slotIndex];
+    if (!slot->_bone._active) return;
+
+    if (time < _frames[0]) {
+        Color &color = slot->_color, &setup = slot->_data._color;
+        switch (blend) {
+            case MixBlend_Setup:
+                color.set(setup);
+                return;
+            case MixBlend_First:
+                color.add((setup.r - color.r) * alpha, (setup.g - color.g) * alpha, (setup.b - color.b) * alpha,
+                          (setup.a - color.a) * alpha);
+            default: {
+            }
+        }
+        return;
+    }
+
+    float r = 0, g = 0, b = 0;
+    int i = Animation::search(_frames, time, RGBTimeline::ENTRIES);
+    int curveType = (int) _curves[i / RGBTimeline::ENTRIES];
+    switch (curveType) {
+        case RGBTimeline::LINEAR: {
+            float before = _frames[i];
+            r = _frames[i + RGBTimeline::R];
+            g = _frames[i + RGBTimeline::G];
+            b = _frames[i + RGBTimeline::B];
+            float t = (time - before) / (_frames[i + RGBTimeline::ENTRIES] - before);
+            r += (_frames[i + RGBTimeline::ENTRIES + RGBTimeline::R] - r) * t;
+            g += (_frames[i + RGBTimeline::ENTRIES + RGBTimeline::G] - g) * t;
+            b += (_frames[i + RGBTimeline::ENTRIES + RGBTimeline::B] - b) * t;
+            break;
+        }
+        case RGBTimeline::STEPPED: {
+            r = _frames[i + RGBTimeline::R];
+            g = _frames[i + RGBTimeline::G];
+            b = _frames[i + RGBTimeline::B];
+            break;
+        }
+        default: {
+            r = getBezierValue(time, i, RGBTimeline::R, curveType - RGBTimeline::BEZIER);
+            g = getBezierValue(time, i, RGBTimeline::G,
+                               curveType + RGBTimeline::BEZIER_SIZE - RGBTimeline::BEZIER);
+            b = getBezierValue(time, i, RGBTimeline::B,
+                               curveType + RGBTimeline::BEZIER_SIZE * 2 - RGBTimeline::BEZIER);
+        }
+    }
+    Color &color = slot->_color;
+    if (alpha == 1)
+        color.set(r, g, b);
+    else {
+        Color &setup = slot->_data._color;
+        if (blend == MixBlend_Setup) color.set(setup.r, setup.g, setup.b);
+        color.add((r - color.r) * alpha, (g - color.g) * alpha, (b - color.b) * alpha);
+    }
+}
+
+void RGBTimeline::setFrame(int frame, float time, float r, float g, float b) {
+    frame *= ENTRIES;
+    _frames[frame] = time;
+    _frames[frame + R] = r;
+    _frames[frame + G] = g;
+    _frames[frame + B] = b;
+}
+
+RTTI_IMPL(AlphaTimeline, CurveTimeline1)
+
+AlphaTimeline::AlphaTimeline(size_t frameCount, size_t bezierCount, int slotIndex) : CurveTimeline1(frameCount,
+                                                                                                    bezierCount),
+                                                                                     _slotIndex(slotIndex) {
+    PropertyId ids[] = {((PropertyId) Property_Alpha << 32) | slotIndex};
+    setPropertyIds(ids, 1);
+}
+
+AlphaTimeline::~AlphaTimeline() {
+}
+
+void AlphaTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha,
+                          MixBlend blend, MixDirection direction
+) {
+    SP_UNUSED(lastTime);
+    SP_UNUSED(pEvents);
+    SP_UNUSED(direction);
+
+    Slot *slot = skeleton._slots[_slotIndex];
+    if (!slot->_bone._active) return;
+
+    if (time < _frames[0]) { // Time is before first frame.
+        Color &color = slot->_color, &setup = slot->_data._color;
+        switch (blend) {
+            case MixBlend_Setup:
+                color.a = setup.a;
+                return;
+            case MixBlend_First:
+                color.a += (setup.a - color.a) * alpha;
+            default: {
+            }
+        }
+        return;
+    }
+
+    float a = getCurveValue(time);
+    if (alpha == 1)
+        slot->_color.a = a;
+    else {
+        if (blend == MixBlend_Setup) slot->_color.a = slot->_data._color.a;
+        slot->_color.a += (a - slot->_color.a) * alpha;
+    }
+}
+
+RTTI_IMPL(RGBA2Timeline, CurveTimeline)
+
+RGBA2Timeline::RGBA2Timeline(size_t frameCount, size_t bezierCount, int slotIndex) : CurveTimeline(frameCount,
+                                                                                                   RGBA2Timeline::ENTRIES,
+                                                                                                   bezierCount),
+                                                                                     _slotIndex(slotIndex) {
+    PropertyId ids[] = {((PropertyId) Property_Rgb << 32) | slotIndex,
+                        ((PropertyId) Property_Alpha << 32) | slotIndex,
+                        ((PropertyId) Property_Rgb2 << 32) | slotIndex};
+    setPropertyIds(ids, 3);
+}
+
+RGBA2Timeline::~RGBA2Timeline() {
+}
+
+void RGBA2Timeline::apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha,
+                          MixBlend blend, MixDirection direction
+) {
+    SP_UNUSED(lastTime);
+    SP_UNUSED(pEvents);
+    SP_UNUSED(direction);
 
-ColorTimeline::ColorTimeline(int frameCount) : CurveTimeline(frameCount), _slotIndex(0) {
-	_frames.setSize(frameCount * ENTRIES, 0);
+    Slot *slot = skeleton._slots[_slotIndex];
+    if (!slot->_bone._active) return;
+
+    if (time < _frames[0]) {
+        Color &light = slot->_color, &dark = slot->_darkColor, &setupLight = slot->_data._color, &setupDark = slot->_data._darkColor;
+        switch (blend) {
+            case MixBlend_Setup:
+                light.set(setupLight);
+                dark.set(setupDark.r, setupDark.g, setupDark.b);
+                return;
+            case MixBlend_First:
+                light.add((setupLight.r - light.r) * alpha, (setupLight.g - light.g) * alpha,
+                          (setupLight.b - light.b) * alpha,
+                          (setupLight.a - light.a) * alpha);
+                dark.r += (setupDark.r - dark.r) * alpha;
+                dark.g += (setupDark.g - dark.g) * alpha;
+                dark.b += (setupDark.b - dark.b) * alpha;
+            default: {
+            }
+        }
+        return;
+    }
+
+    float r = 0, g = 0, b = 0, a = 0, r2 = 0, g2 = 0, b2 = 0;
+    int i = Animation::search(_frames, time, RGBA2Timeline::ENTRIES);
+    int curveType = (int) _curves[i / RGBA2Timeline::ENTRIES];
+    switch (curveType) {
+        case RGBA2Timeline::LINEAR: {
+            float before = _frames[i];
+            r = _frames[i + RGBA2Timeline::R];
+            g = _frames[i + RGBA2Timeline::G];
+            b = _frames[i + RGBA2Timeline::B];
+            a = _frames[i + RGBA2Timeline::A];
+            r2 = _frames[i + RGBA2Timeline::R2];
+            g2 = _frames[i + RGBA2Timeline::G2];
+            b2 = _frames[i + RGBA2Timeline::B2];
+            float t = (time - before) / (_frames[i + RGBA2Timeline::ENTRIES] - before);
+            r += (_frames[i + RGBA2Timeline::ENTRIES + RGBA2Timeline::R] - r) * t;
+            g += (_frames[i + RGBA2Timeline::ENTRIES + RGBA2Timeline::G] - g) * t;
+            b += (_frames[i + RGBA2Timeline::ENTRIES + RGBA2Timeline::B] - b) * t;
+            a += (_frames[i + RGBA2Timeline::ENTRIES + RGBA2Timeline::A] - a) * t;
+            r2 += (_frames[i + RGBA2Timeline::ENTRIES + RGBA2Timeline::R2] - r2) * t;
+            g2 += (_frames[i + RGBA2Timeline::ENTRIES + RGBA2Timeline::G2] - g2) * t;
+            b2 += (_frames[i + RGBA2Timeline::ENTRIES + RGBA2Timeline::B2] - b2) * t;
+            break;
+        }
+        case RGBA2Timeline::STEPPED: {
+            r = _frames[i + RGBA2Timeline::R];
+            g = _frames[i + RGBA2Timeline::G];
+            b = _frames[i + RGBA2Timeline::B];
+            a = _frames[i + RGBA2Timeline::A];
+            r2 = _frames[i + RGBA2Timeline::R2];
+            g2 = _frames[i + RGBA2Timeline::G2];
+            b2 = _frames[i + RGBA2Timeline::B2];
+            break;
+        }
+        default: {
+            r = getBezierValue(time, i, RGBA2Timeline::R, curveType - RGBA2Timeline::BEZIER);
+            g = getBezierValue(time, i, RGBA2Timeline::G,
+                               curveType + RGBA2Timeline::BEZIER_SIZE - RGBA2Timeline::BEZIER);
+            b = getBezierValue(time, i, RGBA2Timeline::B,
+                               curveType + RGBA2Timeline::BEZIER_SIZE * 2 - RGBA2Timeline::BEZIER);
+            a = getBezierValue(time, i, RGBA2Timeline::A,
+                               curveType + RGBA2Timeline::BEZIER_SIZE * 3 - RGBA2Timeline::BEZIER);
+            r2 = getBezierValue(time, i, RGBA2Timeline::R2,
+                                curveType + RGBA2Timeline::BEZIER_SIZE * 4 - RGBA2Timeline::BEZIER);
+            g2 = getBezierValue(time, i, RGBA2Timeline::G2,
+                                curveType + RGBA2Timeline::BEZIER_SIZE * 5 - RGBA2Timeline::BEZIER);
+            b2 = getBezierValue(time, i, RGBA2Timeline::B2,
+                                curveType + RGBA2Timeline::BEZIER_SIZE * 6 - RGBA2Timeline::BEZIER);
+        }
+    }
+    Color &light = slot->_color, &dark = slot->_darkColor;
+    if (alpha == 1) {
+        light.set(r, g, b, a);
+        dark.set(r2, g2, b2);
+    } else {
+        if (blend == MixBlend_Setup) {
+            light.set(slot->_data._color);
+            dark.set(slot->_data._darkColor);
+        }
+        light.add((r - light.r) * alpha, (g - light.g) * alpha, (b - light.b) * alpha, (a - light.a) * alpha);
+        dark.r += (r2 - dark.r) * alpha;
+        dark.g += (g2 - dark.g) * alpha;
+        dark.b += (b2 - dark.b) * alpha;
+    }
+}
+
+void
+RGBA2Timeline::setFrame(int frame, float time, float r, float g, float b, float a, float r2, float g2, float b2) {
+    frame *= ENTRIES;
+    _frames[frame] = time;
+    _frames[frame + R] = r;
+    _frames[frame + G] = g;
+    _frames[frame + B] = b;
+    _frames[frame + A] = a;
+    _frames[frame + R2] = r2;
+    _frames[frame + G2] = g2;
+    _frames[frame + B2] = b2;
+}
+
+RTTI_IMPL(RGB2Timeline, CurveTimeline)
+
+RGB2Timeline::RGB2Timeline(size_t frameCount, size_t bezierCount, int slotIndex) : CurveTimeline(frameCount,
+                                                                                                   RGB2Timeline::ENTRIES,
+                                                                                                   bezierCount),
+                                                                                     _slotIndex(slotIndex) {
+    PropertyId ids[] = {((PropertyId) Property_Rgb << 32) | slotIndex,
+                        ((PropertyId) Property_Rgb2 << 32) | slotIndex};
+    setPropertyIds(ids, 2);
 }
 
-void ColorTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha,
-	MixBlend blend, MixDirection direction
+RGB2Timeline::~RGB2Timeline() {
+}
+
+void RGB2Timeline::apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha,
+                          MixBlend blend, MixDirection direction
 ) {
-	SP_UNUSED(lastTime);
-	SP_UNUSED(pEvents);
-	SP_UNUSED(direction);
-
-	Slot *slotP = skeleton._slots[_slotIndex];
-	Slot &slot = *slotP;
-	if (!slot._bone.isActive()) return;
-	if (time < _frames[0]) {
-		switch (blend) {
-		case MixBlend_Setup:
-			slot._color.set(slot._data._color);
-			return;
-		case MixBlend_First: {
-			Color &color = slot._color, setup = slot._data._color;
-			color.add((setup.r - color.r) * alpha, (setup.g - color.g) * alpha, (setup.b - color.b) * alpha,
-				(setup.a - color.a) * alpha);
-		}
-		default: ;
-		}
-		return;
-	}
-
-	float r, g, b, a;
-	if (time >= _frames[_frames.size() - ENTRIES]) {
-		// Time is after last frame.
-		size_t i = _frames.size();
-		r = _frames[i + PREV_R];
-		g = _frames[i + PREV_G];
-		b = _frames[i + PREV_B];
-		a = _frames[i + PREV_A];
-	} else {
-		// Interpolate between the previous frame and the current frame.
-		size_t frame = (size_t)Animation::binarySearch(_frames, time, ENTRIES);
-		r = _frames[frame + PREV_R];
-		g = _frames[frame + PREV_G];
-		b = _frames[frame + PREV_B];
-		a = _frames[frame + PREV_A];
-		float frameTime = _frames[frame];
-		float percent = getCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime));
-
-		r += (_frames[frame + R] - r) * percent;
-		g += (_frames[frame + G] - g) * percent;
-		b += (_frames[frame + B] - b) * percent;
-		a += (_frames[frame + A] - a) * percent;
-	}
-
-	if (alpha == 1) {
-		slot.getColor().set(r, g, b, a);
-	} else {
-		Color &color = slot.getColor();
-		if (blend == MixBlend_Setup) color.set(slot.getData().getColor());
-		color.add((r - color.r) * alpha, (g - color.g) * alpha, (b - color.b) * alpha, (a - color.a) * alpha);
-	}
-}
-
-int ColorTimeline::getPropertyId() {
-	return ((int) TimelineType_Color << 24) + _slotIndex;
-}
-
-void ColorTimeline::setFrame(int frameIndex, float time, float r, float g, float b, float a) {
-	frameIndex *= ENTRIES;
-	_frames[frameIndex] = time;
-	_frames[frameIndex + R] = r;
-	_frames[frameIndex + G] = g;
-	_frames[frameIndex + B] = b;
-	_frames[frameIndex + A] = a;
-}
-
-int ColorTimeline::getSlotIndex() {
-	return _slotIndex;
-}
-
-void ColorTimeline::setSlotIndex(int inValue) {
-	_slotIndex = inValue;
-}
-
-Vector<float> &ColorTimeline::getFrames() {
-	return _frames;
+    SP_UNUSED(lastTime);
+    SP_UNUSED(pEvents);
+    SP_UNUSED(direction);
+
+    Slot *slot = skeleton._slots[_slotIndex];
+    if (!slot->_bone._active) return;
+
+    if (time < _frames[0]) {
+        Color &light = slot->_color, &dark = slot->_darkColor, &setupLight = slot->_data._color, &setupDark = slot->_data._darkColor;
+        switch (blend) {
+            case MixBlend_Setup:
+                light.set(setupLight.r, setupLight.g, setupLight.b);
+                dark.set(setupDark.r, setupDark.g, setupDark.b);
+                return;
+            case MixBlend_First:
+                light.add((setupLight.r - light.r) * alpha, (setupLight.g - light.g) * alpha,
+                          (setupLight.b - light.b) * alpha);
+                dark.r += (setupDark.r - dark.r) * alpha;
+                dark.g += (setupDark.g - dark.g) * alpha;
+                dark.b += (setupDark.b - dark.b) * alpha;
+            default: {
+            }
+        }
+        return;
+    }
+
+    float r = 0, g = 0, b = 0, r2 = 0, g2 = 0, b2 = 0;
+    int i = Animation::search(_frames, time, RGB2Timeline::ENTRIES);
+    int curveType = (int) _curves[i / RGB2Timeline::ENTRIES];
+    switch (curveType) {
+        case RGB2Timeline::LINEAR: {
+            float before = _frames[i];
+            r = _frames[i + RGB2Timeline::R];
+            g = _frames[i + RGB2Timeline::G];
+            b = _frames[i + RGB2Timeline::B];
+            r2 = _frames[i + RGB2Timeline::R2];
+            g2 = _frames[i + RGB2Timeline::G2];
+            b2 = _frames[i + RGB2Timeline::B2];
+            float t = (time - before) / (_frames[i + RGB2Timeline::ENTRIES] - before);
+            r += (_frames[i + RGB2Timeline::ENTRIES + RGB2Timeline::R] - r) * t;
+            g += (_frames[i + RGB2Timeline::ENTRIES + RGB2Timeline::G] - g) * t;
+            b += (_frames[i + RGB2Timeline::ENTRIES + RGB2Timeline::B] - b) * t;
+            r2 += (_frames[i + RGB2Timeline::ENTRIES + RGB2Timeline::R2] - r2) * t;
+            g2 += (_frames[i + RGB2Timeline::ENTRIES + RGB2Timeline::G2] - g2) * t;
+            b2 += (_frames[i + RGB2Timeline::ENTRIES + RGB2Timeline::B2] - b2) * t;
+            break;
+        }
+        case RGB2Timeline::STEPPED: {
+            r = _frames[i + RGB2Timeline::R];
+            g = _frames[i + RGB2Timeline::G];
+            b = _frames[i + RGB2Timeline::B];
+            r2 = _frames[i + RGB2Timeline::R2];
+            g2 = _frames[i + RGB2Timeline::G2];
+            b2 = _frames[i + RGB2Timeline::B2];
+            break;
+        }
+        default: {
+            r = getBezierValue(time, i, RGB2Timeline::R, curveType - RGB2Timeline::BEZIER);
+            g = getBezierValue(time, i, RGB2Timeline::G,
+                               curveType + RGB2Timeline::BEZIER_SIZE - RGB2Timeline::BEZIER);
+            b = getBezierValue(time, i, RGB2Timeline::B,
+                               curveType + RGB2Timeline::BEZIER_SIZE * 2 - RGB2Timeline::BEZIER);
+            r2 = getBezierValue(time, i, RGB2Timeline::R2,
+                                curveType + RGB2Timeline::BEZIER_SIZE * 4 - RGB2Timeline::BEZIER);
+            g2 = getBezierValue(time, i, RGB2Timeline::G2,
+                                curveType + RGB2Timeline::BEZIER_SIZE * 5 - RGB2Timeline::BEZIER);
+            b2 = getBezierValue(time, i, RGB2Timeline::B2,
+                                curveType + RGB2Timeline::BEZIER_SIZE * 6 - RGB2Timeline::BEZIER);
+        }
+    }
+    Color &light = slot->_color, &dark = slot->_darkColor;
+    if (alpha == 1) {
+        light.set(r, g, b);
+        dark.set(r2, g2, b2);
+    } else {
+        if (blend == MixBlend_Setup) {
+            light.set(slot->_data._color.r, slot->_data._color.g, slot->_data._color.b);
+            dark.set(slot->_data._darkColor);
+        }
+        light.add((r - light.r) * alpha, (g - light.g) * alpha, (b - light.b) * alpha);
+        dark.r += (r2 - dark.r) * alpha;
+        dark.g += (g2 - dark.g) * alpha;
+        dark.b += (b2 - dark.b) * alpha;
+    }
+}
+
+void
+RGB2Timeline::setFrame(int frame, float time, float r, float g, float b, float r2, float g2, float b2) {
+    frame *= ENTRIES;
+    _frames[frame] = time;
+    _frames[frame + R] = r;
+    _frames[frame + G] = g;
+    _frames[frame + B] = b;
+    _frames[frame + R2] = r2;
+    _frames[frame + G2] = g2;
+    _frames[frame + B2] = b2;
 }

+ 1 - 5
spine-cpp/spine-cpp/src/spine/CurveTimeline.cpp

@@ -93,8 +93,7 @@ float CurveTimeline::getBezierValue(float time, size_t frame, size_t valueOffset
 }
 
 RTTI_IMPL(CurveTimeline1, CurveTimeline)
-const int CurveTimeline1::ENTRIES = 2;
-const int CurveTimeline1::VALUE = 1;
+
 
 CurveTimeline1::CurveTimeline1(size_t frameCount, size_t bezierCount): CurveTimeline(frameCount, CurveTimeline1::ENTRIES, bezierCount) {
 }
@@ -131,9 +130,6 @@ float CurveTimeline1::getCurveValue(float time) {
 }
 
 RTTI_IMPL(CurveTimeline2, CurveTimeline)
-const int CurveTimeline2::ENTRIES = 3;
-const int CurveTimeline2::VALUE1 = 1;
-const int CurveTimeline2::VALUE2 = 2;
 
 CurveTimeline2::CurveTimeline2(size_t frameCount, size_t bezierCount): CurveTimeline(frameCount, CurveTimeline2::ENTRIES, bezierCount) {
 }

+ 250 - 217
spine-cpp/spine-cpp/src/spine/DeformTimeline.cpp

@@ -48,251 +48,284 @@ using namespace spine;
 
 RTTI_IMPL(DeformTimeline, CurveTimeline)
 
-DeformTimeline::DeformTimeline(int frameCount) : CurveTimeline(frameCount), _slotIndex(0), _attachment(NULL) {
-	_frames.ensureCapacity(frameCount);
-	_frameVertices.ensureCapacity(frameCount);
+DeformTimeline::DeformTimeline(size_t frameCount, size_t bezierCount, int slotIndex, VertexAttachment *attachment)
+        : CurveTimeline(frameCount, 1, bezierCount), _slotIndex(slotIndex), _attachment(attachment) {
+    PropertyId ids[] = {((PropertyId) Property_Deform << 32) | ((slotIndex << 16 | attachment->_id) & 0xffffffff)};
+    setPropertyIds(ids, 1);
 
-	_frames.setSize(frameCount, 0);
-
-	for (int i = 0; i < frameCount; ++i) {
-		Vector<float> vec;
-		_frameVertices.add(vec);
-	}
+    _vertices.ensureCapacity(frameCount);
+    for (size_t i = 0; i < frameCount; ++i) {
+        Vector<float> vec;
+        _vertices.add(vec);
+    }
 }
 
 void DeformTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha,
-	MixBlend blend, MixDirection direction
+                           MixBlend blend, MixDirection direction
 ) {
-	SP_UNUSED(lastTime);
-	SP_UNUSED(pEvents);
-	SP_UNUSED(direction);
-
-	Slot *slotP = skeleton._slots[_slotIndex];
-	Slot &slot = *slotP;
-	if (!slot._bone.isActive()) return;
+    SP_UNUSED(lastTime);
+    SP_UNUSED(pEvents);
+    SP_UNUSED(direction);
 
-	Attachment *slotAttachment = slot.getAttachment();
-	if (slotAttachment == NULL || !slotAttachment->getRTTI().instanceOf(VertexAttachment::rtti)) {
-		return;
-	}
+    Slot *slotP = skeleton._slots[_slotIndex];
+    Slot &slot = *slotP;
+    if (!slot._bone.isActive()) return;
 
-	VertexAttachment *attachment = static_cast<VertexAttachment *>(slotAttachment);
-	if (attachment->_deformAttachment != _attachment) {
-		return;
-	}
+    Attachment *slotAttachment = slot.getAttachment();
+    if (slotAttachment == NULL || !slotAttachment->getRTTI().instanceOf(VertexAttachment::rtti)) {
+        return;
+    }
 
-	Vector<float> &deformArray = slot._deform;
-	if (deformArray.size() == 0) {
-		blend = MixBlend_Setup;
-	}
+    VertexAttachment *attachment = static_cast<VertexAttachment *>(slotAttachment);
+    if (attachment->_deformAttachment != _attachment) {
+        return;
+    }
 
-	Vector< Vector<float> > &frameVertices = _frameVertices;
-	size_t vertexCount = frameVertices[0].size();
+    Vector<float> &deformArray = slot._deform;
+    if (deformArray.size() == 0) {
+        blend = MixBlend_Setup;
+    }
 
-	Vector<float> &frames = _frames;
-	if (time < _frames[0]) {
-		switch (blend) {
-		case MixBlend_Setup:
-			deformArray.clear();
-			return;
-		case MixBlend_First: {
-			if (alpha == 1) {
-				deformArray.clear();
-				return;
-			}
-			deformArray.setSize(vertexCount, 0);
-			Vector<float> &deformInner = deformArray;
-			if (attachment->getBones().size() == 0) {
-				// Unweighted vertex positions.
-				Vector<float> &setupVertices = attachment->getVertices();
-				for (size_t i = 0; i < vertexCount; i++)
-					deformInner[i] += (setupVertices[i] - deformInner[i]) * alpha;
-			} else {
-				// Weighted deform offsets.
-				alpha = 1 - alpha;
-				for (size_t i = 0; i < vertexCount; i++)
-					deformInner[i] *= alpha;
-			}
-		}
-		case MixBlend_Replace:
-		case MixBlend_Add:
-			return;
-		}
-	}
+    Vector<Vector<float> > &vertices = _vertices;
+    size_t vertexCount = vertices[0].size();
 
-	deformArray.setSize(vertexCount, 0);
-	Vector<float> &deform = deformArray;
+    Vector<float> &frames = _frames;
+    if (time < _frames[0]) {
+        switch (blend) {
+            case MixBlend_Setup:
+                deformArray.clear();
+                return;
+            case MixBlend_First: {
+                if (alpha == 1) {
+                    deformArray.clear();
+                    return;
+                }
+                deformArray.setSize(vertexCount, 0);
+                Vector<float> &deformInner = deformArray;
+                if (attachment->getBones().size() == 0) {
+                    // Unweighted vertex positions.
+                    Vector<float> &setupVertices = attachment->getVertices();
+                    for (size_t i = 0; i < vertexCount; i++)
+                        deformInner[i] += (setupVertices[i] - deformInner[i]) * alpha;
+                } else {
+                    // Weighted deform offsets.
+                    alpha = 1 - alpha;
+                    for (size_t i = 0; i < vertexCount; i++)
+                        deformInner[i] *= alpha;
+                }
+            }
+            case MixBlend_Replace:
+            case MixBlend_Add:
+                return;
+        }
+    }
 
-	if (time >= frames[frames.size() - 1]) { // Time is after last frame.
-		Vector<float> &lastVertices = frameVertices[frames.size() - 1];
-		if (alpha == 1) {
-			if (blend == MixBlend_Add) {
-				VertexAttachment *vertexAttachment = static_cast<VertexAttachment*>(slotAttachment);
-				if (vertexAttachment->getBones().size() == 0) {
-					// Unweighted vertex positions, no alpha.
-					Vector<float> &setupVertices = vertexAttachment->getVertices();
-					for (size_t i = 0; i < vertexCount; i++)
-						deform[i] += lastVertices[i] - setupVertices[i];
-				} else {
-					// Weighted deform offsets, no alpha.
-					for (size_t i = 0; i < vertexCount; i++)
-						deform[i] += lastVertices[i];
-				}
-			} else {
-				// Vertex positions or deform offsets, no alpha.
-				memcpy(deform.buffer(), lastVertices.buffer(), vertexCount * sizeof(float));
-			}
-		} else {
-			switch (blend) {
-			case MixBlend_Setup: {
-				VertexAttachment *vertexAttachment = static_cast<VertexAttachment *>(slotAttachment);
-				if (vertexAttachment->getBones().size() == 0) {
-					// Unweighted vertex positions, with alpha.
-					Vector<float> &setupVertices = vertexAttachment->getVertices();
-					for (size_t i = 0; i < vertexCount; i++) {
-						float setup = setupVertices[i];
-						deform[i] = setup + (lastVertices[i] - setup) * alpha;
-					}
-				} else {
-					// Weighted deform offsets, with alpha.
-					for (size_t i = 0; i < vertexCount; i++)
-						deform[i] = lastVertices[i] * alpha;
-				}
-				break;
-			}
-			case MixBlend_First:
-			case MixBlend_Replace:
-				// Vertex positions or deform offsets, with alpha.
-				for (size_t i = 0; i < vertexCount; i++)
-					deform[i] += (lastVertices[i] - deform[i]) * alpha;
-				break;
-			case MixBlend_Add:
-				VertexAttachment *vertexAttachment = static_cast<VertexAttachment *>(slotAttachment);
-				if (vertexAttachment->getBones().size() == 0) {
-					// Unweighted vertex positions, no alpha.
-					Vector<float> &setupVertices = vertexAttachment->getVertices();
-					for (size_t i = 0; i < vertexCount; i++)
-						deform[i] += (lastVertices[i] - setupVertices[i]) * alpha;
-				} else {
-					// Weighted deform offsets, alpha.
-					for (size_t i = 0; i < vertexCount; i++)
-						deform[i] += lastVertices[i] * alpha;
-				}
-			}
-		}
-		return;
-	}
+    deformArray.setSize(vertexCount, 0);
+    Vector<float> &deform = deformArray;
 
-	// Interpolate between the previous frame and the current frame.
-	int frame = Animation::binarySearch(frames, time);
-	Vector<float> &prevVertices = frameVertices[frame - 1];
-	Vector<float> &nextVertices = frameVertices[frame];
-	float frameTime = frames[frame];
-	float percent = getCurvePercent(frame - 1, 1 - (time - frameTime) / (frames[frame - 1] - frameTime));
+    if (time >= frames[frames.size() - 1]) { // Time is after last frame.
+        Vector<float> &lastVertices = vertices[frames.size() - 1];
+        if (alpha == 1) {
+            if (blend == MixBlend_Add) {
+                VertexAttachment *vertexAttachment = static_cast<VertexAttachment *>(slotAttachment);
+                if (vertexAttachment->getBones().size() == 0) {
+                    // Unweighted vertex positions, no alpha.
+                    Vector<float> &setupVertices = vertexAttachment->getVertices();
+                    for (size_t i = 0; i < vertexCount; i++)
+                        deform[i] += lastVertices[i] - setupVertices[i];
+                } else {
+                    // Weighted deform offsets, no alpha.
+                    for (size_t i = 0; i < vertexCount; i++)
+                        deform[i] += lastVertices[i];
+                }
+            } else {
+                // Vertex positions or deform offsets, no alpha.
+                memcpy(deform.buffer(), lastVertices.buffer(), vertexCount * sizeof(float));
+            }
+        } else {
+            switch (blend) {
+                case MixBlend_Setup: {
+                    VertexAttachment *vertexAttachment = static_cast<VertexAttachment *>(slotAttachment);
+                    if (vertexAttachment->getBones().size() == 0) {
+                        // Unweighted vertex positions, with alpha.
+                        Vector<float> &setupVertices = vertexAttachment->getVertices();
+                        for (size_t i = 0; i < vertexCount; i++) {
+                            float setup = setupVertices[i];
+                            deform[i] = setup + (lastVertices[i] - setup) * alpha;
+                        }
+                    } else {
+                        // Weighted deform offsets, with alpha.
+                        for (size_t i = 0; i < vertexCount; i++)
+                            deform[i] = lastVertices[i] * alpha;
+                    }
+                    break;
+                }
+                case MixBlend_First:
+                case MixBlend_Replace:
+                    // Vertex positions or deform offsets, with alpha.
+                    for (size_t i = 0; i < vertexCount; i++)
+                        deform[i] += (lastVertices[i] - deform[i]) * alpha;
+                    break;
+                case MixBlend_Add:
+                    VertexAttachment *vertexAttachment = static_cast<VertexAttachment *>(slotAttachment);
+                    if (vertexAttachment->getBones().size() == 0) {
+                        // Unweighted vertex positions, no alpha.
+                        Vector<float> &setupVertices = vertexAttachment->getVertices();
+                        for (size_t i = 0; i < vertexCount; i++)
+                            deform[i] += (lastVertices[i] - setupVertices[i]) * alpha;
+                    } else {
+                        // Weighted deform offsets, alpha.
+                        for (size_t i = 0; i < vertexCount; i++)
+                            deform[i] += lastVertices[i] * alpha;
+                    }
+            }
+        }
+        return;
+    }
 
-	if (alpha == 1) {
-		if (blend == MixBlend_Add) {
-			VertexAttachment *vertexAttachment = static_cast<VertexAttachment *>(slotAttachment);
-			if (vertexAttachment->getBones().size() == 0) {
-				// Unweighted vertex positions, no alpha.
-				Vector<float> &setupVertices = vertexAttachment->getVertices();
-				for (size_t i = 0; i < vertexCount; i++) {
-					float prev = prevVertices[i];
-					deform[i] += prev + (nextVertices[i] - prev) * percent - setupVertices[i];
-				}
-			} else {
-				// Weighted deform offsets, no alpha.
-				for (size_t i = 0; i < vertexCount; i++) {
-					float prev = prevVertices[i];
-					deform[i] += prev + (nextVertices[i] - prev) * percent;
-				}
-			}
-		} else {
-			// Vertex positions or deform offsets, no alpha.
-			for (size_t i = 0; i < vertexCount; i++) {
-				float prev = prevVertices[i];
-				deform[i] = prev + (nextVertices[i] - prev) * percent;
-			}
-		}
-	} else {
-		switch (blend) {
-		case MixBlend_Setup: {
-			VertexAttachment *vertexAttachment = static_cast<VertexAttachment *>(slotAttachment);
-			if (vertexAttachment->getBones().size() == 0) {
-				// Unweighted vertex positions, with alpha.
-				Vector<float> &setupVertices = vertexAttachment->getVertices();
-				for (size_t i = 0; i < vertexCount; i++) {
-					float prev = prevVertices[i], setup = setupVertices[i];
-					deform[i] = setup + (prev + (nextVertices[i] - prev) * percent - setup) * alpha;
-				}
-			} else {
-				// Weighted deform offsets, with alpha.
-				for (size_t i = 0; i < vertexCount; i++) {
-					float prev = prevVertices[i];
-					deform[i] = (prev + (nextVertices[i] - prev) * percent) * alpha;
-				}
-			}
-			break;
-		}
-		case MixBlend_First:
-		case MixBlend_Replace:
-			// Vertex positions or deform offsets, with alpha.
-			for (size_t i = 0; i < vertexCount; i++) {
-				float prev = prevVertices[i];
-				deform[i] += (prev + (nextVertices[i] - prev) * percent - deform[i]) * alpha;
-			}
-			break;
-		case MixBlend_Add:
-			VertexAttachment *vertexAttachment = static_cast<VertexAttachment *>(slotAttachment);
-			if (vertexAttachment->getBones().size() == 0) {
-				// Unweighted vertex positions, with alpha.
-				Vector<float> &setupVertices = vertexAttachment->getVertices();
-				for (size_t i = 0; i < vertexCount; i++) {
-					float prev = prevVertices[i];
-					deform[i] += (prev + (nextVertices[i] - prev) * percent - setupVertices[i]) * alpha;
-				}
-			} else {
-				// Weighted deform offsets, with alpha.
-				for (size_t i = 0; i < vertexCount; i++) {
-					float prev = prevVertices[i];
-					deform[i] += (prev + (nextVertices[i] - prev) * percent) * alpha;
-				}
-			}
-		}
-	}
-}
+    // Interpolate between the previous frame and the current frame.
+    int frame = Animation::search(frames, time);
+    float percent = getCurvePercent(time, frame);
+    Vector<float> &prevVertices = vertices[frame];
+    Vector<float> &nextVertices = vertices[frame + 1];
 
-int DeformTimeline::getPropertyId() {
-	assert(_attachment != NULL);
-	return ((int) TimelineType_Deform << 24) + _attachment->_id + _slotIndex;
+    if (alpha == 1) {
+        if (blend == MixBlend_Add) {
+            VertexAttachment *vertexAttachment = static_cast<VertexAttachment *>(slotAttachment);
+            if (vertexAttachment->getBones().size() == 0) {
+                // Unweighted vertex positions, no alpha.
+                Vector<float> &setupVertices = vertexAttachment->getVertices();
+                for (size_t i = 0; i < vertexCount; i++) {
+                    float prev = prevVertices[i];
+                    deform[i] += prev + (nextVertices[i] - prev) * percent - setupVertices[i];
+                }
+            } else {
+                // Weighted deform offsets, no alpha.
+                for (size_t i = 0; i < vertexCount; i++) {
+                    float prev = prevVertices[i];
+                    deform[i] += prev + (nextVertices[i] - prev) * percent;
+                }
+            }
+        } else {
+            // Vertex positions or deform offsets, no alpha.
+            for (size_t i = 0; i < vertexCount; i++) {
+                float prev = prevVertices[i];
+                deform[i] = prev + (nextVertices[i] - prev) * percent;
+            }
+        }
+    } else {
+        switch (blend) {
+            case MixBlend_Setup: {
+                VertexAttachment *vertexAttachment = static_cast<VertexAttachment *>(slotAttachment);
+                if (vertexAttachment->getBones().size() == 0) {
+                    // Unweighted vertex positions, with alpha.
+                    Vector<float> &setupVertices = vertexAttachment->getVertices();
+                    for (size_t i = 0; i < vertexCount; i++) {
+                        float prev = prevVertices[i], setup = setupVertices[i];
+                        deform[i] = setup + (prev + (nextVertices[i] - prev) * percent - setup) * alpha;
+                    }
+                } else {
+                    // Weighted deform offsets, with alpha.
+                    for (size_t i = 0; i < vertexCount; i++) {
+                        float prev = prevVertices[i];
+                        deform[i] = (prev + (nextVertices[i] - prev) * percent) * alpha;
+                    }
+                }
+                break;
+            }
+            case MixBlend_First:
+            case MixBlend_Replace:
+                // Vertex positions or deform offsets, with alpha.
+                for (size_t i = 0; i < vertexCount; i++) {
+                    float prev = prevVertices[i];
+                    deform[i] += (prev + (nextVertices[i] - prev) * percent - deform[i]) * alpha;
+                }
+                break;
+            case MixBlend_Add:
+                VertexAttachment *vertexAttachment = static_cast<VertexAttachment *>(slotAttachment);
+                if (vertexAttachment->getBones().size() == 0) {
+                    // Unweighted vertex positions, with alpha.
+                    Vector<float> &setupVertices = vertexAttachment->getVertices();
+                    for (size_t i = 0; i < vertexCount; i++) {
+                        float prev = prevVertices[i];
+                        deform[i] += (prev + (nextVertices[i] - prev) * percent - setupVertices[i]) * alpha;
+                    }
+                } else {
+                    // Weighted deform offsets, with alpha.
+                    for (size_t i = 0; i < vertexCount; i++) {
+                        float prev = prevVertices[i];
+                        deform[i] += (prev + (nextVertices[i] - prev) * percent) * alpha;
+                    }
+                }
+        }
+    }
 }
 
-void DeformTimeline::setFrame(int frameIndex, float time, Vector<float> &vertices) {
-	_frames[frameIndex] = time;
-	_frameVertices[frameIndex].clear();
-	_frameVertices[frameIndex].addAll(vertices);
+void DeformTimeline::setBezier(int bezier, int frame, float value, float time1, float value1, float cx1, float cy1,
+                               float cx2, float cy2, float time2, float value2) {
+    SP_UNUSED(value1);
+    SP_UNUSED(value2);
+    size_t i = getFrameCount() + bezier * DeformTimeline::BEZIER_SIZE;
+    if (value == 0) _curves[frame] = DeformTimeline::BEZIER + i;
+    float tmpx = (time1 - cx1 * 2 + cx2) * 0.03, tmpy = cy2 * 0.03 - cy1 * 0.06;
+    float dddx = ((cx1 - cx2) * 3 - time1 + time2) * 0.006, dddy = (cy1 - cy2 + 0.33333333) * 0.018;
+    float ddx = tmpx * 2 + dddx, ddy = tmpy * 2 + dddy;
+    float dx = (cx1 - time1) * 0.3 + tmpx + dddx * 0.16666667, dy = cy1 * 0.3 + tmpy + dddy * 0.16666667;
+    float x = time1 + dx, y = dy;
+    for (size_t n = i + DeformTimeline::BEZIER_SIZE; i < n; i += 2) {
+        _curves[i] = x;
+        _curves[i + 1] = y;
+        dx += ddx;
+        dy += ddy;
+        ddx += dddx;
+        ddy += dddy;
+        x += dx;
+        y += dy;
+    }
 }
 
-int DeformTimeline::getSlotIndex() {
-	return _slotIndex;
+float DeformTimeline::getCurvePercent(float time, int frame) {
+    int i = (int)_curves[frame];
+    switch (i) {
+        case DeformTimeline::LINEAR: {
+            float x = _frames[frame];
+            return (time - x) / (_frames[frame + getFrameEntries()] - x);
+        }
+        case DeformTimeline::STEPPED: {
+            return 0;
+        }
+        default: {}
+    }
+    i -= DeformTimeline::BEZIER;
+    if (_curves[i] > time) {
+        float x = _frames[frame];
+        return _curves[i + 1] * (time - x) / (_curves[i] - x);
+    }
+    int n = i + DeformTimeline::BEZIER_SIZE;
+    for (i += 2; i < n; i += 2) {
+        if (_curves[i] >= time) {
+            float x = _curves[i - 2], y = _curves[i - 1];
+            return y + (time - x) / (_curves[i] - x) * (_curves[i + 1] - y);
+        }
+    }
+    float x = _curves[n - 2], y = _curves[n - 1];
+    return y + (1 - y) * (time - x) / (_frames[frame + getFrameEntries()] - x);
 }
 
-void DeformTimeline::setSlotIndex(int inValue) {
-	_slotIndex = inValue;
-}
-
-Vector<float> &DeformTimeline::getFrames() {
-	return _frames;
+void DeformTimeline::setFrame(int frameIndex, float time, Vector<float> &vertices) {
+    _frames[frameIndex] = time;
+    _vertices[frameIndex].clear();
+    _vertices[frameIndex].addAll(vertices);
 }
 
 Vector<Vector<float> > &DeformTimeline::getVertices() {
-	return _frameVertices;
+    return _vertices;
 }
 
 VertexAttachment *DeformTimeline::getAttachment() {
-	return _attachment;
+    return _attachment;
 }
 
 void DeformTimeline::setAttachment(VertexAttachment *inValue) {
-	_attachment = inValue;
+    _attachment = inValue;
 }

+ 10 - 30
spine-cpp/spine-cpp/src/spine/DrawOrderTimeline.cpp

@@ -45,13 +45,12 @@ using namespace spine;
 
 RTTI_IMPL(DrawOrderTimeline, Timeline)
 
-DrawOrderTimeline::DrawOrderTimeline(int frameCount) : Timeline() {
-	_frames.ensureCapacity(frameCount);
-	_drawOrders.ensureCapacity(frameCount);
-
-	_frames.setSize(frameCount, 0);
+DrawOrderTimeline::DrawOrderTimeline(size_t frameCount) : Timeline(frameCount, 1) {
+    PropertyId ids[] = {((PropertyId) Property_DrawOrder << 32)};
+    setPropertyIds(ids, 1);
 
-	for (int i = 0; i < frameCount; ++i) {
+	_drawOrders.ensureCapacity(frameCount);
+	for (size_t i = 0; i < frameCount; ++i) {
 		Vector<int> vec;
 		_drawOrders.add(vec);
 	}
@@ -86,14 +85,7 @@ void DrawOrderTimeline::apply(Skeleton &skeleton, float lastTime, float time, Ve
 		return;
 	}
 
-	size_t frame;
-	if (time >= _frames[_frames.size() - 1]) {
-		// Time is after last frame.
-		frame = _frames.size() - 1;
-	} else
-		frame = (size_t)Animation::binarySearch(_frames, time) - 1;
-
-	Vector<int> &drawOrderToSetupIndex = _drawOrders[frame];
+	Vector<int> &drawOrderToSetupIndex = _drawOrders[Animation::search(_frames, time)];
 	if (drawOrderToSetupIndex.size() == 0) {
 		drawOrder.clear();
 		for (size_t i = 0, n = slots.size(); i < n; ++i)
@@ -104,24 +96,12 @@ void DrawOrderTimeline::apply(Skeleton &skeleton, float lastTime, float time, Ve
 	}
 }
 
-int DrawOrderTimeline::getPropertyId() {
-	return ((int) TimelineType_DrawOrder << 24);
-}
-
-void DrawOrderTimeline::setFrame(size_t frameIndex, float time, Vector<int> &drawOrder) {
-	_frames[frameIndex] = time;
-	_drawOrders[frameIndex].clear();
-	_drawOrders[frameIndex].addAll(drawOrder);
-}
-
-Vector<float> &DrawOrderTimeline::getFrames() {
-	return _frames;
+void DrawOrderTimeline::setFrame(size_t frame, float time, Vector<int> &drawOrder) {
+	_frames[frame] = time;
+	_drawOrders[frame].clear();
+	_drawOrders[frame].addAll(drawOrder);
 }
 
 Vector<Vector<int> > &DrawOrderTimeline::getDrawOrders() {
 	return _drawOrders;
 }
-
-size_t DrawOrderTimeline::getFrameCount() {
-	return _frames.size();
-}

+ 18 - 25
spine-cpp/spine-cpp/src/spine/EventTimeline.cpp

@@ -49,8 +49,9 @@ using namespace spine;
 
 RTTI_IMPL(EventTimeline, Timeline)
 
-EventTimeline::EventTimeline(int frameCount) : Timeline() {
-	_frames.setSize(frameCount, 0);
+EventTimeline::EventTimeline(size_t frameCount) : Timeline(frameCount, 1) {
+    PropertyId ids[] = {((PropertyId) Property_Event << 32)};
+    setPropertyIds(ids, 1);
 	_events.setSize(frameCount, NULL);
 }
 
@@ -72,40 +73,32 @@ void EventTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector
 		apply(skeleton, lastTime, FLT_MAX, pEvents, alpha, blend, direction);
 		lastTime = -1.0f;
 	} else if (lastTime >= _frames[frameCount - 1]) {
-		// Last time is after last frame.
+		// Last time is after last i.
 		return;
 	}
 
-	if (time < _frames[0]) return; // Time is before first frame.
+	if (time < _frames[0]) return; // Time is before first i.
 
-	int frame;
+	int i;
 	if (lastTime < _frames[0]) {
-		frame = 0;
+        i = 0;
 	} else {
-		frame = Animation::binarySearch(_frames, lastTime);
-		float frameTime = _frames[frame];
-		while (frame > 0) {
-			// Fire multiple events with the same frame.
-			if (_frames[frame - 1] != frameTime) break;
-			frame--;
+        i = Animation::search(_frames, lastTime) + 1;
+		float frameTime = _frames[i];
+		while (i > 0) {
+			// Fire multiple events with the same i.
+			if (_frames[i - 1] != frameTime) break;
+			i--;
 		}
 	}
 
-	for (; (size_t)frame < frameCount && time >= _frames[frame]; ++frame)
-		events.add(_events[frame]);
+	for (; (size_t)i < frameCount && time >= _frames[i]; i++)
+		events.add(_events[i]);
 }
 
-int EventTimeline::getPropertyId() {
-	return ((int) TimelineType_Event << 24);
+void EventTimeline::setFrame(size_t frame, Event *event) {
+	_frames[frame] = event->getTime();
+	_events[frame] = event;
 }
 
-void EventTimeline::setFrame(size_t frameIndex, Event *event) {
-	_frames[frameIndex] = event->getTime();
-	_events[frameIndex] = event;
-}
-
-Vector<float> EventTimeline::getFrames() { return _frames; }
-
 Vector<Event *> &EventTimeline::getEvents() { return _events; }
-
-size_t EventTimeline::getFrameCount() { return _frames.size(); }

+ 59 - 86
spine-cpp/spine-cpp/src/spine/IkConstraintTimeline.cpp

@@ -47,21 +47,9 @@ using namespace spine;
 
 RTTI_IMPL(IkConstraintTimeline, CurveTimeline)
 
-const int IkConstraintTimeline::ENTRIES = 6;
-const int IkConstraintTimeline::PREV_TIME = -6;
-const int IkConstraintTimeline::PREV_MIX = -5;
-const int IkConstraintTimeline::PREV_SOFTNESS = -4;
-const int IkConstraintTimeline::PREV_BEND_DIRECTION = -3;
-const int IkConstraintTimeline::PREV_COMPRESS = -2;
-const int IkConstraintTimeline::PREV_STRETCH = -1;
-const int IkConstraintTimeline::MIX = 1;
-const int IkConstraintTimeline::SOFTNESS = 2;
-const int IkConstraintTimeline::BEND_DIRECTION = 3;
-const int IkConstraintTimeline::COMPRESS = 4;
-const int IkConstraintTimeline::STRETCH = 5;
-
-IkConstraintTimeline::IkConstraintTimeline(int frameCount) : CurveTimeline(frameCount), _ikConstraintIndex(0) {
-	_frames.setSize(frameCount * ENTRIES, 0);
+IkConstraintTimeline::IkConstraintTimeline(size_t frameCount, size_t bezierCount, int ikConstraintIndex) : CurveTimeline(frameCount, IkConstraintTimeline::ENTRIES, bezierCount), _ikConstraintIndex(ikConstraintIndex) {
+    PropertyId ids[] = { ((PropertyId)Property_IkConstraint << 32) | ikConstraintIndex };
+    setPropertyIds(ids, 1);
 }
 
 void IkConstraintTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha,
@@ -94,77 +82,62 @@ void IkConstraintTimeline::apply(Skeleton &skeleton, float lastTime, float time,
 		}
 	}
 
-	if (time >= _frames[_frames.size() - ENTRIES]) {
-		// Time is after last frame.
-		if (blend == MixBlend_Setup) {
-			constraint._mix =
-					constraint._data._mix + (_frames[_frames.size() + PREV_MIX] - constraint._data._mix) * alpha;
-			constraint._softness = constraint._data._softness
-				+ (_frames[_frames.size() + PREV_SOFTNESS] - constraint._data._softness) * alpha;
-			if (direction == MixDirection_Out) {
-				constraint._bendDirection = constraint._data._bendDirection;
-				constraint._compress = constraint._data._compress;
-				constraint._stretch = constraint._data._stretch;
-			} else {
-				constraint._bendDirection = (int) _frames[_frames.size() + PREV_BEND_DIRECTION];
-				constraint._compress = _frames[_frames.size() + PREV_COMPRESS] != 0;
-				constraint._stretch = _frames[_frames.size() + PREV_STRETCH] != 0;
-			}
-		} else {
-			constraint._mix += (_frames[_frames.size() + PREV_MIX] - constraint._mix) * alpha;
-			constraint._softness += (_frames[_frames.size() + PREV_SOFTNESS] - constraint._softness) * alpha;
-			if (direction == MixDirection_In) {
-				constraint._bendDirection = (int) _frames[_frames.size() + PREV_BEND_DIRECTION];
-				constraint._compress = _frames[_frames.size() + PREV_COMPRESS] != 0;
-				constraint._stretch = _frames[_frames.size() + PREV_STRETCH] != 0;
-			}
-		}
-		return;
-	}
-
-	// Interpolate between the previous frame and the current frame.
-	int frame = Animation::binarySearch(_frames, time, ENTRIES);
-	float mix = _frames[frame + PREV_MIX];
-	float softness = _frames[frame + PREV_SOFTNESS];
-	float frameTime = _frames[frame];
-	float percent = getCurvePercent(frame / ENTRIES - 1,
-									1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime));
-
-	if (blend == MixBlend_Setup) {
-		constraint._mix =
-				constraint._data._mix + (mix + (_frames[frame + MIX] - mix) * percent - constraint._data._mix) * alpha;
-		constraint._softness = constraint._data._softness
-			+ (softness + (_frames[frame + SOFTNESS] - softness) * percent - constraint._data._softness) * alpha;
-		if (direction == MixDirection_Out) {
-			constraint._bendDirection = constraint._data._bendDirection;
-			constraint._compress = constraint._data._compress;
-			constraint._stretch = constraint._data._stretch;
-		} else {
-			constraint._bendDirection = (int) _frames[_frames.size() + PREV_BEND_DIRECTION];
-			constraint._compress = _frames[frame + PREV_COMPRESS] != 0;
-			constraint._stretch = _frames[frame + PREV_STRETCH] != 0;
-		}
-	} else {
-		constraint._mix += (mix + (_frames[frame + MIX] - mix) * percent - constraint._mix) * alpha;
-		constraint._softness += (softness + (_frames[frame + SOFTNESS] - softness) * percent - constraint._softness) * alpha;
-		if (direction == MixDirection_In) {
-			constraint._bendDirection = (int) _frames[frame + PREV_BEND_DIRECTION];
-			constraint._compress = _frames[frame + PREV_COMPRESS] != 0;
-			constraint._stretch = _frames[frame + PREV_STRETCH] != 0;
-		}
-	}
-}
-
-int IkConstraintTimeline::getPropertyId() {
-	return ((int) TimelineType_IkConstraint << 24) + _ikConstraintIndex;
+    float mix = 0, softness = 0;
+	int i = Animation::search(_frames, time, IkConstraintTimeline::ENTRIES);
+    int curveType = (int)_curves[i / IkConstraintTimeline::ENTRIES];
+    switch (curveType) {
+        case IkConstraintTimeline::LINEAR: {
+            float before = _frames[i];
+            mix = _frames[i + IkConstraintTimeline::MIX];
+            softness = _frames[i + IkConstraintTimeline::SOFTNESS];
+            float t = (time - before) / (_frames[i + IkConstraintTimeline::ENTRIES] - before);
+            mix += (_frames[i + IkConstraintTimeline::ENTRIES + IkConstraintTimeline::MIX] - mix) * t;
+            softness += (_frames[i + IkConstraintTimeline::ENTRIES + IkConstraintTimeline::SOFTNESS] - softness) * t;
+            break;
+        }
+        case IkConstraintTimeline::STEPPED: {
+            mix = _frames[i + IkConstraintTimeline::MIX];
+            softness = _frames[i + IkConstraintTimeline::SOFTNESS];
+            break;
+        }
+        default: {
+            mix = getBezierValue(time, i, IkConstraintTimeline::MIX, curveType - IkConstraintTimeline::BEZIER);
+            softness = getBezierValue(time, i, IkConstraintTimeline::SOFTNESS,
+                                           curveType + IkConstraintTimeline::BEZIER_SIZE -
+                                           IkConstraintTimeline::BEZIER);
+        }
+    }
+
+    if (blend == MixBlend_Setup) {
+        constraint._mix = constraint._data._mix + (mix - constraint._data._mix) * alpha;
+        constraint._softness = constraint._data._softness + (softness - constraint._data._softness) * alpha;
+
+        if (direction == MixDirection_Out) {
+            constraint._bendDirection = constraint._data._bendDirection;
+            constraint._compress = constraint._data._compress;
+            constraint._stretch = constraint._data._stretch;
+        } else {
+            constraint._bendDirection = _frames[i + IkConstraintTimeline::BEND_DIRECTION];
+            constraint._compress = _frames[i + IkConstraintTimeline::COMPRESS] != 0;
+            constraint._stretch = _frames[i + IkConstraintTimeline::STRETCH] != 0;
+        }
+    } else {
+        constraint._mix += (mix - constraint._mix) * alpha;
+        constraint._softness += (softness - constraint._softness) * alpha;
+        if (direction == MixDirection_In) {
+            constraint._bendDirection = _frames[i + IkConstraintTimeline::BEND_DIRECTION];
+            constraint._compress = _frames[i + IkConstraintTimeline::COMPRESS] != 0;
+            constraint._stretch = _frames[i + IkConstraintTimeline::STRETCH] != 0;
+        }
+    }
 }
 
-void IkConstraintTimeline::setFrame(int frameIndex, float time, float mix, float softness, int bendDirection, bool compress, bool stretch) {
-	frameIndex *= ENTRIES;
-	_frames[frameIndex] = time;
-	_frames[frameIndex + MIX] = mix;
-	_frames[frameIndex + SOFTNESS] = softness;
-	_frames[frameIndex + BEND_DIRECTION] = (float)bendDirection;
-	_frames[frameIndex + COMPRESS] = compress ? 1 : 0;
-	_frames[frameIndex + STRETCH] = stretch ? 1 : 0;
+void IkConstraintTimeline::setFrame(int frame, float time, float mix, float softness, int bendDirection, bool compress, bool stretch) {
+    frame *= ENTRIES;
+	_frames[frame] = time;
+	_frames[frame + MIX] = mix;
+	_frames[frame + SOFTNESS] = softness;
+	_frames[frame + BEND_DIRECTION] = (float)bendDirection;
+	_frames[frame + COMPRESS] = compress ? 1 : 0;
+	_frames[frame + STRETCH] = stretch ? 1 : 0;
 }

+ 62 - 59
spine-cpp/spine-cpp/src/spine/PathConstraintMixTimeline.cpp

@@ -47,17 +47,10 @@ using namespace spine;
 
 RTTI_IMPL(PathConstraintMixTimeline, CurveTimeline)
 
-const int PathConstraintMixTimeline::ENTRIES = 3;
-const int PathConstraintMixTimeline::PREV_TIME = -3;
-const int PathConstraintMixTimeline::PREV_ROTATE = -2;
-const int PathConstraintMixTimeline::PREV_TRANSLATE = -1;
-const int PathConstraintMixTimeline::ROTATE = 1;
-const int PathConstraintMixTimeline::TRANSLATE = 2;
-
-PathConstraintMixTimeline::PathConstraintMixTimeline(int frameCount) : CurveTimeline(frameCount),
-	_pathConstraintIndex(0)
-{
-	_frames.setSize(frameCount * ENTRIES, 0);
+PathConstraintMixTimeline::PathConstraintMixTimeline(size_t frameCount, size_t bezierCount, int pathConstraintIndex) : CurveTimeline(frameCount, PathConstraintMixTimeline::ENTRIES, bezierCount),
+	_pathConstraintIndex(pathConstraintIndex) {
+    PropertyId ids[] = { ((PropertyId)Property_PathConstraintMix << 32) | pathConstraintIndex };
+    setPropertyIds(ids, 1);
 }
 
 void PathConstraintMixTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha,
@@ -71,56 +64,66 @@ void PathConstraintMixTimeline::apply(Skeleton &skeleton, float lastTime, float
 	PathConstraint &constraint = *constraintP;
 	if (!constraint.isActive()) return;
 
-	if (time < _frames[0]) {
-		switch (blend) {
-		case MixBlend_Setup:
-			constraint._rotateMix = constraint._data._rotateMix;
-			constraint._translateMix = constraint._data._translateMix;
-			return;
-		case MixBlend_First:
-			constraint._rotateMix += (constraint._data._rotateMix - constraint._rotateMix) * alpha;
-			constraint._translateMix += (constraint._data._translateMix - constraint._translateMix) * alpha;
-			return;
-		default:
-			return;
-		}
-	}
-
-	float rotate, translate;
-	if (time >= _frames[_frames.size() - ENTRIES]) {
-		// Time is after last frame.
-		rotate = _frames[_frames.size() + PREV_ROTATE];
-		translate = _frames[_frames.size() + PREV_TRANSLATE];
-	} else {
-		// Interpolate between the previous frame and the current frame.
-		int frame = Animation::binarySearch(_frames, time, ENTRIES);
-		rotate = _frames[frame + PREV_ROTATE];
-		translate = _frames[frame + PREV_TRANSLATE];
-		float frameTime = _frames[frame];
-		float percent = getCurvePercent(frame / ENTRIES - 1,
-			1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime));
-
-		rotate += (_frames[frame + ROTATE] - rotate) * percent;
-		translate += (_frames[frame + TRANSLATE] - translate) * percent;
-	}
-
-	if (blend == MixBlend_Setup) {
-		constraint._rotateMix = constraint._data._rotateMix + (rotate - constraint._data._rotateMix) * alpha;
-		constraint._translateMix =
-			constraint._data._translateMix + (translate - constraint._data._translateMix) * alpha;
-	} else {
-		constraint._rotateMix += (rotate - constraint._rotateMix) * alpha;
-		constraint._translateMix += (translate - constraint._translateMix) * alpha;
-	}
-}
-
-int PathConstraintMixTimeline::getPropertyId() {
-	return ((int) TimelineType_PathConstraintMix << 24) + _pathConstraintIndex;
+    if (time < _frames[0]) {
+        switch (blend) {
+            case MixBlend_Setup:
+                constraint._mixRotate = constraint._data._mixRotate;
+                constraint._mixX = constraint._data._mixX;
+                constraint._mixY = constraint._data._mixY;
+                return;
+            case MixBlend_First:
+                constraint._mixRotate += (constraint._data._mixRotate - constraint._mixRotate) * alpha;
+                constraint._mixX += (constraint._data._mixX - constraint._mixX) * alpha;
+                constraint._mixY += (constraint._data._mixY - constraint._mixY) * alpha;
+            default: {}
+        }
+        return;
+    }
+
+    float rotate, x, y;
+    int i = Animation::search(_frames, time, PathConstraintMixTimeline::ENTRIES);
+    int curveType = (int)_curves[i >> 2];
+    switch (curveType) {
+        case LINEAR: {
+            float before = _frames[i];
+            rotate = _frames[i + ROTATE];
+            x = _frames[i + X];
+            y = _frames[i + Y];
+            float t = (time - before) / (_frames[i + ENTRIES] - before);
+            rotate += (_frames[i + ENTRIES + ROTATE] - rotate) * t;
+            x += (_frames[i + ENTRIES + X] - x) * t;
+            y += (_frames[i + ENTRIES + Y] - y) * t;
+            break;
+        }
+        case STEPPED: {
+            rotate = _frames[i + ROTATE];
+            x = _frames[i + X];
+            y = _frames[i + Y];
+            break;
+        }
+        default: {
+            rotate = getBezierValue(time, i, ROTATE, curveType - BEZIER);
+            x = getBezierValue(time, i, X, curveType + BEZIER_SIZE - BEZIER);
+            y = getBezierValue(time, i, Y, curveType + BEZIER_SIZE * 2 - BEZIER);
+        }
+    }
+
+    if (blend == MixBlend_Setup) {
+        PathConstraintData data = constraint._data;
+        constraint._mixRotate = data._mixRotate + (rotate - data._mixRotate) * alpha;
+        constraint._mixX = data._mixX + (x - data._mixX) * alpha;
+        constraint._mixY = data._mixY + (y - data._mixY) * alpha;
+    } else {
+        constraint._mixRotate += (rotate - constraint._mixRotate) * alpha;
+        constraint._mixX += (x - constraint._mixX) * alpha;
+        constraint._mixY += (y - constraint._mixY) * alpha;
+    }
 }
 
-void PathConstraintMixTimeline::setFrame(int frameIndex, float time, float rotateMix, float translateMix) {
+void PathConstraintMixTimeline::setFrame(int frameIndex, float time, float mixRotate, float mixX, float mixY) {
 	frameIndex *= ENTRIES;
 	_frames[frameIndex] = time;
-	_frames[frameIndex + ROTATE] = rotateMix;
-	_frames[frameIndex + TRANSLATE] = translateMix;
+	_frames[frameIndex + ROTATE] = mixRotate;
+	_frames[frameIndex + X] = mixX;
+    _frames[frameIndex + Y] = mixY;
 }

+ 6 - 33
spine-cpp/spine-cpp/src/spine/PathConstraintPositionTimeline.cpp

@@ -45,17 +45,12 @@
 
 using namespace spine;
 
-RTTI_IMPL(PathConstraintPositionTimeline, CurveTimeline)
+RTTI_IMPL(PathConstraintPositionTimeline, CurveTimeline1)
 
-const int PathConstraintPositionTimeline::ENTRIES = 2;
-const int PathConstraintPositionTimeline::PREV_TIME = -2;
-const int PathConstraintPositionTimeline::PREV_VALUE = -1;
-const int PathConstraintPositionTimeline::VALUE = 1;
-
-PathConstraintPositionTimeline::PathConstraintPositionTimeline(int frameCount) : CurveTimeline(frameCount),
-	_pathConstraintIndex(0)
-{
-	_frames.setSize(frameCount * ENTRIES, 0);
+PathConstraintPositionTimeline::PathConstraintPositionTimeline(size_t frameCount, size_t bezierCount, int pathConstraintIndex) : CurveTimeline1(frameCount, bezierCount),
+	_pathConstraintIndex(pathConstraintIndex) {
+	PropertyId ids[] = { ((PropertyId)Property_PathConstraintPosition << 32) | pathConstraintIndex };
+	setPropertyIds(ids, 1);
 }
 
 PathConstraintPositionTimeline::~PathConstraintPositionTimeline() {
@@ -85,32 +80,10 @@ void PathConstraintPositionTimeline::apply(Skeleton &skeleton, float lastTime, f
 		}
 	}
 
-	float position;
-	if (time >= _frames[_frames.size() - ENTRIES]) {
-		// Time is after last frame.
-		position = _frames[_frames.size() + PREV_VALUE];
-	} else {
-		// Interpolate between the previous frame and the current frame.
-		int frame = Animation::binarySearch(_frames, time, ENTRIES);
-		position = _frames[frame + PREV_VALUE];
-		float frameTime = _frames[frame];
-		float percent = getCurvePercent(frame / ENTRIES - 1,
-			1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime));
+	float position = getCurveValue(time);
 
-		position += (_frames[frame + VALUE] - position) * percent;
-	}
 	if (blend == MixBlend_Setup)
 		constraint._position = constraint._data._position + (position - constraint._data._position) * alpha;
 	else
 		constraint._position += (position - constraint._position) * alpha;
 }
-
-int PathConstraintPositionTimeline::getPropertyId() {
-	return ((int) TimelineType_PathConstraintPosition << 24) + _pathConstraintIndex;
-}
-
-void PathConstraintPositionTimeline::setFrame(int frameIndex, float time, float value) {
-	frameIndex *= ENTRIES;
-	_frames[frameIndex] = time;
-	_frames[frameIndex + VALUE] = value;
-}

+ 8 - 24
spine-cpp/spine-cpp/src/spine/PathConstraintSpacingTimeline.cpp

@@ -47,8 +47,9 @@ using namespace spine;
 
 RTTI_IMPL(PathConstraintSpacingTimeline, PathConstraintPositionTimeline)
 
-PathConstraintSpacingTimeline::PathConstraintSpacingTimeline(int frameCount) : PathConstraintPositionTimeline(
-		frameCount) {
+PathConstraintSpacingTimeline::PathConstraintSpacingTimeline(size_t frameCount, size_t bezierCount, int pathConstraintIndex) : CurveTimeline1(frameCount, bezierCount), _pathConstraintIndex(pathConstraintIndex) {
+    PropertyId ids[] = { ((PropertyId)Property_PathConstraintSpacing << 32) | pathConstraintIndex };
+    setPropertyIds(ids, 1);
 }
 
 void PathConstraintSpacingTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents,
@@ -75,27 +76,10 @@ void PathConstraintSpacingTimeline::apply(Skeleton &skeleton, float lastTime, fl
 		}
 	}
 
-	float spacing;
-	if (time >= _frames[_frames.size() - ENTRIES]) {
-		// Time is after last frame.
-		spacing = _frames[_frames.size() + PREV_VALUE];
-	} else {
-		// Interpolate between the previous frame and the current frame.
-		int frame = Animation::binarySearch(_frames, time, ENTRIES);
-		spacing = _frames[frame + PREV_VALUE];
-		float frameTime = _frames[frame];
-		float percent = getCurvePercent(frame / ENTRIES - 1,
-			1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime));
+    float spacing = getCurveValue(time);
 
-		spacing += (_frames[frame + VALUE] - spacing) * percent;
-	}
-
-	if (blend == MixBlend_Setup)
-		constraint._spacing = constraint._data._spacing + (spacing - constraint._data._spacing) * alpha;
-	else
-		constraint._spacing += (spacing - constraint._spacing) * alpha;
-}
-
-int PathConstraintSpacingTimeline::getPropertyId() {
-	return ((int) TimelineType_PathConstraintSpacing << 24) + _pathConstraintIndex;
+    if (blend == MixBlend_Setup)
+        constraint._spacing = constraint._data._spacing + (spacing - constraint._data._spacing) * alpha;
+    else
+        constraint._spacing += (spacing - constraint._spacing) * alpha;
 }

+ 154 - 54
spine-cpp/spine-cpp/src/spine/ShearTimeline.cpp

@@ -43,9 +43,15 @@
 
 using namespace spine;
 
-RTTI_IMPL(ShearTimeline, TranslateTimeline)
+RTTI_IMPL(ShearTimeline, CurveTimeline2)
 
-ShearTimeline::ShearTimeline(int frameCount) : TranslateTimeline(frameCount) {
+ShearTimeline::ShearTimeline(size_t frameCount, size_t bezierCount, int boneIndex) : CurveTimeline2(frameCount, bezierCount), _boneIndex(boneIndex) {
+    PropertyId ids[] = { ((PropertyId)Property_ShearX << 32) | boneIndex,
+                         ((PropertyId)Property_ShearY << 32) | boneIndex};
+    setPropertyIds(ids, 2);
+}
+
+ShearTimeline::~ShearTimeline() {
 }
 
 void ShearTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha,
@@ -55,58 +61,152 @@ void ShearTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector
 	SP_UNUSED(pEvents);
 	SP_UNUSED(direction);
 
-	Bone *boneP = skeleton._bones[_boneIndex];
-	Bone &bone = *boneP;
-	if (!bone._active) return;
-
-	if (time < _frames[0]) {
-		switch (blend) {
-		case MixBlend_Setup:
-			bone._shearX = bone._data._shearX;
-			bone._shearY = bone._data._shearY;
-			return;
-		case MixBlend_First:
-			bone._shearX += (bone._data._shearX - bone._shearX) * alpha;
-			bone._shearY += (bone._data._shearY - bone._shearY) * alpha;
-		default: {}
-		}
-		return;
-	}
-
-	float x, y;
-	if (time >= _frames[_frames.size() - ENTRIES]) {
-		// Time is after last frame.
-		x = _frames[_frames.size() + PREV_X];
-		y = _frames[_frames.size() + PREV_Y];
-	} else {
-		// Interpolate between the previous frame and the current frame.
-		int frame = Animation::binarySearch(_frames, time, ENTRIES);
-		x = _frames[frame + PREV_X];
-		y = _frames[frame + PREV_Y];
-		float frameTime = _frames[frame];
-		float percent = getCurvePercent(frame / ENTRIES - 1,
-			1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime));
-
-		x = x + (_frames[frame + X] - x) * percent;
-		y = y + (_frames[frame + Y] - y) * percent;
-	}
-
-	switch (blend) {
-		case MixBlend_Setup:
-			bone._shearX = bone._data._shearX + x * alpha;
-			bone._shearY = bone._data._shearY + y * alpha;
-			break;
-		case MixBlend_First:
-		case MixBlend_Replace:
-			bone._shearX += (bone._data._shearX + x - bone._shearX) * alpha;
-			bone._shearY += (bone._data._shearY + y - bone._shearY) * alpha;
-			break;
-		case MixBlend_Add:
-			bone._shearX += x * alpha;
-			bone._shearY += y * alpha;
-	}
+	Bone *bone = skeleton._bones[_boneIndex];
+	if (!bone->_active) return;
+
+    if (time < _frames[0]) {
+        switch (blend) {
+            case MixBlend_Setup:
+                bone->_shearX = bone->_data._shearX;
+                bone->_shearY = bone->_data._shearY;
+                return;
+            case MixBlend_First:
+                bone->_shearX += (bone->_data._shearX - bone->_shearX) * alpha;
+                bone->_shearY += (bone->_data._shearY - bone->_shearY) * alpha;
+            default: {}
+        }
+        return;
+    }
+
+    float x, y;
+    int i = Animation::search(_frames, time, CurveTimeline2::ENTRIES);
+    int curveType = (int)_curves[i / CurveTimeline2::ENTRIES];
+    switch (curveType) {
+        case CurveTimeline2::LINEAR: {
+            float before = _frames[i];
+            x = _frames[i + CurveTimeline2::VALUE1];
+            y = _frames[i + CurveTimeline2::VALUE2];
+            float t = (time - before) / (_frames[i + CurveTimeline2::ENTRIES] - before);
+            x += (_frames[i + CurveTimeline2::ENTRIES + CurveTimeline2::VALUE1] - x) * t;
+            y += (_frames[i + CurveTimeline2::ENTRIES + CurveTimeline2::VALUE2] - y) * t;
+            break;
+        }
+        case CurveTimeline2::STEPPED: {
+            x = _frames[i + CurveTimeline2::VALUE1];
+            y = _frames[i + CurveTimeline2::VALUE2];
+            break;
+        }
+        default: {
+            x = getBezierValue(time, i, CurveTimeline2::VALUE1, curveType - CurveTimeline2::BEZIER);
+            y = getBezierValue(time, i, CurveTimeline2::VALUE2,
+                               curveType + CurveTimeline2::BEZIER_SIZE - CurveTimeline2::BEZIER);
+        }
+    }
+
+    switch (blend) {
+        case MixBlend_Setup:
+            bone->_shearX = bone->_data._shearX + x * alpha;
+            bone->_shearY = bone->_data._shearY + y * alpha;
+            break;
+        case MixBlend_First:
+        case MixBlend_Replace:
+            bone->_shearX += (bone->_data._shearX + x - bone->_shearX) * alpha;
+            bone->_shearY += (bone->_data._shearY + y - bone->_shearY) * alpha;
+            break;
+        case MixBlend_Add:
+            bone->_shearX += x * alpha;
+            bone->_shearY += y * alpha;
+    }
 }
 
-int ShearTimeline::getPropertyId() {
-	return ((int) TimelineType_Shear << 24) + _boneIndex;
+RTTI_IMPL(ShearXTimeline, CurveTimeline1)
+
+ShearXTimeline::ShearXTimeline(size_t frameCount, size_t bezierCount, int boneIndex) : CurveTimeline1(frameCount, bezierCount), _boneIndex(boneIndex) {
+    PropertyId ids[] = { ((PropertyId)Property_ShearX << 32) | boneIndex };
+    setPropertyIds(ids, 1);
+}
+
+ShearXTimeline::~ShearXTimeline() {
+}
+
+void ShearXTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha,
+                          MixBlend blend, MixDirection direction
+) {
+    SP_UNUSED(lastTime);
+    SP_UNUSED(pEvents);
+    SP_UNUSED(direction);
+
+    Bone *bone = skeleton._bones[_boneIndex];
+    if (!bone->_active) return;
+
+    if (time < _frames[0]) {
+        switch (blend) {
+            case MixBlend_Setup:
+                bone->_shearX = bone->_data._shearX;
+                return;
+            case MixBlend_First:
+                bone->_shearX += (bone->_data._shearX - bone->_shearX) * alpha;
+            default: {}
+        }
+        return;
+    }
+
+    float x = getCurveValue(time);
+    switch (blend) {
+        case MixBlend_Setup:
+            bone->_shearX = bone->_data._shearX + x * alpha;
+            break;
+        case MixBlend_First:
+        case MixBlend_Replace:
+            bone->_shearX += (bone->_data._shearX + x - bone->_shearX) * alpha;
+            break;
+        case MixBlend_Add:
+            bone->_shearX += x * alpha;
+    }
+}
+
+RTTI_IMPL(ShearYTimeline, CurveTimeline1)
+
+ShearYTimeline::ShearYTimeline(size_t frameCount, size_t bezierCount, int boneIndex) : CurveTimeline1(frameCount, bezierCount), _boneIndex(boneIndex) {
+    PropertyId ids[] = { ((PropertyId)Property_ShearX << 32) | boneIndex };
+    setPropertyIds(ids, 1);
+}
+
+ShearYTimeline::~ShearYTimeline() {
+}
+
+void ShearYTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha,
+                           MixBlend blend, MixDirection direction
+) {
+    SP_UNUSED(lastTime);
+    SP_UNUSED(pEvents);
+    SP_UNUSED(direction);
+
+    Bone *bone = skeleton._bones[_boneIndex];
+    if (!bone->_active) return;
+
+    if (time < _frames[0]) {
+        switch (blend) {
+            case MixBlend_Setup:
+                bone->_shearY = bone->_data._shearY;
+                return;
+            case MixBlend_First:
+                bone->_shearY += (bone->_data._shearY - bone->_shearY) * alpha;
+            default: {}
+        }
+        return;
+    }
+
+    float y = getCurveValue(time);
+    switch (blend) {
+        case MixBlend_Setup:
+            bone->_shearY = bone->_data._shearY + y * alpha;
+            break;
+        case MixBlend_First:
+        case MixBlend_Replace:
+            bone->_shearY += (bone->_data._shearY + y - bone->_shearY) * alpha;
+            break;
+        case MixBlend_Add:
+            bone->_shearY += y * alpha;
+    }
 }

+ 79 - 71
spine-cpp/spine-cpp/src/spine/TransformConstraintTimeline.cpp

@@ -47,21 +47,10 @@ using namespace spine;
 
 RTTI_IMPL(TransformConstraintTimeline, CurveTimeline)
 
-const int TransformConstraintTimeline::ENTRIES = 5;
-const int TransformConstraintTimeline::PREV_TIME = -5;
-const int TransformConstraintTimeline::PREV_ROTATE = -4;
-const int TransformConstraintTimeline::PREV_TRANSLATE = -3;
-const int TransformConstraintTimeline::PREV_SCALE = -2;
-const int TransformConstraintTimeline::PREV_SHEAR = -1;
-const int TransformConstraintTimeline::ROTATE = 1;
-const int TransformConstraintTimeline::TRANSLATE = 2;
-const int TransformConstraintTimeline::SCALE = 3;
-const int TransformConstraintTimeline::SHEAR = 4;
-
-TransformConstraintTimeline::TransformConstraintTimeline(int frameCount) : CurveTimeline(frameCount),
-	_transformConstraintIndex(0)
-{
-	_frames.setSize(frameCount * ENTRIES, 0);
+TransformConstraintTimeline::TransformConstraintTimeline(size_t frameCount, size_t bezierCount, int transformConstraintIndex) : CurveTimeline(frameCount, TransformConstraintTimeline::ENTRIES, bezierCount),
+	_transformConstraintIndex(transformConstraintIndex) {
+    PropertyId ids[] = { ((PropertyId)Property_TransformConstraint << 32) | transformConstraintIndex };
+    setPropertyIds(ids, 1);
 }
 
 void TransformConstraintTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents,
@@ -74,75 +63,94 @@ void TransformConstraintTimeline::apply(Skeleton &skeleton, float lastTime, floa
 	TransformConstraint &constraint = *constraintP;
 	if (!constraint.isActive()) return;
 
+	TransformConstraintData &data = constraint._data;
 	if (time < _frames[0]) {
 		switch (blend) {
 		case MixBlend_Setup:
-			constraint._rotateMix = constraint._data._rotateMix;
-			constraint._translateMix = constraint._data._translateMix;
-			constraint._scaleMix = constraint._data._scaleMix;
-			constraint._shearMix = constraint._data._shearMix;
+            constraint._mixRotate = data._mixRotate;
+            constraint._mixX = data._mixX;
+            constraint._mixY = data._mixY;
+            constraint._mixScaleX = data._mixScaleX;
+            constraint._mixScaleY = data._mixScaleY;
+            constraint._mixShearY = data._mixShearY;
 			return;
 		case MixBlend_First:
-			constraint._rotateMix += (constraint._data._rotateMix - constraint._rotateMix) * alpha;
-			constraint._translateMix += (constraint._data._translateMix - constraint._translateMix) * alpha;
-			constraint._scaleMix += (constraint._data._scaleMix - constraint._scaleMix) * alpha;
-			constraint._shearMix += (constraint._data._shearMix - constraint._shearMix) * alpha;
+            constraint._mixRotate += (data._mixRotate - constraint._mixRotate) * alpha;
+            constraint._mixX += (data._mixX - constraint._mixX) * alpha;
+            constraint._mixY += (data._mixY - constraint._mixY) * alpha;
+            constraint._mixScaleX += (data._mixScaleX - constraint._mixScaleX) * alpha;
+            constraint._mixScaleY += (data._mixScaleY - constraint._mixScaleY) * alpha;
+            constraint._mixShearY += (data._mixShearY - constraint._mixShearY) * alpha;
 			return;
 		default:
 			return;
 		}
 	}
 
-	float rotate, translate, scale, shear;
-	if (time >= _frames[_frames.size() - ENTRIES]) {
-		// Time is after last frame.
-		size_t i = _frames.size();
-		rotate = _frames[i + PREV_ROTATE];
-		translate = _frames[i + PREV_TRANSLATE];
-		scale = _frames[i + PREV_SCALE];
-		shear = _frames[i + PREV_SHEAR];
-	} else {
-		// Interpolate between the previous frame and the current frame.
-		int frame = Animation::binarySearch(_frames, time, ENTRIES);
-		rotate = _frames[frame + PREV_ROTATE];
-		translate = _frames[frame + PREV_TRANSLATE];
-		scale = _frames[frame + PREV_SCALE];
-		shear = _frames[frame + PREV_SHEAR];
-		float frameTime = _frames[frame];
-		float percent = getCurvePercent(frame / ENTRIES - 1,
-			1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime));
-
-		rotate += (_frames[frame + ROTATE] - rotate) * percent;
-		translate += (_frames[frame + TRANSLATE] - translate) * percent;
-		scale += (_frames[frame + SCALE] - scale) * percent;
-		shear += (_frames[frame + SHEAR] - shear) * percent;
-	}
-
-	if (blend == MixBlend_Setup) {
-		TransformConstraintData &data = constraint._data;
-		constraint._rotateMix = data._rotateMix + (rotate - data._rotateMix) * alpha;
-		constraint._translateMix = data._translateMix + (translate - data._translateMix) * alpha;
-		constraint._scaleMix = data._scaleMix + (scale - data._scaleMix) * alpha;
-		constraint._shearMix = data._shearMix + (shear - data._shearMix) * alpha;
-	} else {
-		constraint._rotateMix += (rotate - constraint._rotateMix) * alpha;
-		constraint._translateMix += (translate - constraint._translateMix) * alpha;
-		constraint._scaleMix += (scale - constraint._scaleMix) * alpha;
-		constraint._shearMix += (shear - constraint._shearMix) * alpha;
-	}
-}
-
-int TransformConstraintTimeline::getPropertyId() {
-	return ((int) TimelineType_TransformConstraint << 24) + _transformConstraintIndex;
+    float rotate, x, y, scaleX, scaleY, shearY;
+	int i = Animation::search(_frames, time, TransformConstraintTimeline::ENTRIES);
+    int curveType = (int)_curves[i / TransformConstraintTimeline::ENTRIES];
+    switch (curveType) {
+        case TransformConstraintTimeline::LINEAR: {
+            float before = _frames[i];
+            rotate = _frames[i + ROTATE];
+            x = _frames[i + X];
+            y = _frames[i + Y];
+            scaleX = _frames[i + SCALEX];
+            scaleY = _frames[i + SCALEY];
+            shearY = _frames[i + SHEARY];
+            float t = (time - before) / (_frames[i + ENTRIES] - before);
+            rotate += (_frames[i + ENTRIES + ROTATE] - rotate) * t;
+            x += (_frames[i + ENTRIES + X] - x) * t;
+            y += (_frames[i + ENTRIES + Y] - y) * t;
+            scaleX += (_frames[i + ENTRIES + SCALEX] - scaleX) * t;
+            scaleY += (_frames[i + ENTRIES + SCALEY] - scaleY) * t;
+            shearY += (_frames[i + ENTRIES + SHEARY] - shearY) * t;
+            break;
+        }
+        case TransformConstraintTimeline::STEPPED: {
+            rotate = _frames[i + ROTATE];
+            x = _frames[i + X];
+            y = _frames[i + Y];
+            scaleX = _frames[i + SCALEX];
+            scaleY = _frames[i + SCALEY];
+            shearY = _frames[i + SHEARY];
+            break;
+        }
+        default: {
+            rotate = getBezierValue(time, i, ROTATE, curveType - BEZIER);
+            x = getBezierValue(time, i, X, curveType + BEZIER_SIZE - BEZIER);
+            y = getBezierValue(time, i, Y, curveType + BEZIER_SIZE * 2 - BEZIER);
+            scaleX = getBezierValue(time, i, SCALEX, curveType + BEZIER_SIZE * 3 - BEZIER);
+            scaleY = getBezierValue(time, i, SCALEY, curveType + BEZIER_SIZE * 4 - BEZIER);
+            shearY = getBezierValue(time, i, SHEARY, curveType + BEZIER_SIZE * 5 - BEZIER);
+        }
+    }
+
+    if (blend == MixBlend_Setup) {
+        constraint._mixRotate = data._mixRotate + (rotate - data._mixRotate) * alpha;
+        constraint._mixX = data._mixX + (x - data._mixX) * alpha;
+        constraint._mixY = data._mixY + (y - data._mixY) * alpha;
+        constraint._mixScaleX = data._mixScaleX + (scaleX - data._mixScaleX) * alpha;
+        constraint._mixScaleY = data._mixScaleY + (scaleY - data._mixScaleY) * alpha;
+        constraint._mixShearY = data._mixShearY + (shearY - data._mixShearY) * alpha;
+    } else {
+        constraint._mixRotate += (rotate - constraint._mixRotate) * alpha;
+        constraint._mixX += (x - constraint._mixX) * alpha;
+        constraint._mixY += (y - constraint._mixY) * alpha;
+        constraint._mixScaleX += (scaleX - constraint._mixScaleX) * alpha;
+        constraint._mixScaleY += (scaleY - constraint._mixScaleY) * alpha;
+        constraint._mixShearY += (shearY - constraint._mixShearY) * alpha;
+    }
 }
 
-void TransformConstraintTimeline::setFrame(size_t frameIndex, float time, float rotateMix, float translateMix, float scaleMix,
-	float shearMix
-) {
+void TransformConstraintTimeline::setFrame(size_t frameIndex, float time, float mixRotate, float mixX, float mixY, float mixScaleX, float mixScaleY, float mixShearY) {
 	frameIndex *= ENTRIES;
 	_frames[frameIndex] = time;
-	_frames[frameIndex + ROTATE] = rotateMix;
-	_frames[frameIndex + TRANSLATE] = translateMix;
-	_frames[frameIndex + SCALE] = scaleMix;
-	_frames[frameIndex + SHEAR] = shearMix;
+	_frames[frameIndex + ROTATE] = mixRotate;
+	_frames[frameIndex + X] = mixX;
+    _frames[frameIndex + Y] = mixY;
+    _frames[frameIndex + SCALEX] = mixScaleX;
+    _frames[frameIndex + SCALEY] = mixScaleY;
+    _frames[frameIndex + SHEARY] = mixShearY;
 }

+ 2 - 2
spine-ts/core/src/Animation.ts

@@ -1034,7 +1034,7 @@ module spine {
 
 	/** Changes a slot's {@link Slot#color}. */
 	export class RGBTimeline extends CurveTimeline implements SlotTimeline {
-		static ENTRIES = 5;
+		static ENTRIES = 4;
 
 		static R = 1; static G = 2; static B = 3;
 
@@ -1264,7 +1264,7 @@ module spine {
 
 			let light = slot.color, dark = slot.darkColor;
 			if (alpha == 1) {
-				slot.color.set(r, g, b, a);
+				light.set(r, g, b, a);
 				dark.r = r2;
 				dark.g = g2;
 				dark.b = b2;