Răsfoiți Sursa

[c] 4.0 porting, constraints & skeleton

badlogic 4 ani în urmă
părinte
comite
1fa5545f19

+ 0 - 17
spine-c/spine-c/include/spine/Bone.h

@@ -55,23 +55,6 @@ struct spBone {
 
 	int/*bool*/ sorted;
 	int/*bool*/ active;
-
-#ifdef __cplusplus
-	spBone() :
-		data(0),
-		skeleton(0),
-		parent(0),
-		childrenCount(0), children(0),
-		x(0), y(0), rotation(0), scaleX(0), scaleY(0),
-		ax(0), ay(0), arotation(0), ascaleX(0), ascaleY(0), ashearX(0), ashearY(0),
-		appliedValid(0),
-
-		a(0), b(0), worldX(0),
-		c(0), d(0), worldY(0),
-
-		sorted(0), active(0) {
-	}
-#endif
 };
 
 SP_API void spBone_setYDown (int/*bool*/yDown);

+ 1 - 1
spine-c/spine-c/include/spine/IkConstraint.h

@@ -59,7 +59,7 @@ typedef struct spIkConstraint {
 SP_API spIkConstraint* spIkConstraint_create (spIkConstraintData* data, const struct spSkeleton* skeleton);
 SP_API void spIkConstraint_dispose (spIkConstraint* self);
 
-SP_API void spIkConstraint_apply (spIkConstraint* self);
+SP_API void spIkConstraint_update (spIkConstraint* self);
 
 SP_API void spIkConstraint_apply1 (spBone* bone, float targetX, float targetY, int /*boolean*/ compress, int /*boolean*/ stretch, int /*boolean*/ uniform, float alpha);
 SP_API void spIkConstraint_apply2 (spBone* parent, spBone* child, float targetX, float targetY, int bendDirection, int /*boolean*/ stretch, float softness, float alpha);

+ 4 - 3
spine-c/spine-c/include/spine/PathConstraint.h

@@ -47,7 +47,8 @@ typedef struct spPathConstraint {
 	int bonesCount;
 	spBone** const bones;
 	spSlot* target;
-	float position, spacing, rotateMix, translateMix;
+	float position, spacing;
+	float mixRotate, mixX, mixY;
 
 	int spacesCount;
 	float* spaces;
@@ -74,8 +75,8 @@ typedef struct spPathConstraint {
 SP_API spPathConstraint* spPathConstraint_create (spPathConstraintData* data, const struct spSkeleton* skeleton);
 SP_API void spPathConstraint_dispose (spPathConstraint* self);
 
-SP_API void spPathConstraint_apply (spPathConstraint* self);
-SP_API float* spPathConstraint_computeWorldPositions(spPathConstraint* self, spPathAttachment* path, int spacesCount, int/*bool*/ tangents, int/*bool*/percentPosition, int/**/percentSpacing);
+SP_API void spPathConstraint_update (spPathConstraint* self);
+SP_API float* spPathConstraint_computeWorldPositions(spPathConstraint* self, spPathAttachment* path, int spacesCount, int/*bool*/ tangents);
 
 #ifdef __cplusplus
 }

+ 3 - 2
spine-c/spine-c/include/spine/PathConstraintData.h

@@ -43,7 +43,7 @@ typedef enum {
 } spPositionMode;
 
 typedef enum {
-	SP_SPACING_MODE_LENGTH, SP_SPACING_MODE_FIXED, SP_SPACING_MODE_PERCENT
+	SP_SPACING_MODE_LENGTH, SP_SPACING_MODE_FIXED, SP_SPACING_MODE_PERCENT, SP_SPACING_MODE_PROPORTIONAL
 } spSpacingMode;
 
 typedef enum {
@@ -61,7 +61,8 @@ typedef struct spPathConstraintData {
 	spSpacingMode spacingMode;
 	spRotateMode rotateMode;
 	float offsetRotation;
-	float position, spacing, rotateMix, translateMix;
+	float position, spacing;
+	float mixRotate, mixX, mixY;
 } spPathConstraintData;
 
 SP_API spPathConstraintData* spPathConstraintData_create (const char* name);

+ 2 - 2
spine-c/spine-c/include/spine/TransformConstraint.h

@@ -45,14 +45,14 @@ typedef struct spTransformConstraint {
 	int bonesCount;
 	spBone** const bones;
 	spBone* target;
-	float rotateMix, translateMix, scaleMix, shearMix;
+	float mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY;
 	int /*boolean*/ active;
 } spTransformConstraint;
 
 SP_API spTransformConstraint* spTransformConstraint_create (spTransformConstraintData* data, const struct spSkeleton* skeleton);
 SP_API void spTransformConstraint_dispose (spTransformConstraint* self);
 
-SP_API void spTransformConstraint_apply (spTransformConstraint* self);
+SP_API void spTransformConstraint_update (spTransformConstraint* self);
 
 #ifdef __cplusplus
 }

+ 1 - 1
spine-c/spine-c/include/spine/TransformConstraintData.h

@@ -44,7 +44,7 @@ typedef struct spTransformConstraintData {
 	int bonesCount;
 	spBoneData** const bones;
 	spBoneData* target;
-	float rotateMix, translateMix, scaleMix, shearMix;
+    float mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY;
 	float offsetRotation, offsetX, offsetY, offsetScaleX, offsetScaleY, offsetShearY;
 	int /*boolean*/ relative;
 	int /*boolean*/ local;

+ 3 - 5
spine-c/spine-c/src/spine/IkConstraint.c

@@ -57,7 +57,8 @@ void spIkConstraint_dispose(spIkConstraint *self) {
 	FREE(self);
 }
 
-void spIkConstraint_apply(spIkConstraint *self) {
+void spIkConstraint_update(spIkConstraint *self) {
+    if (self->mix == 0) return;
 	switch (self->bonesCount) {
 	case 1:
 		spIkConstraint_apply1(self->bones[0], self->target->worldX, self->target->worldY, self->compress, self->stretch, self->data->uniform, self->mix);
@@ -131,10 +132,7 @@ void spIkConstraint_apply2 (spBone* parent, spBone* child, float targetX, float
 	float tx, ty, dd, dx, dy, l1, l2, a1, a2, r, td, sd, p;
 	float id, x, y;
 	float aa, bb, ll, ta, c0, c1, c2;
-	if (alpha == 0) {
-		spBone_updateWorldTransform(child);
-		return;
-	}
+
 	if (!parent->appliedValid) spBone_updateAppliedTransform(parent);
 	if (!child->appliedValid) spBone_updateAppliedTransform(child);
 	px = parent->ax; py = parent->ay; psx = parent->ascaleX; sx = psx; psy = parent->ascaleY; csx = child->ascaleX;

+ 111 - 72
spine-c/spine-c/src/spine/PathConstraint.c

@@ -47,8 +47,9 @@ spPathConstraint* spPathConstraint_create (spPathConstraintData* data, const spS
 	self->target = spSkeleton_findSlot(skeleton, self->data->target->name);
 	self->position = data->position;
 	self->spacing = data->spacing;
-	self->rotateMix = data->rotateMix;
-	self->translateMix = data->translateMix;
+	self->mixRotate = data->mixRotate;
+	self->mixX = data->mixX;
+	self->mixY = data->mixY;
 	self->spacesCount = 0;
 	self->spaces = 0;
 	self->positionsCount = 0;
@@ -72,26 +73,23 @@ void spPathConstraint_dispose (spPathConstraint* self) {
 	FREE(self);
 }
 
-void spPathConstraint_apply (spPathConstraint* self) {
+void spPathConstraint_update (spPathConstraint* self) {
 	int i, p, n;
-	float length, setupLength, x, y, dx, dy, s;
+	float length, setupLength, x, y, dx, dy, s, sum;
 	float* spaces, *lengths, *positions;
 	float spacing;
 	float boneX, boneY, offsetRotation;
 	int/*bool*/tip;
-	float rotateMix = self->rotateMix, translateMix = self->translateMix;
-	int/*bool*/ translate = translateMix > 0, rotate = rotateMix > 0;
+    float mixRotate = self->mixRotate, mixX = self->mixX, mixY = self->mixY;
 	int lengthSpacing;
 	spPathAttachment* attachment = (spPathAttachment*)self->target->attachment;
 	spPathConstraintData* data = self->data;
-	int percentSpacing = data->spacingMode == SP_SPACING_MODE_PERCENT;
-	spRotateMode rotateMode = data->rotateMode;
-	int tangents = rotateMode == SP_ROTATE_MODE_TANGENT, scale = rotateMode == SP_ROTATE_MODE_CHAIN_SCALE;
+	int tangents = data->rotateMode == SP_ROTATE_MODE_TANGENT, scale = data->rotateMode == SP_ROTATE_MODE_CHAIN_SCALE;
 	int boneCount = self->bonesCount, spacesCount = tangents ? boneCount : boneCount + 1;
 	spBone** bones = self->bones;
 	spBone* pa;
 
-	if (!translate && !rotate) return;
+	if (mixRotate == 0 && mixX == 0 && mixY == 0) return;
 	if ((attachment == 0) || (attachment->super.super.type != SP_ATTACHMENT_PATH)) return;
 
 	if (self->spacesCount != spacesCount) {
@@ -103,48 +101,77 @@ void spPathConstraint_apply (spPathConstraint* self) {
 	spaces[0] = 0;
 	lengths = 0;
 	spacing = self->spacing;
-	if (scale || !percentSpacing) {
-		if (scale) {
-			if (self->lengthsCount != boneCount) {
-				if (self->lengths) FREE(self->lengths);
-				self->lengths = MALLOC(float, boneCount);
-				self->lengthsCount = boneCount;
-			}
-			lengths = self->lengths;
-		}
-		lengthSpacing = data->spacingMode == SP_SPACING_MODE_LENGTH;
-		for (i = 0, n = spacesCount - 1; i < n;) {
-			spBone *bone = bones[i];
-			setupLength = bone->data->length;
-			if (setupLength < EPSILON) {
-				if (scale) lengths[i] = 0;
-				spaces[++i] = 0;
-			} else if (percentSpacing) {
-				if (scale) {
-					x = setupLength * bone->a, y = setupLength * bone->c;
-					length = SQRT(x * x + y * y);
-					lengths[i] = length;
-				}
-				spaces[++i] = spacing;
-			} else {
-				x = setupLength * bone->a, y = setupLength * bone->c;
-				length = SQRT(x * x + y * y);
-				if (scale) lengths[i] = length;
-				spaces[++i] = (lengthSpacing ? setupLength + spacing : spacing) * length / setupLength;
-			}
-		}
-	} else {
-		for (i = 1; i < spacesCount; i++) {
-			spaces[i] = spacing;
-		}
-	}
 
-	positions = spPathConstraint_computeWorldPositions(self, attachment, spacesCount, tangents,
-		data->positionMode == SP_POSITION_MODE_PERCENT, percentSpacing);
+    if (scale) {
+        if (self->lengthsCount != boneCount) {
+            if (self->lengths) FREE(self->lengths);
+            self->lengths = MALLOC(float, boneCount);
+            self->lengthsCount = boneCount;
+        }
+        lengths = self->lengths;
+    }
+
+    switch (data->spacingMode) {
+        case SP_SPACING_MODE_PERCENT:
+            if (scale) {
+                for (i = 0, n = spacesCount - 1; i < n; i++) {
+                    spBone* bone = bones[i];
+                    setupLength = bone->data->length;
+                    if (setupLength < EPSILON)
+                        lengths[i] = 0;
+                    else {
+                        x = setupLength * bone->a;
+                        y = setupLength * bone->c;
+                        lengths[i] = SQRT(x * x + y * y);
+                    }
+                }
+            }
+            for (i = 1, n = spacesCount; i < n; i++) spaces[i] = spacing;
+            break;
+        case SP_SPACING_MODE_PROPORTIONAL:
+            sum = 0;
+            for (i = 0; i < boneCount;) {
+                spBone* bone = bones[i];
+                setupLength = bone->data->length;
+                if (setupLength < EPSILON) {
+                    if (scale) lengths[i] = 0;
+                    spaces[++i] = spacing;
+                } else {
+                    x = setupLength * bone->a, y = setupLength * bone->c;
+                    length = SQRT(x * x + y * y);
+                    if (scale) lengths[i] = length;
+                    spaces[++i] = length;
+                    sum += length;
+                }
+            }
+            if (sum > 0) {
+                sum = spacesCount / sum * spacing;
+                for (i = 1; i < spacesCount; i++)
+                    spaces[i] *= sum;
+            }
+            break;
+        default:
+            lengthSpacing = data->spacingMode == SP_SPACING_MODE_LENGTH;
+            for (i = 0, n = spacesCount - 1; i < n;) {
+                spBone* bone = bones[i];
+                setupLength = bone->data->length;
+                if (setupLength < EPSILON) {
+                    if (scale) lengths[i] = 0;
+                    spaces[++i] = spacing;
+                } else {
+                    x = setupLength * bone->a, y = setupLength * bone->c;
+                    length = SQRT(x * x + y * y);
+                    if (scale) lengths[i] = length;
+                    spaces[++i] = (lengthSpacing ? setupLength + spacing : spacing) * length / setupLength;
+                }
+            }
+    }
+
+	positions = spPathConstraint_computeWorldPositions(self, attachment, spacesCount, tangents);
 	boneX = positions[0], boneY = positions[1], offsetRotation = self->data->offsetRotation;
 	tip = 0;
 	if (offsetRotation == 0)
-		tip = rotateMode == SP_ROTATE_MODE_CHAIN;
+		tip = data->rotateMode == SP_ROTATE_MODE_CHAIN;
 	else {
 		tip = 0;
 		pa = self->target->bone;
@@ -152,20 +179,20 @@ void spPathConstraint_apply (spPathConstraint* self) {
 	}
 	for (i = 0, p = 3; i < boneCount; i++, p += 3) {
 		spBone* bone = bones[i];
-		CONST_CAST(float, bone->worldX) += (boneX - bone->worldX) * translateMix;
-		CONST_CAST(float, bone->worldY) += (boneY - bone->worldY) * translateMix;
+		CONST_CAST(float, bone->worldX) += (boneX - bone->worldX) * mixX;
+		CONST_CAST(float, bone->worldY) += (boneY - bone->worldY) * mixY;
 		x = positions[p], y = positions[p + 1], dx = x - boneX, dy = y - boneY;
 		if (scale) {
 			length = lengths[i];
 			if (length != 0) {
-				s = (SQRT(dx * dx + dy * dy) / length - 1) * rotateMix + 1;
+				s = (SQRT(dx * dx + dy * dy) / length - 1) * mixRotate + 1;
 				CONST_CAST(float, bone->a) *= s;
 				CONST_CAST(float, bone->c) *= s;
 			}
 		}
 		boneX = x;
 		boneY = y;
-		if (rotate) {
+		if (mixRotate > 0) {
 			float a = bone->a, b = bone->b, c = bone->c, d = bone->d, r, cosine, sine;
 			if (tangents)
 				r = positions[p - 1];
@@ -178,15 +205,15 @@ void spPathConstraint_apply (spPathConstraint* self) {
 				cosine = COS(r);
 				sine = SIN(r);
 				length = bone->data->length;
-				boneX += (length * (cosine * a - sine * c) - dx) * rotateMix;
-				boneY += (length * (sine * a + cosine * c) - dy) * rotateMix;
+				boneX += (length * (cosine * a - sine * c) - dx) * mixRotate;
+				boneY += (length * (sine * a + cosine * c) - dy) * mixRotate;
 			} else
 				r += offsetRotation;
 			if (r > PI)
 				r -= PI2;
 			else if (r < -PI)
 				r += PI2;
-			r *= rotateMix;
+			r *= mixRotate;
 			cosine = COS(r);
 			sine = SIN(r);
 			CONST_CAST(float, bone->a) = cosine * a - sine * c;
@@ -236,11 +263,11 @@ static void _addCurvePosition (float p, float x1, float y1, float cx1, float cy1
 	}
 }
 
-float* spPathConstraint_computeWorldPositions(spPathConstraint* self, spPathAttachment* path, int spacesCount, int/*bool*/ tangents, int/*bool*/percentPosition, int/**/percentSpacing) {
+float* spPathConstraint_computeWorldPositions(spPathConstraint* self, spPathAttachment* path, int spacesCount, int/*bool*/ tangents) {
 	int i, o, w, curve, segment, /*bool*/closed, verticesLength, curveCount, prevCurve;
 	float* out, *curves, *segments;
 	float tmpx, tmpy, dddfx, dddfy, ddfx, ddfy, dfx, dfy, pathLength, curveLength, p;
-	float x1, y1, cx1, cy1, cx2, cy2, x2, y2;
+	float x1, y1, cx1, cy1, cx2, cy2, x2, y2, multiplier;
 	spSlot* target = self->target;
 	float position = self->position;
 	float* spaces = self->spaces, *world = 0;
@@ -257,11 +284,18 @@ float* spPathConstraint_computeWorldPositions(spPathConstraint* self, spPathAtta
 		float* lengths = path->lengths;
 		curveCount -= closed ? 1 : 2;
 		pathLength = lengths[curveCount];
-		if (percentPosition) position *= pathLength;
-		if (percentSpacing) {
-			for (i = 1; i < spacesCount; i++)
-				spaces[i] *= pathLength;
-		}
+		if (self->data->positionMode == SP_POSITION_MODE_PERCENT) position += pathLength;
+        switch (self->data->spacingMode) {
+            case SP_SPACING_MODE_PERCENT:
+                multiplier = pathLength;
+                break;
+            case SP_SPACING_MODE_PROPORTIONAL:
+                multiplier = pathLength / spacesCount;
+                break;
+            default:
+                multiplier = 1;
+        }
+
 		if (self->worldCount != 8) {
 			if (self->world) FREE(self->world);
 			self->world = MALLOC(float, 8);
@@ -269,7 +303,7 @@ float* spPathConstraint_computeWorldPositions(spPathConstraint* self, spPathAtta
 		}
 		world = self->world;
 		for (i = 0, o = 0, curve = 0; i < spacesCount; i++, o += 3) {
-			float space = spaces[i];
+			float space = spaces[i] * multiplier;
 			position += space;
 			p = position;
 
@@ -384,19 +418,24 @@ float* spPathConstraint_computeWorldPositions(spPathConstraint* self, spPathAtta
 		x1 = x2;
 		y1 = y2;
 	}
-	if (percentPosition)
-		position *= pathLength;
-	else
-		position *= pathLength / path->lengths[curveCount - 1];
-	if (percentSpacing) {
-		for (i = 1; i < spacesCount; i++)
-			spaces[i] *= pathLength;
-	}
+
+    if (self->data->positionMode == SP_POSITION_MODE_PERCENT) position *= pathLength;
+
+    switch (self->data->spacingMode) {
+        case SP_SPACING_MODE_PERCENT:
+            multiplier = pathLength;
+            break;
+        case SP_SPACING_MODE_PROPORTIONAL:
+            multiplier = pathLength / spacesCount;
+            break;
+        default:
+            multiplier = 1;
+    }
 
 	segments = self->segments;
 	curveLength = 0;
 	for (i = 0, o = 0, curve = 0, segment = 0; i < spacesCount; i++, o += 3) {
-		float space = spaces[i];
+		float space = spaces[i] * multiplier;
 		position += space;
 		p = position;
 

+ 71 - 69
spine-c/spine-c/src/spine/Skeleton.c

@@ -47,10 +47,6 @@ typedef struct {
 	int updateCacheCount;
 	int updateCacheCapacity;
 	_spUpdate* updateCache;
-
-	int updateCacheResetCount;
-	int updateCacheResetCapacity;
-	spBone** updateCacheReset;
 } _spSkeleton;
 
 spSkeleton* spSkeleton_create (spSkeletonData* data) {
@@ -133,7 +129,6 @@ void spSkeleton_dispose (spSkeleton* self) {
 	_spSkeleton* internal = SUB_CAST(_spSkeleton, self);
 
 	FREE(internal->updateCache);
-	FREE(internal->updateCacheReset);
 
 	for (i = 0; i < self->bonesCount; ++i)
 		spBone_dispose(self->bones[i]);
@@ -171,15 +166,6 @@ static void _addToUpdateCache(_spSkeleton* const internal, _spUpdateType type, v
 	++internal->updateCacheCount;
 }
 
-static void _addToUpdateCacheReset(_spSkeleton* const internal, spBone* bone) {
-	if (internal->updateCacheResetCount == internal->updateCacheResetCapacity) {
-		internal->updateCacheResetCapacity *= 2;
-		internal->updateCacheReset = (spBone**)realloc(internal->updateCacheReset, sizeof(spBone*) * internal->updateCacheResetCapacity);
-	}
-	internal->updateCacheReset[internal->updateCacheResetCount] = bone;
-	++internal->updateCacheResetCount;
-}
-
 static void _sortBone(_spSkeleton* const internal, spBone* bone) {
 	if (bone->sorted) return;
 	if (bone->parent) _sortBone(internal, bone->parent);
@@ -199,11 +185,13 @@ static void _sortPathConstraintAttachmentBones(_spSkeleton* const internal, spAt
 	else {
 		spBone** bones = internal->super.bones;
 		int i = 0, n;
-		while (i < pathBonesCount) {
-			int boneCount = pathBones[i++];
-			for (n = i + boneCount; i < n; i++)
-				_sortBone(internal, bones[pathBones[i]]);
-		}
+
+        for (i = 0, n = pathBonesCount; i < n;) {
+            int nn = pathBones[i++];
+            nn += i;
+            while (i < nn)
+                _sortBone(internal, bones[pathBones[i++]]);
+        }
 	}
 }
 
@@ -241,23 +229,18 @@ static void _sortIkConstraint (_spSkeleton* const internal, spIkConstraint* cons
 	parent = constrained[0];
 	_sortBone(internal, parent);
 
-	if (constraint->bonesCount > 1) {
+	if (constraint->bonesCount == 1) {
+	    _addToUpdateCache(internal, SP_UPDATE_IK_CONSTRAINT, constraint);
+	    _sortReset(parent->children, parent->childrenCount);
+    } else {
 		spBone* child = constrained[constraint->bonesCount - 1];
-		contains = 0;
-		for (i = 0; i < internal->updateCacheCount; i++) {
-			_spUpdate update = internal->updateCache[i];
-			if (update.object == child) {
-				contains = -1;
-				break;
-			}
-		}
-		if (!contains) _addToUpdateCacheReset(internal, child);
-	}
+		_sortBone(internal, child);
 
-	_addToUpdateCache(internal, SP_UPDATE_IK_CONSTRAINT, constraint);
+        _addToUpdateCache(internal, SP_UPDATE_IK_CONSTRAINT, constraint);
 
-	_sortReset(parent->children, parent->childrenCount);
-	constrained[constraint->bonesCount-1]->sorted = 1;
+        _sortReset(parent->children, parent->childrenCount);
+        child->sorted = 1;
+	}
 }
 
 static void _sortPathConstraint(_spSkeleton* const internal, spPathConstraint* constraint) {
@@ -310,16 +293,8 @@ static void _sortTransformConstraint(_spSkeleton* const internal, spTransformCon
 	if (constraint->data->local) {
 		for (i = 0; i < boneCount; i++) {
 			child = constrained[i];
+			_sortBone(internal, child->parent);
 			_sortBone(internal, child);
-			contains = 0;
-			for (i = 0; i < internal->updateCacheCount; i++) {
-				_spUpdate update = internal->updateCache[i];
-				if (update.object == child) {
-					contains = -1;
-					break;
-				}
-			}
-			if (!contains) _addToUpdateCacheReset(internal, child);
 		}
 	} else {
 		for (i = 0; i < boneCount; i++)
@@ -348,11 +323,6 @@ void spSkeleton_updateCache (spSkeleton* self) {
 	internal->updateCache = MALLOC(_spUpdate, internal->updateCacheCapacity);
 	internal->updateCacheCount = 0;
 
-	internal->updateCacheResetCapacity = self->bonesCount;
-	FREE(internal->updateCacheReset);
-	internal->updateCacheReset = MALLOC(spBone*, internal->updateCacheResetCapacity);
-	internal->updateCacheResetCount = 0;
-
 	bones = self->bones;
 	for (i = 0; i < self->bonesCount; ++i) {
 		spBone* bone = bones[i];
@@ -417,19 +387,6 @@ void spSkeleton_updateCache (spSkeleton* self) {
 void spSkeleton_updateWorldTransform (const spSkeleton* self) {
 	int i;
 	_spSkeleton* internal = SUB_CAST(_spSkeleton, self);
-	spBone** updateCacheReset = internal->updateCacheReset;
-	for (i = 0; i < internal->updateCacheResetCount; i++) {
-		spBone* bone = updateCacheReset[i];
-		CONST_CAST(float, bone->ax) = bone->x;
-		CONST_CAST(float, bone->ay) = bone->y;
-		CONST_CAST(float, bone->arotation) = bone->rotation;
-		CONST_CAST(float, bone->ascaleX) = bone->scaleX;
-		CONST_CAST(float, bone->ascaleY) = bone->scaleY;
-		CONST_CAST(float, bone->ashearX) = bone->shearX;
-		CONST_CAST(float, bone->ashearY) = bone->shearY;
-		CONST_CAST(int, bone->appliedValid) = 1;
-	}
-
 	for (i = 0; i < internal->updateCacheCount; ++i) {
 		_spUpdate* update = internal->updateCache + i;
 		switch (update->type) {
@@ -437,18 +394,60 @@ void spSkeleton_updateWorldTransform (const spSkeleton* self) {
 			spBone_updateWorldTransform((spBone*)update->object);
 			break;
 		case SP_UPDATE_IK_CONSTRAINT:
-			spIkConstraint_apply((spIkConstraint*)update->object);
+            spIkConstraint_update((spIkConstraint *) update->object);
 			break;
 		case SP_UPDATE_TRANSFORM_CONSTRAINT:
-			spTransformConstraint_apply((spTransformConstraint*)update->object);
+            spTransformConstraint_update((spTransformConstraint *) update->object);
 			break;
 		case SP_UPDATE_PATH_CONSTRAINT:
-			spPathConstraint_apply((spPathConstraint*)update->object);
+            spPathConstraint_update((spPathConstraint *) update->object);
 			break;
 		}
 	}
 }
 
+void spSkeleton_updateWorldTransformWith (const spSkeleton* self, const spBone *parent) {
+/* Apply the parent bone transform to the root bone. The root bone always inherits scale, rotation and reflection. */
+    int i, n;
+    float rotationY, la, lb, lc, ld;
+    _spUpdate *updateCache;
+    _spSkeleton* internal = SUB_CAST(_spSkeleton, self);
+    spBone *rootBone = self->root;
+    float pa = parent->a, pb = parent->b, pc = parent->c, pd = parent->d;
+    CONST_CAST(float, rootBone->worldX) = pa * self->x + pb * self->y + parent->worldX;
+    CONST_CAST(float, rootBone->worldY) = pc * self->x + pd * self->y + parent->worldY;
+
+    rotationY = rootBone->rotation + 90 + rootBone->shearY;
+    la = COS_DEG(rootBone->rotation + rootBone->shearX) * rootBone->scaleX;
+    lb = COS_DEG(rotationY) * rootBone->scaleY;
+    lc = SIN_DEG(rootBone->rotation + rootBone->shearX) * rootBone->scaleX;
+    ld = SIN_DEG(rotationY) * rootBone->scaleY;
+    CONST_CAST(float, rootBone->a) = (pa * la + pb * lc) * self->scaleX;
+    CONST_CAST(float, rootBone->b) = (pa * lb + pb * ld) * self->scaleX;
+    CONST_CAST(float, rootBone->c) = (pc * la + pd * lc) * self->scaleY;
+    CONST_CAST(float, rootBone->d) = (pc * lb + pd * ld) * self->scaleY;
+
+    /* Update everything except root bone. */
+    updateCache = internal->updateCache;
+    for (i = 0; i < internal->updateCacheCount; ++i) {
+        _spUpdate* update = internal->updateCache + i;
+        switch (update->type) {
+            case SP_UPDATE_BONE:
+                if ((spBone*)update->object != rootBone) spBone_updateWorldTransform((spBone*)update->object);
+                break;
+            case SP_UPDATE_IK_CONSTRAINT:
+                spIkConstraint_update((spIkConstraint *) update->object);
+                break;
+            case SP_UPDATE_TRANSFORM_CONSTRAINT:
+                spTransformConstraint_update((spTransformConstraint *) update->object);
+                break;
+            case SP_UPDATE_PATH_CONSTRAINT:
+                spPathConstraint_update((spPathConstraint *) update->object);
+                break;
+        }
+    }
+}
+
 void spSkeleton_setToSetupPose (const spSkeleton* self) {
 	spSkeleton_setBonesToSetupPose(self);
 	spSkeleton_setSlotsToSetupPose(self);
@@ -471,10 +470,12 @@ void spSkeleton_setBonesToSetupPose (const spSkeleton* self) {
 	for (i = 0; i < self->transformConstraintsCount; ++i) {
 		spTransformConstraint* constraint = self->transformConstraints[i];
 		spTransformConstraintData* data = constraint->data;
-		constraint->rotateMix = data->rotateMix;
-		constraint->translateMix = data->translateMix;
-		constraint->scaleMix = data->scaleMix;
-		constraint->shearMix = data->shearMix;
+        constraint->mixRotate = data->mixRotate;
+        constraint->mixX = data->mixX;
+        constraint->mixY = data->mixY;
+        constraint->mixScaleX = data->mixScaleX;
+        constraint->mixScaleY = data->mixScaleY;
+        constraint->mixShearY = data->mixShearY;
 	}
 
 	for (i = 0; i < self->pathConstraintsCount; ++i) {
@@ -482,8 +483,9 @@ void spSkeleton_setBonesToSetupPose (const spSkeleton* self) {
 		spPathConstraintData* data = constraint->data;
 		constraint->position = data->position;
 		constraint->spacing = data->spacing;
-		constraint->rotateMix = data->rotateMix;
-		constraint->translateMix = data->translateMix;
+        constraint->mixRotate = data->mixRotate;
+        constraint->mixX = data->mixX;
+        constraint->mixY = data->mixY;
 	}
 }
 

+ 72 - 88
spine-c/spine-c/src/spine/TransformConstraint.c

@@ -35,10 +35,12 @@ spTransformConstraint* spTransformConstraint_create (spTransformConstraintData*
 	int i;
 	spTransformConstraint* self = NEW(spTransformConstraint);
 	CONST_CAST(spTransformConstraintData*, self->data) = data;
-	self->rotateMix = data->rotateMix;
-	self->translateMix = data->translateMix;
-	self->scaleMix = data->scaleMix;
-	self->shearMix = data->shearMix;
+    self->mixRotate = data->mixRotate;
+    self->mixX = data->mixX;
+    self->mixY = data->mixY;
+    self->mixScaleX = data->mixScaleX;
+    self->mixScaleY = data->mixScaleY;
+    self->mixShearY = data->mixShearY;
 	self->bonesCount = data->bonesCount;
 	CONST_CAST(spBone**, self->bones) = MALLOC(spBone*, self->bonesCount);
 	for (i = 0; i < self->bonesCount; ++i)
@@ -53,134 +55,128 @@ void spTransformConstraint_dispose (spTransformConstraint* self) {
 }
 
 void _spTransformConstraint_applyAbsoluteWorld (spTransformConstraint* self) {
-	float rotateMix = self->rotateMix, translateMix = self->translateMix, scaleMix = self->scaleMix, shearMix = self->shearMix;
+    float mixRotate = self->mixRotate, mixX = self->mixX, mixY = self->mixY, mixScaleX = self->mixScaleX,
+            mixScaleY = self->mixScaleY, mixShearY = self->mixShearY;
+    int /*bool*/ translate = mixX != 0 || mixY != 0;
 	spBone* target = self->target;
 	float ta = target->a, tb = target->b, tc = target->c, td = target->d;
 	float degRadReflect = ta * td - tb * tc > 0 ? DEG_RAD : -DEG_RAD;
 	float offsetRotation = self->data->offsetRotation * degRadReflect, offsetShearY = self->data->offsetShearY * degRadReflect;
-	int /*bool*/ modified;
 	int i;
 	float a, b, c, d, r, cosine, sine, x, y, s, ts, by;
 	for (i = 0; i < self->bonesCount; ++i) {
 		spBone* bone = self->bones[i];
-		modified = 0;
 
-		if (rotateMix != 0) {
+		if (mixRotate != 0) {
 			a = bone->a, b = bone->b, c = bone->c, d = bone->d;
 			r = ATAN2(tc, ta) - ATAN2(c, a) + offsetRotation;
 			if (r > PI) r -= PI2;
 			else if (r < -PI) r += PI2;
-			r *= rotateMix;
+			r *= mixRotate;
 			cosine = COS(r);
 			sine = SIN(r);
 			CONST_CAST(float, bone->a) = cosine * a - sine * c;
 			CONST_CAST(float, bone->b) = cosine * b - sine * d;
 			CONST_CAST(float, bone->c) = sine * a + cosine * c;
 			CONST_CAST(float, bone->d) = sine * b + cosine * d;
-			modified = 1;
 		}
 
-		if (translateMix != 0) {
+		if (translate) {
 			spBone_localToWorld(target, self->data->offsetX, self->data->offsetY, &x, &y);
-			CONST_CAST(float, bone->worldX) += (x - bone->worldX) * translateMix;
-			CONST_CAST(float, bone->worldY) += (y - bone->worldY) * translateMix;
-			modified = 1;
+			CONST_CAST(float, bone->worldX) += (x - bone->worldX) * mixX;
+			CONST_CAST(float, bone->worldY) += (y - bone->worldY) * mixY;
 		}
 
-		if (scaleMix > 0) {
-			s = SQRT(bone->a * bone->a + bone->c * bone->c);
-			ts = SQRT(ta * ta + tc * tc);
-			if (s > 0.00001f) s = (s + (ts - s + self->data->offsetScaleX) * scaleMix) / s;
-			CONST_CAST(float, bone->a) *= s;
-			CONST_CAST(float, bone->c) *= s;
-			s = SQRT(bone->b * bone->b + bone->d * bone->d);
-			ts = SQRT(tb * tb + td * td);
-			if (s > 0.00001f) s = (s + (ts - s + self->data->offsetScaleY) * scaleMix) / s;
-			CONST_CAST(float, bone->b) *= s;
-			CONST_CAST(float, bone->d) *= s;
-			modified = 1;
+		if (mixScaleX > 0) {
+            s = SQRT(bone->a * bone->a + bone->c * bone->c);
+            if (s != 0) s = (s + (SQRT(ta * ta + tc * tc) - s + self->data->offsetScaleX) * mixScaleX) / s;
+            CONST_CAST(float, bone->a) *= s;
+            CONST_CAST(float, bone->c) *= s;
 		}
-
-		if (shearMix > 0) {
+        if (mixScaleY != 0) {
+            s = SQRT(bone->b * bone->b + bone->d * bone->d);
+            if (s != 0) s = (s + (SQRT(tb * tb + td * td) - s + self->data->offsetScaleY) * mixScaleY) / s;
+            CONST_CAST(float, bone->b) *= s;
+            CONST_CAST(float, bone->d) *= s;
+        }
+
+		if (mixShearY > 0) {
 			b = bone->b, d = bone->d;
 			by = ATAN2(d, b);
 			r = ATAN2(td, tb) - ATAN2(tc, ta) - (by - ATAN2(bone->c, bone->a));
 			s = SQRT(b * b + d * d);
 			if (r > PI) r -= PI2;
 			else if (r < -PI) r += PI2;
-			r = by + (r + offsetShearY) * shearMix;
+			r = by + (r + offsetShearY) * mixShearY;
 			CONST_CAST(float, bone->b) = COS(r) * s;
 			CONST_CAST(float, bone->d) = SIN(r) * s;
-			modified = 1;
 		}
-
-		if (modified) CONST_CAST(int, bone->appliedValid) = 0;
+		CONST_CAST(int, bone->appliedValid) = 0;
 	}
 }
 
 void _spTransformConstraint_applyRelativeWorld (spTransformConstraint* self) {
-	float rotateMix = self->rotateMix, translateMix = self->translateMix, scaleMix = self->scaleMix, shearMix = self->shearMix;
+    float mixRotate = self->mixRotate, mixX = self->mixX, mixY = self->mixY, mixScaleX = self->mixScaleX,
+            mixScaleY = self->mixScaleY, mixShearY = self->mixShearY;
+    int /*bool*/ translate = mixX != 0 || mixY != 0;
 	spBone* target = self->target;
 	float ta = target->a, tb = target->b, tc = target->c, td = target->d;
 	float degRadReflect = ta * td - tb * tc > 0 ? DEG_RAD : -DEG_RAD;
 	float offsetRotation = self->data->offsetRotation * degRadReflect, offsetShearY = self->data->offsetShearY * degRadReflect;
-	int /*bool*/ modified;
 	int i;
 	float a, b, c, d, r, cosine, sine, x, y, s;
 	for (i = 0; i < self->bonesCount; ++i) {
 		spBone* bone = self->bones[i];
-		modified = 0;
 
-		if (rotateMix != 0) {
+		if (mixRotate != 0) {
 			a = bone->a, b = bone->b, c = bone->c, d = bone->d;
 			r = ATAN2(tc, ta) + offsetRotation;
 			if (r > PI) r -= PI2;
 			else if (r < -PI) r += PI2;
-			r *= rotateMix;
+			r *= mixRotate;
 			cosine = COS(r);
 			sine = SIN(r);
 			CONST_CAST(float, bone->a) = cosine * a - sine * c;
 			CONST_CAST(float, bone->b) = cosine * b - sine * d;
 			CONST_CAST(float, bone->c) = sine * a + cosine * c;
 			CONST_CAST(float, bone->d) = sine * b + cosine * d;
-			modified = 1;
 		}
 
-		if (translateMix != 0) {
+		if (translate != 0) {
 			spBone_localToWorld(target, self->data->offsetX, self->data->offsetY, &x, &y);
-			CONST_CAST(float, bone->worldX) += (x * translateMix);
-			CONST_CAST(float, bone->worldY) += (y * translateMix);
-			modified = 1;
+			CONST_CAST(float, bone->worldX) += (x * mixX);
+			CONST_CAST(float, bone->worldY) += (y * mixY);
 		}
 
-		if (scaleMix > 0) {
-			s = (SQRT(ta * ta + tc * tc) - 1 + self->data->offsetScaleX) * scaleMix + 1;
-			CONST_CAST(float, bone->a) *= s;
-			CONST_CAST(float, bone->c) *= s;
-			s = (SQRT(tb * tb + td * td) - 1 + self->data->offsetScaleY) * scaleMix + 1;
+        if (mixScaleX != 0) {
+            s = (SQRT(ta * ta + tc * tc) - 1 + self->data->offsetScaleX) * mixScaleX + 1;
+            CONST_CAST(float, bone->a) *= s;
+            CONST_CAST(float, bone->c) *= s;
+        }
+		if (mixScaleY > 0) {
+			s = (SQRT(tb * tb + td * td) - 1 + self->data->offsetScaleY) * mixScaleY + 1;
 			CONST_CAST(float, bone->b) *= s;
 			CONST_CAST(float, bone->d) *= s;
-			modified = 1;
 		}
 
-		if (shearMix > 0) {
+		if (mixShearY > 0) {
 			r = ATAN2(td, tb) - ATAN2(tc, ta);
 			if (r > PI) r -= PI2;
 			else if (r < -PI) r += PI2;
 			b = bone->b, d = bone->d;
-			r = ATAN2(d, b) + (r - PI / 2 + offsetShearY) * shearMix;
+			r = ATAN2(d, b) + (r - PI / 2 + offsetShearY) * mixShearY;
 			s = SQRT(b * b + d * d);
 			CONST_CAST(float, bone->b) = COS(r) * s;
 			CONST_CAST(float, bone->d) = SIN(r) * s;
-			modified = 1;
 		}
 
-		if (modified) CONST_CAST(int, bone->appliedValid) = 0;
+		CONST_CAST(int, bone->appliedValid) = 0;
 	}
 }
 
 void _spTransformConstraint_applyAbsoluteLocal (spTransformConstraint* self) {
-	float rotateMix = self->rotateMix, translateMix = self->translateMix, scaleMix = self->scaleMix, shearMix = self->shearMix;
+    float mixRotate = self->mixRotate, mixX = self->mixX, mixY = self->mixY, mixScaleX = self->mixScaleX,
+            mixScaleY = self->mixScaleY, mixShearY = self->mixShearY;
 	spBone* target = self->target;
 	int i;
 	float rotation, r, x, y, scaleX, scaleY, shearY;
@@ -191,29 +187,27 @@ void _spTransformConstraint_applyAbsoluteLocal (spTransformConstraint* self) {
 		if (!bone->appliedValid) spBone_updateAppliedTransform(bone);
 
 		rotation = bone->arotation;
-		if (rotateMix != 0) {
+		if (mixRotate != 0) {
 			r = target->arotation - rotation + self->data->offsetRotation;
 			r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360;
-			rotation += r * rotateMix;
+			rotation += r * mixRotate;
 		}
 
 		x = bone->ax, y = bone->ay;
-		if (translateMix != 0) {
-			x += (target->ax - x + self->data->offsetX) * translateMix;
-			y += (target->ay - y + self->data->offsetY) * translateMix;
-		}
+		x += (target->ax - x + self->data->offsetX) * mixX;
+		y += (target->ay - y + self->data->offsetY) * mixY;
 
 		scaleX = bone->ascaleX, scaleY = bone->ascaleY;
-		if (scaleMix != 0) {
-			if (scaleX > 0.00001) scaleX = (scaleX + (target->ascaleX - scaleX + self->data->offsetScaleX) * scaleMix) / scaleX;
-			if (scaleY > 0.00001) scaleY = (scaleY + (target->ascaleY - scaleY + self->data->offsetScaleY) * scaleMix) / scaleY;
-		}
+        if (mixScaleX != 0 && scaleX != 0)
+            scaleX = (scaleX + (target->ascaleX - scaleX + self->data->offsetScaleX) * mixScaleX) / scaleX;
+        if (mixScaleY != 0 && scaleY != 0)
+            scaleY = (scaleY + (target->ascaleY - scaleY + self->data->offsetScaleY) * mixScaleY) / scaleY;
 
 		shearY = bone->ashearY;
-		if (shearMix != 0) {
+		if (mixShearY != 0) {
 			r = target->ashearY - shearY + self->data->offsetShearY;
 			r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360;
-			bone->shearY += r * shearMix;
+			shearY += r * mixShearY;
 		}
 
 		spBone_updateWorldTransformWith(bone, x, y, rotation, scaleX, scaleY, bone->ashearX, shearY);
@@ -221,7 +215,8 @@ void _spTransformConstraint_applyAbsoluteLocal (spTransformConstraint* self) {
 }
 
 void _spTransformConstraint_applyRelativeLocal (spTransformConstraint* self) {
-	float rotateMix = self->rotateMix, translateMix = self->translateMix, scaleMix = self->scaleMix, shearMix = self->shearMix;
+    float mixRotate = self->mixRotate, mixX = self->mixX, mixY = self->mixY, mixScaleX = self->mixScaleX,
+            mixScaleY = self->mixScaleY, mixShearY = self->mixShearY;
 	spBone* target = self->target;
 	int i;
 	float rotation, x, y, scaleX, scaleY, shearY;
@@ -232,32 +227,21 @@ void _spTransformConstraint_applyRelativeLocal (spTransformConstraint* self) {
 		spBone* bone = self->bones[i];
 		if (!bone->appliedValid) spBone_updateAppliedTransform(bone);
 
-		rotation = bone->arotation;
-		if (rotateMix != 0) rotation += (target->arotation + self->data->offsetRotation) * rotateMix;
+        rotation = bone->arotation + (target->arotation + self->data->offsetRotation) * mixRotate;
+        x = bone->ax + (target->ax + self->data->offsetX) * mixX;
+        y = bone->ay + (target->ay + self->data->offsetY) * mixY;
+        scaleX = (bone->ascaleX * ((target->ascaleX - 1 + self->data->offsetScaleX) * mixScaleX) + 1);
+        scaleY = (bone->ascaleY * ((target->ascaleY - 1 + self->data->offsetScaleY) * mixScaleY) + 1);
+        shearY = bone->ashearY + (target->ashearY + self->data-> offsetShearY) * mixShearY;
 
-		x = bone->ax;
-		y = bone->ay;
-		if (translateMix != 0) {
-			x += (target->ax + self->data->offsetX) * translateMix;
-			y += (target->ay + self->data->offsetY) * translateMix;
-		}
-
-		scaleX = bone->ascaleX;
-		scaleY = bone->ascaleY;
-		if (scaleMix != 0) {
-			if (scaleX > 0.00001f) scaleX *= ((target->ascaleX - 1 + self->data->offsetScaleX) * scaleMix) + 1;
-			if (scaleY > 0.00001f) scaleY *= ((target->ascaleY - 1 + self->data->offsetScaleY) * scaleMix) + 1;
-		}
-
-		shearY = bone->ashearY;
-		if (shearMix != 0) shearY += (target->ashearY + self->data->offsetShearY) * shearMix;
-
-		spBone_updateWorldTransformWith(bone, x, y, rotation, scaleX, scaleY, bone->ashearX, shearY);
+        spBone_updateWorldTransformWith(bone, x, y, rotation, scaleX, scaleY, bone->ashearX, shearY);
 	}
 }
 
-void spTransformConstraint_apply (spTransformConstraint* self) {
-	if (self->data->local) {
+void spTransformConstraint_update (spTransformConstraint* self) {
+    if (self->mixRotate == 0 && self->mixX == 0 && self->mixY == 0 && self->mixScaleX == 0 && self->mixScaleX == 0 && self->mixShearY == 0) return;
+
+    if (self->data->local) {
 		if (self->data->relative)
 			_spTransformConstraint_applyRelativeLocal(self);
 		else