Преглед изворни кода

[cpp] Ported skin bones/constraints changes. See #1346.

badlogic пре 6 година
родитељ
комит
29322c5c51
49 измењених фајлова са 378 додато и 150 уклоњено
  1. 5 0
      spine-cpp/spine-cpp/include/spine/Bone.h
  2. 4 0
      spine-cpp/spine-cpp/include/spine/BoneData.h
  3. 17 6
      spine-cpp/spine-cpp/include/spine/ConstraintData.h
  4. 1 0
      spine-cpp/spine-cpp/include/spine/Debug.h
  5. 7 2
      spine-cpp/spine-cpp/include/spine/IkConstraint.h
  6. 2 9
      spine-cpp/spine-cpp/include/spine/IkConstraintData.h
  7. 2 0
      spine-cpp/spine-cpp/include/spine/Json.h
  8. 8 2
      spine-cpp/spine-cpp/include/spine/PathConstraint.h
  9. 2 8
      spine-cpp/spine-cpp/include/spine/PathConstraintData.h
  10. 3 1
      spine-cpp/spine-cpp/include/spine/SkeletonBinary.h
  11. 1 0
      spine-cpp/spine-cpp/include/spine/SkeletonData.h
  12. 7 0
      spine-cpp/spine-cpp/include/spine/Skin.h
  13. 7 2
      spine-cpp/spine-cpp/include/spine/TransformConstraint.h
  14. 2 5
      spine-cpp/spine-cpp/include/spine/TransformConstraintData.h
  15. 4 0
      spine-cpp/spine-cpp/include/spine/Updatable.h
  16. 1 1
      spine-cpp/spine-cpp/include/spine/spine.h
  17. 1 0
      spine-cpp/spine-cpp/src/spine/AnimationState.cpp
  18. 3 0
      spine-cpp/spine-cpp/src/spine/AttachmentTimeline.cpp
  19. 10 1
      spine-cpp/spine-cpp/src/spine/Bone.cpp
  20. 10 1
      spine-cpp/spine-cpp/src/spine/BoneData.cpp
  21. 2 0
      spine-cpp/spine-cpp/src/spine/ColorTimeline.cpp
  22. 22 4
      spine-cpp/spine-cpp/src/spine/ConstraintData.cpp
  23. 2 0
      spine-cpp/spine-cpp/src/spine/DeformTimeline.cpp
  24. 13 3
      spine-cpp/spine-cpp/src/spine/IkConstraint.cpp
  25. 1 14
      spine-cpp/spine-cpp/src/spine/IkConstraintData.cpp
  26. 2 0
      spine-cpp/spine-cpp/src/spine/IkConstraintTimeline.cpp
  27. 12 0
      spine-cpp/spine-cpp/src/spine/Json.cpp
  28. 12 3
      spine-cpp/spine-cpp/src/spine/PathConstraint.cpp
  29. 1 15
      spine-cpp/spine-cpp/src/spine/PathConstraintData.cpp
  30. 2 0
      spine-cpp/spine-cpp/src/spine/PathConstraintMixTimeline.cpp
  31. 2 0
      spine-cpp/spine-cpp/src/spine/PathConstraintPositionTimeline.cpp
  32. 2 0
      spine-cpp/spine-cpp/src/spine/PathConstraintSpacingTimeline.cpp
  33. 1 1
      spine-cpp/spine-cpp/src/spine/RegionAttachment.cpp
  34. 1 0
      spine-cpp/spine-cpp/src/spine/RotateTimeline.cpp
  35. 2 0
      spine-cpp/spine-cpp/src/spine/ScaleTimeline.cpp
  36. 1 0
      spine-cpp/spine-cpp/src/spine/ShearTimeline.cpp
  37. 28 3
      spine-cpp/spine-cpp/src/spine/Skeleton.cpp
  38. 53 32
      spine-cpp/spine-cpp/src/spine/SkeletonBinary.cpp
  39. 3 0
      spine-cpp/spine-cpp/src/spine/SkeletonData.cpp
  40. 75 18
      spine-cpp/spine-cpp/src/spine/SkeletonJson.cpp
  41. 21 0
      spine-cpp/spine-cpp/src/spine/Skin.cpp
  42. 13 3
      spine-cpp/spine-cpp/src/spine/TransformConstraint.cpp
  43. 1 11
      spine-cpp/spine-cpp/src/spine/TransformConstraintData.cpp
  44. 1 0
      spine-cpp/spine-cpp/src/spine/TransformConstraintTimeline.cpp
  45. 1 0
      spine-cpp/spine-cpp/src/spine/TranslateTimeline.cpp
  46. 2 0
      spine-cpp/spine-cpp/src/spine/TwoColorTimeline.cpp
  47. 1 1
      spine-sfml/c/src/spine/spine-sfml.cpp
  48. 2 2
      spine-sfml/cpp/example/main.cpp
  49. 2 2
      spine-sfml/cpp/src/spine/spine-sfml.cpp

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

@@ -223,6 +223,10 @@ public:
 	bool isAppliedValid();
 	void setAppliedValid(bool valid);
 
+	bool isActive();
+
+	void setActive(bool inValue);
+
 private:
 	static bool yDown;
 
@@ -236,6 +240,7 @@ private:
 	float _a, _b, _worldX;
 	float _c, _d, _worldY;
 	bool _sorted;
+	bool _active;
 
 	/// Computes the individual applied transform values from the world transform. This can be useful to perform processing using
 	/// the applied transform after the world transform has been modified directly (eg, by a constraint)..

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

@@ -106,6 +106,9 @@ public:
 
 	void setTransformMode(TransformMode inValue);
 
+	bool isSkinRequired();
+	void setSkinRequired(bool inValue);
+
 private:
 	const int _index;
 	const String _name;
@@ -113,6 +116,7 @@ private:
 	float _length;
 	float _x, _y, _rotation, _scaleX, _scaleY, _shearX, _shearY;
 	TransformMode _transformMode;
+	bool _skinRequired;
 };
 }
 

+ 17 - 6
spine-cpp/spine-cpp/include/spine/Constraint.h → spine-cpp/spine-cpp/include/spine/ConstraintData.h

@@ -31,21 +31,32 @@
 #define Spine_Constraint_h
 
 #include <spine/Updatable.h>
