Browse Source

[cpp] 4.0 port, half done.

badlogic 4 years ago
parent
commit
653c06a6b8
45 changed files with 1187 additions and 790 deletions
  1. 1 1
      spine-cocos2dx/example/proj.win32/spine-cocos2d-x.vcxproj
  2. 1 1
      spine-cocos2dx/example/proj.win32/spine-cocos2d-x.vcxproj.filters
  3. 9 9
      spine-cpp/spine-cpp/include/spine/Animation.h
  4. 8 0
      spine-cpp/spine-cpp/include/spine/Bone.h
  5. 8 0
      spine-cpp/spine-cpp/include/spine/BoneData.h
  6. 41 20
      spine-cpp/spine-cpp/include/spine/CurveTimeline.h
  7. 10 6
      spine-cpp/spine-cpp/include/spine/PathConstraint.h
  8. 9 5
      spine-cpp/spine-cpp/include/spine/PathConstraintData.h
  9. 24 19
      spine-cpp/spine-cpp/include/spine/Property.h
  10. 1 1
      spine-cpp/spine-cpp/include/spine/RegionAttachment.h
  11. 4 18
      spine-cpp/spine-cpp/include/spine/RotateTimeline.h
  12. 49 3
      spine-cpp/spine-cpp/include/spine/ScaleTimeline.h
  13. 12 1
      spine-cpp/spine-cpp/include/spine/Skeleton.h
  14. 2 1
      spine-cpp/spine-cpp/include/spine/SpacingMode.h
  15. 18 2
      spine-cpp/spine-cpp/include/spine/Timeline.h
  16. 16 12
      spine-cpp/spine-cpp/include/spine/TransformConstraint.h
  17. 7 5
      spine-cpp/spine-cpp/include/spine/TransformConstraintData.h
  18. 46 17
      spine-cpp/spine-cpp/include/spine/TranslateTimeline.h
  19. 1 1
      spine-cpp/spine-cpp/include/spine/spine.h
  20. 21 50
      spine-cpp/spine-cpp/src/spine/Animation.cpp
  21. 1 1
      spine-cpp/spine-cpp/src/spine/AttachmentTimeline.cpp
  22. 1 1
      spine-cpp/spine-cpp/src/spine/ColorTimeline.cpp
  23. 91 69
      spine-cpp/spine-cpp/src/spine/CurveTimeline.cpp
  24. 1 1
      spine-cpp/spine-cpp/src/spine/DeformTimeline.cpp
  25. 1 1
      spine-cpp/spine-cpp/src/spine/DrawOrderTimeline.cpp
  26. 1 1
      spine-cpp/spine-cpp/src/spine/EventTimeline.cpp
  27. 1 9
      spine-cpp/spine-cpp/src/spine/IkConstraint.cpp
  28. 1 1
      spine-cpp/spine-cpp/src/spine/IkConstraintTimeline.cpp
  29. 122 84
      spine-cpp/spine-cpp/src/spine/PathConstraint.cpp
  30. 42 33
      spine-cpp/spine-cpp/src/spine/PathConstraintData.cpp
  31. 1 1
      spine-cpp/spine-cpp/src/spine/PathConstraintMixTimeline.cpp
  32. 1 1
      spine-cpp/spine-cpp/src/spine/PathConstraintPositionTimeline.cpp
  33. 1 1
      spine-cpp/spine-cpp/src/spine/PathConstraintSpacingTimeline.cpp
  34. 2 2
      spine-cpp/spine-cpp/src/spine/RegionAttachment.cpp
  35. 36 89
      spine-cpp/spine-cpp/src/spine/RotateTimeline.cpp
  36. 243 96
      spine-cpp/spine-cpp/src/spine/ScaleTimeline.cpp
  37. 45 28
      spine-cpp/spine-cpp/src/spine/Skeleton.cpp
  38. 30 1
      spine-cpp/spine-cpp/src/spine/Timeline.cpp
  39. 102 112
      spine-cpp/spine-cpp/src/spine/TransformConstraint.cpp
  40. 22 12
      spine-cpp/spine-cpp/src/spine/TransformConstraintData.cpp
  41. 1 1
      spine-cpp/spine-cpp/src/spine/TransformConstraintTimeline.cpp
  42. 150 69
      spine-cpp/spine-cpp/src/spine/TranslateTimeline.cpp
  43. 1 1
      spine-cpp/spine-cpp/src/spine/TwoColorTimeline.cpp
  44. 1 2
      spine-cpp/spine-cpp/src/spine/VertexAttachment.cpp
  45. 1 1
      spine-sfml/cpp/CMakeLists.txt

+ 1 - 1
spine-cocos2dx/example/proj.win32/spine-cocos2d-x.vcxproj

@@ -286,7 +286,7 @@ xcopy "$(ProjectDir)..\Resources" "$(OutDir)\Resources" /D /E /I /F /Y
     <ClInclude Include="..\..\..\spine-cpp\spine-cpp\include\spine\String.h" />
     <ClInclude Include="..\..\..\spine-cpp\spine-cpp\include\spine\TextureLoader.h" />
     <ClInclude Include="..\..\..\spine-cpp\spine-cpp\include\spine\Timeline.h" />
-    <ClInclude Include="..\..\..\spine-cpp\spine-cpp\include\spine\TimelineType.h" />
+    <ClInclude Include="..\..\..\spine-cpp\spine-cpp\include\spine\Property.h" />
     <ClInclude Include="..\..\..\spine-cpp\spine-cpp\include\spine\TransformConstraint.h" />
     <ClInclude Include="..\..\..\spine-cpp\spine-cpp\include\spine\TransformConstraintData.h" />
     <ClInclude Include="..\..\..\spine-cpp\spine-cpp\include\spine\TransformConstraintTimeline.h" />

+ 1 - 1
spine-cocos2dx/example/proj.win32/spine-cocos2d-x.vcxproj.filters

@@ -491,7 +491,7 @@
     <ClInclude Include="..\..\..\spine-cpp\spine-cpp\include\spine\Timeline.h">
       <Filter>spine</Filter>
     </ClInclude>
-    <ClInclude Include="..\..\..\spine-cpp\spine-cpp\include\spine\TimelineType.h">
+    <ClInclude Include="..\..\..\spine-cpp\spine-cpp\include\spine\Property.h">
       <Filter>spine</Filter>
     </ClInclude>
     <ClInclude Include="..\..\..\spine-cpp\spine-cpp\include\spine\TransformConstraint.h">

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

@@ -36,6 +36,7 @@
 #include <spine/MixDirection.h>
 #include <spine/SpineObject.h>
 #include <spine/SpineString.h>
+#include <spine/Property.h>
 
 namespace spine {
 class Timeline;
@@ -79,6 +80,10 @@ class SP_API Animation : public SpineObject {
 
 	friend class TranslateTimeline;
 
+    friend class TranslateXTimeline;
+
+    friend class TranslateYTimeline;
+
 	friend class TwoColorTimeline;
 
 public:
@@ -95,27 +100,22 @@ public:
 
 	Vector<Timeline *> &getTimelines();
 
-	bool hasTimeline(int id);
+	bool hasTimeline(Vector<PropertyId> ids);
 
 	float getDuration();
 
 	void setDuration(float inValue);
 
-
-
 private:
 	Vector<Timeline *> _timelines;
-	HashMap<int, bool> _timelineIds;
+	HashMap<PropertyId, bool> _timelineIds;
 	float _duration;
 	String _name;
 
 	/// @param target After the first and before the last entry.
-	static int binarySearch(Vector<float> &values, float target, int step);
-
-	/// @param target After the first and before the last entry.
-	static int binarySearch(Vector<float> &values, float target);
+	static int search(Vector<float> &values, float target);
 
-	static int linearSearch(Vector<float> &values, float target, int step);
+	static int search(Vector<float> &values, float target, int step);
 };
 }
 

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

@@ -65,10 +65,18 @@ class SP_API Bone : public Updatable {
 
 	friend class ScaleTimeline;
 
+    friend class ScaleXTimeline;
+
+    friend class ScaleYTimeline;
+
 	friend class ShearTimeline;
 
 	friend class TranslateTimeline;
 
+    friend class TranslateXTimeline;
+
+    friend class TranslateYTimeline;
+
 RTTI_DECL
 
 public:

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

@@ -46,10 +46,18 @@ class SP_API BoneData : public SpineObject {
 
 	friend class ScaleTimeline;
 
+    friend class ScaleXTimeline;
+
+    friend class ScaleYTimeline;
+
 	friend class ShearTimeline;
 
 	friend class TranslateTimeline;
 
+    friend class TranslateXTimeline;
+
+    friend class TranslateYTimeline;
+
 public:
 	BoneData(int index, const String &name, BoneData *parent = NULL);
 

+ 41 - 20
spine-cpp/spine-cpp/include/spine/CurveTimeline.h

@@ -39,38 +39,59 @@ namespace spine {
 		RTTI_DECL
 
 	public:
-		explicit CurveTimeline(int frameCount);
+		explicit CurveTimeline(size_t frameCount, size_t frameEntries, size_t bezierCount);
 
 		virtual ~CurveTimeline();
 
-		virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>* pEvents, float alpha, MixBlend blend, MixDirection direction) = 0;
+		void setLinear(size_t frame);
 
-		virtual int getPropertyId() = 0;
+		void setStepped(size_t frame);
 
-		size_t getFrameCount();
+        void setBezier (size_t bezier, size_t frame, float value, float time1, float value1, float cx1, float cy1, float cx2, float cy2, float time2, float value2);
 
-		void setLinear(size_t frameIndex);
+        float getBezierValue(float time, size_t frame, size_t valueOffset, size_t i);
 
-		void setStepped(size_t frameIndex);
+	protected:
+		static const int LINEAR = 0;
+		static const int STEPPED = 1;
+		static const int BEZIER = 2;
+		static const int BEZIER_SIZE = 18;
 
-		/// Sets the control handle positions for an interpolation bezier curve used to transition from this keyframe to the next.
-		/// cx1 and cx2 are from 0 to 1, representing the percent of time between the two keyframes. cy1 and cy2 are the percent of
-		/// the difference between the keyframe's values.
-		void setCurve(size_t frameIndex, float cx1, float cy1, float cx2, float cy2);
+		Vector<float> _curves; // type, x, y, ...
+	};
 
-		float getCurvePercent(size_t frameIndex, float percent);
+    class SP_API CurveTimeline1 : public CurveTimeline {
+        RTTI_DECL
 
-		float getCurveType(size_t frameIndex);
+    public:
+        explicit CurveTimeline1(size_t frameCount, size_t bezierCount);
 
-	protected:
-		static const float LINEAR;
-		static const float STEPPED;
-		static const float BEZIER;
-		static const int BEZIER_SIZE;
+        virtual ~CurveTimeline1();
 
-	private:
-		Vector<float> _curves; // type, x, y, ...
-	};
+        void setFrame(size_t frame, float time, float value);
+
+        float getCurveValue(float time);
+    protected:
+        static const int ENTRIES;
+        static const int VALUE;
+    };
+
+    class SP_API CurveTimeline2 : public CurveTimeline {
+        RTTI_DECL
+
+    public:
+        explicit CurveTimeline2(size_t frameCount, size_t bezierCount);
+
+        virtual ~CurveTimeline2();
+
+        void setFrame(size_t frame, float time, float value1, float value2);
+
+        float getCurveValue(float time);
+    protected:
+        static const int ENTRIES;
+        static const int VALUE1;
+        static const int VALUE2;
+    };
 }
 
 #endif /* Spine_CurveTimeline_h */

+ 10 - 6
spine-cpp/spine-cpp/include/spine/PathConstraint.h

