瀏覽代碼

[c] More 4.2 porting, only loaders left.

Mario Zechner 1 年之前
父節點
當前提交
f7f4d5cbe7

+ 72 - 13
spine-c/spine-c/include/spine/Animation.h

@@ -101,8 +101,17 @@ typedef enum {
 	SP_TIMELINE_TRANSLATE,
 	SP_TIMELINE_DEFORM,
 	SP_TIMELINE_SEQUENCE,
+    SP_TIMELINE_INHERIT,
 	SP_TIMELINE_IKCONSTRAINT,
 	SP_TIMELINE_PATHCONSTRAINTMIX,
+    SP_TIMELINE_PHYSICSCONSTRAINT_INERTIA,
+    SP_TIMELINE_PHYSICSCONSTRAINT_STRENGTH,
+    SP_TIMELINE_PHYSICSCONSTRAINT_DAMPING,
+    SP_TIMELINE_PHYSICSCONSTRAINT_MASS,
+    SP_TIMELINE_PHYSICSCONSTRAINT_WIND,
+    SP_TIMELINE_PHYSICSCONSTRAINT_GRAVITY,
+    SP_TIMELINE_PHYSICSCONSTRAINT_MIX,
+    SP_TIMELINE_PHYSICSCONSTRAINT_RESET,
 	SP_TIMELINE_RGB2,
 	SP_TIMELINE_RGBA2,
 	SP_TIMELINE_RGBA,
@@ -122,19 +131,28 @@ typedef enum {
 	SP_PROPERTY_SCALEY = 1 << 4,
 	SP_PROPERTY_SHEARX = 1 << 5,
 	SP_PROPERTY_SHEARY = 1 << 6,
-	SP_PROPERTY_RGB = 1 << 7,
-	SP_PROPERTY_ALPHA = 1 << 8,
-	SP_PROPERTY_RGB2 = 1 << 9,
-	SP_PROPERTY_ATTACHMENT = 1 << 10,
-	SP_PROPERTY_DEFORM = 1 << 11,
-	SP_PROPERTY_EVENT = 1 << 12,
-	SP_PROPERTY_DRAWORDER = 1 << 13,
-	SP_PROPERTY_IKCONSTRAINT = 1 << 14,
-	SP_PROPERTY_TRANSFORMCONSTRAINT = 1 << 15,
-	SP_PROPERTY_PATHCONSTRAINT_POSITION = 1 << 16,
-	SP_PROPERTY_PATHCONSTRAINT_SPACING = 1 << 17,
-	SP_PROPERTY_PATHCONSTRAINT_MIX = 1 << 18,
-	SP_PROPERTY_SEQUENCE = 1 << 19
+    SP_PROPERTY_INHERIT = 1 << 7,
+	SP_PROPERTY_RGB = 1 << 8,
+	SP_PROPERTY_ALPHA = 1 << 9,
+	SP_PROPERTY_RGB2 = 1 << 10,
+	SP_PROPERTY_ATTACHMENT = 1 << 11,
+	SP_PROPERTY_DEFORM = 1 << 12,
+	SP_PROPERTY_EVENT = 1 << 13,
+	SP_PROPERTY_DRAWORDER = 1 << 14,
+	SP_PROPERTY_IKCONSTRAINT = 1 << 15,
+	SP_PROPERTY_TRANSFORMCONSTRAINT = 1 << 16,
+	SP_PROPERTY_PATHCONSTRAINT_POSITION = 1 << 17,
+	SP_PROPERTY_PATHCONSTRAINT_SPACING = 1 << 18,
+	SP_PROPERTY_PATHCONSTRAINT_MIX = 1 << 19,
+    SP_PROPERTY_PHYSICSCONSTRAINT_INERTIA = 1 << 20,
+    SP_PROPERTY_PHYSICSCONSTRAINT_STRENGTH = 1 << 21,
+    SP_PROPERTY_PHYSICSCONSTRAINT_DAMPING = 1 << 22,
+    SP_PROPERTY_PHYSICSCONSTRAINT_MASS = 1 << 23,
+    SP_PROPERTY_PHYSICSCONSTRAINT_WIND = 1 << 24,
+    SP_PROPERTY_PHYSICSCONSTRAINT_GRAVITY = 1 << 25,
+    SP_PROPERTY_PHYSICSCONSTRAINT_MIX = 1 << 26,
+    SP_PROPERTY_PHYSICSCONSTRAINT_RESET = 1 << 27,
+	SP_PROPERTY_SEQUENCE = 1 << 28
 } spProperty;
 
 #define SP_MAX_PROPERTY_IDS 3
@@ -196,6 +214,14 @@ SP_API void spCurveTimeline1_setFrame(spCurveTimeline1 *self, int frame, float t
 
 SP_API float spCurveTimeline1_getCurveValue(spCurveTimeline1 *self, float time);
 
+SP_API float spCurveTimeline1_getRelativeValue(spCurveTimeline1 *timeline, float time, float alpha, spMixBlend blend, float current, float setup);
+
+SP_API float spCurveTimeline1_getAbsoluteValue(spCurveTimeline1 *timeline, float time, float alpha, spMixBlend blend, float current, float setup);
+
+SP_API float spCurveTimeline1_getAbsoluteValue2(spCurveTimeline1 *timeline, float time, float alpha, spMixBlend blend, float current, float setup, float value);
+
+SP_API float spCurveTimeline1_getScaleValue (spCurveTimeline1 *timeline, float time, float alpha, spMixBlend blend, spMixDirection direction, float current, float setup);
+
 typedef struct spCurveTimeline spCurveTimeline2;
 
 SP_API void spCurveTimeline2_setFrame(spCurveTimeline1 *self, int frame, float time, float value1, float value2);
@@ -437,6 +463,18 @@ SP_API spDrawOrderTimeline *spDrawOrderTimeline_create(int framesCount, int slot
 
 SP_API void spDrawOrderTimeline_setFrame(spDrawOrderTimeline *self, int frameIndex, float time, const int *drawOrder);
 
+/**/
+
+typedef struct spInheritTimeline {
+    spTimeline super;
+    int boneIndex;
+} spInheritTimeline;
+
+SP_API spInheritTimeline *spInheritTimeline_create(int framesCount, int boneIndex);
+
+SP_API void spInheritTimeline_setFrame(spDrawOrderTimeline *self, int frameIndex, float time, spInherit inherit);
+
+
 /**/
 
 typedef struct spIkConstraintTimeline {
@@ -508,6 +546,27 @@ spPathConstraintMixTimeline_setFrame(spPathConstraintMixTimeline *self, int fram
 
 /**/
 
+typedef struct spPhysicsConstraintTimeline {
+    spCurveTimeline super;
+    int physicsConstraintIndex;
+} spPhysicsConstraintTimeline;
+
+SP_API spPhysicsConstraintTimeline *
+spPhysicsConstraintTimeline_create(int framesCount, int bezierCount, int physicsConstraintIndex, spTimelineType type);
+
+SP_API void spPhysicsConstraintTimeline_setFrame(spPhysicsConstraintTimeline *self, int frame, float time, float value);
+
+/**/
+
+typedef struct spPhysicsConstraintResetTimeline {
+    spTimeline super;
+    int physicsConstraintIndex;
+} spPhysicsConstraintResetTimeline;
+
+SP_API spPhysicsConstraintResetTimeline *spPhysicsConstraintResetTimeline_create(int framesCount, int boneIndex);
+
+SP_API void spPhysicsConstraintResetTimeline_setFrame(spPhysicsConstraintResetTimeline *self, int frameIndex, float time);
+
 #ifdef __cplusplus
 }
 #endif

+ 5 - 1
spine-c/spine-c/include/spine/AnimationState.h

@@ -69,7 +69,7 @@ struct spTrackEntry {
 	int /*boolean*/ holdPrevious;
 	int /*boolean*/ reverse;
 	int /*boolean*/ shortestRotation;
-	float eventThreshold, attachmentThreshold, drawOrderThreshold;
+	float eventThreshold, mixAttachmentThreshold, alphaAttachmentThreshold, mixDrawOrderThreshold;
 	float animationStart, animationEnd, animationLast, nextAnimationLast;
 	float delay, trackTime, trackLast, nextTrackLast, trackEnd, timeScale;
 	float alpha, mixTime, mixDuration, interruptAlpha, totalAlpha;
@@ -146,6 +146,10 @@ SP_API void spTrackEntry_resetRotationDirections(spTrackEntry *entry);
 
 SP_API float spTrackEntry_getTrackComplete(spTrackEntry *entry);
 
+SP_API void spTrackEntry_setMixDuration(spTrackEntry *entry, float mixDuration, float delay);
+
+SP_API int/*bool*/ spTrackEntry_wasApplied(spTrackEntry *entry);
+
 SP_API void spAnimationState_clearNext(spAnimationState *self, spTrackEntry *entry);
 
 /** Use this to dispose static memory before your app exits to appease your memory leak detector*/

+ 2 - 0
spine-c/spine-c/include/spine/IkConstraint.h

@@ -62,6 +62,8 @@ SP_API void spIkConstraint_dispose(spIkConstraint *self);
 
 SP_API void spIkConstraint_update(spIkConstraint *self);
 
+SP_API void spIkConstraint_setToSetupPose(spIkConstraint *self);
+
 SP_API void
 spIkConstraint_apply1(spBone *bone, float targetX, float targetY, int /*boolean*/ compress, int /*boolean*/ stretch,
 					  int /*boolean*/ uniform, float alpha);

+ 2 - 0
spine-c/spine-c/include/spine/PathConstraint.h

@@ -78,6 +78,8 @@ SP_API void spPathConstraint_dispose(spPathConstraint *self);
 
 SP_API void spPathConstraint_update(spPathConstraint *self);
 
+SP_API void spPathConstraint_setToSetupPose(spPathConstraint *self);
+
 SP_API float *spPathConstraint_computeWorldPositions(spPathConstraint *self, spPathAttachment *path, int spacesCount,
 													 int/*bool*/ tangents);
 

+ 4 - 0
spine-c/spine-c/include/spine/Skeleton.h

@@ -130,6 +130,10 @@ SP_API spPathConstraint *spSkeleton_findPathConstraint(const spSkeleton *self, c
 /* Returns 0 if the physics constraint was not found. */
 SP_API spPhysicsConstraint *spSkeleton_findPhysicsConstraint(const spSkeleton *self, const char *constraintName);
 
+SP_API void spSkeleton_physicsTranslate(spSkeleton *self, float x, float y);
+
+SP_API void spSkeleton_physicsRotate(spSkeleton *self, float x, float y, float degrees);
+
 #ifdef __cplusplus
 }
 #endif

+ 4 - 0
spine-c/spine-c/include/spine/Skin.h

@@ -35,6 +35,7 @@
 #include <spine/IkConstraintData.h>
 #include <spine/TransformConstraintData.h>
 #include <spine/PathConstraintData.h>
+#include <spine/PhysicsConstraintData.h>
 #include <spine/Array.h>
 
 #ifdef __cplusplus
@@ -54,6 +55,8 @@ _SP_ARRAY_DECLARE_TYPE(spTransformConstraintDataArray, spTransformConstraintData
 
 _SP_ARRAY_DECLARE_TYPE(spPathConstraintDataArray, spPathConstraintData*)
 
+_SP_ARRAY_DECLARE_TYPE(spPhysicsConstraintDataArray, spPhysicsConstraintData*)
+
 typedef struct spSkin {
 	char *name;
 
@@ -61,6 +64,7 @@ typedef struct spSkin {
 	spIkConstraintDataArray *ikConstraints;
 	spTransformConstraintDataArray *transformConstraints;
 	spPathConstraintDataArray *pathConstraints;
+    spPhysicsConstraintDataArray *physicsConstraints;
     spColor color;
 } spSkin;
 

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

@@ -56,6 +56,8 @@ SP_API void spTransformConstraint_dispose(spTransformConstraint *self);
 
 SP_API void spTransformConstraint_update(spTransformConstraint *self);
 
+SP_API void spTransformConstraint_setToSetupPose(spTransformConstraint *self);
+
 #ifdef __cplusplus
 }
 #endif

+ 400 - 280
spine-c/spine-c/src/spine/Animation.c

@@ -298,6 +298,110 @@ float spCurveTimeline1_getCurveValue(spCurveTimeline1 *self, float time) {
 	return _spCurveTimeline_getBezierValue(self, time, i, CURVE1_VALUE, curveType - CURVE_BEZIER);
 }
 
+float spCurveTimeline1_getRelativeValue(spCurveTimeline1 *self, float time, float alpha, spMixBlend blend, float current, float setup) {
+    float *frames = self->super.frames->items;
+    if (time < frames[0]) {
+        switch (blend) {
+            case SP_MIX_BLEND_SETUP:
+                return setup;
+            case SP_MIX_BLEND_FIRST:
+                return current + (setup - current) * alpha;
+            default:
+                return current;
+        }
+    }
+    float value = spCurveTimeline1_getCurveValue(self, time);
+    switch (blend) {
+        case SP_MIX_BLEND_SETUP:
+            return setup + value * alpha;
+        case SP_MIX_BLEND_FIRST:
+        case SP_MIX_BLEND_REPLACE:
+            value += setup - current;
+            break;
+        case SP_MIX_BLEND_ADD:
+            break;
+    }
+    return current + value * alpha;
+}
+
+float spCurveTimeline1_getAbsoluteValue(spCurveTimeline1 *self, float time, float alpha, spMixBlend blend, float current, float setup) {
+    float *frames = self->super.frames->items;
+    if (time < frames[0]) {
+        switch (blend) {
+            case SP_MIX_BLEND_SETUP:
+                return setup;
+            case SP_MIX_BLEND_FIRST:
+                return current + (setup - current) * alpha;
+            default:
+                return current;
+        }
+    }
+    float value = spCurveTimeline1_getCurveValue(self, time);
+    if (blend == SP_MIX_BLEND_SETUP) return setup + (value - setup) * alpha;
+    return current + (value - current) * alpha;
+}
+
+float spCurveTimeline1_getAbsoluteValue2(spCurveTimeline1 *self, float time, float alpha, spMixBlend blend, float current, float setup, float value) {
+    float *frames = self->super.frames->items;
+    if (time < frames[0]) {
+        switch (blend) {
+            case SP_MIX_BLEND_SETUP:
+                return setup;
+            case SP_MIX_BLEND_FIRST:
+                return current + (setup - current) * alpha;
+            default:
+                return current;
+        }
+    }
+    if (blend == SP_MIX_BLEND_SETUP) return setup + (value - setup) * alpha;
+    return current + (value - current) * alpha;
+}
+
+float spCurveTimeline1_getScaleValue (spCurveTimeline1 *self, float time, float alpha, spMixBlend blend, spMixDirection direction, float current, float setup) {
+    float *frames = self->super.frames->items;
+    if (time < frames[0]) {
+        switch (blend) {
+            case SP_MIX_BLEND_SETUP:
+                return setup;
+            case SP_MIX_BLEND_FIRST:
+                return current + (setup - current) * alpha;
+            default:
+                return current;
+        }
+    }
+    float value = spCurveTimeline1_getCurveValue(self, time) * setup;
+    if (alpha == 1) {
+        if (blend == SP_MIX_BLEND_ADD) return current + value - setup;
+        return value;
+    }
+    // Mixing out uses sign of setup or current pose, else use sign of key.
+    if (direction == SP_MIX_DIRECTION_OUT) {
+        switch (blend) {
+            case SP_MIX_BLEND_SETUP:
+                return setup + (ABS(value) * SIGNUM(setup) - setup) * alpha;
+            case SP_MIX_BLEND_FIRST:
+            case SP_MIX_BLEND_REPLACE:
+                return current + (ABS(value) * SIGNUM(current) - current) * alpha;
+            default:
+                break;
+        }
+    } else {
+        float s;
+        switch (blend) {
+            case SP_MIX_BLEND_SETUP:
+                s = ABS(setup) * SIGNUM(value);
+                return s + (value - s) * alpha;
+            case SP_MIX_BLEND_FIRST:
+            case SP_MIX_BLEND_REPLACE:
+                s = ABS(current) * SIGNUM(value);
+                return s + (value - s) * alpha;
+            default:
+                break;
+        }
+    }
+    return current + (value - setup) * alpha;
+}
+
 #define CURVE2_ENTRIES 3
 #define CURVE2_VALUE1 1
 #define CURVE2_VALUE2 2
@@ -314,40 +418,11 @@ SP_API void spCurveTimeline2_setFrame(spCurveTimeline1 *self, int frame, float t
 
 void _spRotateTimeline_apply(spTimeline *timeline, spSkeleton *skeleton, float lastTime, float time, spEvent **firedEvents,
 							 int *eventsCount, float alpha, spMixBlend blend, spMixDirection direction) {
-	spBone *bone;
-	float r;
 	spRotateTimeline *self = SUB_CAST(spRotateTimeline, timeline);
-	float *frames = self->super.super.frames->items;
-
-	bone = skeleton->bones[self->boneIndex];
-	if (!bone->active) return;
-
-	if (time < frames[0]) {
-		switch (blend) {
-			case SP_MIX_BLEND_SETUP:
-				bone->rotation = bone->data->rotation;
-				return;
-			case SP_MIX_BLEND_FIRST:
-				bone->rotation += (bone->data->rotation - bone->rotation) * alpha;
-			default: {
-			}
-		}
-		return;
-	}
-
-	r = spCurveTimeline1_getCurveValue(SUPER(self), time);
-	switch (blend) {
-		case SP_MIX_BLEND_SETUP:
-			bone->rotation = bone->data->rotation + r * alpha;
-			break;
-		case SP_MIX_BLEND_FIRST:
-		case SP_MIX_BLEND_REPLACE:
-			r += bone->data->rotation - bone->rotation;
-		case SP_MIX_BLEND_ADD:
-			bone->rotation += r * alpha;
-	}
+	spBone *bone = skeleton->bones[self->boneIndex];
+    if (bone->active) bone->rotation = spCurveTimeline1_getRelativeValue(SUPER(self), time, alpha, blend, bone->rotation, bone->data->rotation);
 
-	UNUSED(lastTime);
+    UNUSED(lastTime);
 	UNUSED(firedEvents);
 	UNUSED(eventsCount);
 	UNUSED(direction);
@@ -709,69 +784,12 @@ void spScaleTimeline_setFrame(spScaleTimeline *self, int frame, float time, floa
 void _spScaleXTimeline_apply(spTimeline *timeline, spSkeleton *skeleton, float lastTime, float time,
 							 spEvent **firedEvents, int *eventsCount, float alpha, spMixBlend blend,
 							 spMixDirection direction) {
-	spBone *bone;
-	float x;
-
 	spScaleXTimeline *self = SUB_CAST(spScaleXTimeline, timeline);
-	float *frames = self->super.super.frames->items;
-
-	bone = skeleton->bones[self->boneIndex];
-	if (!bone->active) return;
+	spBone *bone = skeleton->bones[self->boneIndex];
 
-	if (time < frames[0]) {
-		switch (blend) {
-			case SP_MIX_BLEND_SETUP:
-				bone->scaleX = bone->data->scaleX;
-				return;
-			case SP_MIX_BLEND_FIRST:
-				bone->scaleX += (bone->data->scaleX - bone->scaleX) * alpha;
-			default: {
-			}
-		}
-		return;
-	}
+    if (bone->active) bone->scaleX = spCurveTimeline1_getScaleValue(SUPER(self), time, alpha, blend, direction, bone->scaleX, bone->data->scaleX);
 
-	x = spCurveTimeline1_getCurveValue(SUPER(self), time) * bone->data->scaleX;
-	if (alpha == 1) {
-		if (blend == SP_MIX_BLEND_ADD)
-			bone->scaleX += x - bone->data->scaleX;
-		else
-			bone->scaleX = x;
-	} else {
-		/* Mixing out uses sign of setup or current pose, else use sign of key. */
-		float bx;
-		if (direction == SP_MIX_DIRECTION_OUT) {
-			switch (blend) {
-				case SP_MIX_BLEND_SETUP:
-					bx = bone->data->scaleX;
-					bone->scaleX = bx + (ABS(x) * SIGNUM(bx) - bx) * alpha;
-					break;
-				case SP_MIX_BLEND_FIRST:
-				case SP_MIX_BLEND_REPLACE:
-					bx = bone->scaleX;
-					bone->scaleX = bx + (ABS(x) * SIGNUM(bx) - bx) * alpha;
-					break;
-				case SP_MIX_BLEND_ADD:
-					bone->scaleX += (x - bone->data->scaleX) * alpha;
-			}
-		} else {
-			switch (blend) {
-				case SP_MIX_BLEND_SETUP:
-					bx = ABS(bone->data->scaleX) * SIGNUM(x);
-					bone->scaleX = bx + (x - bx) * alpha;
-					break;
-				case SP_MIX_BLEND_FIRST:
-				case SP_MIX_BLEND_REPLACE:
-					bx = ABS(bone->scaleX) * SIGNUM(x);
-					bone->scaleX = bx + (x - bx) * alpha;
-					break;
-				case SP_MIX_BLEND_ADD:
-					bone->scaleX += (x - bone->data->scaleX) * alpha;
-			}
-		}
-	}
-
-	UNUSED(lastTime);
+    UNUSED(lastTime);
 	UNUSED(firedEvents);
 	UNUSED(eventsCount);
 }
@@ -795,69 +813,12 @@ void spScaleXTimeline_setFrame(spScaleXTimeline *self, int frame, float time, fl
 void _spScaleYTimeline_apply(spTimeline *timeline, spSkeleton *skeleton, float lastTime, float time,
 							 spEvent **firedEvents, int *eventsCount, float alpha, spMixBlend blend,
 							 spMixDirection direction) {
-	spBone *bone;
-	float y;
-
 	spScaleYTimeline *self = SUB_CAST(spScaleYTimeline, timeline);
-	float *frames = self->super.super.frames->items;
+	spBone *bone = skeleton->bones[self->boneIndex];
 
-	bone = skeleton->bones[self->boneIndex];
-	if (!bone->active) return;
+    if (bone->active) bone->scaleY = spCurveTimeline1_getScaleValue(SUPER(self), time, alpha, blend, direction, bone->scaleX, bone->data->scaleY);
 
-	if (time < frames[0]) {
-		switch (blend) {
-			case SP_MIX_BLEND_SETUP:
-				bone->scaleY = bone->data->scaleY;
-				return;
-			case SP_MIX_BLEND_FIRST:
-				bone->scaleY += (bone->data->scaleY - bone->scaleY) * alpha;
-			default: {
-			}
-		}
-		return;
-	}
-
-	y = spCurveTimeline1_getCurveValue(SUPER(self), time) * bone->data->scaleY;
-	if (alpha == 1) {
-		if (blend == SP_MIX_BLEND_ADD)
-			bone->scaleY += y - bone->data->scaleY;
-		else
-			bone->scaleY = y;
-	} else {
-		/* Mixing out uses sign of setup or current pose, else use sign of key. */
-		float by = 0;
-		if (direction == SP_MIX_DIRECTION_OUT) {
-			switch (blend) {
-				case SP_MIX_BLEND_SETUP:
-					by = bone->data->scaleY;
-					bone->scaleY = by + (ABS(y) * SIGNUM(by) - by) * alpha;
-					break;
-				case SP_MIX_BLEND_FIRST:
-				case SP_MIX_BLEND_REPLACE:
-					by = bone->scaleY;
-					bone->scaleY = by + (ABS(y) * SIGNUM(by) - by) * alpha;
-					break;
-				case SP_MIX_BLEND_ADD:
-					bone->scaleY += (y - bone->data->scaleY) * alpha;
-			}
-		} else {
-			switch (blend) {
-				case SP_MIX_BLEND_SETUP:
-					by = ABS(bone->data->scaleY) * SIGNUM(y);
-					bone->scaleY = by + (y - by) * alpha;
-					break;
-				case SP_MIX_BLEND_FIRST:
-				case SP_MIX_BLEND_REPLACE:
-					by = ABS(bone->scaleY) * SIGNUM(y);
-					bone->scaleY = by + (y - by) * alpha;
-					break;
-				case SP_MIX_BLEND_ADD:
-					bone->scaleY += (y - bone->data->scaleY) * alpha;
-			}
-		}
-	}
-
-	UNUSED(lastTime);
+    UNUSED(lastTime);
 	UNUSED(firedEvents);
 	UNUSED(eventsCount);
 }
@@ -970,41 +931,12 @@ void spShearTimeline_setFrame(spShearTimeline *self, int frame, float time, floa
 void _spShearXTimeline_apply(spTimeline *timeline, spSkeleton *skeleton, float lastTime, float time,
 							 spEvent **firedEvents, int *eventsCount, float alpha, spMixBlend blend,
 							 spMixDirection direction) {
-	spBone *bone;
-	float x;
-
 	spShearXTimeline *self = SUB_CAST(spShearXTimeline, timeline);
-	float *frames = self->super.super.frames->items;
-
-	bone = skeleton->bones[self->boneIndex];
-	if (!bone->active) return;
+	spBone *bone = skeleton->bones[self->boneIndex];
 
-	if (time < frames[0]) {
-		switch (blend) {
-			case SP_MIX_BLEND_SETUP:
-				bone->shearX = bone->data->shearX;
-				return;
-			case SP_MIX_BLEND_FIRST:
-				bone->shearX += (bone->data->shearX - bone->shearX) * alpha;
-			default: {
-			}
-		}
-		return;
-	}
+    if (bone->active) bone->shearX = spCurveTimeline1_getRelativeValue(SUPER(self), time, alpha, blend, bone->shearX, bone->data->shearX);
 
-	x = spCurveTimeline1_getCurveValue(SUPER(self), time);
-	switch (blend) {
-		case SP_MIX_BLEND_SETUP:
-			bone->shearX = bone->data->shearX + x * alpha;
-			break;
-		case SP_MIX_BLEND_FIRST:
-		case SP_MIX_BLEND_REPLACE:
-			bone->shearX += (bone->data->shearX + x - bone->shearX) * alpha;
-			break;
-		case SP_MIX_BLEND_ADD:
-			bone->shearX += x * alpha;
-	}
-	UNUSED(lastTime);
+    UNUSED(lastTime);
 	UNUSED(firedEvents);
 	UNUSED(eventsCount);
 	UNUSED(direction);
@@ -1029,42 +961,12 @@ void spShearXTimeline_setFrame(spShearXTimeline *self, int frame, float time, fl
 void _spShearYTimeline_apply(spTimeline *timeline, spSkeleton *skeleton, float lastTime, float time,
 							 spEvent **firedEvents, int *eventsCount, float alpha, spMixBlend blend,
 							 spMixDirection direction) {
-	spBone *bone;
-	float y;
-
 	spShearYTimeline *self = SUB_CAST(spShearYTimeline, timeline);
-	float *frames = self->super.super.frames->items;
+	spBone *bone = skeleton->bones[self->boneIndex];
 
-	bone = skeleton->bones[self->boneIndex];
-	if (!bone->active) return;
+    if (bone->active) bone->shearY = spCurveTimeline1_getRelativeValue(SUPER(self), time, alpha, blend, bone->shearY, bone->data->shearY);
 
-	if (time < frames[0]) {
-		switch (blend) {
-			case SP_MIX_BLEND_SETUP:
-				bone->shearY = bone->data->shearY;
-				return;
-			case SP_MIX_BLEND_FIRST:
-				bone->shearY += (bone->data->shearY - bone->shearY) * alpha;
-			default: {
-			}
-		}
-		return;
-	}
-
-	y = spCurveTimeline1_getCurveValue(SUPER(self), time);
-	switch (blend) {
-		case SP_MIX_BLEND_SETUP:
-			bone->shearY = bone->data->shearY + y * alpha;
-			break;
-		case SP_MIX_BLEND_FIRST:
-		case SP_MIX_BLEND_REPLACE:
-			bone->shearY += (bone->data->shearY + y - bone->shearY) * alpha;
-			break;
-		case SP_MIX_BLEND_ADD:
-			bone->shearY += y * alpha;
-	}
-
-	UNUSED(lastTime);
+    UNUSED(lastTime);
 	UNUSED(firedEvents);
 	UNUSED(eventsCount);
 	UNUSED(direction);
@@ -2053,7 +1955,7 @@ void _spSequenceTimeline_apply(spTimeline *timeline, spSkeleton *skeleton, float
 	count = sequence->regions->size;
 	mode = modeAndIndex & 0xf;
 	if (mode != SP_SEQUENCE_MODE_HOLD) {
-		index += (int) (((time - before) / delay + 0.00001));
+		index += (int) (((time - before) / delay + 0.0001));
 		switch (mode) {
 			case SP_SEQUENCE_MODE_ONCE:
 				index = MIN(count - 1, index);
@@ -2252,6 +2154,53 @@ void spDrawOrderTimeline_setFrame(spDrawOrderTimeline *self, int frame, float ti
 	}
 }
 
+/**/
+void _spInheritTimeline_apply(spTimeline *timeline, spSkeleton *skeleton, float lastTime, float time,
+                                spEvent **firedEvents, int *eventsCount, float alpha, spMixBlend blend,
+                                spMixDirection direction) {
+    spInheritTimeline *self = (spInheritTimeline *)timeline;
+    spBone *bone = skeleton->bones[self->boneIndex];
+    float *frames = self->super.frames->items;
+    if (!bone->active) return;
+
+    if (time < frames[0]) {
+        if (blend == SP_MIX_BLEND_SETUP || blend == SP_MIX_BLEND_FIRST) bone->inherit = bone->data->inherit;
+        return;
+    }
+    int idx = search2(self->super.frames, time, 2) + 1;
+    bone->inherit = (spInherit) frames[idx];
+
+    UNUSED(lastTime);
+    UNUSED(firedEvents);
+    UNUSED(eventsCount);
+    UNUSED(alpha);
+    UNUSED(direction);
+}
+
+void _spInheritTimeline_dispose(spTimeline *timeline) {
+    // no-op, spTimeline_dispose disposes frames.
+    UNUSED(timeline);
+}
+
+spInheritTimeline *spInheritTimeline_create(int framesCount, int boneIndex) {
+    spInheritTimeline *self = NEW(spInheritTimeline);
+    spPropertyId ids[1];
+    ids[0] = (spPropertyId) SP_PROPERTY_INHERIT << 32;
+    _spTimeline_init(SUPER(self), framesCount, 2, ids, 1, SP_TIMELINE_INHERIT, _spInheritTimeline_dispose,
+                     _spInheritTimeline_apply, 0);
+
+    self->boneIndex = boneIndex;
+
+    return self;
+}
+
+void spInheritTimeline_setFrame(spDrawOrderTimeline *self, int frame, float time, spInherit inherit) {
+    frame *= 2;
+    self->super.frames->items[frame] = time;
+    self->super.frames->items[frame + 1] = inherit;
+}
+
+
 /**/
 
 static const int IKCONSTRAINT_ENTRIES = 6;
@@ -2515,35 +2464,9 @@ static const int PATHCONSTRAINTPOSITION_VALUE = 1;
 void _spPathConstraintPositionTimeline_apply(spTimeline *timeline, spSkeleton *skeleton, float lastTime, float time,
 											 spEvent **firedEvents, int *eventsCount, float alpha, spMixBlend blend,
 											 spMixDirection direction) {
-	float position;
-	spPathConstraint *constraint;
-	spPathConstraintPositionTimeline *self = (spPathConstraintPositionTimeline *) timeline;
-	float *frames;
-
-	constraint = skeleton->pathConstraints[self->pathConstraintIndex];
-	if (!constraint->active) return;
-
-	frames = self->super.super.frames->items;
-
-	if (time < frames[0]) {
-		switch (blend) {
-			case SP_MIX_BLEND_SETUP:
-				constraint->position = constraint->data->position;
-				return;
-			case SP_MIX_BLEND_FIRST:
-				constraint->position += (constraint->data->position - constraint->position) * alpha;
-				return;
-			default:
-				return;
-		}
-	}
-
-	position = spCurveTimeline1_getCurveValue(SUPER(self), time);
-
-	if (blend == SP_MIX_BLEND_SETUP)
-		constraint->position = constraint->data->position + (position - constraint->data->position) * alpha;
-	else
-		constraint->position += (position - constraint->position) * alpha;
+    spPathConstraintPositionTimeline *self = (spPathConstraintPositionTimeline *) timeline;
+    spPathConstraint *constraint = skeleton->pathConstraints[self->pathConstraintIndex];
+    if (constraint->active) constraint->position = spCurveTimeline1_getAbsoluteValue(SUPER(self), time, alpha, blend, constraint->position, constraint->data->position);
 
 	UNUSED(lastTime);
 	UNUSED(firedEvents);
@@ -2577,40 +2500,14 @@ static const int PATHCONSTRAINTSPACING_VALUE = 1;
 void _spPathConstraintSpacingTimeline_apply(spTimeline *timeline, spSkeleton *skeleton, float lastTime, float time,
 											spEvent **firedEvents, int *eventsCount, float alpha, spMixBlend blend,
 											spMixDirection direction) {
-	float spacing;
-	spPathConstraint *constraint;
-	spPathConstraintSpacingTimeline *self = (spPathConstraintSpacingTimeline *) timeline;
-	float *frames;
+    spPathConstraintSpacingTimeline *self = (spPathConstraintSpacingTimeline *) timeline;
+    spPathConstraint *constraint = skeleton->pathConstraints[self->pathConstraintIndex];
+    if (constraint->active) constraint->spacing = spCurveTimeline1_getAbsoluteValue(SUPER(self), time, alpha, blend, constraint->spacing, constraint->data->spacing);
 
-	constraint = skeleton->pathConstraints[self->pathConstraintIndex];
-	if (!constraint->active) return;
-
-	frames = self->super.super.frames->items;
-
-	if (time < frames[0]) {
-		switch (blend) {
-			case SP_MIX_BLEND_SETUP:
-				constraint->spacing = constraint->data->spacing;
-				return;
-			case SP_MIX_BLEND_FIRST:
-				constraint->spacing += (constraint->data->spacing - constraint->spacing) * alpha;
-				return;
-			default:
-				return;
-		}
-	}
-
-	spacing = spCurveTimeline1_getCurveValue(SUPER(self), time);
-
-	if (blend == SP_MIX_BLEND_SETUP)
-		constraint->spacing = constraint->data->spacing + (spacing - constraint->data->spacing) * alpha;
-	else
-		constraint->spacing += (spacing - constraint->spacing) * alpha;
-
-	UNUSED(lastTime);
-	UNUSED(firedEvents);
-	UNUSED(eventsCount);
-	UNUSED(direction);
+    UNUSED(lastTime);
+    UNUSED(firedEvents);
+    UNUSED(eventsCount);
+    UNUSED(direction);
 }
 
 spPathConstraintSpacingTimeline *
@@ -2740,3 +2637,226 @@ void spPathConstraintMixTimeline_setFrame(spPathConstraintMixTimeline *self, int
 	frames[frame + PATHCONSTRAINTMIX_X] = mixX;
 	frames[frame + PATHCONSTRAINTMIX_Y] = mixY;
 }
+
+/**/
+
+int/*bool*/ _spPhysicsConstraintTimeline_global(spPhysicsConstraintData *data, spTimelineType type) {
+    switch(type) {
+        case SP_TIMELINE_PHYSICSCONSTRAINT_INERTIA:
+            return data->inertiaGlobal;
+        case SP_TIMELINE_PHYSICSCONSTRAINT_STRENGTH:
+            return data->strengthGlobal;
+        case SP_TIMELINE_PHYSICSCONSTRAINT_DAMPING:
+            return data->dampingGlobal;
+        case SP_TIMELINE_PHYSICSCONSTRAINT_MASS:
+            return data->massGlobal;
+        case SP_TIMELINE_PHYSICSCONSTRAINT_WIND:
+            return data->windGlobal;
+        case SP_TIMELINE_PHYSICSCONSTRAINT_GRAVITY:
+            return data->gravityGlobal;
+        case SP_TIMELINE_PHYSICSCONSTRAINT_MIX:
+            return data->mixGlobal;
+        default:
+            // should never happen
+            return 0;
+    }
+}
+
+void _spPhysicsConstraintTimeline_set(spPhysicsConstraint *constraint, spTimelineType type, float value) {
+    switch(type) {
+        case SP_TIMELINE_PHYSICSCONSTRAINT_INERTIA:
+            constraint->inertia = value;
+            break;
+        case SP_TIMELINE_PHYSICSCONSTRAINT_STRENGTH:
+            constraint->strength = value;
+            break;
+        case SP_TIMELINE_PHYSICSCONSTRAINT_DAMPING:
+            constraint->damping = value;
+            break;
+        case SP_TIMELINE_PHYSICSCONSTRAINT_MASS:
+            constraint->massInverse = value;
+            break;
+        case SP_TIMELINE_PHYSICSCONSTRAINT_WIND:
+            constraint->wind = value;
+            break;
+        case SP_TIMELINE_PHYSICSCONSTRAINT_GRAVITY:
+            constraint->gravity = value;
+            break;
+        case SP_TIMELINE_PHYSICSCONSTRAINT_MIX:
+            constraint->mix = value;
+            break;
+        default:
+            // should never happen
+            break;
+    }
+}
+
+float _spPhysicsConstraintTimeline_get(spPhysicsConstraint *constraint, spTimelineType type) {
+    switch(type) {
+        case SP_TIMELINE_PHYSICSCONSTRAINT_INERTIA:
+            return constraint->inertia;
+        case SP_TIMELINE_PHYSICSCONSTRAINT_STRENGTH:
+            return constraint->strength;
+        case SP_TIMELINE_PHYSICSCONSTRAINT_DAMPING:
+            return constraint->damping;
+        case SP_TIMELINE_PHYSICSCONSTRAINT_MASS:
+            return constraint->massInverse;
+        case SP_TIMELINE_PHYSICSCONSTRAINT_WIND:
+            return constraint->wind;
+        case SP_TIMELINE_PHYSICSCONSTRAINT_GRAVITY:
+            return constraint->gravity;
+        case SP_TIMELINE_PHYSICSCONSTRAINT_MIX:
+            return constraint->mix;
+        default:
+            // should never happen
+            return 0;
+    }
+}
+
+float _spPhysicsConstraintTimeline_setup(spPhysicsConstraint *constraint, spTimelineType type) {
+    switch(type) {
+        case SP_TIMELINE_PHYSICSCONSTRAINT_INERTIA:
+            return constraint->data->inertia;
+        case SP_TIMELINE_PHYSICSCONSTRAINT_STRENGTH:
+            return constraint->data->strength;
+        case SP_TIMELINE_PHYSICSCONSTRAINT_DAMPING:
+            return constraint->data->damping;
+        case SP_TIMELINE_PHYSICSCONSTRAINT_MASS:
+            return constraint->data->massInverse;
+        case SP_TIMELINE_PHYSICSCONSTRAINT_WIND:
+            return constraint->data->wind;
+        case SP_TIMELINE_PHYSICSCONSTRAINT_GRAVITY:
+            return constraint->data->gravity;
+        case SP_TIMELINE_PHYSICSCONSTRAINT_MIX:
+            return constraint->data->mix;
+        default:
+            // should never happen
+            return 0;
+    }
+}
+
+void _spPhysicsConstraintTimeline_apply(spTimeline *timeline, spSkeleton *skeleton, float lastTime, float time,
+                                       spEvent **firedEvents, int *eventsCount, float alpha, spMixBlend blend,
+                                       spMixDirection direction) {
+    spPhysicsConstraintTimeline *self = SUB_CAST(spPhysicsConstraintTimeline, timeline);
+    spTimelineType type = self->super.super.type;
+    float *frames = self->super.super.frames->items;
+    if (self->physicsConstraintIndex == -1) {
+        float value = time >= frames[0] ? spCurveTimeline1_getCurveValue(SUPER(self), time) : 0;
+
+        spPhysicsConstraint **physicsConstraints = skeleton->physicsConstraints;
+        for (int i = 0; i < skeleton->physicsConstraintsCount; i++) {
+            spPhysicsConstraint *constraint = physicsConstraints[i];
+            if (constraint->active && _spPhysicsConstraintTimeline_global(constraint->data, type))
+                _spPhysicsConstraintTimeline_set(constraint, type,spCurveTimeline1_getAbsoluteValue2(SUPER(self), time, alpha, blend, _spPhysicsConstraintTimeline_get(constraint, type), _spPhysicsConstraintTimeline_setup(constraint, type), value));
+        }
+    } else {
+        spPhysicsConstraint *constraint = skeleton->physicsConstraints[self->physicsConstraintIndex];
+        if (constraint->active) _spPhysicsConstraintTimeline_set(constraint, type, spCurveTimeline1_getAbsoluteValue(SUPER(self), time, alpha, blend, _spPhysicsConstraintTimeline_get(constraint, type), _spPhysicsConstraintTimeline_setup(constraint, type)));
+    }
+    UNUSED(lastTime);
+    UNUSED(firedEvents);
+    UNUSED(eventsCount);
+    UNUSED(direction);
+}
+
+spPhysicsConstraintTimeline *
+spPhysicsConstraintTimeline_create(int frameCount, int bezierCount, int physicsConstraintIndex, spTimelineType type) {
+    spPhysicsConstraintTimeline *timeline = NEW(spPhysicsConstraintTimeline);
+    spPropertyId ids[1];
+    spPropertyId id;
+    switch(type) {
+        case SP_TIMELINE_PHYSICSCONSTRAINT_INERTIA:
+            id = SP_PROPERTY_PHYSICSCONSTRAINT_INERTIA;
+            break;
+        case SP_TIMELINE_PHYSICSCONSTRAINT_STRENGTH:
+            id = SP_PROPERTY_PHYSICSCONSTRAINT_STRENGTH;
+            break;
+        case SP_TIMELINE_PHYSICSCONSTRAINT_DAMPING:
+            id = SP_PROPERTY_PHYSICSCONSTRAINT_DAMPING;
+            break;
+        case SP_TIMELINE_PHYSICSCONSTRAINT_MASS:
+            id = SP_PROPERTY_PHYSICSCONSTRAINT_MASS;
+            break;
+        case SP_TIMELINE_PHYSICSCONSTRAINT_WIND:
+            id = SP_PROPERTY_PHYSICSCONSTRAINT_WIND;
+            break;
+        case SP_TIMELINE_PHYSICSCONSTRAINT_GRAVITY:
+            id = SP_PROPERTY_PHYSICSCONSTRAINT_GRAVITY;
+            break;
+        case SP_TIMELINE_PHYSICSCONSTRAINT_MIX:
+            id = SP_PROPERTY_PHYSICSCONSTRAINT_MIX;
+            break;
+        default:
+            // should never happen
+            id = SP_PROPERTY_PHYSICSCONSTRAINT_INERTIA;
+    }
+    ids[0] = ((spPropertyId) id << 32) | physicsConstraintIndex;
+    _spCurveTimeline_init(SUPER(timeline), frameCount, CURVE1_ENTRIES, bezierCount, ids, 1, type,
+                          _spCurveTimeline_dispose, _spPhysicsConstraintTimeline_apply, _spCurveTimeline_setBezier);
+    timeline->physicsConstraintIndex = physicsConstraintIndex;
+    return timeline;
+}
+
+void spPhysicsConstraintTimeline_setFrame(spPhysicsConstraintTimeline *self, int frame, float time, float value) {
+    spCurveTimeline1_setFrame(SUPER(self), frame, time, value);
+}
+
+/**/
+void _spPhysicsConstraintResetTimeline_apply(spTimeline *timeline, spSkeleton *skeleton, float lastTime, float time,
+                              spEvent **firedEvents, int *eventsCount, float alpha, spMixBlend blend,
+                              spMixDirection direction) {
+    spPhysicsConstraintResetTimeline *self = (spPhysicsConstraintResetTimeline *)timeline;
+    spPhysicsConstraint *constraint = NULL;
+    if (self->physicsConstraintIndex != -1) {
+        constraint = skeleton->physicsConstraints[self->physicsConstraintIndex];
+        if (!constraint->active) return;
+    }
+
+    float *frames = SUPER(self)->frames->items;
+    if (lastTime > time) {// Apply after lastTime for looped animations.
+        _spPhysicsConstraintResetTimeline_apply(SUPER(self), skeleton, lastTime, INT_MAX, NULL, 0, alpha, blend, direction);
+        lastTime = -1;
+    } else if (lastTime >= frames[SUPER(self)->frameCount - 1])// Last time is after last frame.
+        return;
+    if (time < frames[0]) return;
+
+    if (lastTime < frames[0] || time >= frames[search(self->super.frames, lastTime) + 1]) {
+        if (constraint != NULL)
+            spPhysicsConstraint_reset(constraint);
+        else {
+            spPhysicsConstraint **physicsConstraints = skeleton->physicsConstraints;
+            for (int i = 0; i < skeleton->physicsConstraintsCount; i++) {
+                constraint = physicsConstraints[i];
+                if (constraint->active) spPhysicsConstraint_reset(constraint);
+            }
+        }
+    }
+
+    UNUSED(lastTime);
+    UNUSED(firedEvents);
+    UNUSED(eventsCount);
+    UNUSED(alpha);
+    UNUSED(direction);
+}
+
+void _spPhysicsConstraintResetTimeline_dispose(spTimeline *timeline) {
+    // no-op, spTimeline_dispose disposes frames.
+    UNUSED(timeline);
+}
+
+spPhysicsConstraintResetTimeline *spPhysicsConstraintResetTimeline_create(int framesCount, int physicsConstraintIndex) {
+    spPhysicsConstraintResetTimeline *self = NEW(spPhysicsConstraintResetTimeline);
+    spPropertyId ids[1];
+    ids[0] = (spPropertyId) SP_PROPERTY_PHYSICSCONSTRAINT_RESET << 32;
+    _spTimeline_init(SUPER(self), framesCount, 1, ids, 1, SP_TIMELINE_PHYSICSCONSTRAINT_RESET, _spPhysicsConstraintResetTimeline_dispose,
+                     _spPhysicsConstraintResetTimeline_apply, 0);
+
+    self->physicsConstraintIndex = physicsConstraintIndex;
+
+    return self;
+}
+
+void spPhysicsResetTimeline_setFrame(spPhysicsConstraintResetTimeline *self, int frame, float time) {
+    self->super.frames->items[frame] = time;
+}

+ 40 - 22
spine-c/spine-c/src/spine/AnimationState.c

@@ -390,18 +390,19 @@ int spAnimationState_apply(spAnimationState *self, spSkeleton *skeleton) {
 	if (internal->animationsChanged) _spAnimationState_animationsChanged(self);
 
 	for (i = 0, n = self->tracksCount; i < n; i++) {
-		float mix;
+		float alpha;
 		current = self->tracks[i];
 		if (!current || current->delay > 0) continue;
 		applied = -1;
 		blend = i == 0 ? SP_MIX_BLEND_FIRST : current->mixBlend;
 
 		/* Apply mixing from entries first. */
-		mix = current->alpha;
+		alpha = current->alpha;
 		if (current->mixingFrom)
-			mix *= _spAnimationState_applyMixingFrom(self, current, skeleton, blend);
+            alpha *= _spAnimationState_applyMixingFrom(self, current, skeleton, blend);
 		else if (current->trackTime >= current->trackEnd && current->next == 0)
-			mix = 0;
+            alpha = 0;
+        int/*bool*/ attachments = alpha >= current->alphaAttachmentThreshold;
 
 		/* Apply current entry. */
 		animationLast = current->animationLast;
@@ -414,14 +415,14 @@ int spAnimationState_apply(spAnimationState *self, spSkeleton *skeleton) {
 			applyEvents = NULL;
 		}
 		timelines = current->animation->timelines->items;
-		if ((i == 0 && mix == 1) || blend == SP_MIX_BLEND_ADD) {
+		if ((i == 0 && alpha == 1) || blend == SP_MIX_BLEND_ADD) {
 			for (ii = 0; ii < timelineCount; ii++) {
 				timeline = timelines[ii];
 				if (timeline->type == SP_TIMELINE_ATTACHMENT) {
-					_spAnimationState_applyAttachmentTimeline(self, timeline, skeleton, applyTime, blend, -1);
+					_spAnimationState_applyAttachmentTimeline(self, timeline, skeleton, applyTime, blend, attachments);
 				} else {
 					spTimeline_apply(timelines[ii], skeleton, animationLast, applyTime, applyEvents,
-									 &internal->eventsCount, mix, blend, SP_MIX_DIRECTION_IN);
+                                     &internal->eventsCount, alpha, blend, SP_MIX_DIRECTION_IN);
 				}
 			}
 		} else {
@@ -436,13 +437,13 @@ int spAnimationState_apply(spAnimationState *self, spSkeleton *skeleton) {
 				timeline = timelines[ii];
 				timelineBlend = timelineMode->items[ii] == SUBSEQUENT ? blend : SP_MIX_BLEND_SETUP;
 				if (!shortestRotation && timeline->type == SP_TIMELINE_ROTATE)
-					_spAnimationState_applyRotateTimeline(self, timeline, skeleton, applyTime, mix, timelineBlend,
-														  timelinesRotation, ii << 1, firstFrame);
+					_spAnimationState_applyRotateTimeline(self, timeline, skeleton, applyTime, alpha, timelineBlend,
+                                                          timelinesRotation, ii << 1, firstFrame);
 				else if (timeline->type == SP_TIMELINE_ATTACHMENT)
-					_spAnimationState_applyAttachmentTimeline(self, timeline, skeleton, applyTime, timelineBlend, -1);
+					_spAnimationState_applyAttachmentTimeline(self, timeline, skeleton, applyTime, timelineBlend, attachments);
 				else
 					spTimeline_apply(timeline, skeleton, animationLast, applyTime, applyEvents, &internal->eventsCount,
-									 mix, timelineBlend, SP_MIX_DIRECTION_IN);
+                                     alpha, timelineBlend, SP_MIX_DIRECTION_IN);
 			}
 		}
 		_spAnimationState_queueEvents(self, current, animationTime);
@@ -500,8 +501,8 @@ float _spAnimationState_applyMixingFrom(spAnimationState *self, spTrackEntry *to
 		if (blend != SP_MIX_BLEND_FIRST) blend = from->mixBlend;
 	}
 
-	attachments = mix < from->attachmentThreshold;
-	drawOrder = mix < from->drawOrderThreshold;
+	attachments = mix < from->mixAttachmentThreshold;
+	drawOrder = mix < from->mixDrawOrderThreshold;
 	timelineCount = from->animation->timelines->size;
 	timelines = from->animation->timelines->items;
 	alphaHold = from->alpha * to->interruptAlpha;
@@ -566,7 +567,7 @@ float _spAnimationState_applyMixingFrom(spAnimationState *self, spTrackEntry *to
 													  timelinesRotation, i << 1, firstFrame);
 			else if (timeline->type == SP_TIMELINE_ATTACHMENT)
 				_spAnimationState_applyAttachmentTimeline(self, timeline, skeleton, applyTime, timelineBlend,
-														  attachments);
+														  attachments && alpha >= from->alphaAttachmentThreshold);
 			else {
 				if (drawOrder && timeline->type == SP_TIMELINE_DRAWORDER &&
 					timelineBlend == SP_MIX_BLEND_SETUP)
@@ -664,8 +665,8 @@ void _spAnimationState_applyRotateTimeline(spAnimationState *self, spTimeline *t
 
 	/* Mix between rotations using the direction of the shortest route on the first frame while detecting crosses. */
 	diff = r2 - r1;
-	diff -= (16384 - (int) (16384.499999999996 - diff / 360)) * 360;
-	if (diff == 0) {
+    diff -= CEIL(diff / 360 - 0.5) * 360;
+    if (diff == 0) {
 		total = timelinesRotation[i];
 	} else {
 		float lastTotal, lastDiff, loops;
@@ -714,10 +715,16 @@ void _spAnimationState_queueEvents(spAnimationState *self, spTrackEntry *entry,
 	}
 
 	/* Queue complete if completed a loop iteration or the animation. */
-	if (entry->loop)
-		complete = duration == 0 || (trackLastWrapped > FMOD(entry->trackTime, duration));
-	else
-		complete = (animationTime >= animationEnd && entry->animationLast < animationEnd);
+	if (entry->loop) {
+        if (duration == 0)
+            complete = -1;
+        else {
+            int cycles = (int) (entry->trackTime / duration);
+            complete = cycles > 0 && cycles > (int) (entry->trackLast / duration);
+        }
+    } else {
+        complete = (animationTime >= animationEnd && entry->animationLast < animationEnd);
+    }
 	if (complete) _spEventQueue_complete(internal->queue, entry);
 
 	/* Queue events after complete. */
@@ -910,8 +917,9 @@ _spAnimationState_trackEntry(spAnimationState *self, int trackIndex, spAnimation
 	entry->next = 0;
 
 	entry->eventThreshold = 0;
-	entry->attachmentThreshold = 0;
-	entry->drawOrderThreshold = 0;
+	entry->mixAttachmentThreshold = 0;
+    entry->alphaAttachmentThreshold = 0;
+	entry->mixDrawOrderThreshold = 0;
 
 	entry->animationStart = 0;
 	entry->animationEnd = animation->duration;
@@ -1051,6 +1059,16 @@ float spTrackEntry_getTrackComplete(spTrackEntry *entry) {
 	return entry->trackTime; /* Next update. */
 }
 
+void spTrackEntry_setMixDuration(spTrackEntry *entry, float mixDuration, float delay) {
+    entry->mixDuration = mixDuration;
+    if (entry->previous && delay <= 0) delay += spTrackEntry_getTrackComplete(entry) - mixDuration;
+    entry->delay = delay;
+}
+
+int spTrackEntry_wasApplied(spTrackEntry *entry) {
+    return entry->nextTrackLast != -1;
+}
+
 void _spTrackEntry_computeHold(spTrackEntry *entry, spAnimationState *state) {
 	spTrackEntry *to;
 	spTimeline **timelines;

+ 7 - 0
spine-c/spine-c/src/spine/Debug.c

@@ -209,6 +209,13 @@ void spDebug_printTimeline(spTimeline *timeline) {
 			spSequenceTimeline *t = (spSequenceTimeline *) timeline;
 			_spDebug_printTimelineBase(&t->super);
 		}
+        case SP_TIMELINE_INHERIT: {
+            spInheritTimeline *t = (spInheritTimeline *) timeline;
+            _spDebug_printTimelineBase(&t->super);
+        }
+        default: {
+            _spDebug_printTimelineBase(timeline);
+        }
 	}
 }
 

+ 8 - 0
spine-c/spine-c/src/spine/IkConstraint.c

@@ -71,6 +71,14 @@ void spIkConstraint_update(spIkConstraint *self) {
 	}
 }
 
+void spIkConstraint_setToSetupPose(spIkConstraint *self) {
+    self->bendDirection = self->data->bendDirection;
+    self->compress = self->data->compress;
+    self->stretch = self->data->stretch;
+    self->softness = self->data->softness;
+    self->mix = self->data->mix;
+}
+
 void spIkConstraint_apply1(spBone *bone, float targetX, float targetY, int /*boolean*/ compress, int /*boolean*/ stretch,
 						   int /*boolean*/ uniform, float alpha) {
 	spBone *p = bone->parent;

+ 2 - 2
spine-c/spine-c/src/spine/IkConstraintData.c

@@ -33,11 +33,11 @@
 spIkConstraintData *spIkConstraintData_create(const char *name) {
 	spIkConstraintData *self = NEW(spIkConstraintData);
 	MALLOC_STR(self->name, name);
-	self->bendDirection = 1;
+	self->bendDirection = 0;
 	self->compress = 0;
 	self->stretch = 0;
 	self->uniform = 0;
-	self->mix = 1;
+	self->mix = 0;
 	return self;
 }
 

+ 12 - 7
spine-c/spine-c/src/spine/PathConstraint.c

@@ -117,13 +117,9 @@ void spPathConstraint_update(spPathConstraint *self) {
 				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);
-					}
+                    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;
@@ -225,6 +221,15 @@ void spPathConstraint_update(spPathConstraint *self) {
 	}
 }
 
+void spPathConstraint_setToSetupPose(spPathConstraint *self) {
+    spPathConstraintData *data = self->data;
+    self->position = data->position;
+    self->spacing = data->spacing;
+    self->mixRotate = data->mixRotate;
+    self->mixX = data->mixX;
+    self->mixY = data->mixY;
+}
+
 static void _addBeforePosition(float p, float *temp, int i, float *out, int o) {
 	float x1 = temp[i], y1 = temp[i + 1], dx = temp[i + 2] - x1, dy = temp[i + 3] - y1, r = ATAN2(dy, dx);
 	out[o] = x1 + p * COS(r);

+ 4 - 6
spine-c/spine-c/src/spine/PointAttachment.c

@@ -58,10 +58,8 @@ void spPointAttachment_computeWorldPosition(spPointAttachment *self, spBone *bon
 }
 
 float spPointAttachment_computeWorldRotation(spPointAttachment *self, spBone *bone) {
-	float cosine, sine, x, y;
-	cosine = COS_DEG(self->rotation);
-	sine = SIN_DEG(self->rotation);
-	x = cosine * bone->a + sine * bone->b;
-	y = cosine * bone->c + sine * bone->d;
-	return ATAN2(y, x) * RAD_DEG;
+    float r = self->rotation * DEG_RAD, cosine = COS(r), sine = SIN(r);
+    float x = cosine * bone->a + sine * bone->b;
+    float y = cosine * bone->c + sine * bone->d;
+    return ATAN2DEG(y, x);
 }

+ 66 - 24
spine-c/spine-c/src/spine/Skeleton.c

@@ -60,6 +60,11 @@ spSkeleton *spSkeleton_create(spSkeletonData *data) {
 	_spSkeleton *internal = NEW(_spSkeleton);
 	spSkeleton *self = SUPER(internal);
 	self->data = data;
+	self->skin = NULL;
+	spColor_setFromFloats(&self->color, 1, 1, 1, 1);
+	self->scaleX = 1;
+	self->scaleY = 1;
+	self->time = 0;
 
 	self->bonesCount = self->data->bonesCount;
 	self->bones = MALLOC(spBone *, self->bonesCount);
@@ -116,6 +121,12 @@ spSkeleton *spSkeleton_create(spSkeletonData *data) {
 	for (i = 0; i < self->data->pathConstraintsCount; i++)
 		self->pathConstraints[i] = spPathConstraint_create(self->data->pathConstraints[i], self);
 
+	self->physicsConstraintsCount = data->physicsConstraintsCount;
+	self->physicsConstraints = MALLOC(spPhysicsConstraint *, self->physicsConstraintsCount);
+	for (i = 0; i < self->data->physicsConstraintsCount; i++)
+		self->physicsConstraints[i] = spPhysicsConstraint_create(self->data->physicsConstraints[i], self);
+
+
 	spColor_setFromFloats(&self->color, 1, 1, 1, 1);
 
 	self->scaleX = 1;
@@ -156,6 +167,10 @@ void spSkeleton_dispose(spSkeleton *self) {
 		spPathConstraint_dispose(self->pathConstraints[i]);
 	FREE(self->pathConstraints);
 
+	for (i = 0; i < self->physicsConstraintsCount; i++)
+		spPhysicsConstraint_dispose(self->physicsConstraints[i]);
+	FREE(self->physicsConstraints);
+
 	FREE(self->drawOrder);
 	FREE(self);
 }
@@ -324,17 +339,35 @@ static void _sortTransformConstraint(_spSkeleton *const internal, spTransformCon
 		constrained[i]->sorted = 1;
 }
 
+static void _sortPhysicsConstraint(_spSkeleton *const internal, spPhysicsConstraint *constraint) {
+	spBone *bone = constraint->bone;
+	constraint->active = constraint->bone->active && (!constraint->data->skinRequired || (internal->super.skin != 0 &&
+																						spPhysicsConstraintDataArray_contains(
+																								internal->super.skin->physicsConstraints,
+																								constraint->data)));
+	if (!constraint->active)
+		return;
+
+	_sortBone(internal, bone);
+	_addToUpdateCache(internal, SP_UPDATE_PHYSICS_CONSTRAINT, constraint);
+
+	_sortReset(bone->children, bone->childrenCount);
+	bone->sorted = -1;
+}
+
 void spSkeleton_updateCache(spSkeleton *self) {
 	int i, ii;
 	spBone **bones;
 	spIkConstraint **ikConstraints;
 	spPathConstraint **pathConstraints;
 	spTransformConstraint **transformConstraints;
-	int ikCount, transformCount, pathCount, constraintCount;
+	spPhysicsConstraint **physicsConstraints;
+	int ikCount, transformCount, pathCount, physicsCount, constraintCount;
 	_spSkeleton *internal = SUB_CAST(_spSkeleton, self);
 
 	internal->updateCacheCapacity =
-			self->bonesCount + self->ikConstraintsCount + self->transformConstraintsCount + self->pathConstraintsCount;
+			self->bonesCount + self->ikConstraintsCount + self->transformConstraintsCount + self->pathConstraintsCount +
+			 self->physicsConstraintsCount;
 	FREE(internal->updateCache);
 	internal->updateCache = MALLOC(_spUpdate, internal->updateCacheCapacity);
 	internal->updateCacheCount = 0;
@@ -362,10 +395,12 @@ void spSkeleton_updateCache(spSkeleton *self) {
 	ikConstraints = self->ikConstraints;
 	transformConstraints = self->transformConstraints;
 	pathConstraints = self->pathConstraints;
+	physicsConstraints = self->physicsConstraints;
 	ikCount = self->ikConstraintsCount;
 	transformCount = self->transformConstraintsCount;
 	pathCount = self->pathConstraintsCount;
-	constraintCount = ikCount + transformCount + pathCount;
+	physicsCount = self->physicsConstraintsCount;
+	constraintCount = ikCount + transformCount + pathCount + physicsCount;
 
 	i = 0;
 continue_outer:
@@ -396,6 +431,15 @@ continue_outer:
 				goto continue_outer;
 			}
 		}
+
+		for (ii = 0; ii < physicsCount; ii++) {
+			spPhysicsConstraint *physicsConstraint = physicsConstraints[ii];
+			if (physicsConstraint->data->order == i) {
+				_sortPhysicsConstraint(internal, physicsConstraint);
+				i++;
+				goto continue_outer;
+			}
+		}
 	}
 
 	for (i = 0; i < self->bonesCount; ++i)
@@ -495,33 +539,19 @@ void spSkeleton_setBonesToSetupPose(const spSkeleton *self) {
 		spBone_setToSetupPose(self->bones[i]);
 
 	for (i = 0; i < self->ikConstraintsCount; ++i) {
-		spIkConstraint *ikConstraint = self->ikConstraints[i];
-		ikConstraint->bendDirection = ikConstraint->data->bendDirection;
-		ikConstraint->compress = ikConstraint->data->compress;
-		ikConstraint->stretch = ikConstraint->data->stretch;
-		ikConstraint->softness = ikConstraint->data->softness;
-		ikConstraint->mix = ikConstraint->data->mix;
+		spIkConstraint_setToSetupPose(self->ikConstraints[i]);
 	}
 
 	for (i = 0; i < self->transformConstraintsCount; ++i) {
-		spTransformConstraint *constraint = self->transformConstraints[i];
-		spTransformConstraintData *data = constraint->data;
-		constraint->mixRotate = data->mixRotate;
-		constraint->mixX = data->mixX;
-		constraint->mixY = data->mixY;
-		constraint->mixScaleX = data->mixScaleX;
-		constraint->mixScaleY = data->mixScaleY;
-		constraint->mixShearY = data->mixShearY;
+		spTransformConstraint_setToSetupPose(self->transformConstraints[i]);
 	}
 
 	for (i = 0; i < self->pathConstraintsCount; ++i) {
-		spPathConstraint *constraint = self->pathConstraints[i];
-		spPathConstraintData *data = constraint->data;
-		constraint->position = data->position;
-		constraint->spacing = data->spacing;
-		constraint->mixRotate = data->mixRotate;
-		constraint->mixX = data->mixX;
-		constraint->mixY = data->mixY;
+		spPathConstraint_setToSetupPose(self->pathConstraints[i]);
+	}
+
+	for (i = 0; i < self->physicsConstraintsCount; ++i) {
+		spPhysicsConstraint_setToSetupPose(self->physicsConstraints[i]);
 	}
 }
 
@@ -645,3 +675,15 @@ spPhysicsConstraint *spSkeleton_findPhysicsConstraint(const spSkeleton *self, co
 		if (strcmp(self->physicsConstraints[i]->data->name, constraintName) == 0) return self->physicsConstraints[i];
 	return 0;
 }
+
+void spSkeleton_physicsTranslate(spSkeleton *self, float x, float y) {
+	for (int i = 0; i < (int) self->physicsConstraintsCount; i++) {
+		spPhysicsConstraint_translate(self->physicsConstraints[i], x, y);
+	}
+}
+
+void spSkeleton_physicsRotate(spSkeleton *self, float x, float y, float degrees) {
+	for (int i = 0; i < (int) self->physicsConstraintsCount; i++) {
+		spPhysicsConstraint_rotate(self->physicsConstraints[i], x, y, degrees);
+	}
+}

+ 15 - 0
spine-c/spine-c/src/spine/Skin.c

@@ -39,6 +39,8 @@ _SP_ARRAY_IMPLEMENT_TYPE(spTransformConstraintDataArray, spTransformConstraintDa
 
 _SP_ARRAY_IMPLEMENT_TYPE(spPathConstraintDataArray, spPathConstraintData *)
 
+_SP_ARRAY_IMPLEMENT_TYPE(spPhysicsConstraintDataArray, spPhysicsConstraintData *)
+
 _Entry *_Entry_create(int slotIndex, const char *name, spAttachment *attachment) {
 	_Entry *self = NEW(_Entry);
 	self->slotIndex = slotIndex;
@@ -72,6 +74,7 @@ spSkin *spSkin_create(const char *name) {
 	self->ikConstraints = spIkConstraintDataArray_create(4);
 	self->transformConstraints = spTransformConstraintDataArray_create(4);
 	self->pathConstraints = spPathConstraintDataArray_create(4);
+    self->physicsConstraints = spPhysicsConstraintDataArray_create(4);
     spColor_setFromFloats(&self->color, 0.99607843f, 0.61960787f, 0.30980393f, 1);
 	return self;
 }
@@ -104,6 +107,7 @@ void spSkin_dispose(spSkin *self) {
 	spIkConstraintDataArray_dispose(self->ikConstraints);
 	spTransformConstraintDataArray_dispose(self->transformConstraints);
 	spPathConstraintDataArray_dispose(self->pathConstraints);
+    spPhysicsConstraintDataArray_dispose(self->physicsConstraints);
 	FREE(self->name);
 	FREE(self);
 }
@@ -198,6 +202,11 @@ void spSkin_addSkin(spSkin *self, const spSkin *other) {
 			spPathConstraintDataArray_add(self->pathConstraints, other->pathConstraints->items[i]);
 	}
 
+    for (i = 0; i < other->physicsConstraints->size; i++) {
+        if (!spPhysicsConstraintDataArray_contains(self->physicsConstraints, other->physicsConstraints->items[i]))
+            spPhysicsConstraintDataArray_add(self->physicsConstraints, other->physicsConstraints->items[i]);
+    }
+
 	entry = spSkin_getAttachments(other);
 	while (entry) {
 		spSkin_setAttachment(self, entry->slotIndex, entry->name, entry->attachment);
@@ -229,6 +238,11 @@ void spSkin_copySkin(spSkin *self, const spSkin *other) {
 			spPathConstraintDataArray_add(self->pathConstraints, other->pathConstraints->items[i]);
 	}
 
+    for (i = 0; i < other->physicsConstraints->size; i++) {
+        if (!spPhysicsConstraintDataArray_contains(self->physicsConstraints, other->physicsConstraints->items[i]))
+            spPhysicsConstraintDataArray_add(self->physicsConstraints, other->physicsConstraints->items[i]);
+    }
+
 	entry = spSkin_getAttachments(other);
 	while (entry) {
 		if (entry->attachment->type == SP_ATTACHMENT_MESH) {
@@ -279,4 +293,5 @@ void spSkin_clear(spSkin *self) {
 	spIkConstraintDataArray_clear(self->ikConstraints);
 	spTransformConstraintDataArray_clear(self->transformConstraints);
 	spPathConstraintDataArray_clear(self->pathConstraints);
+    spPhysicsConstraintDataArray_clear(self->physicsConstraints);
 }

+ 14 - 4
spine-c/spine-c/src/spine/TransformConstraint.c

@@ -193,8 +193,8 @@ void _spTransformConstraint_applyAbsoluteLocal(spTransformConstraint *self) {
 		rotation = bone->arotation;
 		if (mixRotate != 0) {
 			r = target->arotation - rotation + self->data->offsetRotation;
-			r -= (16384 - (int) (16384.499999999996 - r / 360)) * 360;
-			rotation += r * mixRotate;
+            r -= CEIL(r / 360 - 0.5) * 360;
+            rotation += r * mixRotate;
 		}
 
 		x = bone->ax, y = bone->ay;
@@ -210,8 +210,8 @@ void _spTransformConstraint_applyAbsoluteLocal(spTransformConstraint *self) {
 		shearY = bone->ashearY;
 		if (mixShearY != 0) {
 			r = target->ashearY - shearY + self->data->offsetShearY;
-			r -= (16384 - (int) (16384.499999999996 - r / 360)) * 360;
-			shearY += r * mixShearY;
+            r -= CEIL(r / 360 - 0.5) * 360;
+            shearY += r * mixShearY;
 		}
 
 		spBone_updateWorldTransformWith(bone, x, y, rotation, scaleX, scaleY, bone->ashearX, shearY);
@@ -257,3 +257,13 @@ void spTransformConstraint_update(spTransformConstraint *self) {
 			_spTransformConstraint_applyAbsoluteWorld(self);
 	}
 }
+
+void spTransformConstraint_setToSetupPose(spTransformConstraint *self) {
+    spTransformConstraintData *data = self->data;
+    self->mixRotate = data->mixRotate;
+    self->mixX = data->mixX;
+    self->mixY = data->mixY;
+    self->mixScaleX = data->mixScaleX;
+    self->mixScaleY = data->mixScaleY;
+    self->mixShearY = data->mixShearY;
+}