+#include <spine/SpineString.h>
 
 namespace spine {
 /// The interface for all constraints.
-class SP_API Constraint : public Updatable {
-RTTI_DECL
+class SP_API ConstraintData : public SpineObject {
 
 public:
-	Constraint();
+	ConstraintData(const String& name);
 
-	virtual ~Constraint();
+	virtual ~ConstraintData();
 
-	virtual void update() = 0;
+	/// The IK constraint's name, which is unique within the skeleton.
+	const String& getName();
 
 	/// The ordinal for the order a skeleton's constraints will be applied.
-	virtual int getOrder() = 0;
+	size_t getOrder();
+	void setOrder(size_t inValue);
+
+	/// Whether the constraint is only active for a specific skin.
+	bool isSkinRequired();
+	void setSkinRequired(bool inValue);
+
+private:
+	const String _name;
+	size_t _order;
+	bool _skinRequired;
 };
 }
 

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

@@ -55,6 +55,7 @@ public:
 
 	void reportLeaks() {
 		for (std::map<void*, Allocation>::iterator it = _allocated.begin(); it != _allocated.end(); it++) {
+			void* addr = it->second.address;
 			printf("\"%s:%i (%zu bytes at %p)\n", it->second.fileName, it->second.line, it->second.size, it->second.address);
 		}
 		printf("allocations: %zu, reallocations: %zu, frees: %zu\n", _allocations, _reallocations, _frees);

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

@@ -30,7 +30,7 @@
 #ifndef Spine_IkConstraint_h
 #define Spine_IkConstraint_h
 
-#include <spine/Constraint.h>
+#include <spine/ConstraintData.h>
 
 #include <spine/Vector.h>
 
@@ -41,7 +41,7 @@ class Skeleton;
 
 class Bone;
 
-class SP_API IkConstraint : public Constraint {
+class SP_API IkConstraint : public Updatable {
 	friend class Skeleton;
 
 	friend class IkConstraintTimeline;
@@ -91,6 +91,10 @@ public:
 
 	void setMix(float inValue);
 
+	bool isActive();
+
+	void setActive(bool inValue);
+
 private:
 	IkConstraintData &_data;
 	Vector<Bone *> _bones;
@@ -99,6 +103,7 @@ private:
 	bool _stretch;
 	float _mix;
 	Bone *_target;
+	bool _active;
 };
 }
 

+ 2 - 9
spine-cpp/spine-cpp/include/spine/IkConstraintData.h

@@ -33,11 +33,12 @@
 #include <spine/Vector.h>
 #include <spine/SpineObject.h>
 #include <spine/SpineString.h>
+#include <spine/ConstraintData.h>
 
 namespace spine {
     class BoneData;
     
-    class SP_API IkConstraintData : public SpineObject {
+    class SP_API IkConstraintData : public ConstraintData {
         friend class SkeletonBinary;
         friend class SkeletonJson;
         friend class IkConstraint;
@@ -47,12 +48,6 @@ namespace spine {
     public:
         explicit IkConstraintData(const String& name);
         
-        /// The IK constraint's name, which is unique within the skeleton.
-        const String& getName();
-
-        size_t getOrder();
-        void setOrder(size_t inValue);
-        
         /// The bones that are constrained by this IK Constraint.
         Vector<BoneData*>& getBones();
         
@@ -77,8 +72,6 @@ namespace spine {
         void setMix(float inValue);
 
     private:
-        const String _name;
-        size_t _order;
         Vector<BoneData*> _bones;
         BoneData* _target;
         int _bendDirection;

+ 2 - 0
spine-cpp/spine-cpp/include/spine/Json.h

@@ -60,6 +60,8 @@ public:
 
 	static int getInt(Json *object, const char *name, int defaultValue);
 
+	static bool getBoolean(Json *object, const char *name, bool defaultValue);
+
 	/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when Json_create() returns 0. 0 when Json_create() succeeds. */
 	static const char *getError();
 

+ 8 - 2
spine-cpp/spine-cpp/include/spine/PathConstraint.h

@@ -30,7 +30,7 @@
 #ifndef Spine_PathConstraint_h
 #define Spine_PathConstraint_h
 
-#include <spine/Constraint.h>
+#include <spine/ConstraintData.h>
 
 #include <spine/Vector.h>
 
@@ -41,7 +41,7 @@ namespace spine {
     class Bone;
     class Slot;
     
-    class SP_API PathConstraint : public Constraint {
+    class SP_API PathConstraint : public Updatable {
         friend class Skeleton;
         friend class PathConstraintMixTimeline;
         friend class PathConstraintPositionTimeline;
@@ -77,6 +77,10 @@ namespace spine {
         void setTarget(Slot* inValue);
         
         PathConstraintData& getData();
+
+		bool isActive();
+
+		void setActive(bool inValue);
         
     private:
         static const float EPSILON;
@@ -95,6 +99,8 @@ namespace spine {
         Vector<float> _curves;
         Vector<float> _lengths;
         Vector<float> _segments;
+
+        bool _active;
         
         Vector<float>& computeWorldPositions(PathAttachment& path, int spacesCount, bool tangents, bool percentPosition, bool percentSpacing);
         

+ 2 - 8
spine-cpp/spine-cpp/include/spine/PathConstraintData.h

@@ -36,12 +36,13 @@
 #include <spine/Vector.h>
 #include <spine/SpineObject.h>
 #include <spine/SpineString.h>
+#include <spine/ConstraintData.h>
 
 namespace spine {
     class BoneData;
     class SlotData;
     
-    class SP_API PathConstraintData : public SpineObject {
+    class SP_API PathConstraintData : public ConstraintData {
         friend class SkeletonBinary;
         friend class SkeletonJson;
         
@@ -53,11 +54,6 @@ namespace spine {
         
     public:
         explicit PathConstraintData(const String& name);
-
-        const String& getName();
-        
-        int getOrder();
-        void setOrder(int inValue);
         
         Vector<BoneData*>& getBones();
         
@@ -89,8 +85,6 @@ namespace spine {
         void setTranslateMix(float inValue);
 
     private:
-        const String _name;
-        int _order;
         Vector<BoneData*> _bones;
         SlotData* _target;
         PositionMode _positionMode;

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

@@ -95,6 +95,8 @@ namespace spine {
         void setError(const char* value1, const char* value2);
         
         char* readString(DataInput* input);
+
+        char* readStringRef(DataInput* input, SkeletonData* skeletonData);
         
         float readFloat(DataInput* input);
         
@@ -110,7 +112,7 @@ namespace spine {
         
         int readVarint(DataInput* input, bool optimizePositive);
         
-        Skin* readSkin(DataInput* input, const String& skinName, SkeletonData* skeletonData, bool nonessential);
+        Skin* readSkin(DataInput* input, bool defaultSkin, SkeletonData* skeletonData, bool nonessential);
         
         Attachment* readAttachment(DataInput* input, Skin* skin, int slotIndex, const String& attachmentName, SkeletonData* skeletonData, bool nonessential);
         

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

@@ -172,6 +172,7 @@ private:
 	float _x, _y, _width, _height;
 	String _version;
 	String _hash;
+	Vector<char*> _strings;
 
 	// Nonessential.
 	float _fps;

+ 7 - 0
spine-cpp/spine-cpp/include/spine/Skin.h

@@ -37,6 +37,8 @@ namespace spine {
 class Attachment;
 
 class Skeleton;
+class BoneData;
+class ConstraintData;
 
 /// Stores attachments by slot index and attachment name.
 /// See SkeletonData::getDefaultSkin, Skeleton::getSkin, and
@@ -142,9 +144,14 @@ public:
 
 	AttachmentMap::Entries getAttachments();
 
+	Vector<BoneData*>& getBones();
+
+	Vector<ConstraintData*>& getConstraints();
 private:
 	const String _name;
 	AttachmentMap _attachments;
+	Vector<BoneData*> _bones;
+	Vector<ConstraintData*> _constraints;
 
 	/// Attach all attachments from this skin if the corresponding attachment from the old skin is currently attached.
 	void attachAll(Skeleton &skeleton, Skin &oldSkin);

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

@@ -30,7 +30,7 @@
 #ifndef Spine_TransformConstraint_h
 #define Spine_TransformConstraint_h
 
-#include <spine/Constraint.h>
+#include <spine/ConstraintData.h>
 
 #include <spine/Vector.h>
 
@@ -39,7 +39,7 @@ namespace spine {
     class Skeleton;
     class Bone;
     
-    class SP_API TransformConstraint : public Constraint {
+    class SP_API TransformConstraint : public Updatable {
         friend class Skeleton;
         friend class TransformConstraintTimeline;
         
@@ -72,12 +72,17 @@ namespace spine {
         
         float getShearMix();
         void setShearMix(float inValue);
+
+		bool isActive();
+
+		void setActive(bool inValue);
         
     private:
         TransformConstraintData& _data;
         Vector<Bone*> _bones;
         Bone* _target;
         float _rotateMix, _translateMix, _scaleMix, _shearMix;
+        bool _active;
         
         void applyAbsoluteWorld();
         

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

@@ -33,11 +33,12 @@
 #include <spine/Vector.h>
 #include <spine/SpineObject.h>
 #include <spine/SpineString.h>
+#include <spine/ConstraintData.h>
 
 namespace spine {
     class BoneData;
     
-    class SP_API TransformConstraintData : public SpineObject {
+    class SP_API TransformConstraintData : public ConstraintData {
         friend class SkeletonBinary;
         friend class SkeletonJson;
         
@@ -48,8 +49,6 @@ namespace spine {
     public:
         explicit TransformConstraintData(const String& name);
 
-        const String& getName();
-        int getOrder();
         Vector<BoneData*>& getBones();
         BoneData* getTarget();
         float getRotateMix();
@@ -68,8 +67,6 @@ namespace spine {
         bool isLocal();
         
     private:
-        const String _name;
-        int _order;
         Vector<BoneData*> _bones;
         BoneData* _target;
         float _rotateMix, _translateMix, _scaleMix, _shearMix;

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

@@ -43,6 +43,10 @@ public:
 	virtual ~Updatable();
 
 	virtual void update() = 0;
+
+	virtual bool isActive() = 0;
+
+	virtual void setActive(bool inValue) = 0;
 };
 }
 

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

@@ -46,7 +46,7 @@
 #include <spine/ClippingAttachment.h>
 #include <spine/Color.h>
 #include <spine/ColorTimeline.h>
-#include <spine/Constraint.h>
+#include <spine/ConstraintData.h>
 #include <spine/ContainerUtil.h>
 #include <spine/CurveTimeline.h>
 #include <spine/Debug.h>

+ 1 - 0
spine-cpp/spine-cpp/src/spine/AnimationState.cpp

@@ -668,6 +668,7 @@ void AnimationState::applyRotateTimeline(RotateTimeline *rotateTimeline, Skeleto
 	}
 
 	Bone *bone = skeleton._bones[rotateTimeline->_boneIndex];
+	if (!bone->isActive()) return;
 	Vector<float>& frames = rotateTimeline->_frames;
 	float r1, r2;
 	if (time < frames[0]) {

+ 3 - 0
spine-cpp/spine-cpp/src/spine/AttachmentTimeline.cpp

@@ -37,6 +37,7 @@
 #include <spine/Event.h>
 
 #include <spine/Animation.h>
+#include <spine/Bone.h>
 #include <spine/TimelineType.h>
 #include <spine/Slot.h>
 #include <spine/SlotData.h>
@@ -67,6 +68,8 @@ void AttachmentTimeline::apply(Skeleton &skeleton, float lastTime, float time, V
 	String *attachmentName;
 	Slot *slotP = skeleton._slots[_slotIndex];
 	Slot &slot = *slotP;
+	if (!slot._bone.isActive()) return;
+
 	if (direction == MixDirection_Out && blend == MixBlend_Setup) {
 		attachmentName = &slot._data._attachmentName;
 		slot.setAttachment(attachmentName->length() == 0 ? NULL : skeleton.getAttachment(_slotIndex, *attachmentName));

+ 10 - 1
spine-cpp/spine-cpp/src/spine/Bone.cpp

@@ -75,7 +75,8 @@ Bone::Bone(BoneData &data, Skeleton &skeleton, Bone *parent) : Updatable(),
 															   _c(0),
 															   _d(1),
 															   _worldY(0),
-															   _sorted(false) {
+															   _sorted(false),
+															   _active(false) {
 	setToSetupPose();
 }
 
@@ -538,3 +539,11 @@ void Bone::updateAppliedTransform() {
 		}
 	}
 }
+
+bool Bone::isActive() {
+	return _active;
+}
+
+void Bone::setActive(bool inValue) {
+	_active = inValue;
+}

+ 10 - 1
spine-cpp/spine-cpp/src/spine/BoneData.cpp

@@ -49,7 +49,8 @@ BoneData::BoneData(int index, const String &name, BoneData *parent) :
 		_scaleY(1),
 		_shearX(0),
 		_shearY(0),
-		_transformMode(TransformMode_Normal) {
+		_transformMode(TransformMode_Normal),
+		_skinRequired(false) {
 	assert(index >= 0);
 	assert(_name.length() > 0);
 }
@@ -137,3 +138,11 @@ TransformMode BoneData::getTransformMode() {
 void BoneData::setTransformMode(TransformMode inValue) {
 	_transformMode = inValue;
 }
+
+bool BoneData::isSkinRequired() {
+	return _skinRequired;
+}
+
+void BoneData::setSkinRequired(bool inValue) {
+	_skinRequired = inValue;
+}

+ 2 - 0
spine-cpp/spine-cpp/src/spine/ColorTimeline.cpp

@@ -37,6 +37,7 @@
 #include <spine/Event.h>
 
 #include <spine/Animation.h>
+#include <spine/Bone.h>
 #include <spine/TimelineType.h>
 #include <spine/Slot.h>
 #include <spine/SlotData.h>
@@ -68,6 +69,7 @@ void ColorTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector
 
 	Slot *slotP = skeleton._slots[_slotIndex];
 	Slot &slot = *slotP;
+	if (!slot._bone.isActive()) return;
 	if (time < _frames[0]) {
 		switch (blend) {
 			case MixBlend_Setup:

+ 22 - 4
spine-cpp/spine-cpp/src/spine/Constraint.cpp → spine-cpp/spine-cpp/src/spine/ConstraintData.cpp

@@ -31,14 +31,32 @@
 #include "SpinePluginPrivatePCH.h"
 #endif
 
-#include <spine/Constraint.h>
+#include <spine/ConstraintData.h>
 
 using namespace spine;
 
-RTTI_IMPL(Constraint, Updatable)
+ConstraintData::ConstraintData(const String& name): _name(name), _order(0), _skinRequired(false) {
+}
+
+ConstraintData::~ConstraintData() {
+}
+
+const String& ConstraintData::getName() {
+	return _name;
+}
+
+size_t ConstraintData::getOrder() {
+	return _order;
+}
+
+void ConstraintData::setOrder(size_t inValue) {
+	_order = inValue;
+}
 
-Constraint::Constraint() {
+bool ConstraintData::isSkinRequired() {
+	return _skinRequired;
 }
 
-Constraint::~Constraint() {
+void ConstraintData::setSkinRequired(bool inValue) {
+	_skinRequired = inValue;
 }

+ 2 - 0
spine-cpp/spine-cpp/src/spine/DeformTimeline.cpp

@@ -41,6 +41,7 @@
 #include <spine/Animation.h>
 #include <spine/TimelineType.h>
 #include <spine/Slot.h>
+#include <spine/Bone.h>
 #include <spine/SlotData.h>
 
 using namespace spine;
@@ -67,6 +68,7 @@ void DeformTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vecto
 
 	Slot *slotP = skeleton._slots[_slotIndex];
 	Slot &slot = *slotP;
+	if (!slot._bone.isActive()) return;
 
 	Attachment *slotAttachment = slot.getAttachment();
 	if (slotAttachment == NULL || !slotAttachment->getRTTI().instanceOf(VertexAttachment::rtti)) {

+ 13 - 3
spine-cpp/spine-cpp/src/spine/IkConstraint.cpp

@@ -41,7 +41,7 @@
 
 using namespace spine;
 
-RTTI_IMPL(IkConstraint, Constraint)
+RTTI_IMPL(IkConstraint, Updatable)
 
 void IkConstraint::apply(Bone &bone, float targetX, float targetY, bool compress, bool stretch, bool uniform, float alpha) {
 	Bone *p = bone.getParent();
@@ -207,14 +207,15 @@ void IkConstraint::apply(Bone &parent, Bone &child, float targetX, float targetY
 	}
 }
 
-IkConstraint::IkConstraint(IkConstraintData &data, Skeleton &skeleton) : Constraint(),
+IkConstraint::IkConstraint(IkConstraintData &data, Skeleton &skeleton) : Updatable(),
 																		 _data(data),
 																		 _bendDirection(data.getBendDirection()),
 																		 _compress(data.getCompress()),
 																		 _stretch(data.getStretch()),
 																		 _mix(data.getMix()),
 																		 _target(skeleton.findBone(
-																				 data.getTarget()->getName())) {
+																				 data.getTarget()->getName())),
+																				 _active(false) {
 	_bones.ensureCapacity(_data.getBones().size());
 	for (size_t i = 0; i < _data.getBones().size(); i++) {
 		BoneData *boneData = _data.getBones()[i];
@@ -294,3 +295,12 @@ bool IkConstraint::getCompress() {
 void IkConstraint::setCompress(bool inValue) {
 	_compress = inValue;
 }
+
+bool IkConstraint::isActive() {
+	return _active;
+}
+
+void IkConstraint::setActive(bool inValue) {
+	_active = inValue;
+}
+

+ 1 - 14
spine-cpp/spine-cpp/src/spine/IkConstraintData.cpp

@@ -38,8 +38,7 @@
 using namespace spine;
 
 IkConstraintData::IkConstraintData(const String &name) :
-		_name(name),
-		_order(0),
+		ConstraintData(name),
 		_target(NULL),
 		_bendDirection(1),
 		_compress(false),
@@ -48,18 +47,6 @@ IkConstraintData::IkConstraintData(const String &name) :
 		_mix(1) {
 }
 
-const String &IkConstraintData::getName() {
-	return _name;
-}
-
-size_t IkConstraintData::getOrder() {
-	return _order;
-}
-
-void IkConstraintData::setOrder(size_t inValue) {
-	_order = inValue;
-}
-
 Vector<BoneData *> &IkConstraintData::getBones() {
 	return _bones;
 }

+ 2 - 0
spine-cpp/spine-cpp/src/spine/IkConstraintTimeline.cpp

@@ -69,6 +69,8 @@ void IkConstraintTimeline::apply(Skeleton &skeleton, float lastTime, float time,
 
 	IkConstraint *constraintP = skeleton._ikConstraints[_ikConstraintIndex];
 	IkConstraint &constraint = *constraintP;
+	if (!constraint.isActive()) return;
+
 	if (time < _frames[0]) {
 		switch (blend) {
 			case MixBlend_Setup:

+ 12 - 0
spine-cpp/spine-cpp/src/spine/Json.cpp

@@ -90,6 +90,18 @@ int Json::getInt(Json *value, const char *name, int defaultValue) {
 	return value ? value->_valueInt : defaultValue;
 }
 
+bool Json::getBoolean(spine::Json *value, const char *name, bool defaultValue) {
+	value = getItem(value, name);
+	if (value) {
+		if (value->_valueString) return strcmp(value->_valueString, "true") == 0;
+		if (value->_type == JSON_NULL) return false;
+		if (value->_type == JSON_NUMBER) return value->_valueFloat != 0;
+		return defaultValue;
+	} else {
+		return defaultValue;
+	}
+}
+
 const char *Json::getError() {
 	return _error;
 }

+ 12 - 3
spine-cpp/spine-cpp/src/spine/PathConstraint.cpp

@@ -44,21 +44,22 @@
 
 using namespace spine;
 
-RTTI_IMPL(PathConstraint, Constraint)
+RTTI_IMPL(PathConstraint, Updatable)
 
 const float PathConstraint::EPSILON = 0.00001f;
 const int PathConstraint::NONE = -1;
 const int PathConstraint::BEFORE = -2;
 const int PathConstraint::AFTER = -3;
 
-PathConstraint::PathConstraint(PathConstraintData &data, Skeleton &skeleton) : Constraint(),
+PathConstraint::PathConstraint(PathConstraintData &data, Skeleton &skeleton) : Updatable(),
 																			   _data(data),
 																			   _target(skeleton.findSlot(
 																					   data.getTarget()->getName())),
 																			   _position(data.getPosition()),
 																			   _spacing(data.getSpacing()),
 																			   _rotateMix(data.getRotateMix()),
-																			   _translateMix(data.getTranslateMix()) {
+																			   _translateMix(data.getTranslateMix()),
+																			   _active(false) {
 	_bones.ensureCapacity(_data.getBones().size());
 	for (size_t i = 0; i < _data.getBones().size(); i++) {
 		BoneData *boneData = _data.getBones()[i];
@@ -568,3 +569,11 @@ void PathConstraint::addCurvePosition(float p, float x1, float y1, float cx1, fl
 		}
 	}
 }
+
+bool PathConstraint::isActive() {
+	return _active;
+}
+
+void PathConstraint::setActive(bool inValue) {
+	_active = inValue;
+}

+ 1 - 15
spine-cpp/spine-cpp/src/spine/PathConstraintData.cpp

@@ -41,8 +41,7 @@
 using namespace spine;
 
 PathConstraintData::PathConstraintData(const String &name) :
-		_name(name),
-		_order(0),
+		ConstraintData(name),
 		_target(NULL),
 		_positionMode(PositionMode_Fixed),
 		_spacingMode(SpacingMode_Length),
@@ -52,19 +51,6 @@ PathConstraintData::PathConstraintData(const String &name) :
 		_spacing(0),
 		_rotateMix(0),
 		_translateMix(0) {
-	assert(_name.length() > 0);
-}
-
-const String &PathConstraintData::getName() {
-	return _name;
-}
-
-int PathConstraintData::getOrder() {
-	return _order;
-}
-
-void PathConstraintData::setOrder(int inValue) {
-	_order = inValue;
 }
 
 Vector<BoneData *> &PathConstraintData::getBones() {

+ 2 - 0
spine-cpp/spine-cpp/src/spine/PathConstraintMixTimeline.cpp

@@ -68,6 +68,8 @@ PathConstraintMixTimeline::apply(Skeleton &skeleton, float lastTime, float time,
 
 	PathConstraint *constraintP = skeleton._pathConstraints[_pathConstraintIndex];
 	PathConstraint &constraint = *constraintP;
+	if (!constraint.isActive()) return;
+
 	if (time < _frames[0]) {
 		switch (blend) {
 			case MixBlend_Setup:

+ 2 - 0
spine-cpp/spine-cpp/src/spine/PathConstraintPositionTimeline.cpp

@@ -68,6 +68,8 @@ void PathConstraintPositionTimeline::apply(Skeleton &skeleton, float lastTime, f
 
 	PathConstraint *constraintP = skeleton._pathConstraints[_pathConstraintIndex];
 	PathConstraint &constraint = *constraintP;
+	if (!constraint.isActive()) return;
+
 	if (time < _frames[0]) {
 		switch (blend) {
 			case MixBlend_Setup:

+ 2 - 0
spine-cpp/spine-cpp/src/spine/PathConstraintSpacingTimeline.cpp

@@ -59,6 +59,8 @@ void PathConstraintSpacingTimeline::apply(Skeleton &skeleton, float lastTime, fl
 
 	PathConstraint *constraintP = skeleton._pathConstraints[_pathConstraintIndex];
 	PathConstraint &constraint = *constraintP;
+	if (!constraint.isActive()) return;
+
 	if (time < _frames[0]) {
 		switch (blend) {
 			case MixBlend_Setup:

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

@@ -303,4 +303,4 @@ Attachment* RegionAttachment::copy() {
 	copy->_vertexOffset.clearAndAddAll(_vertexOffset);
 	copy->_color.set(_color);
 	return copy;
-}
+}

+ 1 - 0
spine-cpp/spine-cpp/src/spine/RotateTimeline.cpp

@@ -56,6 +56,7 @@ void RotateTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vecto
 	SP_UNUSED(direction);
 
 	Bone *bone = skeleton.getBones()[_boneIndex];
+	if (!bone->_active) return;
 
 	if (time < _frames[0]) {
 		switch (blend) {

+ 2 - 0
spine-cpp/spine-cpp/src/spine/ScaleTimeline.cpp

@@ -55,6 +55,8 @@ void ScaleTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector
 	Bone *boneP = skeleton._bones[_boneIndex];
 	Bone &bone = *boneP;
 
+	if (!bone._active) return;
+
 	if (time < _frames[0]) {
 		switch (blend) {
 			case MixBlend_Setup:

+ 1 - 0
spine-cpp/spine-cpp/src/spine/ShearTimeline.cpp

@@ -56,6 +56,7 @@ void ShearTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector
 
 	Bone *boneP = skeleton._bones[_boneIndex];
 	Bone &bone = *boneP;
+	if (!bone._active) return;
 
 	if (time < _frames[0]) {
 		switch (blend) {

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

@@ -135,7 +135,21 @@ void Skeleton::updateCache() {
 	_updateCacheReset.clear();
 
 	for (size_t i = 0, n = _bones.size(); i < n; ++i) {
-		_bones[i]->_sorted = false;
+		Bone* bone = _bones[i];
+		bone->_sorted = bone->_data.isSkinRequired();
+		bone->_active = !bone->_sorted;
+	}
+
+	if (_skin) {
+		Vector<BoneData*>& skinBones = _skin->getBones();
+		for (size_t i = 0, n = skinBones.size(); i < n; i++) {
+			Bone* bone = _bones[skinBones[i]->getIndex()];
+			do {
+				bone->_sorted = false;
+				bone->_active = true;
+				bone = bone->_parent;
+			} while (bone);
+		}
 	}
 
 	size_t ikCount = _ikConstraints.size();
@@ -158,7 +172,7 @@ void Skeleton::updateCache() {
 
 		for (size_t ii = 0; ii < transformCount; ++ii) {
 			TransformConstraint *constraint = _transformConstraints[ii];
-			if (constraint->getData().getOrder() == (int)i) {
+			if (constraint->getData().getOrder() == i) {
 				sortTransformConstraint(constraint);
 				i++;
 				goto continue_outer;
@@ -167,7 +181,7 @@ void Skeleton::updateCache() {
 
 		for (size_t ii = 0; ii < pathCount; ++ii) {
 			PathConstraint *constraint = _pathConstraints[ii];
-			if (constraint->getData().getOrder() == (int)i) {
+			if (constraint->getData().getOrder() == i) {
 				sortPathConstraint(constraint);
 				i++;
 				goto continue_outer;
@@ -407,6 +421,7 @@ void Skeleton::getBounds(float &outX, float &outY, float &outWidth, float &outHe
 
 	for (size_t i = 0; i < _drawOrder.size(); ++i) {
 		Slot *slot = _drawOrder[i];
+		if (!slot->_bone._active) continue;
 		size_t verticesLength = 0;
 		Attachment *attachment = slot->getAttachment();
 
@@ -536,6 +551,9 @@ void Skeleton::setScaleY(float inValue) {
 }
 
 void Skeleton::sortIkConstraint(IkConstraint *constraint) {
+	constraint->_active = constraint->_target->_active && (!constraint->_data.isSkinRequired() || (_skin && _skin->_constraints.contains(&constraint->_data)));
+	if (!constraint->_active) return;
+
 	Bone *target = constraint->getTarget();
 	sortBone(target);
 
@@ -555,6 +573,9 @@ void Skeleton::sortIkConstraint(IkConstraint *constraint) {
 }
 
 void Skeleton::sortPathConstraint(PathConstraint *constraint) {
+	constraint->_active = constraint->_target->_bone._active && (!constraint->_data.isSkinRequired() || (_skin && _skin->_constraints.contains(&constraint->_data)));
+	if (!constraint->_active) return;
+
 	Slot *slot = constraint->getTarget();
 	int slotIndex = slot->getData().getIndex();
 	Bone &slotBone = slot->getBone();
@@ -583,6 +604,9 @@ void Skeleton::sortPathConstraint(PathConstraint *constraint) {
 }
 
 void Skeleton::sortTransformConstraint(TransformConstraint *constraint) {
+	constraint->_active = constraint->_target->_active && (!constraint->_data.isSkinRequired() || (_skin && _skin->_constraints.contains(&constraint->_data)));
+	if (!constraint->_active) return;
+
 	sortBone(constraint->getTarget());
 
 	Vector<Bone *> &constrained = constraint->getBones();
@@ -646,6 +670,7 @@ void Skeleton::sortBone(Bone *bone) {
 void Skeleton::sortReset(Vector<Bone *> &bones) {
 	for (size_t i = 0, n = bones.size(); i < n; ++i) {
 		Bone *bone = bones[i];
+		if (!bone->_active) continue;
 		if (bone->_sorted) sortReset(bone->getChildren());
 		bone->_sorted = false;
 	}

+ 53 - 32
spine-cpp/spine-cpp/src/spine/SkeletonBinary.cpp

@@ -144,6 +144,10 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
 		skeletonData->_audioPath.own(readString(input));
 	}
 
+	int numStrings = readVarint(input, true);
+	for (int i = 0; i < numStrings; i++)
+		skeletonData->_strings.add(readString(input));
+
 	/* Bones. */
 	int numBones = readVarint(input, true);
 	skeletonData->_bones.setSize(numBones, 0);
@@ -160,6 +164,7 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
 		data->_shearY = readFloat(input);
 		data->_length = readFloat(input) * _scale;
 		data->_transformMode = static_cast<TransformMode>(readVarint(input, true));
+		data->_skinRequired = readBoolean(input);
 		if (nonessential) {
 			/* Skip bone color. */
 			readInt(input);
@@ -184,7 +189,7 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
 			slotData->getDarkColor().set(r / 255.0f, g / 255.0f, b / 255.0f, 1);
 			slotData->setHasDarkColor(true);
 		}
-		slotData->_attachmentName.own(readString(input));
+		slotData->_attachmentName = readStringRef(input, skeletonData);
 		slotData->_blendMode = static_cast<BlendMode>(readVarint(input, true));
 		skeletonData->_slots[i] = slotData;
 	}
@@ -195,7 +200,8 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
 	for (int i = 0; i < ikConstraintsCount; ++i) {
 		const char *name = readString(input);
 		IkConstraintData *data = new(__FILE__, __LINE__) IkConstraintData(String(name, true));
-		data->_order = readVarint(input, true);
+		data->setOrder(readVarint(input, true));
+		data->setSkinRequired(readBoolean(input));
 		int bonesCount = readVarint(input, true);
 		data->_bones.setSize(bonesCount, 0);
 		for (int ii = 0; ii < bonesCount; ++ii) {
@@ -216,7 +222,8 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
 	for (int i = 0; i < transformConstraintsCount; ++i) {
 		const char *name = readString(input);
 		TransformConstraintData *data = new(__FILE__, __LINE__) TransformConstraintData(String(name, true));
-		data->_order = readVarint(input, true);
+		data->setOrder(readVarint(input, true));
+		data->setSkinRequired(readBoolean(input));
 		int bonesCount = readVarint(input, true);
 		data->_bones.setSize(bonesCount, 0);
 		for (int ii = 0; ii < bonesCount; ++ii) {
@@ -244,7 +251,8 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
 	for (int i = 0; i < pathConstraintsCount; ++i) {
 		const char *name = readString(input);
 		PathConstraintData *data = new(__FILE__, __LINE__) PathConstraintData(String(name, true));
-		data->_order = readVarint(input, true);
+		data->setOrder(readVarint(input, true));
+		data->setSkinRequired(readBoolean(input));
 		int bonesCount = readVarint(input, true);
 		data->_bones.setSize(bonesCount, 0);
 		for (int ii = 0; ii < bonesCount; ++ii) {
@@ -266,20 +274,15 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
 	}
 
 	/* Default skin. */
-	skeletonData->_defaultSkin = readSkin(input, "default", skeletonData, nonessential);
-	int skinsCount = readVarint(input, true);
-	if (skeletonData->_defaultSkin) {
-		++skinsCount;
-	}
-	skeletonData->_skins.setSize(skinsCount, 0);
-	if (skeletonData->_defaultSkin) {
-		skeletonData->_skins[0] = skeletonData->_defaultSkin;
+	Skin* defaultSkin = readSkin(input, true, skeletonData, nonessential);
+	if (defaultSkin) {
+		skeletonData->_defaultSkin = defaultSkin;
+		skeletonData->_skins.add(defaultSkin);
 	}
 
 	/* Skins. */
-	for (size_t i = skeletonData->_defaultSkin ? 1 : 0; i < skeletonData->_skins.size(); ++i) {
-		String skinName(readString(input), true);
-		skeletonData->_skins[i] = readSkin(input, skinName, skeletonData, nonessential);
+	for (size_t i = 0, n = (size_t)readVarint(input, true); i < n; ++i) {
+		skeletonData->_skins.add(readSkin(input, false, skeletonData, nonessential));
 	}
 
 	/* Linked meshes. */
@@ -312,8 +315,8 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
 	int eventsCount = readVarint(input, true);
 	skeletonData->_events.setSize(eventsCount, 0);
 	for (int i = 0; i < eventsCount; ++i) {
-		const char *name = readString(input);
-		EventData *eventData = new(__FILE__, __LINE__) EventData(String(name, true));
+		const char *name = readStringRef(input, skeletonData);
+		EventData *eventData = new(__FILE__, __LINE__) EventData(String(name));
 		eventData->_intValue = readVarint(input, false);
 		eventData->_floatValue = readFloat(input);
 		eventData->_stringValue.own(readString(input));
@@ -381,6 +384,11 @@ char *SkeletonBinary::readString(DataInput *input) {
 	return string;
 }
 
+char* SkeletonBinary::readStringRef(DataInput* input, SkeletonData* skeletonData) {
+	int index = readVarint(input, true);
+	return index == 0 ? nullptr : skeletonData->_strings[index - 1];
+}
+
 float SkeletonBinary::readFloat(DataInput *input) {
 	union {
 		int intValue;
@@ -447,14 +455,27 @@ int SkeletonBinary::readVarint(DataInput *input, bool optimizePositive) {
 }
 
 Skin *
-SkeletonBinary::readSkin(DataInput *input, const String &skinName, SkeletonData *skeletonData, bool nonessential) {
-	int slotCount = readVarint(input, true);
-	if (slotCount == 0) return NULL;
-	Skin *skin = new(__FILE__, __LINE__) Skin(skinName);
-	for (int i = 0; i < slotCount; ++i) {
+SkeletonBinary::readSkin(DataInput *input, bool defaultSkin, SkeletonData *skeletonData, bool nonessential) {
+	Skin *skin = new(__FILE__, __LINE__) Skin(defaultSkin ? "default" : readStringRef(input, skeletonData));
+
+	if (!defaultSkin) {
+		for (int i = 0, n = readVarint(input, true); i < n; i++)
+			skin->getBones().add(skeletonData->_bones[readVarint(input, true)]);
+
+		for (int i = 0, n = readVarint(input, true); i < n; i++)
+			skin->getConstraints().add(skeletonData->_ikConstraints[readVarint(input, true)]);
+
+		for (int i = 0, n = readVarint(input, true); i < n; i++)
+			skin->getConstraints().add(skeletonData->_transformConstraints[readVarint(input, true)]);
+
+		for (int i = 0, n = readVarint(input, true); i < n; i++)
+			skin->getConstraints().add(skeletonData->_pathConstraints[readVarint(input, true)]);
+	}
+
+	for (int i = 0, n = readVarint(input, true); i < n; ++i) {
 		int slotIndex = readVarint(input, true);
 		for (int ii = 0, nn = readVarint(input, true); ii < nn; ++ii) {
-			String name(readString(input), true);
+			String name(readStringRef(input, skeletonData));
 			Attachment *attachment = readAttachment(input, skin, slotIndex, name, skeletonData, nonessential);
 			if (attachment) skin->setAttachment(slotIndex, String(name), attachment);
 		}
@@ -464,13 +485,13 @@ SkeletonBinary::readSkin(DataInput *input, const String &skinName, SkeletonData
 
 Attachment *SkeletonBinary::readAttachment(DataInput *input, Skin *skin, int slotIndex, const String &attachmentName,
 										   SkeletonData *skeletonData, bool nonessential) {
-	String name(readString(input), true);
+	String name(readStringRef(input, skeletonData));
 	if (name.isEmpty()) name = attachmentName;
 
 	AttachmentType type = static_cast<AttachmentType>(readByte(input));
 	switch (type) {
 		case AttachmentType_Region: {
-			String path(readString(input), true);
+			String path(readStringRef(input, skeletonData));
 			if (path.isEmpty()) path = name;
 			RegionAttachment *region = _attachmentLoader->newRegionAttachment(*skin, String(name), String(path));
 			region->_path = path;
@@ -500,7 +521,7 @@ Attachment *SkeletonBinary::readAttachment(DataInput *input, Skin *skin, int slo
 		case AttachmentType_Mesh: {
 			int vertexCount;
 			MeshAttachment *mesh;
-			String path(readString(input), true);
+			String path(readStringRef(input, skeletonData));
 			if (path.isEmpty()) path = name;
 
 			mesh = _attachmentLoader->newMeshAttachment(*skin, String(name), String(path));
@@ -524,14 +545,14 @@ Attachment *SkeletonBinary::readAttachment(DataInput *input, Skin *skin, int slo
 			return mesh;
 		}
 		case AttachmentType_Linkedmesh: {
-			String path(readString(input), true);
+			String path(readStringRef(input, skeletonData));
 			if (path.isEmpty()) path = name;
 
 			MeshAttachment *mesh = _attachmentLoader->newMeshAttachment(*skin, String(name), String(path));
 			mesh->_path = path;
 			readColor(input, mesh->getColor());
-			String skinName(readString(input), true);
-			String parent(readString(input), true);
+			String skinName(readStringRef(input, skeletonData));
+			String parent(readStringRef(input, skeletonData));
 			bool inheritDeform = readBoolean(input);
 			if (nonessential) {
 				mesh->_width = readFloat(input) * _scale;
@@ -661,7 +682,7 @@ Animation *SkeletonBinary::readAnimation(const String &name, DataInput *input, S
 					timeline->_slotIndex = slotIndex;
 					for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
 						float time = readFloat(input);
-						String attachmentName = String(readString(input), true);
+						String attachmentName(readStringRef(input, skeletonData));
 						timeline->setFrame(frameIndex, time, attachmentName);
 					}
 					timelines.add(timeline);
@@ -888,8 +909,8 @@ Animation *SkeletonBinary::readAnimation(const String &name, DataInput *input, S
 		for (int ii = 0, nn = readVarint(input, true); ii < nn; ++ii) {
 			int slotIndex = readVarint(input, true);
 			for (int iii = 0, nnn = readVarint(input, true); iii < nnn; iii++) {
-				const char *attachmentName = readString(input);
-				Attachment *baseAttachment = skin->getAttachment(slotIndex, String(attachmentName, true));
+				const char *attachmentName = readStringRef(input, skeletonData);
+				Attachment *baseAttachment = skin->getAttachment(slotIndex, String(attachmentName));
 
 				if (!baseAttachment) {
 					ContainerUtil::cleanUpVectorOfPointers(timelines);

+ 3 - 0
spine-cpp/spine-cpp/src/spine/SkeletonData.cpp

@@ -71,6 +71,9 @@ SkeletonData::~SkeletonData() {
 	ContainerUtil::cleanUpVectorOfPointers(_ikConstraints);
 	ContainerUtil::cleanUpVectorOfPointers(_transformConstraints);
 	ContainerUtil::cleanUpVectorOfPointers(_pathConstraints);
+	for (size_t i = 0; i < _strings.size(); i++) {
+		SpineExtension::free(_strings[i], __FILE__, __LINE__);
+	}
 }
 
 BoneData *SkeletonData::findBone(const String &boneName) {

+ 75 - 18
spine-cpp/spine-cpp/src/spine/SkeletonJson.cpp

@@ -189,6 +189,7 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
 		if (strcmp(transformMode, "noScaleOrReflection") == 0) {
 			data->_transformMode = TransformMode_NoScaleOrReflection;
 		}
+		data->_skinRequired = Json::getBoolean(boneMap, "skin", false);
 
 		skeletonData->_bones[i] = data;
 		bonesCount++;
@@ -267,7 +268,8 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
 			IkConstraintData *data = new(__FILE__, __LINE__) IkConstraintData(
 					Json::getString(constraintMap, "name", 0));
 
-			data->_order = Json::getInt(constraintMap, "order", 0);
+			data->setOrder(Json::getInt(constraintMap, "order", 0));
+			data->setSkinRequired(Json::getBoolean(constraintMap, "skin", false));
 
 			boneMap = Json::getItem(constraintMap, "bones");
 			data->_bones.ensureCapacity(boneMap->_size);
@@ -311,7 +313,8 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
 			TransformConstraintData *data = new(__FILE__, __LINE__) TransformConstraintData(
 					Json::getString(constraintMap, "name", 0));
 
-			data->_order = Json::getInt(constraintMap, "order", 0);
+			data->setOrder(Json::getInt(constraintMap, "order", 0));
+			data->setSkinRequired(Json::getBoolean(constraintMap, "skin", false));
 
 			boneMap = Json::getItem(constraintMap, "bones");
 			data->_bones.ensureCapacity(boneMap->_size);
@@ -364,7 +367,8 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
 			PathConstraintData *data = new(__FILE__, __LINE__) PathConstraintData(
 					Json::getString(constraintMap, "name", 0));
 
-			data->_order = Json::getInt(constraintMap, "order", 0);
+			data->setOrder(Json::getInt(constraintMap, "order", 0));
+			data->setSkinRequired(Json::getBoolean(constraintMap, "skin", false));
 
 			boneMap = Json::getItem(constraintMap, "bones");
 			data->_bones.ensureCapacity(boneMap->_size);
@@ -438,15 +442,67 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
 			Json *attachmentsMap;
 			Json *curves;
 
-			Skin *skin = new(__FILE__, __LINE__) Skin(skinMap->_name);
+			Skin *skin = new(__FILE__, __LINE__) Skin(Json::getString(skinMap, "name", ""));
+
+			Json *item = Json::getItem(skinMap, "bones");
+			if (item) {
+				for (item = item->_child; item; item = item->_next) {
+					BoneData* data = skeletonData->findBone(item->_valueString);
+					if (!data) {
+						delete skeletonData;
+						setError(root, String("Skin bone not found: "), item->_valueString);
+						return NULL;
+					}
+					skin->getBones().add(data);
+				}
+			}
+
+			item = Json::getItem(skinMap, "ik");
+			if (item) {
+				for (item = item->_child; item; item = item->_next) {
+					IkConstraintData* data = skeletonData->findIkConstraint(item->_valueString);
+					if (!data) {
+						delete skeletonData;
+						setError(root, String("Skin IK constraint not found: "), item->_valueString);
+						return NULL;
+					}
+					skin->getConstraints().add(data);
+				}
+			}
+
+			item = Json::getItem(skinMap, "transform");
+			if (item) {
+				for (item = item->_child; item; item = item->_next) {
+					TransformConstraintData* data = skeletonData->findTransformConstraint(item->_valueString);
+					if (!data) {
+						delete skeletonData;
+						setError(root, String("Skin transform constraint not found: "), item->_valueString);
+						return NULL;
+					}
+					skin->getConstraints().add(data);
+				}
+			}
+
+			item = Json::getItem(skinMap, "path");
+			if (item) {
+				for (item = item->_child; item; item = item->_next) {
+					PathConstraintData* data = skeletonData->findPathConstraint(item->_valueString);
+					if (!data) {
+						delete skeletonData;
+						setError(root, String("Skin path constraint not found: "), item->_valueString);
+						return NULL;
+					}
+					skin->getConstraints().add(data);
+				}
+			}
 
 			skeletonData->_skins[skinsIndex++] = skin;
-			if (strcmp(skinMap->_name, "default") == 0) {
+			if (strcmp(Json::getString(skinMap, "name", ""), "default") == 0) {
 				skeletonData->_defaultSkin = skin;
 			}
 
-			for (attachmentsMap = skinMap->_child; attachmentsMap; attachmentsMap = attachmentsMap->_next) {
-				int slotIndex = skeletonData->findSlotIndex(attachmentsMap->_name);
+			for (attachmentsMap = Json::getItem(skinMap, "attachments")->_child; attachmentsMap; attachmentsMap = attachmentsMap->_next) {
+				SlotData* slot = skeletonData->findSlot(attachmentsMap->_name);
 				Json *attachmentMap;
 
 				for (attachmentMap = attachmentsMap->_child; attachmentMap; attachmentMap = attachmentMap->_next) {
@@ -574,7 +630,7 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
 																							String(Json::getString(
 																									attachmentMap,
 																									"skin", 0)),
-																							slotIndex,
+																							slot->getIndex(),
 																							String(entry->_valueString), inheritDeform);
 								_linkedMeshes.add(linkedMesh);
 							}
@@ -640,7 +696,7 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
 						}
 					}
 
-					skin->setAttachment(slotIndex, skinAttachmentName, attachment);
+					skin->setAttachment(slot->getIndex(), skinAttachmentName, attachment);
 				}
 			}
 		}
@@ -746,13 +802,12 @@ void SkeletonJson::readCurve(Json *frame, CurveTimeline *timeline, size_t frameI
 	}
 	if (curve->_type == Json::JSON_STRING && strcmp(curve->_valueString, "stepped") == 0) {
 		timeline->setStepped(frameIndex);
-	} else if (curve->_type == Json::JSON_ARRAY) {
-		Json *child0 = curve->_child;
-		Json *child1 = child0->_next;
-		Json *child2 = child1->_next;
-		Json *child3 = child2->_next;
-		timeline->setCurve(frameIndex, child0->_valueFloat, child1->_valueFloat, child2->_valueFloat,
-						   child3->_valueFloat);
+	} else {
+		float c1 = Json::getFloat(frame, "curve", 0);
+		float c2 = Json::getFloat(frame, "c2", 0);
+		float c3 = Json::getFloat(frame, "c3", 1);
+		float c4 = Json::getFloat(frame, "c4", 1);
+		timeline->setCurve(frameIndex, c1, c2, c3, c4);
 	}
 }
 
@@ -905,9 +960,11 @@ Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) {
 				int isShear = strcmp(timelineMap->_name, "shear") == 0;
 				if (isScale || isTranslate || isShear) {
 					float timelineScale = isTranslate ? _scale : 1;
+					float defaultValue = 0;
 					TranslateTimeline *timeline = 0;
 					if (isScale) {
 						timeline = new(__FILE__, __LINE__) ScaleTimeline(timelineMap->_size);
+						defaultValue = 1;
 					} else if (isTranslate) {
 						timeline = new(__FILE__, __LINE__) TranslateTimeline(timelineMap->_size);
 					} else if (isShear) {
@@ -917,8 +974,8 @@ Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) {
 
 					for (valueMap = timelineMap->_child, frameIndex = 0; valueMap; valueMap = valueMap->_next, ++frameIndex) {
 						timeline->setFrame(frameIndex, Json::getFloat(valueMap, "time", 0),
-										   Json::getFloat(valueMap, "x", 0) * timelineScale,
-										   Json::getFloat(valueMap, "y", 0) * timelineScale);
+										   Json::getFloat(valueMap, "x", defaultValue) * timelineScale,
+										   Json::getFloat(valueMap, "y", defaultValue) * timelineScale);
 						readCurve(valueMap, timeline, frameIndex);
 					}
 

+ 21 - 0
spine-cpp/spine-cpp/src/spine/Skin.cpp

@@ -37,6 +37,7 @@
 #include <spine/Skeleton.h>
 
 #include <spine/Slot.h>
+#include <spine/ConstraintData.h>
 
 #include <assert.h>
 
@@ -157,6 +158,12 @@ void Skin::attachAll(Skeleton &skeleton, Skin &oldSkin) {
 }
 
 void Skin::addSkin(Skin* other) {
+	for (int i = 0; i < other->getBones().size(); i++)
+		if (!_bones.contains(other->getBones()[i])) _bones.add(other->getBones()[i]);
+
+	for (int i = 0; i < other->getConstraints().size(); i++)
+		if (!_constraints.contains(other->getConstraints()[i])) _constraints.add(other->getConstraints()[i]);
+
 	AttachmentMap::Entries entries = other->getAttachments();
 	while(entries.hasNext()) {
 		AttachmentMap::Entry& entry = entries.next();
@@ -165,6 +172,12 @@ void Skin::addSkin(Skin* other) {
 }
 
 void Skin::copySkin(Skin* other) {
+	for (int i = 0; i < other->getBones().size(); i++)
+		if (!_bones.contains(other->getBones()[i])) _bones.add(other->getBones()[i]);
+
+	for (int i = 0; i < other->getConstraints().size(); i++)
+		if (!_constraints.contains(other->getConstraints()[i])) _constraints.add(other->getConstraints()[i]);
+
 	AttachmentMap::Entries entries = other->getAttachments();
 	while(entries.hasNext()) {
 		AttachmentMap::Entry& entry = entries.next();
@@ -175,3 +188,11 @@ void Skin::copySkin(Skin* other) {
 		}
 	}
 }
+
+Vector<ConstraintData*>& Skin::getConstraints() {
+	return _constraints;
+}
+
+Vector<BoneData*>& Skin::getBones() {
+	return _bones;
+}

+ 13 - 3
spine-cpp/spine-cpp/src/spine/TransformConstraint.cpp

@@ -41,9 +41,9 @@
 
 using namespace spine;
 
-RTTI_IMPL(TransformConstraint, Constraint)
+RTTI_IMPL(TransformConstraint, Updatable)
 
-TransformConstraint::TransformConstraint(TransformConstraintData &data, Skeleton &skeleton) : Constraint(),
+TransformConstraint::TransformConstraint(TransformConstraintData &data, Skeleton &skeleton) : Updatable(),
 																							  _data(data),
 																							  _target(skeleton.findBone(
 																									  data.getTarget()->getName())),
@@ -54,7 +54,8 @@ TransformConstraint::TransformConstraint(TransformConstraintData &data, Skeleton
 																							  _scaleMix(
 																									  data.getScaleMix()),
 																							  _shearMix(
-																									  data.getShearMix()) {
+																									  data.getShearMix()),
+																									  _active(false) {
 	_bones.ensureCapacity(_data.getBones().size());
 	for (size_t i = 0; i < _data.getBones().size(); ++i) {
 		BoneData *boneData = _data.getBones()[i];
@@ -380,3 +381,12 @@ void TransformConstraint::applyRelativeLocal() {
 		bone.updateWorldTransform(x, y, rotation, scaleX, scaleY, bone._ashearX, shearY);
 	}
 }
+
+bool TransformConstraint::isActive() {
+	return _active;
+}
+
+void TransformConstraint::setActive(bool inValue) {
+	_active = inValue;
+}
+

+ 1 - 11
spine-cpp/spine-cpp/src/spine/TransformConstraintData.cpp

@@ -39,8 +39,7 @@
 
 using namespace spine;
 TransformConstraintData::TransformConstraintData(const String &name) :
-		_name(name),
-		_order(0),
+		ConstraintData(name),
 		_target(NULL),
 		_rotateMix(0),
 		_translateMix(0),
@@ -54,15 +53,6 @@ TransformConstraintData::TransformConstraintData(const String &name) :
 		_offsetShearY(0),
 		_relative(false),
 		_local(false) {
-	assert(_name.length() > 0);
-}
-
-const String &TransformConstraintData::getName() {
-	return _name;
-}
-
-int TransformConstraintData::getOrder() {
-	return _order;
 }
 
 Vector<BoneData *> &TransformConstraintData::getBones() {

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

@@ -71,6 +71,7 @@ void TransformConstraintTimeline::apply(Skeleton &skeleton, float lastTime, floa
 
 	TransformConstraint *constraintP = skeleton._transformConstraints[_transformConstraintIndex];
 	TransformConstraint &constraint = *constraintP;
+	if (!constraint.isActive()) return;
 
 	if (time < _frames[0]) {
 		switch (blend) {

+ 1 - 0
spine-cpp/spine-cpp/src/spine/TranslateTimeline.cpp

@@ -68,6 +68,7 @@ void TranslateTimeline::apply(Skeleton &skeleton, float lastTime, float time, Ve
 
 	Bone *boneP = skeleton._bones[_boneIndex];
 	Bone &bone = *boneP;
+	if (!bone._active) return;
 
 	if (time < _frames[0]) {
 		switch (blend) {

+ 2 - 0
spine-cpp/spine-cpp/src/spine/TwoColorTimeline.cpp

@@ -37,6 +37,7 @@
 #include <spine/Event.h>
 
 #include <spine/Animation.h>
+#include <spine/Bone.h>
 #include <spine/TimelineType.h>
 #include <spine/Slot.h>
 #include <spine/SlotData.h>
@@ -75,6 +76,7 @@ void TwoColorTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vec
 
 	Slot *slotP = skeleton._slots[_slotIndex];
 	Slot &slot = *slotP;
+	if (!slot._bone.isActive()) return;
 
 	if (time < _frames[0]) {
 		// Time is before first frame.

+ 1 - 1
spine-sfml/c/src/spine/spine-sfml.cpp

@@ -128,7 +128,7 @@ void SkeletonDrawable::draw (RenderTarget& target, RenderStates states) const {
 		if (!attachment) continue;
 
 		// Early out if slot is invisible
-		if (slot->color.a == 0) {
+		if (slot->color.a == 0 || !slot->bone->active) {
 			spSkeletonClipping_clipEnd(clipper, slot);
 			continue;
 		}

+ 2 - 2
spine-sfml/cpp/example/main.cpp

@@ -473,7 +473,7 @@ int main () {
 	SpineExtension::setInstance(&dbgExtension);
 
 	testcase(goblins, "data/goblins-pro.json", "data/goblins-pro.skel", "data/goblins-pma.atlas", 1.4f);
-	/*testcase(owl, "data/owl-pro.json", "data/owl-pro.skel", "data/owl-pma.atlas", 0.5f);
+	testcase(owl, "data/owl-pro.json", "data/owl-pro.skel", "data/owl-pma.atlas", 0.5f);
 	testcase(spineboy, "data/spineboy-pro.json", "data/spineboy-pro.skel", "data/spineboy-pma.atlas", 0.6f);
 	testcase(stretchymanStrechyIk, "data/stretchyman-stretchy-ik-pro.json", "data/stretchyman-stretchy-ik-pro.skel", "data/stretchyman-pma.atlas", 0.6f);
 	testcase(raptor, "data/raptor-pro.json", "data/raptor-pro.skel", "data/raptor-pma.atlas", 0.5f);
@@ -481,7 +481,7 @@ int main () {
 	testcase(vine, "data/vine-pro.json", "data/vine-pro.skel", "data/vine-pma.atlas", 0.5f);
 	testcase(tank, "data/tank-pro.json", "data/tank-pro.skel", "data/tank-pma.atlas", 0.2f);
 	testcase(raptor, "data/raptor-pro.json", "data/raptor-pro.skel", "data/raptor-pma.atlas", 0.5f);
-	testcase(stretchyman, "data/stretchyman-pro.json", "data/stretchyman-pro.skel", "data/stretchyman-pma.atlas", 0.6f);*/
+	testcase(stretchyman, "data/stretchyman-pro.json", "data/stretchyman-pro.skel", "data/stretchyman-pma.atlas", 0.6f);
 
 	dbgExtension.reportLeaks();
 	return 0;

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

@@ -102,8 +102,8 @@ void SkeletonDrawable::draw(RenderTarget &target, RenderStates states) const {
 		Attachment *attachment = slot.getAttachment();
 		if (!attachment) continue;
 
-		// Early out if the slot color is 0
-		if (slot.getColor().a == 0) {
+		// Early out if the slot color is 0 or the bone is not active
+		if (slot.getColor().a == 0 || !slot.getBone().isActive()) {
 			clipper.clipEnd(slot);
 			continue;
 		}