@@ -65,11 +65,14 @@ namespace spine {
 		float getSpacing();
 		void setSpacing(float inValue);
 
-		float getRotateMix();
-		void setRotateMix(float inValue);
+		float getMixRotate();
+		void setMixRotate(float inValue);
 
-		float getTranslateMix();
-		void setTranslateMix(float inValue);
+        float getMixX();
+        void setMixX(float inValue);
+
+        float getMixY();
+        void setMixY(float inValue);
 
 		Vector<Bone*>& getBones();
 
@@ -91,7 +94,8 @@ namespace spine {
 		PathConstraintData& _data;
 		Vector<Bone*> _bones;
 		Slot* _target;
-		float _position, _spacing, _rotateMix, _translateMix;
+		float _position, _spacing;
+		float _mixRotate, _mixX, _mixY;
 
 		Vector<float> _spaces;
 		Vector<float> _positions;
@@ -102,7 +106,7 @@ namespace spine {
 
 		bool _active;
 
-		Vector<float>& computeWorldPositions(PathAttachment& path, int spacesCount, bool tangents, bool percentPosition, bool percentSpacing);
+		Vector<float>& computeWorldPositions(PathAttachment& path, int spacesCount, bool tangents);
 
 		static void addBeforePosition(float p, Vector<float>& temp, int i, Vector<float>& output, int o);
 

+ 9 - 5
spine-cpp/spine-cpp/include/spine/PathConstraintData.h

@@ -78,11 +78,14 @@ namespace spine {
 		float getSpacing();
 		void setSpacing(float inValue);
 
-		float getRotateMix();
-		void setRotateMix(float inValue);
+		float getMixRotate();
+		void setMixRotate(float inValue);
 
-		float getTranslateMix();
-		void setTranslateMix(float inValue);
+        float getMixX();
+        void setMixX(float inValue);
+
+        float getMixY();
+        void setMixY(float inValue);
 
 	private:
 		Vector<BoneData*> _bones;
@@ -91,7 +94,8 @@ namespace spine {
 		SpacingMode _spacingMode;
 		RotateMode _rotateMode;
 		float _offsetRotation;
-		float _position, _spacing, _rotateMix, _translateMix;
+		float _position, _spacing;
+		float _mixRotate, _mixX, _mixY;
 	};
 }
 

+ 24 - 19
spine-cpp/spine-cpp/include/spine/TimelineType.h → spine-cpp/spine-cpp/include/spine/Property.h

@@ -27,27 +27,32 @@
  * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
 
-#ifndef Spine_TimelineType_h
-#define Spine_TimelineType_h
+#ifndef Spine_Property_h
+#define Spine_Property_h
 
 namespace spine {
-enum TimelineType {
-	TimelineType_Rotate = 0,
-	TimelineType_Translate,
-	TimelineType_Scale,
-	TimelineType_Shear,
-	TimelineType_Attachment,
-	TimelineType_Color,
-	TimelineType_Deform,
-	TimelineType_Event,
-	TimelineType_DrawOrder,
-	TimelineType_IkConstraint,
-	TimelineType_TransformConstraint,
-	TimelineType_PathConstraintPosition,
-	TimelineType_PathConstraintSpacing,
-	TimelineType_PathConstraintMix,
-	TimelineType_TwoColor
+typedef long long PropertyId;
+enum Property {
+	Property_Rotate = 1 << 0,
+    Property_X = 1 << 1,
+    Property_Y = 1 << 2,
+    Property_ScaleX = 1 << 3,
+    Property_ScaleY = 1 << 4,
+    Property_ShearX = 1 << 5,
+    Property_ShearY = 1 << 6,
+    Property_Rgb = 1 << 7,
+    Property_Alpha = 1 << 8,
+    Property_Rgb2 = 1 << 9,
+    Property_Attachment = 1 << 10,
+    Property_Deform = 1 << 11,
+    Property_Event = 1 << 12,
+    Property_DrawOrder = 1 << 13,
+    Property_IkConstraint = 1 << 14,
+    Property_TransformConstraint = 1 << 15,
+    Property_PathConstraintPosition = 1 << 16,
+    Property_PathConstraintSpacing = 1 << 17,
+    Property_OathConstraintMix = 1 << 18
 };
 }
 
-#endif /* Spine_TimelineType_h */
+#endif /* Spine_Property_h */

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

@@ -54,7 +54,7 @@ namespace spine {
 
 		void updateOffset();
 
-		void setUVs(float u, float v, float u2, float v2, bool rotate);
+		void setUVs(float u, float v, float u2, float v2, float degrees);
 
 		/// Transforms the attachment's four vertices to world coordinates.
 		/// @param bone The parent bone.

+ 4 - 18
spine-cpp/spine-cpp/include/spine/RotateTimeline.h

@@ -33,7 +33,7 @@
 #include <spine/CurveTimeline.h>
 
 namespace spine {
-	class SP_API RotateTimeline : public CurveTimeline {
+	class SP_API RotateTimeline : public CurveTimeline1 {
 		friend class SkeletonBinary;
 		friend class SkeletonJson;
 		friend class AnimationState;
@@ -41,29 +41,15 @@ namespace spine {
 		RTTI_DECL
 
 	public:
-		static const int ENTRIES = 2;
-
-		explicit RotateTimeline(int frameCount);
+		explicit RotateTimeline(size_t frameCount, size_t bezierCount, int boneIndex);
 
 		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 degrees);
-
-		int getBoneIndex();
-		void setBoneIndex(int inValue);
-
-		Vector<float>& getFrames();
+		int getBoneIndex() { return _boneIndex; }
 
+		void setBoneIndex(int inValue) { _boneIndex = inValue; }
 	private:
-		static const int PREV_TIME = -2;
-		static const int PREV_ROTATION = -1;
-		static const int ROTATION = 1;
-
 		int _boneIndex;
-		Vector<float> _frames; // time, angle, ...
 	};
 }
 

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

@@ -33,19 +33,65 @@
 #include <spine/TranslateTimeline.h>
 
 namespace spine {
-	class SP_API ScaleTimeline : public TranslateTimeline {
+	class SP_API ScaleTimeline : public CurveTimeline2 {
 		friend class SkeletonBinary;
 		friend class SkeletonJson;
 
 		RTTI_DECL
 
 	public:
-		explicit ScaleTimeline(int frameCount);
+		explicit ScaleTimeline(size_t frameCount, size_t bezierCount, int boneIndex);
+
+		virtual ~ScaleTimeline();
 
 		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 ScaleXTimeline : public CurveTimeline1 {
+        friend class SkeletonBinary;
+        friend class SkeletonJson;
+
+        RTTI_DECL
+
+    public:
+        explicit ScaleXTimeline(size_t frameCount, size_t bezierCount, int boneIndex);
+
+        virtual ~ScaleXTimeline();
+
+        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 ScaleYTimeline : public CurveTimeline1 {
+        friend class SkeletonBinary;
+        friend class SkeletonJson;
+
+        RTTI_DECL
+
+    public:
+        explicit ScaleYTimeline(size_t frameCount, size_t bezierCount, int boneIndex);
+
+        virtual ~ScaleYTimeline();
+
+        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_ScaleTimeline_h */

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

@@ -82,12 +82,22 @@ class SP_API Skeleton : public SpineObject {
 
 	friend class ScaleTimeline;
 
+    friend class ScaleXTimeline;
+
+    friend class ScaleYTimeline;
+
 	friend class ShearTimeline;
 
 	friend class TransformConstraintTimeline;
 
+    friend class RotateTimeline;
+
 	friend class TranslateTimeline;
 
+    friend class TranslateXTimeline;
+
+    friend class TranslateYTimeline;
+
 	friend class TwoColorTimeline;
 
 public:
@@ -104,6 +114,8 @@ public:
 	/// Updates the world transform for each bone and applies constraints.
 	void updateWorldTransform();
 
+	void updateWorldTransform(Bone* parent);
+
 	/// Sets the bones, constraints, and slots to their setup pose values.
 	void setToSetupPose();
 
@@ -217,7 +229,6 @@ private:
 	Vector<TransformConstraint *> _transformConstraints;
 	Vector<PathConstraint *> _pathConstraints;
 	Vector<Updatable *> _updateCache;
-	Vector<Bone *> _updateCacheReset;
 	Skin *_skin;
 	Color _color;
 	float _time;

+ 2 - 1
spine-cpp/spine-cpp/include/spine/SpacingMode.h

@@ -34,7 +34,8 @@ namespace spine {
 enum SpacingMode {
 	SpacingMode_Length = 0,
 	SpacingMode_Fixed,
-	SpacingMode_Percent
+	SpacingMode_Percent,
+	SpacingMode_Proportional
 };
 }
 

+ 18 - 2
spine-cpp/spine-cpp/include/spine/Timeline.h

@@ -35,6 +35,7 @@
 #include <spine/MixBlend.h>
 #include <spine/MixDirection.h>
 #include <spine/SpineObject.h>
+#include <spine/Property.h>
 
 namespace spine {
 class Skeleton;
@@ -45,7 +46,7 @@ class SP_API Timeline : public SpineObject {
 RTTI_DECL
 
 public:
-	Timeline();
+	Timeline(size_t frameCount, size_t frameEntries);
 
 	virtual ~Timeline();
 
@@ -63,7 +64,22 @@ public:
 	apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
 		MixDirection direction) = 0;
 
-	virtual int getPropertyId() = 0;
+	size_t getFrameEntries();
+
+	size_t getFrameCount();
+
+	Vector<float> &getFrames();
+
+	float getDuration();
+
+	virtual Vector<PropertyId> &getPropertyIds();
+
+protected:
+    void setPropertyIds(PropertyId propertyIds[], size_t propertyIdsCount);
+
+    Vector<PropertyId> _propertyIds;
+    Vector<float> _frames;
+    size_t _frameEntries;
 };
 }
 

+ 16 - 12
spine-cpp/spine-cpp/include/spine/TransformConstraint.h

@@ -48,8 +48,6 @@ namespace spine {
 	public:
 		TransformConstraint(TransformConstraintData& data, Skeleton& skeleton);
 
-		void apply();
-
 		virtual void update();
 
 		virtual int getOrder();
@@ -61,17 +59,23 @@ namespace spine {
 		Bone* getTarget();
 		void setTarget(Bone* inValue);
 
-		float getRotateMix();
-		void setRotateMix(float inValue);
+		float getMixRotate();
+		void setMixRotate(float inValue);
+
+		float getMixX();
+		void setMixX(float inValue);
+
+		float getMixY();
+		void setMixY(float inValue);
 
-		float getTranslateMix();
-		void setTranslateMix(float inValue);
+		float getMixScaleX();
+		void setMixScaleX(float inValue);
 
-		float getScaleMix();
-		void setScaleMix(float inValue);
+        float getMixScaleY();
+        void setMixScaleY(float inValue);
 
-		float getShearMix();
-		void setShearMix(float inValue);
+        float getMixShearY();
+        void setMixShearY(float inValue);
 
 		bool isActive();
 
@@ -81,8 +85,8 @@ namespace spine {
 		TransformConstraintData& _data;
 		Vector<Bone*> _bones;
 		Bone* _target;
-		float _rotateMix, _translateMix, _scaleMix, _shearMix;
-		bool _active;
+        float _mixRotate, _mixX, _mixY, _mixScaleX, _mixScaleY, _mixShearY;
+        bool _active;
 
 		void applyAbsoluteWorld();
 

+ 7 - 5
spine-cpp/spine-cpp/include/spine/TransformConstraintData.h

@@ -51,10 +51,12 @@ namespace spine {
 
 		Vector<BoneData*>& getBones();
 		BoneData* getTarget();
-		float getRotateMix();
-		float getTranslateMix();
-		float getScaleMix();
-		float getShearMix();
+        float getMixRotate();
+        float getMixX();
+        float getMixY();
+        float getMixScaleX();
+        float getMixScaleY();
+        float getMixShearY();
 
 		float getOffsetRotation();
 		float getOffsetX();
@@ -69,7 +71,7 @@ namespace spine {
 	private:
 		Vector<BoneData*> _bones;
 		BoneData* _target;
-		float _rotateMix, _translateMix, _scaleMix, _shearMix;
+        float _mixRotate, _mixX, _mixY, _mixScaleX, _mixScaleY, _mixShearY;
 		float _offsetRotation, _offsetX, _offsetY, _offsetScaleX, _offsetScaleY, _offsetShearY;
 		bool _relative, _local;
 	};

+ 46 - 17
spine-cpp/spine-cpp/include/spine/TranslateTimeline.h

@@ -33,40 +33,69 @@
 #include <spine/CurveTimeline.h>
 
 #include <spine/Animation.h>
-#include <spine/TimelineType.h>
+#include <spine/Property.h>
 
 namespace spine {
 
-	class SP_API TranslateTimeline : public CurveTimeline {
+	class SP_API TranslateTimeline : public CurveTimeline2 {
 		friend class SkeletonBinary;
 		friend class SkeletonJson;
 
 		RTTI_DECL
 
 	public:
-		static const int ENTRIES;
-
-		explicit TranslateTimeline(int frameCount);
+		explicit TranslateTimeline(size_t frameCount, size_t bezierCount, int boneIndex);
 
 		virtual ~TranslateTimeline();
 
 		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;
+	};
 
-		/// Sets the time and value of the specified keyframe.
-		void setFrame(int frameIndex, float time, float x, float y);
+    class SP_API TranslateXTimeline : public CurveTimeline1 {
+        friend class SkeletonBinary;
+        friend class SkeletonJson;
 
-	protected:
-		static const int PREV_TIME;
-		static const int PREV_X;
-		static const int PREV_Y;
-		static const int X;
-		static const int Y;
+    RTTI_DECL
 
-		Vector<float> _frames;
-		int _boneIndex;
-	};
+    public:
+        explicit TranslateXTimeline(size_t frameCount, size_t bezierCount, int boneIndex);
+
+        virtual ~TranslateXTimeline();
+
+        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 TranslateYTimeline : public CurveTimeline1 {
+        friend class SkeletonBinary;
+        friend class SkeletonJson;
+
+    RTTI_DECL
+
+    public:
+        explicit TranslateYTimeline(size_t frameCount, size_t bezierCount, int boneIndex);
+
+        virtual ~TranslateYTimeline();
+
+        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_TranslateTimeline_h */

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

@@ -95,7 +95,7 @@
 #include <spine/SpineString.h>
 #include <spine/TextureLoader.h>
 #include <spine/Timeline.h>
-#include <spine/TimelineType.h>
+#include <spine/Property.h>
 #include <spine/TransformConstraint.h>
 #include <spine/TransformConstraintData.h>
 #include <spine/TransformConstraintTimeline.h>

+ 21 - 50
spine-cpp/spine-cpp/src/spine/Animation.cpp

@@ -48,12 +48,18 @@ Animation::Animation(const String &name, Vector<Timeline *> &timelines, float du
 		_duration(duration),
 		_name(name) {
 	assert(_name.length() > 0);
-	for (int i = 0; i < (int)timelines.size(); i++)
-		_timelineIds.put(timelines[i]->getPropertyId(), true);
+	for (size_t i = 0; i < timelines.size(); i++) {
+	    Vector<PropertyId> propertyIds = timelines[i]->getPropertyIds();
+	    for (size_t ii = 0; ii < propertyIds.size(); ii++)
+            _timelineIds.put(propertyIds[ii], true);
+    }
 }
 
-bool Animation::hasTimeline(int id) {
-	return _timelineIds.containsKey(id);
+bool Animation::hasTimeline(Vector<PropertyId> ids) {
+    for (size_t i = 0; i < ids.size(); i++) {
+        if (_timelineIds.containsKey(ids[i])) return true;
+    }
+	return false;
 }
 
 Animation::~Animation() {
@@ -91,52 +97,17 @@ void Animation::setDuration(float inValue) {
 	_duration = inValue;
 }
 
-int Animation::binarySearch(Vector<float> &values, float target, int step) {
-	int low = 0;
-	int size = (int)values.size();
-	int high = size / step - 2;
-	if (high == 0) {
-		return step;
-	}
-
-	int current = (int) (static_cast<uint32_t>(high) >> 1);
-	while (true) {
-		if (values[(current + 1) * step] <= target)
-			low = current + 1;
-		else
-			high = current;
-
-		if (low == high) return (low + 1) * step;
-
-		current = (int) (static_cast<uint32_t>(low + high) >> 1);
-	}
-}
-
-int Animation::binarySearch(Vector<float> &values, float target) {
-	int low = 0;
-	int size = (int)values.size();
-	int high = size - 2;
-	if (high == 0) return 1;
-
-	int current = (int) (static_cast<uint32_t>(high) >> 1);
-	while (true) {
-		if (values[(current + 1)] <= target)
-			low = current + 1;
-		else
-			high = current;
-
-		if (low == high) return (low + 1);
-
-		current = (int) (static_cast<uint32_t>(low + high) >> 1);
-	}
+int Animation::search(Vector<float> &frames, float target) {
+    size_t n = (int)frames.size();
+    for (size_t i = 1; i < n; i++) {
+        if (frames[i] > target) return i - 1;
+    }
+    return n - 1;
 }
 
-int Animation::linearSearch(Vector<float> &values, float target, int step) {
-	for (int i = 0, last = (int)values.size() - step; i <= last; i += step) {
-		if (values[i] > target) {
-			return i;
-		}
-	}
-
-	return -1;
+int Animation::search(Vector<float> &frames, float target, int step) {
+    size_t n = frames.size();
+    for (size_t i = step; i < n; i += step)
+        if (frames[i] > target) return i - step;
+    return n - step;
 }

+ 1 - 1
spine-cpp/spine-cpp/src/spine/AttachmentTimeline.cpp

@@ -38,7 +38,7 @@
 
 #include <spine/Animation.h>
 #include <spine/Bone.h>
-#include <spine/TimelineType.h>
+#include <spine/Property.h>
 #include <spine/Slot.h>
 #include <spine/SlotData.h>
 

+ 1 - 1
spine-cpp/spine-cpp/src/spine/ColorTimeline.cpp

@@ -38,7 +38,7 @@
 
 #include <spine/Animation.h>
 #include <spine/Bone.h>
-#include <spine/TimelineType.h>
+#include <spine/Property.h>
 #include <spine/Slot.h>
 #include <spine/SlotData.h>
 

+ 91 - 69
spine-cpp/spine-cpp/src/spine/CurveTimeline.cpp

@@ -39,89 +39,111 @@ using namespace spine;
 
 RTTI_IMPL(CurveTimeline, Timeline)
 
-const float CurveTimeline::LINEAR = 0;
-const float CurveTimeline::STEPPED = 1;
-const float CurveTimeline::BEZIER = 2;
-const int CurveTimeline::BEZIER_SIZE = 10 * 2 - 1;
+CurveTimeline::CurveTimeline(size_t frameCount, size_t frameEntries, size_t bezierCount): Timeline(frameCount, frameEntries) {
+	_curves.setSize(frameCount + bezierCount * BEZIER_SIZE, 0);
+	_curves[frameCount - 1] = STEPPED;
+}
+
+CurveTimeline::~CurveTimeline() {
+}
 
-CurveTimeline::CurveTimeline(int frameCount) {
-	assert(frameCount > 0);
+void CurveTimeline::setLinear(size_t frame) {
+	_curves[frame] = LINEAR;
+}
 
-	_curves.setSize((frameCount - 1) * BEZIER_SIZE, 0);
+void CurveTimeline::setStepped(size_t frame) {
+	_curves[frame] = STEPPED;
 }
 
-CurveTimeline::~CurveTimeline() {
+void CurveTimeline::setBezier (size_t bezier, size_t frame, float value, float time1, float value1, float cx1, float cy1, float cx2, float cy2, float time2, float value2) {
+    size_t i = getFrameCount() + bezier * BEZIER_SIZE;
+    if (value == 0) _curves[frame] = BEZIER + i;
+    float tmpx = (time1 - cx1 * 2 + cx2) * 0.03, tmpy = (value1 - cy1 * 2 + cy2) * 0.03;
+    float dddx = ((cx1 - cx2) * 3 - time1 + time2) * 0.006, dddy = ((cy1 - cy2) * 3 - value1 + value2) * 0.006;
+    float ddx = tmpx * 2 + dddx, ddy = tmpy * 2 + dddy;
+    float dx = (cx1 - time1) * 0.3 + tmpx + dddx * 0.16666667, dy = (cy1 - value1) * 0.3 + tmpy + dddy * 0.16666667;
+    float x = time1 + dx, y = value1 + dy;
+    for (size_t n = i + 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;
+    }
 }
 
-size_t CurveTimeline::getFrameCount() {
-	return _curves.size() / BEZIER_SIZE + 1;
+float CurveTimeline::getBezierValue(float time, size_t frame, size_t valueOffset, size_t i) {
+    if (_curves[i] > time) {
+        float x = _frames[frame], y = _frames[frame + valueOffset];
+        return y + (time - x) / (_curves[i] - x) * (_curves[i + 1] - y);
+    }
+    size_t n = i + 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);
+        }
+    }
+    frame += getFrameEntries();
+    float x = _curves[n - 2], y = _curves[n - 1];
+    return y + (time - x) / (_frames[frame] - x) * (_frames[frame + valueOffset] - y);
 }
 
-void CurveTimeline::setLinear(size_t frameIndex) {
-	_curves[frameIndex * BEZIER_SIZE] = LINEAR;
+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) {
+}
+
+CurveTimeline1::~CurveTimeline1() {
 }
 
-void CurveTimeline::setStepped(size_t frameIndex) {
-	_curves[frameIndex * BEZIER_SIZE] = STEPPED;
+void CurveTimeline1::setFrame(size_t frame, float time, float value) {
+    frame <<= 1;
+    _frames[frame] = time;
+    _frames[frame + CurveTimeline1::VALUE] = value;
 }
 
-void CurveTimeline::setCurve(size_t frameIndex, float cx1, float cy1, float cx2, float cy2) {
-	float tmpx = (-cx1 * 2 + cx2) * 0.03f, tmpy = (-cy1 * 2 + cy2) * 0.03f;
-	float dddfx = ((cx1 - cx2) * 3 + 1) * 0.006f, dddfy = ((cy1 - cy2) * 3 + 1) * 0.006f;
-	float ddfx = tmpx * 2 + dddfx, ddfy = tmpy * 2 + dddfy;
-	float dfx = cx1 * 0.3f + tmpx + dddfx * 0.16666667f, dfy = cy1 * 0.3f + tmpy + dddfy * 0.16666667f;
-
-	size_t i = frameIndex * BEZIER_SIZE;
-	_curves[i++] = BEZIER;
-
-	float x = dfx, y = dfy;
-	for (size_t n = i + BEZIER_SIZE - 1; i < n; i += 2) {
-		_curves[i] = x;
-		_curves[i + 1] = y;
-		dfx += ddfx;
-		dfy += ddfy;
-		ddfx += dddfx;
-		ddfy += dddfy;
-		x += dfx;
-		y += dfy;
-	}
+float CurveTimeline1::getCurveValue(float time) {
+    int i = _frames.size() - 2;
+    for (int ii = 2; ii <= i; ii += 2) {
+        if (_frames[ii] > time) {
+            i = ii - 2;
+            break;
+        }
+    }
+
+    int curveType = (int)_curves[i >> 1];
+    switch (curveType) {
+        case CurveTimeline::LINEAR: {
+            float before = _frames[i], value = _frames[i + CurveTimeline1::VALUE];
+            return value + (time - before) / (_frames[i + CurveTimeline1::ENTRIES] - before) *
+                           (_frames[i + CurveTimeline1::ENTRIES + CurveTimeline1::VALUE] - value);
+        }
+        case CurveTimeline::STEPPED:
+            return _frames[i + CurveTimeline1::VALUE];
+    }
+    return getBezierValue(time, i, CurveTimeline1::VALUE, curveType - CurveTimeline1::BEZIER);
+}
+
+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) {
 }
 
-float CurveTimeline::getCurvePercent(size_t frameIndex, float percent) {
-	percent = MathUtil::clamp(percent, 0, 1);
-	size_t i = frameIndex * BEZIER_SIZE;
-	float type = _curves[i];
-
-	if (type == LINEAR) {
-		return percent;
-	}
-
-	if (type == STEPPED) {
-		return 0;
-	}
-
-	i++;
-	float x = 0;
-	for (size_t start = i, n = i + BEZIER_SIZE - 1; i < n; i += 2) {
-		x = _curves[i];
-		if (x >= percent) {
-			float prevX, prevY;
-			if (i == start) {
-				prevX = 0;
-				prevY = 0;
-			} else {
-				prevX = _curves[i - 2];
-				prevY = _curves[i - 1];
-			}
-			return prevY + (_curves[i + 1] - prevY) * (percent - prevX) / (x - prevX);
-		}
-	}
-
-	float y = _curves[i - 1];
-
-	return y + (1 - y) * (percent - x) / (1 - x); // Last point is 1,1.
+CurveTimeline2::~CurveTimeline2() {
 }
 
-float CurveTimeline::getCurveType(size_t frameIndex) {
-	return _curves[frameIndex * BEZIER_SIZE];
+void CurveTimeline2::setFrame(size_t frame, float time, float value1, float value2) {
+    frame *= CurveTimeline2::ENTRIES;
+    _frames[frame] = time;
+    _frames[frame + CurveTimeline2::VALUE1] = value1;
+    _frames[frame + CurveTimeline2::VALUE2] = value2;
 }

+ 1 - 1
spine-cpp/spine-cpp/src/spine/DeformTimeline.cpp

@@ -39,7 +39,7 @@
 #include <spine/VertexAttachment.h>
 
 #include <spine/Animation.h>
-#include <spine/TimelineType.h>
+#include <spine/Property.h>
 #include <spine/Slot.h>
 #include <spine/Bone.h>
 #include <spine/SlotData.h>

+ 1 - 1
spine-cpp/spine-cpp/src/spine/DrawOrderTimeline.cpp

@@ -37,7 +37,7 @@
 #include <spine/Event.h>
 
 #include <spine/Animation.h>
-#include <spine/TimelineType.h>
+#include <spine/Property.h>
 #include <spine/Slot.h>
 #include <spine/SlotData.h>
 

+ 1 - 1
spine-cpp/spine-cpp/src/spine/EventTimeline.cpp

@@ -37,7 +37,7 @@
 #include <spine/Event.h>
 
 #include <spine/Animation.h>
-#include <spine/TimelineType.h>
+#include <spine/Property.h>
 #include <spine/Slot.h>
 #include <spine/SlotData.h>
 #include <spine/EventData.h>

+ 1 - 9
spine-cpp/spine-cpp/src/spine/IkConstraint.cpp

@@ -101,10 +101,6 @@ void IkConstraint::apply(Bone &parent, Bone &child, float targetX, float targetY
 	Bone *pp = parent.getParent();
 	float tx, ty, dx, dy, dd, l1, l2, a1, a2, r, td, sd, p;
 	float id, x, y;
-	if (alpha == 0) {
-		child.updateWorldTransform();
-		return;
-	}
 	if (!parent._appliedValid) parent.updateAppliedTransform();
 	if (!child._appliedValid) child.updateAppliedTransform();
 	px = parent._ax;
@@ -268,12 +264,8 @@ IkConstraint::IkConstraint(IkConstraintData &data, Skeleton &skeleton) : Updatab
 	}
 }
 
-/// Applies the constraint to the constrained bones.
-void IkConstraint::apply() {
-	update();
-}
-
 void IkConstraint::update() {
+    if (_mix == 0) return;
 	switch (_bones.size()) {
 	case 1: {
 		Bone *bone0 = _bones[0];

+ 1 - 1
spine-cpp/spine-cpp/src/spine/IkConstraintTimeline.cpp

@@ -37,7 +37,7 @@
 #include <spine/Event.h>
 
 #include <spine/Animation.h>
-#include <spine/TimelineType.h>
+#include <spine/Property.h>
 #include <spine/Slot.h>
 #include <spine/SlotData.h>
 #include <spine/IkConstraint.h>

+ 122 - 84
spine-cpp/spine-cpp/src/spine/PathConstraint.cpp

@@ -57,8 +57,9 @@ PathConstraint::PathConstraint(PathConstraintData &data, Skeleton &skeleton) : U
 	data.getTarget()->getName())),
 	_position(data.getPosition()),
 	_spacing(data.getSpacing()),
-	_rotateMix(data.getRotateMix()),
-	_translateMix(data.getTranslateMix()),
+	_mixRotate(data.getMixRotate()),
+	_mixX(data.getMixX()),
+    _mixY(data.getMixY()),
 	_active(false)
 {
 	_bones.ensureCapacity(_data.getBones().size());
@@ -70,77 +71,95 @@ PathConstraint::PathConstraint(PathConstraintData &data, Skeleton &skeleton) : U
 	_segments.setSize(10, 0);
 }
 
-void PathConstraint::apply() {
-	update();
-}
-
 void PathConstraint::update() {
 	Attachment *baseAttachment = _target->getAttachment();
 	if (baseAttachment == NULL || !baseAttachment->getRTTI().instanceOf(PathAttachment::rtti)) {
 		return;
 	}
-
 	PathAttachment *attachment = static_cast<PathAttachment *>(baseAttachment);
 
-	float rotateMix = _rotateMix;
-	float translateMix = _translateMix;
-	bool translate = translateMix > 0;
-	bool rotate = rotateMix > 0;
-	if (!translate && !rotate) {
-		return;
-	}
+	float mixRotate = _mixRotate, mixX = _mixX, mixY = _mixY;
+	if (mixRotate == 0 && mixX == 0 && mixY == 0) return;
 
 	PathConstraintData &data = _data;
-	bool percentSpacing = data._spacingMode == SpacingMode_Percent;
-	RotateMode rotateMode = data._rotateMode;
-	bool tangents = rotateMode == RotateMode_Tangent, scale = rotateMode == RotateMode_ChainScale;
+	bool tangents = data._rotateMode == RotateMode_Tangent, scale = data._rotateMode == RotateMode_ChainScale;
 	size_t boneCount = _bones.size();
 	size_t spacesCount = tangents ? boneCount : boneCount + 1;
 	_spaces.setSize(spacesCount, 0);
+    if (scale) _lengths.setSize(boneCount, 0);
 	float spacing = _spacing;
-	if (scale || !percentSpacing) {
-		if (scale) _lengths.setSize(boneCount, 0);
-		bool lengthSpacing = data._spacingMode == SpacingMode_Length;
-
-		for (size_t i = 0, n = spacesCount - 1; i < n;) {
-			Bone *boneP = _bones[i];
-			Bone &bone = *boneP;
-			float setupLength = bone._data.getLength();
-			if (setupLength < PathConstraint::EPSILON) {
-				if (scale) _lengths[i] = 0;
-				_spaces[++i] = 0;
-			} else if (percentSpacing) {
-				if (scale) {
-					float x = setupLength * bone._a, y = setupLength * bone._c;
-					float length = MathUtil::sqrt(x * x + y * y);
-					_lengths[i] = length;
-				}
-				_spaces[++i] = spacing;
-			} else {
-				float x = setupLength * bone._a;
-				float y = setupLength * bone._c;
-				float length = MathUtil::sqrt(x * x + y * y);
-				if (scale) {
-					_lengths[i] = length;
-				}
 
-				_spaces[++i] = (lengthSpacing ? setupLength + spacing : spacing) * length / setupLength;
-			}
-		}
-	} else {
-		for (size_t i = 1; i < spacesCount; ++i) {
-			_spaces[i] = spacing;
-		}
+	switch(data._spacingMode) {
+	    case SpacingMode_Percent: {
+            if (scale) {
+                for (size_t i = 0, n = spacesCount - 1; i < n; i++) {
+                    Bone *boneP = _bones[i];
+                    Bone &bone = *boneP;
+                    float setupLength = bone._data.getLength();
+                    if (setupLength < PathConstraint::EPSILON) {
+                        _lengths[i] = 0;
+                    } else {
+                        float x = setupLength * bone._a, y = setupLength * bone._c;
+                        _lengths[i] = MathUtil::sqrt(x * x + y * y);
+                    }
+                }
+            }
+            for (size_t i = 1; i < spacesCount; ++i) {
+                _spaces[i] = spacing;
+            }
+            break;
+        }
+	    case SpacingMode_Proportional: {
+            float sum = 0;
+            for (size_t i = 0; i < boneCount;) {
+                Bone *boneP = _bones[i];
+                Bone &bone = *boneP;
+                float setupLength = bone._data.getLength();
+                if (setupLength < PathConstraint::EPSILON) {
+                    if (scale) _lengths[i] = 0;
+                    _spaces[++i] = spacing;
+                } else {
+                    float x = setupLength * bone._a, y = setupLength * bone._c;
+                    float length = MathUtil::sqrt(x * x + y * y);
+                    if (scale) _lengths[i] = length;
+                    _spaces[++i] = length;
+                    sum += length;
+                }
+            }
+            if (sum > 0) {
+                sum = spacesCount / sum * spacing;
+                for (size_t i = 1; i < spacesCount; i++) {
+                    _spaces[i] *= sum;
+                }
+            }
+            break;
+        }
+	    default: {
+            bool lengthSpacing = data._spacingMode == SpacingMode_Length;
+            for (size_t i = 0, n = spacesCount - 1; i < n;) {
+                Bone *boneP = _bones[i];
+                Bone &bone = *boneP;
+                float setupLength = bone._data.getLength();
+                if (setupLength < PathConstraint::EPSILON) {
+                    if (scale) _lengths[i] = 0;
+                    _spaces[++i] = spacing;
+                } else {
+                    float x = setupLength * bone._a, y = setupLength * bone._c;
+                    float length = MathUtil::sqrt(x * x + y * y);
+                    if (scale) _lengths[i] = length;
+                    _spaces[++i] = (lengthSpacing ? setupLength + spacing : spacing) * length / setupLength;
+                }
+            }
+        }
 	}
 
-	Vector<float>& positions = computeWorldPositions(*attachment, spacesCount, tangents,
-		data.getPositionMode() == PositionMode_Percent, percentSpacing);
+	Vector<float>& positions = computeWorldPositions(*attachment, spacesCount, tangents);
 	float boneX = positions[0];
 	float boneY = positions[1];
 	float offsetRotation = data.getOffsetRotation();
 	bool tip;
 	if (offsetRotation == 0) {
-		tip = rotateMode == RotateMode_Chain;
+		tip = data._rotateMode == RotateMode_Chain;
 	} else {
 		tip = false;
 		Bone &p = _target->getBone();
@@ -150,8 +169,8 @@ void PathConstraint::update() {
 	for (size_t i = 0, p = 3; i < boneCount; i++, p += 3) {
 		Bone *boneP = _bones[i];
 		Bone &bone = *boneP;
-		bone._worldX += (boneX - bone._worldX) * translateMix;
-		bone._worldY += (boneY - bone._worldY) * translateMix;
+		bone._worldX += (boneX - bone._worldX) * mixX;
+		bone._worldY += (boneY - bone._worldY) * mixY;
 		float x = positions[p];
 		float y = positions[p + 1];
 		float dx = x - boneX;
@@ -159,7 +178,7 @@ void PathConstraint::update() {
 		if (scale) {
 			float length = _lengths[i];
 			if (length >= PathConstraint::EPSILON) {
-				float s = (MathUtil::sqrt(dx * dx + dy * dy) / length - 1) * rotateMix + 1;
+				float s = (MathUtil::sqrt(dx * dx + dy * dy) / length - 1) * mixRotate + 1;
 				bone._a *= s;
 				bone._c *= s;
 			}
@@ -168,7 +187,7 @@ void PathConstraint::update() {
 		boneX = x;
 		boneY = y;
 
-		if (rotate) {
+		if (mixRotate > 0) {
 			float a = bone._a, b = bone._b, c = bone._c, d = bone._d, r, cos, sin;
 			if (tangents)
 				r = positions[p - 1];
@@ -183,8 +202,8 @@ void PathConstraint::update() {
 				cos = MathUtil::cos(r);
 				sin = MathUtil::sin(r);
 				float length = bone._data.getLength();
-				boneX += (length * (cos * a - sin * c) - dx) * rotateMix;
-				boneY += (length * (sin * a + cos * c) - dy) * rotateMix;
+				boneX += (length * (cos * a - sin * c) - dx) * mixRotate;
+				boneY += (length * (sin * a + cos * c) - dy) * mixRotate;
 			} else
 				r += offsetRotation;
 
@@ -193,7 +212,7 @@ void PathConstraint::update() {
 			else if (r < -MathUtil::Pi)
 				r += MathUtil::Pi_2;
 
-			r *= rotateMix;
+			r *= mixRotate;
 			cos = MathUtil::cos(r);
 			sin = MathUtil::sin(r);
 			bone._a = cos * a - sin * c;
@@ -226,20 +245,28 @@ void PathConstraint::setSpacing(float inValue) {
 	_spacing = inValue;
 }
 
-float PathConstraint::getRotateMix() {
-	return _rotateMix;
+float PathConstraint::getMixRotate() {
+	return _mixRotate;
 }
 
-void PathConstraint::setRotateMix(float inValue) {
-	_rotateMix = inValue;
+void PathConstraint::setMixRotate(float inValue) {
+	_mixRotate = inValue;
 }
 
-float PathConstraint::getTranslateMix() {
-	return _translateMix;
+float PathConstraint::getMixX() {
+	return _mixX;
 }
 
-void PathConstraint::setTranslateMix(float inValue) {
-	_translateMix = inValue;
+void PathConstraint::setMixX(float inValue) {
+	_mixX = inValue;
+}
+
+float PathConstraint::getMixY() {
+    return _mixY;
+}
+
+void PathConstraint::setMixY(float inValue) {
+    _mixY = inValue;
 }
 
 Vector<Bone *> &PathConstraint::getBones() {
@@ -259,7 +286,7 @@ PathConstraintData &PathConstraint::getData() {
 }
 
 Vector<float>&
-PathConstraint::computeWorldPositions(PathAttachment &path, int spacesCount, bool tangents, bool percentPosition, bool percentSpacing) {
+PathConstraint::computeWorldPositions(PathAttachment &path, int spacesCount, bool tangents) {
 	Slot &target = *_target;
 	float position = _position;
 	_positions.setSize(spacesCount * 3 + 2, 0);
@@ -275,16 +302,23 @@ PathConstraint::computeWorldPositions(PathAttachment &path, int spacesCount, boo
 		Vector<float> &lengths = path.getLengths();
 		curveCount -= closed ? 1 : 2;
 		pathLength = lengths[curveCount];
-		if (percentPosition) position *= pathLength;
-
-		if (percentSpacing) {
-			for (int i = 1; i < spacesCount; ++i)
-				_spaces[i] *= pathLength;
-		}
+        if (_data._positionMode == PositionMode_Percent) position *= pathLength;
+
+        float multiplier = 0;
+        switch (_data._spacingMode) {
+            case SpacingMode_Percent:
+                multiplier = pathLength;
+                break;
+            case SpacingMode_Proportional:
+                multiplier = pathLength / spacesCount;
+                break;
+            default:
+                multiplier = 1;
+        }
 
 		world.setSize(8, 0);
 		for (int i = 0, o = 0, curve = 0; i < spacesCount; i++, o += 3) {
-			float space = _spaces[i];
+			float space = _spaces[i] * multiplier;
 			position += space;
 			float p = position;
 
@@ -393,19 +427,23 @@ PathConstraint::computeWorldPositions(PathAttachment &path, int spacesCount, boo
 		y1 = y2;
 	}
 
-	if (percentPosition)
-		position *= pathLength;
-	else
-		position *= pathLength / path.getLengths()[curveCount - 1];
+    if (_data._positionMode == PositionMode_Percent) position *= pathLength;
 
-	if (percentSpacing) {
-		for (int i = 1; i < spacesCount; ++i)
-			_spaces[i] *= pathLength;
-	}
+    float multiplier = 0;
+    switch (_data._spacingMode) {
+        case SpacingMode_Percent:
+            multiplier = pathLength;
+            break;
+        case SpacingMode_Proportional:
+            multiplier = pathLength / spacesCount;
+            break;
+        default:
+            multiplier = 1;
+    }
 
 	float curveLength = 0;
 	for (int i = 0, o = 0, curve = 0, segment = 0; i < spacesCount; i++, o += 3) {
-		float space = _spaces[i];
+		float space = _spaces[i] * multiplier;
 		position += space;
 		float p = position;
 

+ 42 - 33
spine-cpp/spine-cpp/src/spine/PathConstraintData.cpp

@@ -41,90 +41,99 @@
 using namespace spine;
 
 PathConstraintData::PathConstraintData(const String &name) :
-		ConstraintData(name),
-		_target(NULL),
-		_positionMode(PositionMode_Fixed),
-		_spacingMode(SpacingMode_Length),
-		_rotateMode(RotateMode_Tangent),
-		_offsetRotation(0),
-		_position(0),
-		_spacing(0),
-		_rotateMix(0),
-		_translateMix(0) {
+        ConstraintData(name),
+        _target(NULL),
+        _positionMode(PositionMode_Fixed),
+        _spacingMode(SpacingMode_Length),
+        _rotateMode(RotateMode_Tangent),
+        _offsetRotation(0),
+        _position(0),
+        _spacing(0),
+        _mixRotate(0),
+        _mixX(0),
+        _mixY(0) {
 }
 
 Vector<BoneData *> &PathConstraintData::getBones() {
-	return _bones;
+    return _bones;
 }
 
 SlotData *PathConstraintData::getTarget() {
-	return _target;
+    return _target;
 }
 
 void PathConstraintData::setTarget(SlotData *inValue) {
-	_target = inValue;
+    _target = inValue;
 }
 
 PositionMode PathConstraintData::getPositionMode() {
-	return _positionMode;
+    return _positionMode;
 }
 
 void PathConstraintData::setPositionMode(PositionMode inValue) {
-	_positionMode = inValue;
+    _positionMode = inValue;
 }
 
 SpacingMode PathConstraintData::getSpacingMode() {
-	return _spacingMode;
+    return _spacingMode;
 }
 
 void PathConstraintData::setSpacingMode(SpacingMode inValue) {
-	_spacingMode = inValue;
+    _spacingMode = inValue;
 }
 
 RotateMode PathConstraintData::getRotateMode() {
-	return _rotateMode;
+    return _rotateMode;
 }
 
 void PathConstraintData::setRotateMode(RotateMode inValue) {
-	_rotateMode = inValue;
+    _rotateMode = inValue;
 }
 
 float PathConstraintData::getOffsetRotation() {
-	return _offsetRotation;
+    return _offsetRotation;
 }
 
 void PathConstraintData::setOffsetRotation(float inValue) {
-	_offsetRotation = inValue;
+    _offsetRotation = inValue;
 }
 
 float PathConstraintData::getPosition() {
-	return _position;
+    return _position;
 }
 
 void PathConstraintData::setPosition(float inValue) {
-	_position = inValue;
+    _position = inValue;
 }
 
 float PathConstraintData::getSpacing() {
-	return _spacing;
+    return _spacing;
 }
 
 void PathConstraintData::setSpacing(float inValue) {
-	_spacing = inValue;
+    _spacing = inValue;
 }
 
-float PathConstraintData::getRotateMix() {
-	return _rotateMix;
+float PathConstraintData::getMixRotate() {
+    return _mixRotate;
 }
 
-void PathConstraintData::setRotateMix(float inValue) {
-	_rotateMix = inValue;
+void PathConstraintData::setMixRotate(float inValue) {
+    _mixRotate = inValue;
 }
 
-float PathConstraintData::getTranslateMix() {
-	return _translateMix;
+float PathConstraintData::getMixX() {
+    return _mixX;
 }
 
-void PathConstraintData::setTranslateMix(float inValue) {
-	_translateMix = inValue;
+void PathConstraintData::setMixX(float inValue) {
+    _mixX = inValue;
+}
+
+float PathConstraintData::getMixY() {
+    return _mixY;
+}
+
+void PathConstraintData::setMixY(float inValue) {
+    _mixY = inValue;
 }

+ 1 - 1
spine-cpp/spine-cpp/src/spine/PathConstraintMixTimeline.cpp

@@ -37,7 +37,7 @@
 #include <spine/Event.h>
 
 #include <spine/Animation.h>
-#include <spine/TimelineType.h>
+#include <spine/Property.h>
 #include <spine/Slot.h>
 #include <spine/SlotData.h>
 #include <spine/PathConstraint.h>

+ 1 - 1
spine-cpp/spine-cpp/src/spine/PathConstraintPositionTimeline.cpp

@@ -37,7 +37,7 @@
 #include <spine/Event.h>
 
 #include <spine/Animation.h>
-#include <spine/TimelineType.h>
+#include <spine/Property.h>
 #include <spine/Slot.h>
 #include <spine/SlotData.h>
 #include <spine/PathConstraint.h>

+ 1 - 1
spine-cpp/spine-cpp/src/spine/PathConstraintSpacingTimeline.cpp

@@ -37,7 +37,7 @@
 #include <spine/Event.h>
 
 #include <spine/Animation.h>
-#include <spine/TimelineType.h>
+#include <spine/Property.h>
 #include <spine/Slot.h>
 #include <spine/SlotData.h>
 #include <spine/PathConstraint.h>

+ 2 - 2
spine-cpp/spine-cpp/src/spine/RegionAttachment.cpp

@@ -103,8 +103,8 @@ void RegionAttachment::updateOffset() {
 	_vertexOffset[BRY] = localYCos + localX2Sin;
 }
 
-void RegionAttachment::setUVs(float u, float v, float u2, float v2, bool rotate) {
-	if (rotate) {
+void RegionAttachment::setUVs(float u, float v, float u2, float v2, float degrees) {
+	if (degrees == 90) {
 		_uvs[URX] = u;
 		_uvs[URY] = v2;
 		_uvs[BRX] = u;

+ 36 - 89
spine-cpp/spine-cpp/src/spine/RotateTimeline.cpp

@@ -39,101 +39,48 @@
 #include <spine/Bone.h>
 #include <spine/BoneData.h>
 #include <spine/Animation.h>
-#include <spine/TimelineType.h>
+#include <spine/Property.h>
 
 using namespace spine;
 
-RTTI_IMPL(RotateTimeline, CurveTimeline)
+RTTI_IMPL(RotateTimeline, CurveTimeline1)
 
-RotateTimeline::RotateTimeline(int frameCount) : CurveTimeline(frameCount), _boneIndex(0) {
-	_frames.setSize(frameCount << 1, 0);
+RotateTimeline::RotateTimeline(size_t frameCount, size_t bezierCount, int boneIndex) : CurveTimeline1(frameCount, bezierCount), _boneIndex(boneIndex) {
+    PropertyId ids[] = { ((PropertyId)Property_Rotate << 32) | boneIndex };
+    setPropertyIds(ids, 1);
 }
 
 void RotateTimeline::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.getBones()[_boneIndex];
-	if (!bone->_active) return;
-
-	if (time < _frames[0]) {
-		switch (blend) {
-		case MixBlend_Setup: {
-			bone->_rotation = bone->_data._rotation;
-			break;
-		}
-		case MixBlend_First: {
-			float r = bone->_data._rotation - bone->_rotation;
-			bone->_rotation += (r - (16384 - (int) (16384.499999999996 - r / 360)) * 360) * alpha;
-			break;
-		}
-		default: {
-			// TODO?
-			break;
-		}
-		}
-		return;
-	}
-
-	if (time >= _frames[_frames.size() - ENTRIES]) {
-		float r = _frames[_frames.size() + PREV_ROTATION];
-		switch (blend) {
-			case MixBlend_Setup:
-				bone->_rotation = bone->_data._rotation + r * alpha;
-				break;
-			case MixBlend_First:
-			case MixBlend_Replace:
-				r += bone->_data._rotation - bone->_rotation;
-				r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360;
-				// Fall through.
-			case MixBlend_Add:
-				bone->_rotation += r * alpha;
-		}
-		return;
-	}
-
-	// Interpolate between the previous frame and the current frame.
-	int frame = Animation::binarySearch(_frames, time, ENTRIES);
-	float prevRotation = _frames[frame + PREV_ROTATION];
-	float frameTime = _frames[frame];
-	float percent = getCurvePercent((frame >> 1) - 1,
-		1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime));
-	float r = _frames[frame + ROTATION] - prevRotation;
-	r = prevRotation + (r - (16384 - (int)(16384.499999999996 - r / 360)) * 360) * percent;
-	switch (blend) {
-		case MixBlend_Setup:
-			bone->_rotation = bone->_data._rotation + (r - (16384 - (int)(16384.499999999996 - r / 360)) * 360) * alpha;
-			break;
-		case MixBlend_First:
-		case MixBlend_Replace:
-			r += bone->_data._rotation - bone->_rotation;
-			// Fall through.
-		case MixBlend_Add:
-			bone->_rotation += (r - (16384 - (int)(16384.499999999996 - r / 360)) * 360) * alpha;
-	}
-}
-
-int RotateTimeline::getPropertyId() {
-	return ((int) TimelineType_Rotate << 24) + _boneIndex;
-}
-
-void RotateTimeline::setFrame(int frameIndex, float time, float degrees) {
-	frameIndex <<= 1;
-	_frames[frameIndex] = time;
-	_frames[frameIndex + ROTATION] = degrees;
-}
-
-int RotateTimeline::getBoneIndex() {
-	return _boneIndex;
-}
-
-void RotateTimeline::setBoneIndex(int inValue) {
-	_boneIndex = inValue;
-}
-
-Vector<float> &RotateTimeline::getFrames() {
-	return _frames;
-}
+    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->_rotation = bone->_data._rotation;
+                return;
+            case MixBlend_First:
+                bone->_rotation += (bone->_data._rotation - bone->_rotation) * alpha;
+            default: {}
+        }
+        return;
+    }
+
+    float r = getCurveValue(time);
+    switch (blend) {
+        case MixBlend_Setup:
+            bone->_rotation = bone->_data._rotation + r * alpha;
+            break;
+        case MixBlend_First:
+        case MixBlend_Replace:
+            r += bone->_data._rotation - bone->_rotation;
+        case MixBlend_Add:
+            bone->_rotation += r * alpha;
+    }
+}

+ 243 - 96
spine-cpp/spine-cpp/src/spine/ScaleTimeline.cpp

@@ -43,9 +43,12 @@
 
 using namespace spine;
 
-RTTI_IMPL(ScaleTimeline, TranslateTimeline)
+RTTI_IMPL(ScaleTimeline, CurveTimeline2)
 
-ScaleTimeline::ScaleTimeline(int frameCount) : TranslateTimeline(frameCount) {
+ScaleTimeline::ScaleTimeline(size_t frameCount, size_t bezierCount, int boneIndex) : CurveTimeline2(frameCount, bezierCount), _boneIndex(boneIndex) {
+    PropertyId ids[] = { ((PropertyId)Property_ScaleX << 32) | boneIndex,
+                         ((PropertyId)Property_ScaleY << 32) | boneIndex};
+    setPropertyIds(ids, 2);
 }
 
 void ScaleTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha,
@@ -54,100 +57,244 @@ void ScaleTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector
 	SP_UNUSED(lastTime);
 	SP_UNUSED(pEvents);
 
-	Bone *boneP = skeleton._bones[_boneIndex];
-	Bone &bone = *boneP;
-
-	if (!bone._active) return;
-
-	if (time < _frames[0]) {
-		switch (blend) {
-		case MixBlend_Setup:
-			bone._scaleX = bone._data._scaleX;
-			bone._scaleY = bone._data._scaleY;
-			return;
-		case MixBlend_First:
-			bone._scaleX += (bone._data._scaleX - bone._scaleX) * alpha;
-			bone._scaleY += (bone._data._scaleY - bone._scaleY) * alpha;
-		default: {}
-		}
-		return;
-	}
-
-	float x, y;
-	if (time >= _frames[_frames.size() - ENTRIES]) {
-		// Time is after last frame.
-		x = _frames[_frames.size() + PREV_X] * bone._data._scaleX;
-		y = _frames[_frames.size() + PREV_Y] * bone._data._scaleY;
-	} 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) * bone._data._scaleX;
-		y = (y + (_frames[frame + Y] - y) * percent) * bone._data._scaleY;
-	}
-
-	if (alpha == 1) {
-		if (blend == MixBlend_Add) {
-			bone._scaleX += x - bone._data._scaleX;
-			bone._scaleY += y - bone._data._scaleY;
-		} else {
-			bone._scaleX = x;
-			bone._scaleY = y;
-		}
-	} else {
-		// Mixing out uses sign of setup or current pose, else use sign of key.
-		float bx, by;
-		if (direction == MixDirection_Out) {
-			switch (blend) {
-			case MixBlend_Setup:
-				bx = bone._data._scaleX;
-				by = bone._data._scaleY;
-				bone._scaleX = bx + (MathUtil::abs(x) * MathUtil::sign(bx) - bx) * alpha;
-				bone._scaleY = by + (MathUtil::abs(y) * MathUtil::sign(by) - by) * alpha;
-				break;
-			case MixBlend_First:
-			case MixBlend_Replace:
-				bx = bone._scaleX;
-				by = bone._scaleY;
-				bone._scaleX = bx + (MathUtil::abs(x) * MathUtil::sign(bx) - bx) * alpha;
-				bone._scaleY = by + (MathUtil::abs(y) * MathUtil::sign(by) - by) * alpha;
-				break;
-			case MixBlend_Add:
-				bx = bone._scaleX;
-				by = bone._scaleY;
-				bone._scaleX = bx + (MathUtil::abs(x) * MathUtil::sign(bx) - bone._data._scaleX) * alpha;
-				bone._scaleY = by + (MathUtil::abs(y) * MathUtil::sign(by) - bone._data._scaleY) * alpha;
-			}
-		} else {
-			switch (blend) {
-			case MixBlend_Setup:
-				bx = MathUtil::abs(bone._data._scaleX) * MathUtil::sign(x);
-				by = MathUtil::abs(bone._data._scaleY) * MathUtil::sign(y);
-				bone._scaleX = bx + (x - bx) * alpha;
-				bone._scaleY = by + (y - by) * alpha;
-				break;
-			case MixBlend_First:
-			case MixBlend_Replace:
-				bx = MathUtil::abs(bone._scaleX) * MathUtil::sign(x);
-				by = MathUtil::abs(bone._scaleY) * MathUtil::sign(y);
-				bone._scaleX = bx + (x - bx) * alpha;
-				bone._scaleY = by + (y - by) * alpha;
-				break;
-			case MixBlend_Add:
-				bx = MathUtil::sign(x);
-				by = MathUtil::sign(y);
-				bone._scaleX = MathUtil::abs(bone._scaleX) * bx + (x - MathUtil::abs(bone._data._scaleX) * bx) * alpha;
-				bone._scaleY = MathUtil::abs(bone._scaleY) * by + (y - MathUtil::abs(bone._data._scaleY) * by) * alpha;
-			}
-		}
-	}
+	Bone *bone = skeleton._bones[_boneIndex];
+	if (!bone->_active) return;
+
+    if (time < _frames[0]) {
+        switch (blend) {
+            case MixBlend_Setup:
+                bone->_scaleX = bone->_data._scaleX;
+                bone->_scaleY = bone->_data._scaleY;
+                return;
+            case MixBlend_First:
+                bone->_scaleX += (bone->_data._scaleX - bone->_scaleX) * alpha;
+                bone->_scaleY += (bone->_data._scaleY - bone->_scaleY) * alpha;
+            default: {}
+        }
+        return;
+    }
+
+    float x, y;
+    int i = Animation::search(_frames, time, CurveTimeline2::ENTRIES);
+    int curveType = (int)_curves[i / CurveTimeline2::ENTRIES];
+    switch (curveType) {
+        case CurveTimeline::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 CurveTimeline::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);
+        }
+    }
+    x *= bone->_data._scaleX;
+    y *= bone->_data._scaleY;
+
+    if (alpha == 1) {
+        if (blend == MixBlend_Add) {
+            bone->_scaleX += x - bone->_data._scaleX;
+            bone->_scaleY += y - bone->_data._scaleY;
+        } else {
+            bone->_scaleX = x;
+            bone->_scaleY = y;
+        }
+    } else {
+        float bx, by;
+        if (direction == MixDirection_Out) {
+            switch (blend) {
+                case MixBlend_Setup:
+                    bx = bone->_data._scaleX;
+                    by = bone->_data._scaleY;
+                    bone->_scaleX = bx + (MathUtil::abs(x) * MathUtil::sign(bx) - bx) * alpha;
+                    bone->_scaleY = by + (MathUtil::abs(y) * MathUtil::sign(by) - by) * alpha;
+                    break;
+                case MixBlend_First:
+                case MixBlend_Replace:
+                    bx = bone->_scaleX;
+                    by = bone->_scaleY;
+                    bone->_scaleX = bx + (MathUtil::abs(x) * MathUtil::sign(bx) - bx) * alpha;
+                    bone->_scaleY = by + (MathUtil::abs(y) * MathUtil::sign(by) - by) * alpha;
+                    break;
+                case MixBlend_Add:
+                    bx = bone->_scaleX;
+                    by = bone->_scaleY;
+                    bone->_scaleX = bx + (MathUtil::abs(x) * MathUtil::sign(bx) - bone->_data._scaleX) * alpha;
+                    bone->_scaleY = by + (MathUtil::abs(y) * MathUtil::sign(by) - bone->_data._scaleY) * alpha;
+            }
+        } else {
+            switch (blend) {
+                case MixBlend_Setup:
+                    bx = MathUtil::abs(bone->_data._scaleX) * MathUtil::sign(x);
+                    by = MathUtil::abs(bone->_data._scaleY) * MathUtil::sign(y);
+                    bone->_scaleX = bx + (x - bx) * alpha;
+                    bone->_scaleY = by + (y - by) * alpha;
+                    break;
+                case MixBlend_First:
+                case MixBlend_Replace:
+                    bx = MathUtil::abs(bone->_scaleX) * MathUtil::sign(x);
+                    by = MathUtil::abs(bone->_scaleY) * MathUtil::sign(y);
+                    bone->_scaleX = bx + (x - bx) * alpha;
+                    bone->_scaleY = by + (y - by) * alpha;
+                    break;
+                case MixBlend_Add:
+                    bx = MathUtil::sign(x);
+                    by = MathUtil::sign(y);
+                    bone->_scaleX = MathUtil::abs(bone->_scaleX) * bx + (x - MathUtil::abs(bone->_data._scaleX) * bx) * alpha;
+                    bone->_scaleY = MathUtil::abs(bone->_scaleY) * by + (y - MathUtil::abs(bone->_data._scaleY) * by) * alpha;
+            }
+        }
+    }
+}
+
+RTTI_IMPL(ScaleXTimeline, CurveTimeline1)
+
+ScaleXTimeline::ScaleXTimeline(size_t frameCount, size_t bezierCount, int boneIndex) : CurveTimeline1(frameCount, bezierCount), _boneIndex(boneIndex) {
+    PropertyId ids[] = { ((PropertyId)Property_ScaleX << 32) | boneIndex };
+    setPropertyIds(ids, 1);
 }
 
-int ScaleTimeline::getPropertyId() {
-	return ((int) TimelineType_Scale << 24) + _boneIndex;
+void ScaleXTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha,
+                          MixBlend blend, MixDirection direction
+) {
+    SP_UNUSED(lastTime);
+    SP_UNUSED(pEvents);
+
+    Bone *bone = skeleton._bones[_boneIndex];
+    if (!bone->_active) return;
+
+    if (time < _frames[0]) {
+        switch (blend) {
+            case MixBlend_Setup:
+                bone->_scaleX = bone->_data._scaleX;
+                return;
+            case MixBlend_First:
+                bone->_scaleX += (bone->_data._scaleX - bone->_scaleX) * alpha;
+            default: {}
+        }
+        return;
+    }
+
+    float x = getCurveValue(time) * bone->_data._scaleX;
+    if (alpha == 1) {
+        if (blend == MixBlend_Add)
+            bone->_scaleX += x - bone->_data._scaleX;
+        else
+            bone->_scaleX = x;
+    } else {
+        // Mixing out uses sign of setup or current pose, else use sign of key.
+        float bx;
+        if (direction == MixDirection_Out) {
+            switch (blend) {
+                case MixBlend_Setup:
+                    bx = bone->_data._scaleX;
+                    bone->_scaleX = bx + (MathUtil::abs(x) * MathUtil::sign(bx) - bx) * alpha;
+                    break;
+                case MixBlend_First:
+                case MixBlend_Replace:
+                    bx = bone->_scaleX;
+                    bone->_scaleX = bx + (MathUtil::abs(x) * MathUtil::sign(bx) - bx) * alpha;
+                    break;
+                case MixBlend_Add:
+                    bx = bone->_scaleX;
+                    bone->_scaleX = bx + (MathUtil::abs(x) * MathUtil::sign(bx) - bone->_data._scaleX) * alpha;
+            }
+        } else {
+            switch (blend) {
+                case MixBlend_Setup:
+                    bx = MathUtil::abs(bone->_data._scaleX) * MathUtil::sign(x);
+                    bone->_scaleX = bx + (x - bx) * alpha;
+                    break;
+                case MixBlend_First:
+                case MixBlend_Replace:
+                    bx = MathUtil::abs(bone->_scaleX) * MathUtil::sign(x);
+                    bone->_scaleX = bx + (x - bx) * alpha;
+                    break;
+                case MixBlend_Add:
+                    bx = MathUtil::sign(x);
+                    bone->_scaleX = MathUtil::abs(bone->_scaleX) * bx + (x - MathUtil::abs(bone->_data._scaleX) * bx) * alpha;
+            }
+        }
+    }
+}
+
+RTTI_IMPL(ScaleYTimeline, CurveTimeline1)
+
+ScaleYTimeline::ScaleYTimeline(size_t frameCount, size_t bezierCount, int boneIndex) : CurveTimeline1(frameCount, bezierCount), _boneIndex(boneIndex) {
+    PropertyId ids[] = { ((PropertyId)Property_ScaleY << 32) | boneIndex };
+    setPropertyIds(ids, 1);
+}
+
+void ScaleYTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha,
+                           MixBlend blend, MixDirection direction
+) {
+    SP_UNUSED(lastTime);
+    SP_UNUSED(pEvents);
+
+    Bone *bone = skeleton._bones[_boneIndex];
+    if (!bone->_active) return;
+
+    if (time < _frames[0]) {
+        switch (blend) {
+            case MixBlend_Setup:
+                bone->_scaleY = bone->_data._scaleY;
+                return;
+            case MixBlend_First:
+                bone->_scaleY += (bone->_data._scaleY - bone->_scaleY) * alpha;
+            default: {}
+        }
+        return;
+    }
+
+    float y = getCurveValue(time) * bone->_data._scaleY;
+    if (alpha == 1) {
+        if (blend == MixBlend_Add)
+            bone->_scaleY += y - bone->_data._scaleY;
+        else
+            bone->_scaleY = y;
+    } else {
+        // Mixing out uses sign of setup or current pose, else use sign of key.
+        float by = 0;
+        if (direction == MixDirection_Out) {
+            switch (blend) {
+                case MixBlend_Setup:
+                    by = bone->_data._scaleY;
+                    bone->_scaleY = by + (MathUtil::abs(y) * MathUtil::sign(by) - by) * alpha;
+                    break;
+                case MixBlend_First:
+                case MixBlend_Replace:
+                    by = bone->_scaleY;
+                    bone->_scaleY = by + (MathUtil::abs(y) * MathUtil::sign(by) - by) * alpha;
+                    break;
+                case MixBlend_Add:
+                    by = bone->_scaleY;
+                    bone->_scaleY = by + (MathUtil::abs(y) * MathUtil::sign(by) - bone->_data._scaleY) * alpha;
+            }
+        } else {
+            switch (blend) {
+                case MixBlend_Setup:
+                    by = MathUtil::abs(bone->_data._scaleY) * MathUtil::sign(y);
+                    bone->_scaleY = by + (y - by) * alpha;
+                    break;
+                case MixBlend_First:
+                case MixBlend_Replace:
+                    by = MathUtil::abs(bone->_scaleY) * MathUtil::sign(y);
+                    bone->_scaleY = by + (y - by) * alpha;
+                    break;
+                case MixBlend_Add:
+                    by = MathUtil::sign(y);
+                    bone->_scaleY = MathUtil::abs(bone->_scaleY) * by + (y - MathUtil::abs(bone->_data._scaleY) * by) * alpha;
+            }
+        }
+    }
 }

+ 45 - 28
spine-cpp/spine-cpp/src/spine/Skeleton.cpp

@@ -134,7 +134,6 @@ Skeleton::~Skeleton() {
 
 void Skeleton::updateCache() {
 	_updateCache.clear();
-	_updateCacheReset.clear();
 
 	for (size_t i = 0, n = _bones.size(); i < n; ++i) {
 		Bone* bone = _bones[i];
@@ -213,24 +212,36 @@ void Skeleton::printUpdateCache() {
 }
 
 void Skeleton::updateWorldTransform() {
-	for (size_t i = 0, n = _updateCacheReset.size(); i < n; ++i) {
-		Bone *boneP = _updateCacheReset[i];
-		Bone &bone = *boneP;
-		bone._ax = bone._x;
-		bone._ay = bone._y;
-		bone._arotation = bone._rotation;
-		bone._ascaleX = bone._scaleX;
-		bone._ascaleY = bone._scaleY;
-		bone._ashearX = bone._shearX;
-		bone._ashearY = bone._shearY;
-		bone._appliedValid = true;
-	}
-
 	for (size_t i = 0, n = _updateCache.size(); i < n; ++i) {
 		_updateCache[i]->update();
 	}
 }
 
+void Skeleton::updateWorldTransform(Bone* parent) {
+    // Apply the parent bone transform to the root bone. The root bone always inherits scale, rotation and reflection.
+    Bone& rootBone = *getRootBone();
+    float pa = parent->_a, pb = parent->_b, pc = parent->_c, pd = parent->_d;
+    rootBone._worldX = pa * _x + pb * _y + parent->_worldX;
+    rootBone._worldY = pc * _x + pd * _y + parent->_worldY;
+
+    float rotationY = rootBone._rotation + 90 + rootBone._shearY;
+    float la = MathUtil::cosDeg(rootBone._rotation + rootBone._shearX) * rootBone._scaleX;
+    float lb = MathUtil::cosDeg(rotationY) * rootBone._scaleY;
+    float lc = MathUtil::sinDeg(rootBone._rotation + rootBone._shearX) * rootBone._scaleX;
+    float ld = MathUtil::sinDeg(rotationY) * rootBone._scaleY;
+    rootBone._a = (pa * la + pb * lc) * _scaleX;
+    rootBone._b = (pa * lb + pb * ld) * _scaleX;
+    rootBone._c = (pc * la + pd * lc) * _scaleY;
+    rootBone._d = (pc * lb + pd * ld) * _scaleY;
+
+    // Update everything except root bone.
+    Bone *rb = getRootBone();
+    for (size_t i = 0, n = _updateCache.size(); i < n; i++) {
+        Updatable *updatable = _updateCache[i];
+        if (updatable != rb) updatable->update();
+    }
+}
+
 void Skeleton::setToSetupPose() {
 	setBonesToSetupPose();
 	setSlotsToSetupPose();
@@ -257,10 +268,12 @@ void Skeleton::setBonesToSetupPose() {
 		TransformConstraint &constraint = *constraintP;
 		TransformConstraintData &constraintData = constraint._data;
 
-		constraint._rotateMix = constraintData._rotateMix;
-		constraint._translateMix = constraintData._translateMix;
-		constraint._scaleMix = constraintData._scaleMix;
-		constraint._shearMix = constraintData._shearMix;
+		constraint._mixRotate = constraintData._mixRotate;
+		constraint._mixX = constraintData._mixX;
+		constraint._mixY = constraintData._mixY;
+		constraint._mixScaleX = constraintData._mixScaleX;
+		constraint._mixScaleY = constraintData._mixScaleY;
+        constraint._mixShearY = constraintData._mixShearY;
 	}
 
 	for (size_t i = 0, n = _pathConstraints.size(); i < n; ++i) {
@@ -270,8 +283,9 @@ void Skeleton::setBonesToSetupPose() {
 
 		constraint._position = constraintData._position;
 		constraint._spacing = constraintData._spacing;
-		constraint._rotateMix = constraintData._rotateMix;
-		constraint._translateMix = constraintData._translateMix;
+		constraint._mixRotate = constraintData._mixRotate;
+        constraint._mixX = constraintData._mixX;
+        constraint._mixY = constraintData._mixY;
 	}
 }
 
@@ -563,15 +577,18 @@ void Skeleton::sortIkConstraint(IkConstraint *constraint) {
 	Bone *parent = constrained[0];
 	sortBone(parent);
 
-	if (constrained.size() > 1) {
-		Bone *child = constrained[constrained.size() - 1];
-		if (!_updateCache.contains(child)) _updateCacheReset.add(child);
-	}
+	if (constrained.size() == 1) {
+	    _updateCache.add(constraint);
+	    sortReset(parent->_children);
+	} else {
+        Bone* child = constrained[constrained.size() - 1];
+        sortBone(child);
 
-	_updateCache.add(constraint);
+        _updateCache.add(constraint);
 
-	sortReset(parent->getChildren());
-	constrained[constrained.size() - 1]->_sorted = true;
+        sortReset(parent->_children);
+        child->_sorted = true;
+	}
 }
 
 void Skeleton::sortPathConstraint(PathConstraint *constraint) {
@@ -617,7 +634,7 @@ void Skeleton::sortTransformConstraint(TransformConstraint *constraint) {
 		for (size_t i = 0; i < boneCount; i++) {
 			Bone *child = constrained[i];
 			sortBone(child->getParent());
-			if (!_updateCache.contains(child)) _updateCacheReset.add(child);
+			sortBone(child);
 		}
 	} else {
 		for (size_t i = 0; i < boneCount; ++i) {

+ 30 - 1
spine-cpp/spine-cpp/src/spine/Timeline.cpp

@@ -39,10 +39,39 @@
 namespace spine {
 RTTI_IMPL_NOPARENT(Timeline)
 
-Timeline::Timeline() {
+Timeline::Timeline(size_t frameCount, size_t frameEntries): _propertyIds(), _frames(), _frameEntries(frameEntries) {
+    _frames.setSize(frameCount * frameEntries, 0);
 }
 
 Timeline::~Timeline() {
 }
 
+Vector<PropertyId> &Timeline::getPropertyIds() {
+    return _propertyIds;
+}
+
+void Timeline::setPropertyIds(PropertyId propertyIds[], size_t propertyIdsCount) {
+    _propertyIds.clear();
+    _propertyIds.ensureCapacity(propertyIdsCount);
+    for (size_t i = 0; i < propertyIdsCount; i++) {
+        _propertyIds.add(propertyIds[i]);
+    }
+}
+
+size_t Timeline::getFrameCount() {
+    return _frames.size() / _frameEntries;
+}
+
+Vector<float> &Timeline::getFrames() {
+    return _frames;
+}
+
+size_t Timeline::getFrameEntries() {
+    return _frameEntries;
+}
+
+float Timeline::getDuration() {
+    return _frames[_frames.size() - getFrameEntries()];
+}
+
 }

+ 102 - 112
spine-cpp/spine-cpp/src/spine/TransformConstraint.cpp

@@ -45,18 +45,14 @@ RTTI_IMPL(TransformConstraint, Updatable)
 
 TransformConstraint::TransformConstraint(TransformConstraintData &data, Skeleton &skeleton) : Updatable(),
 	_data(data),
-	_target(skeleton.findBone(
-	data.getTarget()->getName())),
-	_rotateMix(
-	data.getRotateMix()),
-	_translateMix(
-	data.getTranslateMix()),
-	_scaleMix(
-	data.getScaleMix()),
-	_shearMix(
-	data.getShearMix()),
-	_active(false)
-{
+	_target(skeleton.findBone(data.getTarget()->getName())),
+	_mixRotate(data.getMixRotate()),
+	_mixX(data.getMixX()),
+    _mixY(data.getMixY()),
+	_mixScaleX(data.getMixScaleX()),
+    _mixScaleY(data.getMixScaleY()),
+    _mixShearY(data.getMixShearY()),
+	_active(false) {
 	_bones.ensureCapacity(_data.getBones().size());
 	for (size_t i = 0; i < _data.getBones().size(); ++i) {
 		BoneData *boneData = _data.getBones()[i];
@@ -64,11 +60,9 @@ TransformConstraint::TransformConstraint(TransformConstraintData &data, Skeleton
 	}
 }
 
-void TransformConstraint::apply() {
-	update();
-}
-
 void TransformConstraint::update() {
+	if (_mixRotate == 0 && _mixX == 0 && _mixY == 0 && _mixScaleX == 0 && _mixScaleX == 0 && _mixShearY == 0) return;
+
 	if (_data.isLocal()) {
 		if (_data.isRelative())
 			applyRelativeLocal();
@@ -102,40 +96,57 @@ void TransformConstraint::setTarget(Bone *inValue) {
 	_target = inValue;
 }
 
-float TransformConstraint::getRotateMix() {
-	return _rotateMix;
+float TransformConstraint::getMixRotate() {
+	return _mixRotate;
+}
+
+void TransformConstraint::setMixRotate(float inValue) {
+	_mixRotate = inValue;
+}
+
+float TransformConstraint::getMixX() {
+	return _mixX;
+}
+
+void TransformConstraint::setMixX(float inValue) {
+	_mixX = inValue;
+}
+
+float TransformConstraint::getMixY() {
+	return _mixY;
 }
 
-void TransformConstraint::setRotateMix(float inValue) {
-	_rotateMix = inValue;
+void TransformConstraint::setMixY(float inValue) {
+	_mixY = inValue;
 }
 
-float TransformConstraint::getTranslateMix() {
-	return _translateMix;
+void TransformConstraint::setMixScaleX(float inValue) {
+	_mixScaleX = inValue;
 }
 
-void TransformConstraint::setTranslateMix(float inValue) {
-	_translateMix = inValue;
+float TransformConstraint::getMixScaleX() {
+	return _mixScaleX;
 }
 
-float TransformConstraint::getScaleMix() {
-	return _scaleMix;
+float TransformConstraint::getMixScaleY() {
+	return _mixScaleY;
 }
 
-void TransformConstraint::setScaleMix(float inValue) {
-	_scaleMix = inValue;
+void TransformConstraint::setMixScaleY(float inValue) {
+	_mixScaleY = inValue;
 }
 
-float TransformConstraint::getShearMix() {
-	return _shearMix;
+float TransformConstraint::getMixShearY() {
+	return _mixShearY;
 }
 
-void TransformConstraint::setShearMix(float inValue) {
-	_shearMix = inValue;
+void TransformConstraint::setMixShearY(float inValue) {
+	_mixShearY = inValue;
 }
 
 void TransformConstraint::applyAbsoluteWorld() {
-	float rotateMix = _rotateMix, translateMix = _translateMix, scaleMix = _scaleMix, shearMix = _shearMix;
+	float mixRotate = _mixRotate, mixX = _mixX, mixY = _mixY, mixScaleX = _mixScaleX, mixScaleY = _mixScaleY, mixShearY = _mixShearY;
+	bool translate = mixX != 0 || mixY != 0;
 	Bone &target = *_target;
 	float ta = target._a, tb = target._b, tc = target._c, td = target._d;
 	float degRadReflect = ta * td - tb * tc > 0 ? MathUtil::Deg_Rad : -MathUtil::Deg_Rad;
@@ -145,9 +156,7 @@ void TransformConstraint::applyAbsoluteWorld() {
 		Bone *item = _bones[i];
 		Bone &bone = *item;
 
-		bool modified = false;
-
-		if (rotateMix != 0) {
+		if (mixRotate != 0) {
 			float a = bone._a, b = bone._b, c = bone._c, d = bone._d;
 			float r = MathUtil::atan2(tc, ta) - MathUtil::atan2(c, a) + offsetRotation;
 			if (r > MathUtil::Pi)
@@ -155,38 +164,36 @@ void TransformConstraint::applyAbsoluteWorld() {
 			else if (r < -MathUtil::Pi)
 				r += MathUtil::Pi_2;
 
-			r *= rotateMix;
+			r *= mixRotate;
 			float cos = MathUtil::cos(r), sin = MathUtil::sin(r);
 			bone._a = cos * a - sin * c;
 			bone._b = cos * b - sin * d;
 			bone._c = sin * a + cos * c;
 			bone._d = sin * b + cos * d;
-			modified = true;
 		}
 
-		if (translateMix != 0) {
+		if (translate) {
 			float tx, ty;
 			target.localToWorld(_data._offsetX, _data._offsetY, tx, ty);
-			bone._worldX += (tx - bone._worldX) * translateMix;
-			bone._worldY += (ty - bone._worldY) * translateMix;
-			modified = true;
+			bone._worldX += (tx - bone._worldX) * mixX;
+			bone._worldY += (ty - bone._worldY) * mixY;
 		}
 
-		if (scaleMix > 0) {
+		if (mixScaleX > 0) {
 			float s = MathUtil::sqrt(bone._a * bone._a + bone._c * bone._c);
-
-			if (s > 0.00001f) s = (s + (MathUtil::sqrt(ta * ta + tc * tc) - s + _data._offsetScaleX) * scaleMix) / s;
+			if (s != 0) s = (s + (MathUtil::sqrt(ta * ta + tc * tc) - s + _data._offsetScaleX) * mixScaleX) / s;
 			bone._a *= s;
 			bone._c *= s;
-			s = MathUtil::sqrt(bone._b * bone._b + bone._d * bone._d);
-
-			if (s > 0.00001f) s = (s + (MathUtil::sqrt(tb * tb + td * td) - s + _data._offsetScaleY) * scaleMix) / s;
-			bone._b *= s;
-			bone._d *= s;
-			modified = true;
 		}
 
-		if (shearMix > 0) {
+        if (mixScaleY > 0) {
+            float s = MathUtil::sqrt(bone._b * bone._b + bone._d * bone._d);
+            if (s != 0) s = (s + (MathUtil::sqrt(tb * tb + td * td) - s + _data._offsetScaleY) * mixScaleY) / s;
+            bone._b *= s;
+            bone._d *= s;
+        }
+
+		if (mixShearY > 0) {
 			float b = bone._b, d = bone._d;
 			float by = MathUtil::atan2(d, b);
 			float r = MathUtil::atan2(td, tb) - MathUtil::atan2(tc, ta) - (by - MathUtil::atan2(bone._c, bone._a));
@@ -195,19 +202,19 @@ void TransformConstraint::applyAbsoluteWorld() {
 			else if (r < -MathUtil::Pi)
 				r += MathUtil::Pi_2;
 
-			r = by + (r + offsetShearY) * shearMix;
+			r = by + (r + offsetShearY) * mixShearY;
 			float s = MathUtil::sqrt(b * b + d * d);
 			bone._b = MathUtil::cos(r) * s;
 			bone._d = MathUtil::sin(r) * s;
-			modified = true;
 		}
 
-		if (modified) bone._appliedValid = false;
+		bone._appliedValid = false;
 	}
 }
 
 void TransformConstraint::applyRelativeWorld() {
-	float rotateMix = _rotateMix, translateMix = _translateMix, scaleMix = _scaleMix, shearMix = _shearMix;
+    float mixRotate = _mixRotate, mixX = _mixX, mixY = _mixY, mixScaleX = _mixScaleX, mixScaleY = _mixScaleY, mixShearY = _mixShearY;
+    bool translate = mixX != 0 || mixY != 0;
 	Bone &target = *_target;
 	float ta = target._a, tb = target._b, tc = target._c, td = target._d;
 	float degRadReflect = ta * td - tb * tc > 0 ? MathUtil::Deg_Rad : -MathUtil::Deg_Rad;
@@ -216,9 +223,7 @@ void TransformConstraint::applyRelativeWorld() {
 		Bone *item = _bones[i];
 		Bone &bone = *item;
 
-		bool modified = false;
-
-		if (rotateMix != 0) {
+		if (mixRotate != 0) {
 			float a = bone._a, b = bone._b, c = bone._c, d = bone._d;
 			float r = MathUtil::atan2(tc, ta) + offsetRotation;
 			if (r > MathUtil::Pi)
@@ -226,34 +231,33 @@ void TransformConstraint::applyRelativeWorld() {
 			else if (r < -MathUtil::Pi)
 				r += MathUtil::Pi_2;
 
-			r *= rotateMix;
+			r *= mixRotate;
 			float cos = MathUtil::cos(r), sin = MathUtil::sin(r);
 			bone._a = cos * a - sin * c;
 			bone._b = cos * b - sin * d;
 			bone._c = sin * a + cos * c;
 			bone._d = sin * b + cos * d;
-			modified = true;
 		}
 
-		if (translateMix != 0) {
+		if (translate) {
 			float tx, ty;
 			target.localToWorld(_data._offsetX, _data._offsetY, tx, ty);
-			bone._worldX += tx * translateMix;
-			bone._worldY += ty * translateMix;
-			modified = true;
+			bone._worldX += tx * mixX;
+			bone._worldY += ty * mixY;
 		}
 
-		if (scaleMix > 0) {
-			float s = (MathUtil::sqrt(ta * ta + tc * tc) - 1 + _data._offsetScaleX) * scaleMix + 1;
-			bone._a *= s;
-			bone._c *= s;
-			s = (MathUtil::sqrt(tb * tb + td * td) - 1 + _data._offsetScaleY) * scaleMix + 1;
-			bone._b *= s;
-			bone._d *= s;
-			modified = true;
-		}
-
-		if (shearMix > 0) {
+        if (mixScaleX != 0) {
+            float s = (MathUtil::sqrt(ta * ta + tc * tc) - 1 + _data._offsetScaleX) * mixScaleX + 1;
+            bone._a *= s;
+            bone._c *= s;
+        }
+        if (mixScaleY != 0) {
+            float s = (MathUtil::sqrt(tb * tb + td * td) - 1 + _data._offsetScaleY) * mixScaleY + 1;
+            bone._b *= s;
+            bone._d *= s;
+        }
+
+		if (mixShearY > 0) {
 			float r = MathUtil::atan2(td, tb) - MathUtil::atan2(tc, ta);
 			if (r > MathUtil::Pi)
 				r -= MathUtil::Pi_2;
@@ -261,19 +265,18 @@ void TransformConstraint::applyRelativeWorld() {
 				r += MathUtil::Pi_2;
 
 			float b = bone._b, d = bone._d;
-			r = MathUtil::atan2(d, b) + (r - MathUtil::Pi / 2 + offsetShearY) * shearMix;
+			r = MathUtil::atan2(d, b) + (r - MathUtil::Pi / 2 + offsetShearY) * mixShearY;
 			float s = MathUtil::sqrt(b * b + d * d);
 			bone._b = MathUtil::cos(r) * s;
 			bone._d = MathUtil::sin(r) * s;
-			modified = true;
 		}
 
-		if (modified) bone._appliedValid = false;
+		bone._appliedValid = false;
 	}
 }
 
 void TransformConstraint::applyAbsoluteLocal() {
-	float rotateMix = _rotateMix, translateMix = _translateMix, scaleMix = _scaleMix, shearMix = _shearMix;
+    float mixRotate = _mixRotate, mixX = _mixX, mixY = _mixY, mixScaleX = _mixScaleX, mixScaleY = _mixScaleY, mixShearY = _mixShearY;
 	Bone &target = *_target;
 	if (!target._appliedValid) target.updateAppliedTransform();
 
@@ -284,29 +287,27 @@ void TransformConstraint::applyAbsoluteLocal() {
 		if (!bone._appliedValid) bone.updateAppliedTransform();
 
 		float rotation = bone._arotation;
-		if (rotateMix != 0) {
+		if (mixRotate != 0) {
 			float r = target._arotation - rotation + _data._offsetRotation;
 			r -= (16384 - (int) (16384.499999999996 - r / 360)) * 360;
-			rotation += r * rotateMix;
+			rotation += r * mixRotate;
 		}
 
 		float x = bone._ax, y = bone._ay;
-		if (translateMix != 0) {
-			x += (target._ax - x + _data._offsetX) * translateMix;
-			y += (target._ay - y + _data._offsetY) * translateMix;
-		}
+		x += (target._ax - x + _data._offsetX) * mixX;
+		y += (target._ay - y + _data._offsetY) * mixY;
 
 		float scaleX = bone._ascaleX, scaleY = bone._ascaleY;
-		if (scaleMix != 0) {
-			if (scaleX > 0.00001f) scaleX = (scaleX + (target._ascaleX - scaleX + _data._offsetScaleX) * scaleMix) / scaleX;
-			if (scaleY > 0.00001f) scaleY = (scaleY + (target._ascaleY - scaleY + _data._offsetScaleY) * scaleMix) / scaleY;
-		}
+        if (mixScaleX != 0 && scaleX != 0)
+            scaleX = (scaleX + (target._ascaleX - scaleX + _data._offsetScaleX) * mixScaleX) / scaleX;
+        if (mixScaleY != 0 && scaleY != 0)
+            scaleY = (scaleY + (target._ascaleY - scaleY + _data._offsetScaleY) * mixScaleY) / scaleY;
 
-		float shearY = bone._ashearY;
-		if (shearMix != 0) {
+        float shearY = bone._ashearY;
+		if (mixShearY != 0) {
 			float r = target._ashearY - shearY + _data._offsetShearY;
 			r -= (16384 - (int) (16384.499999999996 - r / 360)) * 360;
-			bone._shearY += r * shearMix;
+			bone._shearY += r * mixShearY;
 		}
 
 		bone.updateWorldTransform(x, y, rotation, scaleX, scaleY, bone._ashearX, shearY);
@@ -314,7 +315,7 @@ void TransformConstraint::applyAbsoluteLocal() {
 }
 
 void TransformConstraint::applyRelativeLocal() {
-	float rotateMix = _rotateMix, translateMix = _translateMix, scaleMix = _scaleMix, shearMix = _shearMix;
+    float mixRotate = _mixRotate, mixX = _mixX, mixY = _mixY, mixScaleX = _mixScaleX, mixScaleY = _mixScaleY, mixShearY = _mixShearY;
 	Bone &target = *_target;
 	if (!target._appliedValid) target.updateAppliedTransform();
 
@@ -324,25 +325,14 @@ void TransformConstraint::applyRelativeLocal() {
 
 		if (!bone._appliedValid) bone.updateAppliedTransform();
 
-		float rotation = bone._arotation;
-		if (rotateMix != 0) rotation += (target._arotation + _data._offsetRotation) * rotateMix;
+        float rotation = bone._arotation + (target._arotation + _data._offsetRotation) * mixRotate;
+        float x = bone._ax + (target._ax + _data._offsetX) * mixX;
+        float y = bone._ay + (target._ay + _data._offsetY) * mixY;
+        float scaleX = (bone._ascaleX * ((target._ascaleX - 1 + _data._offsetScaleX) * mixScaleX) + 1);
+        float scaleY = (bone._ascaleY * ((target._ascaleY - 1 + _data._offsetScaleY) * mixScaleY) + 1);
+        float shearY = bone._ashearY + (target._ashearY + _data._offsetShearY) * mixShearY;
 
-		float x = bone._ax, y = bone._ay;
-		if (translateMix != 0) {
-			x += (target._ax + _data._offsetX) * translateMix;
-			y += (target._ay + _data._offsetY) * translateMix;
-		}
-
-		float scaleX = bone._ascaleX, scaleY = bone._ascaleY;
-		if (scaleMix != 0) {
-			if (scaleX > 0.00001f) scaleX *= ((target._ascaleX - 1 + _data._offsetScaleX) * scaleMix) + 1;
-			if (scaleY > 0.00001f) scaleY *= ((target._ascaleY - 1 + _data._offsetScaleY) * scaleMix) + 1;
-		}
-
-		float shearY = bone._ashearY;
-		if (shearMix != 0) shearY += (target._ashearY + _data._offsetShearY) * shearMix;
-
-		bone.updateWorldTransform(x, y, rotation, scaleX, scaleY, bone._ashearX, shearY);
+        bone.updateWorldTransform(x, y, rotation, scaleX, scaleY, bone._ashearX, shearY);
 	}
 }
 

+ 22 - 12
spine-cpp/spine-cpp/src/spine/TransformConstraintData.cpp

@@ -41,10 +41,12 @@ using namespace spine;
 TransformConstraintData::TransformConstraintData(const String &name) :
 		ConstraintData(name),
 		_target(NULL),
-		_rotateMix(0),
-		_translateMix(0),
-		_scaleMix(0),
-		_shearMix(0),
+		_mixRotate(0),
+		_mixX(0),
+		_mixY(0),
+		_mixScaleX(0),
+        _mixScaleY(0),
+        _mixShearY(0),
 		_offsetRotation(0),
 		_offsetX(0),
 		_offsetY(0),
@@ -63,20 +65,28 @@ BoneData *TransformConstraintData::getTarget() {
 	return _target;
 }
 
-float TransformConstraintData::getRotateMix() {
-	return _rotateMix;
+float TransformConstraintData::getMixRotate() {
+	return _mixRotate;
 }
 
-float TransformConstraintData::getTranslateMix() {
-	return _translateMix;
+float TransformConstraintData::getMixX() {
+    return _mixX;
 }
 
-float TransformConstraintData::getScaleMix() {
-	return _scaleMix;
+float TransformConstraintData::getMixY() {
+    return _mixY;
 }
 
-float TransformConstraintData::getShearMix() {
-	return _shearMix;
+float TransformConstraintData::getMixScaleX() {
+    return _mixScaleX;
+}
+
+float TransformConstraintData::getMixScaleY() {
+    return _mixScaleY;
+}
+
+float TransformConstraintData::getMixShearY() {
+    return _mixShearY;
 }
 
 float TransformConstraintData::getOffsetRotation() {

+ 1 - 1
spine-cpp/spine-cpp/src/spine/TransformConstraintTimeline.cpp

@@ -37,7 +37,7 @@
 #include <spine/Event.h>
 
 #include <spine/Animation.h>
-#include <spine/TimelineType.h>
+#include <spine/Property.h>
 #include <spine/Slot.h>
 #include <spine/SlotData.h>
 #include <spine/TransformConstraint.h>

+ 150 - 69
spine-cpp/spine-cpp/src/spine/TranslateTimeline.cpp

@@ -43,18 +43,12 @@
 
 using namespace spine;
 
-RTTI_IMPL(TranslateTimeline, CurveTimeline)
-
-const int TranslateTimeline::ENTRIES = 3;
-const int TranslateTimeline::PREV_TIME = -3;
-const int TranslateTimeline::PREV_X = -2;
-const int TranslateTimeline::PREV_Y = -1;
-const int TranslateTimeline::X = 1;
-const int TranslateTimeline::Y = 2;
-
-TranslateTimeline::TranslateTimeline(int frameCount) : CurveTimeline(frameCount), _boneIndex(0) {
-	_frames.ensureCapacity(frameCount * ENTRIES);
-	_frames.setSize(frameCount * ENTRIES, 0);
+RTTI_IMPL(TranslateTimeline, CurveTimeline2)
+
+TranslateTimeline::TranslateTimeline(size_t frameCount, size_t bezierCount, int boneIndex) : CurveTimeline2(frameCount, bezierCount), _boneIndex(boneIndex) {
+    PropertyId ids[] = { ((PropertyId)Property_X << 32) | boneIndex,
+                         ((PropertyId)Property_Y << 32) | boneIndex};
+    setPropertyIds(ids, 2);
 }
 
 TranslateTimeline::~TranslateTimeline() {
@@ -67,65 +61,152 @@ void TranslateTimeline::apply(Skeleton &skeleton, float lastTime, float time, Ve
 	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._x = bone._data._x;
-			bone._y = bone._data._y;
-			return;
-		case MixBlend_First:
-			bone._x += (bone._data._x - bone._x) * alpha;
-			bone._y += (bone._data._y - bone._y) * 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 += (_frames[frame + X] - x) * percent;
-		y += (_frames[frame + Y] - y) * percent;
-	}
-
-	switch (blend) {
-	case MixBlend_Setup:
-		bone._x = bone._data._x + x * alpha;
-		bone._y = bone._data._y + y * alpha;
-		break;
-	case MixBlend_First:
-	case MixBlend_Replace:
-		bone._x += (bone._data._x + x - bone._x) * alpha;
-		bone._y += (bone._data._y + y - bone._y) * alpha;
-		break;
-	case MixBlend_Add:
-		bone._x += x * alpha;
-		bone._y += y * alpha;
-	}
+    Bone* bone = skeleton._bones[_boneIndex];
+    if (!bone->_active) return;
+
+    if (time < _frames[0]) {
+        switch (blend) {
+            case MixBlend_Setup:
+                bone->_x = bone->_data._x;
+                bone->_y = bone->_data._y;
+                return;
+            case MixBlend_First:
+                bone->_x += (bone->_data._x - bone->_x) * alpha;
+                bone->_y += (bone->_data._y - bone->_y) * alpha;
+            default: {}
+        }
+        return;
+    }
+
+    float x = 0, y = 0;
+    int i = Animation::search(_frames, time, CurveTimeline2::ENTRIES);
+    int curveType = (int)_curves[i / CurveTimeline2::ENTRIES];
+    switch (curveType) {
+        case CurveTimeline::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 CurveTimeline::STEPPED: {
+            x = _frames[i + CurveTimeline2::VALUE1];
+            y = _frames[i + CurveTimeline2::VALUE2];
+            break;
+        }
+        default: {
+            x = getBezierValue(time, i, CurveTimeline2::VALUE1, curveType - CurveTimeline::BEZIER);
+            y = getBezierValue(time, i, CurveTimeline2::VALUE2,
+                                    curveType + CurveTimeline::BEZIER_SIZE - CurveTimeline::BEZIER);
+        }
+    }
+
+    switch (blend) {
+        case MixBlend_Setup:
+            bone->_x = bone->_data._x + x * alpha;
+            bone->_y = bone->_data._y + y * alpha;
+            break;
+        case MixBlend_First:
+        case MixBlend_Replace:
+            bone->_x += (bone->_data._x + x - bone->_x) * alpha;
+            bone->_y += (bone->_data._y + y - bone->_y) * alpha;
+            break;
+        case MixBlend_Add:
+            bone->_x += x * alpha;
+            bone->_y += y * alpha;
+    }
+}
+
+RTTI_IMPL(TranslateXTimeline, CurveTimeline1)
+
+TranslateXTimeline::TranslateXTimeline(size_t frameCount, size_t bezierCount, int boneIndex) : CurveTimeline1(frameCount, bezierCount), _boneIndex(boneIndex) {
+    PropertyId ids[] = { ((PropertyId)Property_X << 32) | boneIndex };
+    setPropertyIds(ids, 1);
 }
 
-int TranslateTimeline::getPropertyId() {
-	return ((int) TimelineType_Translate << 24) + _boneIndex;
+TranslateXTimeline::~TranslateXTimeline() {
+}
+
+void TranslateXTimeline::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->_x = bone->_data._x;
+                return;
+            case MixBlend_First:
+                bone->_x += (bone->_data._x - bone->_x) * alpha;
+            default: {}
+        }
+        return;
+    }
+
+    float x = getCurveValue(time);
+    switch (blend) {
+        case MixBlend_Setup:
+            bone->_x = bone->_data._x + x * alpha;
+            break;
+        case MixBlend_First:
+        case MixBlend_Replace:
+            bone->_x += (bone->_data._x + x - bone->_x) * alpha;
+            break;
+        case MixBlend_Add:
+            bone->_x += x * alpha;
+    }
 }
 
-void TranslateTimeline::setFrame(int frameIndex, float time, float x, float y) {
-	frameIndex *= ENTRIES;
-	_frames[frameIndex] = time;
-	_frames[frameIndex + X] = x;
-	_frames[frameIndex + Y] = y;
+RTTI_IMPL(TranslateYTimeline, CurveTimeline1)
+
+TranslateYTimeline::TranslateYTimeline(size_t frameCount, size_t bezierCount, int boneIndex) : CurveTimeline1(frameCount, bezierCount), _boneIndex(boneIndex) {
+    PropertyId ids[] = { ((PropertyId)Property_Y << 32) | boneIndex };
+    setPropertyIds(ids, 1);
+}
+
+TranslateYTimeline::~TranslateYTimeline() {
+}
+
+void TranslateYTimeline::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->_y = bone->_data._y;
+                return;
+            case MixBlend_First:
+                bone->_y += (bone->_data._y - bone->_y) * alpha;
+            default: {}
+        }
+        return;
+    }
+
+    float y = getCurveValue(time);
+    switch (blend) {
+        case MixBlend_Setup:
+            bone->_y = bone->_data._y + y * alpha;
+            break;
+        case MixBlend_First:
+        case MixBlend_Replace:
+            bone->_y += (bone->_data._y + y - bone->_y) * alpha;
+            break;
+        case MixBlend_Add:
+            bone->_y += y * alpha;
+    }
 }

+ 1 - 1
spine-cpp/spine-cpp/src/spine/TwoColorTimeline.cpp

@@ -38,7 +38,7 @@
 
 #include <spine/Animation.h>
 #include <spine/Bone.h>
-#include <spine/TimelineType.h>
+#include <spine/Property.h>
 #include <spine/Slot.h>
 #include <spine/SlotData.h>
 

+ 1 - 2
spine-cpp/spine-cpp/src/spine/VertexAttachment.cpp

@@ -157,8 +157,7 @@ void VertexAttachment::setDeformAttachment(VertexAttachment* attachment) {
 
 int VertexAttachment::getNextID() {
 	static int nextID = 0;
-
-	return (nextID++ & 65535) << 11;
+	return nextID++;
 }
 
 void VertexAttachment::copyTo(VertexAttachment* other) {

+ 1 - 1
spine-sfml/cpp/CMakeLists.txt

@@ -1,6 +1,6 @@
 cmake_minimum_required(VERSION 2.8.9)
 #
-# First download and extract SFML 2.3.2 for the respective OS we are on
+# First download and extract SFML 2.4.1 for the respective OS we are on
 #
 set(DEPS_DIR "${CMAKE_CURRENT_LIST_DIR}/dependencies/")
 if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")