瀏覽代碼

[c][cpp] Fix all warnings on Clang, more physics constraints porting

Mario Zechner 1 年之前
父節點
當前提交
8c79c7c1de
共有 49 個文件被更改,包括 637 次插入386 次删除
  1. 1 1
      spine-c/spine-c/include/spine/AnimationState.h
  2. 1 1
      spine-c/spine-c/include/spine/Atlas.h
  3. 1 1
      spine-c/spine-c/include/spine/Bone.h
  4. 1 1
      spine-c/spine-c/include/spine/Color.h
  5. 1 1
      spine-c/spine-c/include/spine/SkeletonBounds.h
  6. 1 1
      spine-c/spine-c/include/spine/SkeletonClipping.h
  7. 1 1
      spine-c/spine-c/include/spine/SkeletonData.h
  8. 1 1
      spine-c/spine-c/include/spine/Triangulator.h
  9. 2 2
      spine-c/spine-c/include/spine/extension.h
  10. 1 1
      spine-c/spine-c/src/spine/AnimationState.c
  11. 1 1
      spine-c/spine-c/src/spine/Atlas.c
  12. 1 1
      spine-c/spine-c/src/spine/AttachmentLoader.c
  13. 26 21
      spine-c/spine-c/src/spine/Bone.c
  14. 1 1
      spine-c/spine-c/src/spine/Color.c
  15. 0 2
      spine-c/spine-c/src/spine/Json.c
  16. 1 1
      spine-c/spine-c/src/spine/Sequence.c
  17. 2 2
      spine-c/spine-c/src/spine/SkeletonBinary.c
  18. 1 1
      spine-c/spine-c/src/spine/SkeletonBounds.c
  19. 1 1
      spine-c/spine-c/src/spine/SkeletonClipping.c
  20. 1 1
      spine-c/spine-c/src/spine/SkeletonData.c
  21. 1 1
      spine-c/spine-c/src/spine/SkeletonJson.c
  22. 1 1
      spine-c/spine-c/src/spine/Triangulator.c
  23. 4 4
      spine-c/spine-c/src/spine/extension.c
  24. 8 0
      spine-cpp/spine-cpp/include/spine/CurveTimeline.h
  25. 3 3
      spine-cpp/spine-cpp/include/spine/IkConstraintTimeline.h
  26. 3 4
      spine-cpp/spine-cpp/include/spine/PathConstraintMixTimeline.h
  27. 3 3
      spine-cpp/spine-cpp/include/spine/PathConstraintPositionTimeline.h
  28. 4 4
      spine-cpp/spine-cpp/include/spine/Physics.h
  29. 18 0
      spine-cpp/spine-cpp/include/spine/PhysicsConstraint.h
  30. 288 0
      spine-cpp/spine-cpp/include/spine/PhysicsConstraintTimeline.h
  31. 9 1
      spine-cpp/spine-cpp/include/spine/Property.h
  32. 1 1
      spine-cpp/spine-cpp/include/spine/Timeline.h
  33. 3 3
      spine-cpp/spine-cpp/include/spine/TransformConstraintTimeline.h
  34. 103 0
      spine-cpp/spine-cpp/src/spine/CurveTimeline.cpp
  35. 2 2
      spine-cpp/spine-cpp/src/spine/IkConstraintTimeline.cpp
  36. 2 2
      spine-cpp/spine-cpp/src/spine/PathConstraintMixTimeline.cpp
  37. 3 24
      spine-cpp/spine-cpp/src/spine/PathConstraintPositionTimeline.cpp
  38. 3 23
      spine-cpp/spine-cpp/src/spine/PathConstraintSpacingTimeline.cpp
  39. 5 5
      spine-cpp/spine-cpp/src/spine/PhysicsConstraint.cpp
  40. 102 0
      spine-cpp/spine-cpp/src/spine/PhysicsConstraintTimeline.cpp
  41. 1 26
      spine-cpp/spine-cpp/src/spine/RotateTimeline.cpp
  42. 3 109
      spine-cpp/spine-cpp/src/spine/ScaleTimeline.cpp
  43. 2 54
      spine-cpp/spine-cpp/src/spine/ShearTimeline.cpp
  44. 0 1
      spine-cpp/spine-cpp/src/spine/Timeline.cpp
  45. 2 2
      spine-cpp/spine-cpp/src/spine/TransformConstraintTimeline.cpp
  46. 2 54
      spine-cpp/spine-cpp/src/spine/TranslateTimeline.cpp
  47. 13 13
      spine-sfml/cpp/example/main.cpp
  48. 1 2
      spine-sfml/cpp/example/testbed.cpp
  49. 1 1
      spine-sfml/cpp/src/spine/spine-sfml.h

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

@@ -149,7 +149,7 @@ SP_API float spTrackEntry_getTrackComplete(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*/
-SP_API void spAnimationState_disposeStatics();
+SP_API void spAnimationState_disposeStatics(void);
 
 #ifdef __cplusplus
 }

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

@@ -110,7 +110,7 @@ struct spAtlasRegion {
 	spAtlasRegion *next;
 };
 
-SP_API spAtlasRegion *spAtlasRegion_create();
+SP_API spAtlasRegion *spAtlasRegion_create(void);
 
 SP_API void spAtlasRegion_dispose(spAtlasRegion *self);
 

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

@@ -58,7 +58,7 @@ struct spBone {
 
 SP_API void spBone_setYDown(int/*bool*/yDown);
 
-SP_API int/*bool*/spBone_isYDown();
+SP_API int/*bool*/spBone_isYDown(void);
 
 /* @param parent May be 0. */
 SP_API spBone *spBone_create(spBoneData *data, struct spSkeleton *skeleton, spBone *parent);

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

@@ -41,7 +41,7 @@ typedef struct spColor {
 } spColor;
 
 /* @param attachmentName May be 0 for no setup pose attachment. */
-SP_API spColor *spColor_create();
+SP_API spColor *spColor_create(void);
 
 SP_API void spColor_dispose(spColor *self);
 

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

@@ -62,7 +62,7 @@ typedef struct spSkeletonBounds {
 	float minX, minY, maxX, maxY;
 } spSkeletonBounds;
 
-SP_API spSkeletonBounds *spSkeletonBounds_create();
+SP_API spSkeletonBounds *spSkeletonBounds_create(void);
 
 SP_API void spSkeletonBounds_dispose(spSkeletonBounds *self);
 

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

@@ -52,7 +52,7 @@ typedef struct spSkeletonClipping {
 	spArrayFloatArray *clippingPolygons;
 } spSkeletonClipping;
 
-SP_API spSkeletonClipping *spSkeletonClipping_create();
+SP_API spSkeletonClipping *spSkeletonClipping_create(void);
 
 SP_API int spSkeletonClipping_clipStart(spSkeletonClipping *self, spSlot *slot, spClippingAttachment *clip);
 

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

@@ -81,7 +81,7 @@ typedef struct spSkeletonData {
 	spPathConstraintData **pathConstraints;
 } spSkeletonData;
 
-SP_API spSkeletonData *spSkeletonData_create();
+SP_API spSkeletonData *spSkeletonData_create(void);
 
 SP_API void spSkeletonData_dispose(spSkeletonData *self);
 

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

@@ -49,7 +49,7 @@ typedef struct spTriangulator {
 	spArrayShortArray *polygonIndicesPool;
 } spTriangulator;
 
-SP_API spTriangulator *spTriangulator_create();
+SP_API spTriangulator *spTriangulator_create(void);
 
 SP_API spShortArray *spTriangulator_triangulate(spTriangulator *self, spFloatArray *verticesArray);
 

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

@@ -175,7 +175,7 @@ void *_spRealloc(void *ptr, size_t size);
 
 void _spFree(void *ptr);
 
-float _spRandom();
+float _spRandom(void);
 
 SP_API void _spSetMalloc(void *(*_malloc)(size_t size));
 
@@ -185,7 +185,7 @@ SP_API void _spSetRealloc(void *(*_realloc)(void *ptr, size_t size));
 
 SP_API void _spSetFree(void (*_free)(void *ptr));
 
-SP_API void _spSetRandom(float (*_random)());
+SP_API void _spSetRandom(float (*_random)(void));
 
 char *_spReadFile(const char *path, int *length);
 

+ 1 - 1
spine-c/spine-c/src/spine/AnimationState.c

@@ -44,7 +44,7 @@ _SP_ARRAY_IMPLEMENT_TYPE(spTrackEntryArray, spTrackEntry *)
 
 static spAnimation *SP_EMPTY_ANIMATION = 0;
 
-void spAnimationState_disposeStatics() {
+void spAnimationState_disposeStatics(void) {
 	if (SP_EMPTY_ANIMATION) spAnimation_dispose(SP_EMPTY_ANIMATION);
 	SP_EMPTY_ANIMATION = 0;
 }

+ 1 - 1
spine-c/spine-c/src/spine/Atlas.c

@@ -115,7 +115,7 @@ void spAtlasPage_dispose(spAtlasPage *self) {
 
 /**/
 
-spAtlasRegion *spAtlasRegion_create() {
+spAtlasRegion *spAtlasRegion_create(void) {
 	spAtlasRegion *region = NEW(spAtlasRegion);
 	region->keyValues = spKeyValueArray_create(2);
 	return region;

+ 1 - 1
spine-c/spine-c/src/spine/AttachmentLoader.c

@@ -96,6 +96,6 @@ void _spAttachmentLoader_setError(spAttachmentLoader *self, const char *error1,
 
 void _spAttachmentLoader_setUnknownTypeError(spAttachmentLoader *self, spAttachmentType type) {
 	char buffer[16];
-	sprintf(buffer, "%d", type);
+	snprintf(buffer, 16,"%d", type);
 	_spAttachmentLoader_setError(self, "Unknown attachment type: ", buffer);
 }

+ 26 - 21
spine-c/spine-c/src/spine/Bone.c

@@ -37,7 +37,7 @@ void spBone_setYDown(int value) {
 	yDown = value;
 }
 
-int spBone_isYDown() {
+int spBone_isYDown(void) {
 	return yDown;
 }
 
@@ -261,26 +261,31 @@ void spBone_updateAppliedTransform(spBone *self) {
 				break;
 			}
 			case SP_TRANSFORMMODE_NOSCALE:
-			case SP_TRANSFORMMODE_NOSCALEORREFLECTION:
-				cosine = COS_DEG(self->rotation), sine = SIN_DEG(self->rotation);
-				pa = (pa * cosine + pb * sine) / self->skeleton->scaleX;
-				pc = (pc * cosine + pd * sine) / self->skeleton->scaleY;
-				s = SQRT(pa * pa + pc * pc);
-				if (s > 0.00001f) s = 1 / s;
-				pa *= s;
-				pc *= s;
-				s = SQRT(pa * pa + pc * pc);
-				if (self->data->transformMode == SP_TRANSFORMMODE_NOSCALE &&
-					pid < 0 != (self->skeleton->scaleX < 0 != self->skeleton->scaleY < 0))
-					s = -s;
-				r = PI / 2 + ATAN2(pc, pa);
-				pb = COS(r) * s;
-				pd = SIN(r) * s;
-				pid = 1 / (pa * pd - pb * pc);
-				ia = pd * pid;
-				ib = pb * pid;
-				ic = pc * pid;
-				id = pa * pid;
+			case SP_TRANSFORMMODE_NOSCALEORREFLECTION: {
+                cosine = COS_DEG(self->rotation), sine = SIN_DEG(self->rotation);
+                pa = (pa * cosine + pb * sine) / self->skeleton->scaleX;
+                pc = (pc * cosine + pd * sine) / self->skeleton->scaleY;
+                s = SQRT(pa * pa + pc * pc);
+                if (s > 0.00001f) s = 1 / s;
+                pa *= s;
+                pc *= s;
+                s = SQRT(pa * pa + pc * pc);
+                if (self->data->transformMode == SP_TRANSFORMMODE_NOSCALE &&
+                    pid < 0 != (self->skeleton->scaleX < 0 != self->skeleton->scaleY < 0))
+                    s = -s;
+                r = PI / 2 + ATAN2(pc, pa);
+                pb = COS(r) * s;
+                pd = SIN(r) * s;
+                pid = 1 / (pa * pd - pb * pc);
+                ia = pd * pid;
+                ib = pb * pid;
+                ic = pc * pid;
+                id = pa * pid;
+                break;
+            }
+            case SP_TRANSFORMMODE_ONLYTRANSLATION:
+            case SP_TRANSFORMMODE_NORMAL:
+                break;
 		}
 		ra = ia * self->a - ib * self->c;
 		rb = ia * self->b - ib * self->d;

+ 1 - 1
spine-c/spine-c/src/spine/Color.c

@@ -30,7 +30,7 @@
 #include <spine/Color.h>
 #include <spine/extension.h>
 
-spColor *spColor_create() {
+spColor *spColor_create(void) {
 	return MALLOC(spColor, 1);
 }
 

+ 0 - 2
spine-c/spine-c/src/spine/Json.c

@@ -122,7 +122,6 @@ static const char *parse_number(Json *item, const char *num) {
 	if (*ptr == 'e' || *ptr == 'E') {
 		double exponent = 0;
 		int expNegative = 0;
-		int n = 0;
 		++ptr;
 
 		if (*ptr == '-') {
@@ -135,7 +134,6 @@ static const char *parse_number(Json *item, const char *num) {
 		while (*ptr >= '0' && *ptr <= '9') {
 			exponent = (exponent * 10.0) + (*ptr - '0');
 			++ptr;
-			++n;
 		}
 
 		if (expNegative)

+ 1 - 1
spine-c/spine-c/src/spine/Sequence.c

@@ -102,7 +102,7 @@ static char *string_append(char *str, const char *b) {
 
 static char *string_append_int(char *str, int value) {
 	char intStr[20];
-	sprintf(intStr, "%i", value);
+	snprintf(intStr, 20, "%i", value);
 	return string_append(str, intStr);
 }
 

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

@@ -1325,7 +1325,7 @@ spSkeletonData *spSkeletonBinary_readSkeletonData(spSkeletonBinary *self, const
 	skeletonData = spSkeletonData_create();
 	lowHash = readInt(input);
 	highHash = readInt(input);
-	sprintf(buffer, "%x%x", highHash, lowHash);
+	snprintf(buffer, 32, "%x%x", highHash, lowHash);
 	buffer[31] = 0;
 	MALLOC_STR(skeletonData->hash, buffer);
 
@@ -1336,7 +1336,7 @@ spSkeletonData *spSkeletonBinary_readSkeletonData(spSkeletonBinary *self, const
 	} else {
 		if (!string_starts_with(skeletonData->version, SPINE_VERSION_STRING)) {
 			char errorMsg[255];
-			sprintf(errorMsg, "Skeleton version %s does not match runtime version %s", skeletonData->version, SPINE_VERSION_STRING);
+			snprintf(errorMsg, 255, "Skeleton version %s does not match runtime version %s", skeletonData->version, SPINE_VERSION_STRING);
 			_spSkeletonBinary_setError(self, errorMsg, NULL);
 			return NULL;
 		}

+ 1 - 1
spine-c/spine-c/src/spine/SkeletonBounds.c

@@ -89,7 +89,7 @@ typedef struct {
 	int capacity;
 } _spSkeletonBounds;
 
-spSkeletonBounds *spSkeletonBounds_create() {
+spSkeletonBounds *spSkeletonBounds_create(void) {
 	return SUPER(NEW(_spSkeletonBounds));
 }
 

+ 1 - 1
spine-c/spine-c/src/spine/SkeletonClipping.c

@@ -30,7 +30,7 @@
 #include <spine/SkeletonClipping.h>
 #include <spine/extension.h>
 
-spSkeletonClipping *spSkeletonClipping_create() {
+spSkeletonClipping *spSkeletonClipping_create(void) {
 	spSkeletonClipping *clipping = CALLOC(spSkeletonClipping, 1);
 
 	clipping->triangulator = spTriangulator_create();

+ 1 - 1
spine-c/spine-c/src/spine/SkeletonData.c

@@ -31,7 +31,7 @@
 #include <spine/extension.h>
 #include <string.h>
 
-spSkeletonData *spSkeletonData_create() {
+spSkeletonData *spSkeletonData_create(void) {
 	return NEW(spSkeletonData);
 }
 

+ 1 - 1
spine-c/spine-c/src/spine/SkeletonJson.c

@@ -988,7 +988,7 @@ spSkeletonData *spSkeletonJson_readSkeletonData(spSkeletonJson *self, const char
 		MALLOC_STR(skeletonData->version, Json_getString(skeleton, "spine", "0"));
 		if (!string_starts_with(skeletonData->version, SPINE_VERSION_STRING)) {
 			char errorMsg[255];
-			sprintf(errorMsg, "Skeleton version %s does not match runtime version %s", skeletonData->version, SPINE_VERSION_STRING);
+			snprintf(errorMsg, 255, "Skeleton version %s does not match runtime version %s", skeletonData->version, SPINE_VERSION_STRING);
 			_spSkeletonJson_setError(self, 0, errorMsg, NULL);
 			return NULL;
 		}

+ 1 - 1
spine-c/spine-c/src/spine/Triangulator.c

@@ -31,7 +31,7 @@
 #include <spine/extension.h>
 #include <stdio.h>
 
-spTriangulator *spTriangulator_create() {
+spTriangulator *spTriangulator_create(void) {
 	spTriangulator *triangulator = CALLOC(spTriangulator, 1);
 
 	triangulator->convexPolygons = spArrayFloatArray_create(16);

+ 4 - 4
spine-c/spine-c/src/spine/extension.c

@@ -30,7 +30,7 @@
 #include <spine/extension.h>
 #include <stdio.h>
 
-float _spInternalRandom() {
+float _spInternalRandom(void) {
 	return rand() / (float) RAND_MAX;
 }
 
@@ -42,7 +42,7 @@ static void *(*debugMallocFunc)(size_t size, const char *file, int line) = NULL;
 
 static void (*freeFunc)(void *ptr) = free;
 
-static float (*randomFunc)() = _spInternalRandom;
+static float (*randomFunc)(void) = _spInternalRandom;
 
 void *_spMalloc(size_t size, const char *file, int line) {
 	if (debugMallocFunc)
@@ -65,7 +65,7 @@ void _spFree(void *ptr) {
 	freeFunc(ptr);
 }
 
-float _spRandom() {
+float _spRandom(void) {
 	return randomFunc();
 }
 
@@ -85,7 +85,7 @@ void _spSetFree(void (*free)(void *ptr)) {
 	freeFunc = free;
 }
 
-void _spSetRandom(float (*random)()) {
+void _spSetRandom(float (*random)(void)) {
 	randomFunc = random;
 }
 

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

@@ -76,6 +76,14 @@ namespace spine {
 
 		float getCurveValue(float time);
 
+        float getRelativeValue(float time, float alpha, MixBlend blend, float current, float setup);
+
+        float getAbsoluteValue(float time, float alpha, MixBlend blend, float current, float setup);
+
+        float getAbsoluteValue (float time, float alpha, MixBlend blend, float current, float setup, float value);
+
+        float getScaleValue (float time, float alpha, MixBlend blend, MixDirection direction, float current, float setup);
+
 	protected:
 		static const int ENTRIES = 2;
 		static const int VALUE = 1;

+ 3 - 3
spine-cpp/spine-cpp/include/spine/IkConstraintTimeline.h

@@ -51,12 +51,12 @@ namespace spine {
 		/// Sets the time, mix and bend direction of the specified keyframe.
 		void setFrame(int frame, float time, float mix, float softness, int bendDirection, bool compress, bool stretch);
 
-		int getIkConstraintIndex() { return _ikConstraintIndex; }
+		int getIkConstraintIndex() { return _constraintIndex; }
 
-		void setIkConstraintIndex(int inValue) { _ikConstraintIndex = inValue; }
+		void setIkConstraintIndex(int inValue) { _constraintIndex = inValue; }
 
 	private:
-		int _ikConstraintIndex;
+		int _constraintIndex;
 
 		static const int ENTRIES = 6;
 		static const int MIX = 1;

+ 3 - 4
spine-cpp/spine-cpp/include/spine/PathConstraintMixTimeline.h

@@ -33,7 +33,6 @@
 #include <spine/CurveTimeline.h>
 
 namespace spine {
-#define SP_PATHCONSTRAINTMIXTIMELINE_ENTRIES 5
 
 	class SP_API PathConstraintMixTimeline : public CurveTimeline {
 		friend class SkeletonBinary;
@@ -52,12 +51,12 @@ namespace spine {
 		/// Sets the time and mixes of the specified keyframe.
 		void setFrame(int frameIndex, float time, float mixRotate, float mixX, float mixY);
 
-		int getPathConstraintIndex() { return _pathConstraintIndex; }
+		int getPathConstraintIndex() { return _constraintIndex; }
 
-		void setPathConstraintIndex(int inValue) { _pathConstraintIndex = inValue; }
+		void setPathConstraintIndex(int inValue) { _constraintIndex = inValue; }
 
 	private:
-		int _pathConstraintIndex;
+		int _constraintIndex;
 
 		static const int ENTRIES = 4;
 		static const int ROTATE = 1;

+ 3 - 3
spine-cpp/spine-cpp/include/spine/PathConstraintPositionTimeline.h

@@ -52,12 +52,12 @@ namespace spine {
 		apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
 			  MixDirection direction);
 
-		int getPathConstraintIndex() { return _pathConstraintIndex; }
+		int getPathConstraintIndex() { return _constraintIndex; }
 
-		void setPathConstraintIndex(int inValue) { _pathConstraintIndex = inValue; }
+		void setPathConstraintIndex(int inValue) { _constraintIndex = inValue; }
 
 	protected:
-		int _pathConstraintIndex;
+		int _constraintIndex;
 	};
 }
 

+ 4 - 4
spine-cpp/spine-cpp/include/spine/Physics.h

@@ -33,16 +33,16 @@
 namespace spine {
     enum Physics {
         /** Physics are not updated or applied. */
-        none,
+        Physics_None,
 
         /** Physics are reset to the current pose. */
-        reset,
+        Physics_Reset,
 
         /** Physics are updated and the pose from physics is applied. */
-        update,
+        Physics_Update,
 
         /** Physics are not updated but the pose from physics is applied. */
-        pose
+        Physics_Pose
     };
 }
 

+ 18 - 0
spine-cpp/spine-cpp/include/spine/PhysicsConstraint.h

@@ -45,6 +45,24 @@ namespace spine {
 
         friend class Skeleton;
 
+        friend class PhysicsConstraintTimeline;
+
+        friend class PhysicsConstraintInertiaTimeline;
+
+        friend class PhysicsConstraintStrengthTimeline;
+
+        friend class PhysicsConstraintDampingTimeline;
+
+        friend class PhysicsConstraintMassTimeline;
+
+        friend class PhysicsConstraintWindTimeline;
+
+        friend class PhysicsConstraintGravityTimeline;
+
+        friend class PhysicsConstraintMixTimeline;
+
+        friend class PhysicsConstraintResetTimeline;
+
     RTTI_DECL
 
     public:

+ 288 - 0
spine-cpp/spine-cpp/include/spine/PhysicsConstraintTimeline.h

@@ -0,0 +1,288 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated July 28, 2023. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2023, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software or
+ * otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
+ * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE
+ * SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef Spine_PhysicsConstraintTimeline_h
+#define Spine_PhysicsConstraintTimeline_h
+
+#include <spine/CurveTimeline.h>
+#include <spine/PhysicsConstraint.h>
+#include <spine/PhysicsConstraintData.h>
+
+namespace spine {
+
+	class SP_API PhysicsConstraintTimeline : public CurveTimeline1 {
+		friend class SkeletonBinary;
+
+		friend class SkeletonJson;
+
+	RTTI_DECL
+
+	public:
+		explicit PhysicsConstraintTimeline(size_t frameCount, size_t bezierCount, int physicsConstraintIndex, Property property);
+
+		virtual void
+		apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
+			  MixDirection direction);
+
+		int getPhysicsConstraintIndex() { return _constraintIndex; }
+
+		void setPhysicsConstraintIndex(int inValue) { _constraintIndex = inValue; }
+
+    protected:
+        virtual float setup(PhysicsConstraint *constraint) = 0;
+        virtual float get(PhysicsConstraint *constraint) = 0;
+        virtual void set(PhysicsConstraint *constraint, float value) = 0;
+        virtual bool global(PhysicsConstraintData &constraintData) = 0;
+
+	private:
+		int _constraintIndex;
+	};
+
+    class SP_API PhysicsConstraintInertiaTimeline : public PhysicsConstraintTimeline {
+        friend class SkeletonBinary;
+
+        friend class SkeletonJson;
+
+    RTTI_DECL
+
+    public:
+        explicit PhysicsConstraintInertiaTimeline(size_t frameCount, size_t bezierCount, int physicsConstraintIndex): PhysicsConstraintTimeline(frameCount, bezierCount, physicsConstraintIndex, Property_PhysicsConstraintInertia) {};
+
+    protected:
+        float setup(PhysicsConstraint *constraint) {
+            return constraint->_data.getInertia();
+        }
+
+        float get(PhysicsConstraint *constraint) {
+            return constraint->_inertia;
+        }
+
+        void set(PhysicsConstraint *constraint, float value) {
+            constraint->_inertia = value;
+        }
+
+        bool global(PhysicsConstraintData &constraintData) {
+            return constraintData.isInertiaGlobal();
+        }
+    };
+
+    class SP_API PhysicsConstraintStrengthTimeline : public PhysicsConstraintTimeline {
+        friend class SkeletonBinary;
+
+        friend class SkeletonJson;
+
+    RTTI_DECL
+
+    public:
+        explicit PhysicsConstraintStrengthTimeline(size_t frameCount, size_t bezierCount, int physicsConstraintIndex): PhysicsConstraintTimeline(frameCount, bezierCount, physicsConstraintIndex, Property_PhysicsConstraintStrength) {};
+
+    protected:
+        float setup(PhysicsConstraint *constraint) {
+            return constraint->_data.getStrength();
+        }
+
+        float get(PhysicsConstraint *constraint) {
+            return constraint->_strength;
+        }
+
+        void set(PhysicsConstraint *constraint, float value) {
+            constraint->_strength = value;
+        }
+
+        bool global(PhysicsConstraintData &constraintData) {
+            return constraintData.isStrengthGlobal();
+        }
+    };
+
+    class SP_API PhysicsConstraintDampingTimeline : public PhysicsConstraintTimeline {
+        friend class SkeletonBinary;
+
+        friend class SkeletonJson;
+
+    RTTI_DECL
+
+    public:
+        explicit PhysicsConstraintDampingTimeline(size_t frameCount, size_t bezierCount, int physicsConstraintIndex): PhysicsConstraintTimeline(frameCount, bezierCount, physicsConstraintIndex, Property_PhysicsConstraintDamping) {};
+
+    protected:
+        float setup(PhysicsConstraint *constraint) {
+            return constraint->_data.getDamping();
+        }
+
+        float get(PhysicsConstraint *constraint) {
+            return constraint->_damping;
+        }
+
+        void set(PhysicsConstraint *constraint, float value) {
+            constraint->_damping = value;
+        }
+
+        bool global(PhysicsConstraintData &constraintData) {
+            return constraintData.isDampingGlobal();
+        }
+    };
+
+    class SP_API PhysicsConstraintMassTimeline : public PhysicsConstraintTimeline {
+        friend class SkeletonBinary;
+
+        friend class SkeletonJson;
+
+    RTTI_DECL
+
+    public:
+        explicit PhysicsConstraintMassTimeline(size_t frameCount, size_t bezierCount, int physicsConstraintIndex): PhysicsConstraintTimeline(frameCount, bezierCount, physicsConstraintIndex, Property_PhysicsConstraintMass) {};
+
+    protected:
+        float setup(PhysicsConstraint *constraint) {
+            return 1 / constraint->_data.getMassInverse();
+        }
+
+        float get(PhysicsConstraint *constraint) {
+            return 1 / constraint->_massInverse;
+        }
+
+        void set(PhysicsConstraint *constraint, float value) {
+            constraint->_massInverse = 1 / value;
+        }
+
+        bool global(PhysicsConstraintData &constraintData) {
+            return constraintData.isMassGlobal();
+        }
+    };
+
+    class SP_API PhysicsConstraintWindTimeline : public PhysicsConstraintTimeline {
+        friend class SkeletonBinary;
+
+        friend class SkeletonJson;
+
+    RTTI_DECL
+
+    public:
+        explicit PhysicsConstraintWindTimeline(size_t frameCount, size_t bezierCount, int physicsConstraintIndex): PhysicsConstraintTimeline(frameCount, bezierCount, physicsConstraintIndex, Property_PhysicsConstraintWind) {};
+
+    protected:
+        float setup(PhysicsConstraint *constraint) {
+            return constraint->_data.getWind();
+        }
+
+        float get(PhysicsConstraint *constraint) {
+            return constraint->_wind;
+        }
+
+        void set(PhysicsConstraint *constraint, float value) {
+            constraint->_wind = value;
+        }
+
+        bool global(PhysicsConstraintData &constraintData) {
+            return constraintData.isWindGlobal();
+        }
+    };
+
+    class SP_API PhysicsConstraintGravityTimeline : public PhysicsConstraintTimeline {
+        friend class SkeletonBinary;
+
+        friend class SkeletonJson;
+
+    RTTI_DECL
+
+    public:
+        explicit PhysicsConstraintGravityTimeline(size_t frameCount, size_t bezierCount, int physicsConstraintIndex): PhysicsConstraintTimeline(frameCount, bezierCount, physicsConstraintIndex, Property_PhysicsConstraintGravity) {};
+
+    protected:
+        float setup(PhysicsConstraint *constraint) {
+            return constraint->_data.getGravity();
+        }
+
+        float get(PhysicsConstraint *constraint) {
+            return constraint->_gravity;
+        }
+
+        void set(PhysicsConstraint *constraint, float value) {
+            constraint->_gravity = value;
+        }
+
+        bool global(PhysicsConstraintData &constraintData) {
+            return constraintData.isGravityGlobal();
+        }
+    };
+
+    class SP_API PhysicsConstraintMixTimeline : public PhysicsConstraintTimeline {
+        friend class SkeletonBinary;
+
+        friend class SkeletonJson;
+
+    RTTI_DECL
+
+    public:
+        explicit PhysicsConstraintMixTimeline(size_t frameCount, size_t bezierCount, int physicsConstraintIndex): PhysicsConstraintTimeline(frameCount, bezierCount, physicsConstraintIndex, Property_PhysicsConstraintMix) {};
+
+    protected:
+        float setup(PhysicsConstraint *constraint) {
+            return constraint->_data.getMix();
+        }
+
+        float get(PhysicsConstraint *constraint) {
+            return constraint->_mix;
+        }
+
+        void set(PhysicsConstraint *constraint, float value) {
+            constraint->_mix = value;
+        }
+
+        bool global(PhysicsConstraintData &constraintData) {
+            return constraintData.isMixGlobal();
+        }
+    };
+
+    class SP_API PhysicsConstraintResetTimeline : public Timeline {
+        friend class SkeletonBinary;
+
+        friend class SkeletonJson;
+
+    RTTI_DECL
+
+    public:
+        explicit PhysicsConstraintResetTimeline(size_t frameCount, int physicsConstraintIndex): Timeline(frameCount, 1), _constraintIndex(physicsConstraintIndex) {
+            PropertyId ids[] = {((PropertyId)Property_PhysicsConstraintReset) << 32};
+            setPropertyIds(ids, 1);
+        }
+
+        virtual void
+        apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha, MixBlend blend,
+              MixDirection direction);
+
+        void setFrame(int frame, float time) {
+            _frames[frame] = time;
+        }
+    private:
+        int _constraintIndex;
+    };
+}
+
+#endif /* Spine_PhysicsConstraintTimeline_h */

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

@@ -52,7 +52,15 @@ namespace spine {
 		Property_PathConstraintPosition = 1 << 16,
 		Property_PathConstraintSpacing = 1 << 17,
 		Property_PathConstraintMix = 1 << 18,
-		Property_Sequence = 1 << 19
+        Property_PhysicsConstraintInertia = 1 << 19,
+        Property_PhysicsConstraintStrength = 1 << 20,
+        Property_PhysicsConstraintDamping = 1 << 21,
+        Property_PhysicsConstraintMass = 1 << 22,
+        Property_PhysicsConstraintWind = 1 << 23,
+        Property_PhysicsConstraintGravity = 1 << 24,
+        Property_PhysicsConstraintMix = 1 << 25,
+        Property_PhysicsConstraintReset = 1 << 26,
+		Property_Sequence = 1 << 27
 	};
 }
 

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

@@ -77,7 +77,7 @@ namespace spine {
 	protected:
 		void setPropertyIds(PropertyId propertyIds[], size_t propertyIdsCount);
 
-		Vector <PropertyId> _propertyIds;
+        Vector <PropertyId> _propertyIds;
 		Vector<float> _frames;
 		size_t _frameEntries;
 	};

+ 3 - 3
spine-cpp/spine-cpp/include/spine/TransformConstraintTimeline.h

@@ -51,12 +51,12 @@ namespace spine {
 		void setFrame(size_t frameIndex, float time, float mixRotate, float mixX, float mixY, float mixScaleX,
 					  float mixScaleY, float mixShearY);
 
-		int getTransformConstraintIndex() { return _transformConstraintIndex; }
+		int getTransformConstraintIndex() { return _constraintIndex; }
 
-		void setTransformConstraintIndex(int inValue) { _transformConstraintIndex = inValue; }
+		void setTransformConstraintIndex(int inValue) { _constraintIndex = inValue; }
 
 	private:
-		int _transformConstraintIndex;
+		int _constraintIndex;
 
 		static const int ENTRIES = 7;
 		static const int ROTATE = 1;

+ 103 - 0
spine-cpp/spine-cpp/src/spine/CurveTimeline.cpp

@@ -132,6 +132,109 @@ float CurveTimeline1::getCurveValue(float time) {
 	return getBezierValue(time, i, CurveTimeline1::VALUE, curveType - CurveTimeline1::BEZIER);
 }
 
+float CurveTimeline1::getRelativeValue(float time, float alpha, MixBlend blend, float current, float setup) {
+    if (time < _frames[0]) {
+        switch (blend) {
+            case MixBlend_Setup:
+                return setup;
+            case MixBlend_First:
+                return current + (setup - current) * alpha;
+            default:
+                return current;
+        }
+    }
+    float value = getCurveValue(time);
+    switch (blend) {
+        case MixBlend_Setup:
+            return setup + value * alpha;
+        case MixBlend_First:
+        case MixBlend_Replace:
+            value += setup - current;
+            break;
+        case MixBlend_Add:
+            break;
+    }
+    return current + value * alpha;
+
+}
+
+float CurveTimeline1::getAbsoluteValue(float time, float alpha, MixBlend blend, float current, float setup) {
+    if (time < _frames[0]) {
+        switch (blend) {
+            case MixBlend_Setup:
+                return setup;
+            case MixBlend_First:
+                return current + (setup - current) * alpha;
+            default:
+                return current;
+        }
+    }
+    float value = getCurveValue(time);
+    if (blend == MixBlend_Setup) return setup + (value - setup) * alpha;
+    return current + (value - current) * alpha;
+}
+
+float CurveTimeline1::getAbsoluteValue(float time, float alpha, MixBlend blend, float current, float setup, float value) {
+    if (time < _frames[0]) {
+        switch (blend) {
+            case MixBlend_Setup:
+                return setup;
+            case MixBlend_First:
+                return current + (setup - current) * alpha;
+            default:
+                return current;
+        }
+    }
+    if (blend == MixBlend_Setup) return setup + (value - setup) * alpha;
+    return current + (value - current) * alpha;
+}
+
+float CurveTimeline1::getScaleValue(float time, float alpha, MixBlend blend, MixDirection direction, float current,
+                                    float setup) {
+    if (time < _frames[0]) {
+        switch (blend) {
+            case MixBlend_Setup:
+                return setup;
+            case MixBlend_First:
+                return current + (setup - current) * alpha;
+            default:
+                return current;
+        }
+    }
+    float value = getCurveValue(time) * setup;
+    if (alpha == 1) {
+        if (blend == MixBlend_Add) return current + value - setup;
+        return value;
+    }
+    // Mixing out uses sign of setup or current pose, else use sign of key.
+    if (direction == MixDirection_Out) {
+        switch (blend) {
+            case MixBlend_Setup:
+                return setup + (MathUtil::abs(value) * MathUtil::sign(setup) - setup) * alpha;
+            case MixBlend_First:
+            case MixBlend_Replace:
+                return current + (MathUtil::abs(value) * MathUtil::sign(current) - current) * alpha;
+            default:
+                break;
+        }
+    } else {
+        float s;
+        switch (blend) {
+            case MixBlend_Setup:
+                s = MathUtil::abs(setup) * MathUtil::sign(value);
+                return s + (value - s) * alpha;
+            case MixBlend_First:
+            case MixBlend_Replace:
+                s = MathUtil::abs(current) * MathUtil::sign(value);
+                return s + (value - s) * alpha;
+            default:
+                break;
+        }
+    }
+    return current + (value - setup) * alpha;
+}
+
+
 RTTI_IMPL(CurveTimeline2, CurveTimeline)
 
 CurveTimeline2::CurveTimeline2(size_t frameCount, size_t bezierCount) : CurveTimeline(frameCount,

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

@@ -44,7 +44,7 @@ using namespace spine;
 RTTI_IMPL(IkConstraintTimeline, CurveTimeline)
 
 IkConstraintTimeline::IkConstraintTimeline(size_t frameCount, size_t bezierCount, int ikConstraintIndex)
-	: CurveTimeline(frameCount, IkConstraintTimeline::ENTRIES, bezierCount), _ikConstraintIndex(ikConstraintIndex) {
+	: CurveTimeline(frameCount, IkConstraintTimeline::ENTRIES, bezierCount), _constraintIndex(ikConstraintIndex) {
 	PropertyId ids[] = {((PropertyId) Property_IkConstraint << 32) | ikConstraintIndex};
 	setPropertyIds(ids, 1);
 }
@@ -54,7 +54,7 @@ void IkConstraintTimeline::apply(Skeleton &skeleton, float lastTime, float time,
 	SP_UNUSED(lastTime);
 	SP_UNUSED(pEvents);
 
-	IkConstraint *constraintP = skeleton._ikConstraints[_ikConstraintIndex];
+	IkConstraint *constraintP = skeleton._ikConstraints[_constraintIndex];
 	IkConstraint &constraint = *constraintP;
 	if (!constraint.isActive()) return;
 

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

@@ -45,7 +45,7 @@ RTTI_IMPL(PathConstraintMixTimeline, CurveTimeline)
 
 PathConstraintMixTimeline::PathConstraintMixTimeline(size_t frameCount, size_t bezierCount, int pathConstraintIndex)
 	: CurveTimeline(frameCount, PathConstraintMixTimeline::ENTRIES, bezierCount),
-	  _pathConstraintIndex(pathConstraintIndex) {
+      _constraintIndex(pathConstraintIndex) {
 	PropertyId ids[] = {((PropertyId) Property_PathConstraintMix << 32) | pathConstraintIndex};
 	setPropertyIds(ids, 1);
 }
@@ -56,7 +56,7 @@ void PathConstraintMixTimeline::apply(Skeleton &skeleton, float lastTime, float
 	SP_UNUSED(pEvents);
 	SP_UNUSED(direction);
 
-	PathConstraint *constraintP = skeleton._pathConstraints[_pathConstraintIndex];
+	PathConstraint *constraintP = skeleton._pathConstraints[_constraintIndex];
 	PathConstraint &constraint = *constraintP;
 	if (!constraint.isActive()) return;
 

+ 3 - 24
spine-cpp/spine-cpp/src/spine/PathConstraintPositionTimeline.cpp

@@ -46,7 +46,7 @@ RTTI_IMPL(PathConstraintPositionTimeline, CurveTimeline1)
 PathConstraintPositionTimeline::PathConstraintPositionTimeline(size_t frameCount, size_t bezierCount,
 															   int pathConstraintIndex) : CurveTimeline1(frameCount,
 																										 bezierCount),
-																						  _pathConstraintIndex(
+                                                                                          _constraintIndex(
 																								  pathConstraintIndex) {
 	PropertyId ids[] = {((PropertyId) Property_PathConstraintPosition << 32) | pathConstraintIndex};
 	setPropertyIds(ids, 1);
@@ -61,27 +61,6 @@ void PathConstraintPositionTimeline::apply(Skeleton &skeleton, float lastTime, f
 	SP_UNUSED(pEvents);
 	SP_UNUSED(direction);
 
-	PathConstraint *constraintP = skeleton._pathConstraints[_pathConstraintIndex];
-	PathConstraint &constraint = *constraintP;
-	if (!constraint.isActive()) return;
-
-	if (time < _frames[0]) {
-		switch (blend) {
-			case MixBlend_Setup:
-				constraint._position = constraint._data._position;
-				return;
-			case MixBlend_First:
-				constraint._position += (constraint._data._position - constraint._position) * alpha;
-				return;
-			default:
-				return;
-		}
-	}
-
-	float position = getCurveValue(time);
-
-	if (blend == MixBlend_Setup)
-		constraint._position = constraint._data._position + (position - constraint._data._position) * alpha;
-	else
-		constraint._position += (position - constraint._position) * alpha;
+	PathConstraint *constraint = skeleton._pathConstraints[_constraintIndex];
+    if (constraint->_active) constraint->_position = getAbsoluteValue(time, alpha, blend, constraint->_position, constraint->_data._position);
 }

+ 3 - 23
spine-cpp/spine-cpp/src/spine/PathConstraintSpacingTimeline.cpp

@@ -58,27 +58,7 @@ void PathConstraintSpacingTimeline::apply(Skeleton &skeleton, float lastTime, fl
 	SP_UNUSED(pEvents);
 	SP_UNUSED(direction);
 
-	PathConstraint *constraintP = skeleton._pathConstraints[_pathConstraintIndex];
-	PathConstraint &constraint = *constraintP;
-	if (!constraint.isActive()) return;
-
-	if (time < _frames[0]) {
-		switch (blend) {
-			case MixBlend_Setup:
-				constraint._spacing = constraint._data._spacing;
-				return;
-			case MixBlend_First:
-				constraint._spacing += (constraint._data._spacing - constraint._spacing) * alpha;
-				return;
-			default:
-				return;
-		}
-	}
-
-	float spacing = getCurveValue(time);
-
-	if (blend == MixBlend_Setup)
-		constraint._spacing = constraint._data._spacing + (spacing - constraint._data._spacing) * alpha;
-	else
-		constraint._spacing += (spacing - constraint._spacing) * alpha;
+	PathConstraint *constraint = skeleton._pathConstraints[_pathConstraintIndex];
+    if (constraint->_active)
+        constraint->_spacing = getAbsoluteValue(time, alpha, blend, constraint->_spacing, constraint->_data._spacing);
 }

+ 5 - 5
spine-cpp/spine-cpp/src/spine/PhysicsConstraint.cpp

@@ -318,12 +318,12 @@ void PhysicsConstraint::update(Physics physics) {
     float l = bone->_data.getLength();
 
     switch (physics) {
-        case Physics::none:
+        case Physics::Physics_None:
             return;
-        case Physics::reset:
+        case Physics::Physics_Reset:
             reset();
             // Fall through.
-        case Physics::update: {
+        case Physics::Physics_Update: {
             _remaining += MathUtil::max(_skeleton.getTime() - _lastTime, 0.0f);
             _lastTime = _skeleton.getTime();
 
@@ -416,7 +416,7 @@ void PhysicsConstraint::update(Physics physics) {
             _cy = bone->_worldY;
             break;
         }
-        case Physics::pose: {
+        case Physics::Physics_Pose: {
             if (x) bone->_worldX += _xOffset * mix * _data._x;
             if (y) bone->_worldY += _yOffset * mix * _data._y;
             break;
@@ -458,7 +458,7 @@ void PhysicsConstraint::update(Physics physics) {
         bone->_a *= s;
         bone->_c *= s;
     }
-    if (physics != Physics::pose) {
+    if (physics != Physics::Physics_Pose) {
         _tx = l * bone->_a;
         _ty = l * bone->_c;
     }

+ 102 - 0
spine-cpp/spine-cpp/src/spine/PhysicsConstraintTimeline.cpp

@@ -0,0 +1,102 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated July 28, 2023. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2023, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software or
+ * otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
+ * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE
+ * SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#include <spine/PhysicsConstraintTimeline.h>
+
+#include <spine/Event.h>
+#include <spine/Skeleton.h>
+
+#include <spine/Animation.h>
+#include <spine/Property.h>
+#include <spine/Slot.h>
+#include <spine/SlotData.h>
+#include <spine/PhysicsConstraint.h>
+#include <cfloat>
+
+using namespace spine;
+
+RTTI_IMPL(PhysicsConstraintTimeline, CurveTimeline)
+RTTI_IMPL(PhysicsConstraintInertiaTimeline, PhysicsConstraintTimeline)
+RTTI_IMPL(PhysicsConstraintStrengthTimeline, PhysicsConstraintTimeline)
+RTTI_IMPL(PhysicsConstraintDampingTimeline, PhysicsConstraintTimeline)
+RTTI_IMPL(PhysicsConstraintMassTimeline, PhysicsConstraintTimeline)
+RTTI_IMPL(PhysicsConstraintWindTimeline, PhysicsConstraintTimeline)
+RTTI_IMPL(PhysicsConstraintGravityTimeline, PhysicsConstraintTimeline)
+RTTI_IMPL(PhysicsConstraintMixTimeline, PhysicsConstraintTimeline)
+RTTI_IMPL(PhysicsConstraintResetTimeline, Timeline)
+
+PhysicsConstraintTimeline::PhysicsConstraintTimeline(size_t frameCount, size_t bezierCount,
+														 int constraintIndex, Property property) : CurveTimeline1(frameCount, bezierCount),
+                                                                                         _constraintIndex(constraintIndex) {
+	PropertyId ids[] = {((PropertyId) property << 32) | constraintIndex};
+	setPropertyIds(ids, 1);
+}
+
+void PhysicsConstraintTimeline::apply(Skeleton &skeleton, float, float time, Vector<Event *> *,
+										float alpha, MixBlend blend, MixDirection) {
+    if (_constraintIndex == -1) {
+        float value = time >= _frames[0] ?getCurveValue(time) : 0;
+
+        Vector<PhysicsConstraint*> &physicsConstraints = skeleton.getPhysicsConstraints();
+        for (size_t i = 0; i < physicsConstraints.size(); i++) {
+            PhysicsConstraint *constraint = physicsConstraints[i];
+            if (constraint->_active && global(constraint->_data))
+                set(constraint, getAbsoluteValue(time, alpha, blend, get(constraint), setup(constraint), value));
+        }
+    } else {
+        PhysicsConstraint *constraint = skeleton.getPhysicsConstraints()[_constraintIndex];
+        if (constraint->_active) set(constraint, getAbsoluteValue(time, alpha, blend, get(constraint), setup(constraint)));
+    }
+}
+
+void PhysicsConstraintResetTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *, float alpha, MixBlend blend, MixDirection direction) {
+    PhysicsConstraint *constraint = nullptr;
+    if (_constraintIndex != -1) {
+        constraint = skeleton.getPhysicsConstraints()[_constraintIndex];
+        if (!constraint->_active) return;
+    }
+
+    if (lastTime > time) { // Apply after lastTime for looped animations.
+        apply(skeleton, lastTime, FLT_MAX, nullptr, alpha, blend, direction);
+        lastTime = -1;
+    } else if (lastTime >= _frames[_frames.size() - 1]) // Last time is after last frame.
+        return;
+    if (time < _frames[0]) return;
+
+    if (lastTime < _frames[0] || time >= _frames[Animation::search(_frames, lastTime) + 1]) {
+        if (constraint != nullptr)
+            constraint->reset();
+        else {
+            Vector<PhysicsConstraint *> &physicsConstraints = skeleton.getPhysicsConstraints();
+            for (size_t i = 0; i < physicsConstraints.size(); i++) {
+                if (constraint->_active) constraint->reset();
+            }
+        }
+    }
+}

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

@@ -55,30 +55,5 @@ void RotateTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vecto
 	SP_UNUSED(direction);
 
 	Bone *bone = skeleton._bones[_boneIndex];
-	if (!bone->_active) return;
-
-	if (time < _frames[0]) {
-		switch (blend) {
-			case MixBlend_Setup:
-				bone->_rotation = bone->_data._rotation;
-				return;
-			case MixBlend_First:
-				bone->_rotation += (bone->_data._rotation - bone->_rotation) * alpha;
-			default: {
-			}
-		}
-		return;
-	}
-
-	float r = getCurveValue(time);
-	switch (blend) {
-		case MixBlend_Setup:
-			bone->_rotation = bone->_data._rotation + r * alpha;
-			break;
-		case MixBlend_First:
-		case MixBlend_Replace:
-			r += bone->_data._rotation - bone->_rotation;
-		case MixBlend_Add:
-			bone->_rotation += r * alpha;
-	}
+    if (bone->isActive()) bone->_rotation = getRelativeValue(time, alpha, blend, bone->_rotation, bone->getData()._rotation);
 }

+ 3 - 109
spine-cpp/spine-cpp/src/spine/ScaleTimeline.cpp

@@ -170,60 +170,7 @@ void ScaleXTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vecto
 	SP_UNUSED(pEvents);
 
 	Bone *bone = skeleton._bones[_boneIndex];
-	if (!bone->_active) return;
-
-	if (time < _frames[0]) {
-		switch (blend) {
-			case MixBlend_Setup:
-				bone->_scaleX = bone->_data._scaleX;
-				return;
-			case MixBlend_First:
-				bone->_scaleX += (bone->_data._scaleX - bone->_scaleX) * alpha;
-			default: {
-			}
-		}
-		return;
-	}
-
-	float x = getCurveValue(time) * bone->_data._scaleX;
-	if (alpha == 1) {
-		if (blend == MixBlend_Add)
-			bone->_scaleX += x - bone->_data._scaleX;
-		else
-			bone->_scaleX = x;
-	} else {
-		// Mixing out uses sign of setup or current pose, else use sign of key.
-		float bx;
-		if (direction == MixDirection_Out) {
-			switch (blend) {
-				case MixBlend_Setup:
-					bx = bone->_data._scaleX;
-					bone->_scaleX = bx + (MathUtil::abs(x) * MathUtil::sign(bx) - bx) * alpha;
-					break;
-				case MixBlend_First:
-				case MixBlend_Replace:
-					bx = bone->_scaleX;
-					bone->_scaleX = bx + (MathUtil::abs(x) * MathUtil::sign(bx) - bx) * alpha;
-					break;
-				case MixBlend_Add:
-					bone->_scaleX += (x - bone->_data._scaleX) * alpha;
-			}
-		} else {
-			switch (blend) {
-				case MixBlend_Setup:
-					bx = MathUtil::abs(bone->_data._scaleX) * MathUtil::sign(x);
-					bone->_scaleX = bx + (x - bx) * alpha;
-					break;
-				case MixBlend_First:
-				case MixBlend_Replace:
-					bx = MathUtil::abs(bone->_scaleX) * MathUtil::sign(x);
-					bone->_scaleX = bx + (x - bx) * alpha;
-					break;
-				case MixBlend_Add:
-					bone->_scaleX += (x - bone->_data._scaleX) * alpha;
-			}
-		}
-	}
+    if (bone->_active) bone->_scaleX = getScaleValue(time, alpha, blend, direction, bone->_scaleX, bone->_data._scaleX);
 }
 
 RTTI_IMPL(ScaleYTimeline, CurveTimeline1)
@@ -243,58 +190,5 @@ void ScaleYTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vecto
 	SP_UNUSED(pEvents);
 
 	Bone *bone = skeleton._bones[_boneIndex];
-	if (!bone->_active) return;
-
-	if (time < _frames[0]) {
-		switch (blend) {
-			case MixBlend_Setup:
-				bone->_scaleY = bone->_data._scaleY;
-				return;
-			case MixBlend_First:
-				bone->_scaleY += (bone->_data._scaleY - bone->_scaleY) * alpha;
-			default: {
-			}
-		}
-		return;
-	}
-
-	float y = getCurveValue(time) * bone->_data._scaleY;
-	if (alpha == 1) {
-		if (blend == MixBlend_Add)
-			bone->_scaleY += y - bone->_data._scaleY;
-		else
-			bone->_scaleY = y;
-	} else {
-		// Mixing out uses sign of setup or current pose, else use sign of key.
-		float by = 0;
-		if (direction == MixDirection_Out) {
-			switch (blend) {
-				case MixBlend_Setup:
-					by = bone->_data._scaleY;
-					bone->_scaleY = by + (MathUtil::abs(y) * MathUtil::sign(by) - by) * alpha;
-					break;
-				case MixBlend_First:
-				case MixBlend_Replace:
-					by = bone->_scaleY;
-					bone->_scaleY = by + (MathUtil::abs(y) * MathUtil::sign(by) - by) * alpha;
-					break;
-				case MixBlend_Add:
-					bone->_scaleY += (y - bone->_data._scaleY) * alpha;
-			}
-		} else {
-			switch (blend) {
-				case MixBlend_Setup:
-					by = MathUtil::abs(bone->_data._scaleY) * MathUtil::sign(y);
-					bone->_scaleY = by + (y - by) * alpha;
-					break;
-				case MixBlend_First:
-				case MixBlend_Replace:
-					by = MathUtil::abs(bone->_scaleY) * MathUtil::sign(y);
-					bone->_scaleY = by + (y - by) * alpha;
-					break;
-				case MixBlend_Add:
-					bone->_scaleY += (y - bone->_data._scaleY) * alpha;
-			}
-		}
-	}
-}
+    if (bone->_active) bone->_scaleY = getScaleValue(time, alpha, blend, direction, bone->_scaleX, bone->_data._scaleY);
+}

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

@@ -136,33 +136,7 @@ void ShearXTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vecto
 	SP_UNUSED(direction);
 
 	Bone *bone = skeleton._bones[_boneIndex];
-	if (!bone->_active) return;
-
-	if (time < _frames[0]) {
-		switch (blend) {
-			case MixBlend_Setup:
-				bone->_shearX = bone->_data._shearX;
-				return;
-			case MixBlend_First:
-				bone->_shearX += (bone->_data._shearX - bone->_shearX) * alpha;
-			default: {
-			}
-		}
-		return;
-	}
-
-	float x = getCurveValue(time);
-	switch (blend) {
-		case MixBlend_Setup:
-			bone->_shearX = bone->_data._shearX + x * alpha;
-			break;
-		case MixBlend_First:
-		case MixBlend_Replace:
-			bone->_shearX += (bone->_data._shearX + x - bone->_shearX) * alpha;
-			break;
-		case MixBlend_Add:
-			bone->_shearX += x * alpha;
-	}
+    if (bone->_active) bone->_shearX = getRelativeValue(time, alpha, blend, bone->_shearX, bone->_data._shearX);
 }
 
 RTTI_IMPL(ShearYTimeline, CurveTimeline1)
@@ -184,31 +158,5 @@ void ShearYTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vecto
 	SP_UNUSED(direction);
 
 	Bone *bone = skeleton._bones[_boneIndex];
-	if (!bone->_active) return;
-
-	if (time < _frames[0]) {
-		switch (blend) {
-			case MixBlend_Setup:
-				bone->_shearY = bone->_data._shearY;
-				return;
-			case MixBlend_First:
-				bone->_shearY += (bone->_data._shearY - bone->_shearY) * alpha;
-			default: {
-			}
-		}
-		return;
-	}
-
-	float y = getCurveValue(time);
-	switch (blend) {
-		case MixBlend_Setup:
-			bone->_shearY = bone->_data._shearY + y * alpha;
-			break;
-		case MixBlend_First:
-		case MixBlend_Replace:
-			bone->_shearY += (bone->_data._shearY + y - bone->_shearY) * alpha;
-			break;
-		case MixBlend_Add:
-			bone->_shearY += y * alpha;
-	}
+    if (bone->_active) bone->_shearY = getRelativeValue(time, alpha, blend, bone->_shearX, bone->_data._shearY);
 }

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

@@ -70,5 +70,4 @@ namespace spine {
 	float Timeline::getDuration() {
 		return _frames[_frames.size() - getFrameEntries()];
 	}
-
 }// namespace spine

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

@@ -47,7 +47,7 @@ TransformConstraintTimeline::TransformConstraintTimeline(size_t frameCount, size
 														 int transformConstraintIndex) : CurveTimeline(frameCount,
 																									   TransformConstraintTimeline::ENTRIES,
 																									   bezierCount),
-																						 _transformConstraintIndex(
+                                                                                         _constraintIndex(
 																								 transformConstraintIndex) {
 	PropertyId ids[] = {((PropertyId) Property_TransformConstraint << 32) | transformConstraintIndex};
 	setPropertyIds(ids, 1);
@@ -59,7 +59,7 @@ void TransformConstraintTimeline::apply(Skeleton &skeleton, float lastTime, floa
 	SP_UNUSED(pEvents);
 	SP_UNUSED(direction);
 
-	TransformConstraint *constraintP = skeleton._transformConstraints[_transformConstraintIndex];
+	TransformConstraint *constraintP = skeleton._transformConstraints[_constraintIndex];
 	TransformConstraint &constraint = *constraintP;
 	if (!constraint.isActive()) return;
 

+ 2 - 54
spine-cpp/spine-cpp/src/spine/TranslateTimeline.cpp

@@ -136,33 +136,7 @@ void TranslateXTimeline::apply(Skeleton &skeleton, float lastTime, float time, V
 	SP_UNUSED(direction);
 
 	Bone *bone = skeleton._bones[_boneIndex];
-	if (!bone->_active) return;
-
-	if (time < _frames[0]) {
-		switch (blend) {
-			case MixBlend_Setup:
-				bone->_x = bone->_data._x;
-				return;
-			case MixBlend_First:
-				bone->_x += (bone->_data._x - bone->_x) * alpha;
-			default: {
-			}
-		}
-		return;
-	}
-
-	float x = getCurveValue(time);
-	switch (blend) {
-		case MixBlend_Setup:
-			bone->_x = bone->_data._x + x * alpha;
-			break;
-		case MixBlend_First:
-		case MixBlend_Replace:
-			bone->_x += (bone->_data._x + x - bone->_x) * alpha;
-			break;
-		case MixBlend_Add:
-			bone->_x += x * alpha;
-	}
+    if (bone->_active) bone->_x = getRelativeValue(time, alpha, blend, bone->_x, bone->_data._x);
 }
 
 RTTI_IMPL(TranslateYTimeline, CurveTimeline1)
@@ -184,31 +158,5 @@ void TranslateYTimeline::apply(Skeleton &skeleton, float lastTime, float time, V
 	SP_UNUSED(direction);
 
 	Bone *bone = skeleton._bones[_boneIndex];
-	if (!bone->_active) return;
-
-	if (time < _frames[0]) {
-		switch (blend) {
-			case MixBlend_Setup:
-				bone->_y = bone->_data._y;
-				return;
-			case MixBlend_First:
-				bone->_y += (bone->_data._y - bone->_y) * alpha;
-			default: {
-			}
-		}
-		return;
-	}
-
-	float y = getCurveValue(time);
-	switch (blend) {
-		case MixBlend_Setup:
-			bone->_y = bone->_data._y + y * alpha;
-			break;
-		case MixBlend_First:
-		case MixBlend_Replace:
-			bone->_y += (bone->_data._y + y - bone->_y) * alpha;
-			break;
-		case MixBlend_Add:
-			bone->_y += y * alpha;
-	}
+    if (bone->_active) bone->_y = getRelativeValue(time, alpha, blend, bone->_y, bone->_data._y);
 }

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

@@ -124,7 +124,7 @@ void spineboy(SkeletonData *skeletonData, Atlas *atlas) {
 	skeleton->setToSetupPose();
 
 	skeleton->setPosition(320, 590);
-	skeleton->updateWorldTransform(Physics::update);
+	skeleton->updateWorldTransform(Physics_Update);
 
 	Slot *headSlot = skeleton->findSlot("head");
 
@@ -227,7 +227,7 @@ void ikDemo(SkeletonData *skeletonData, Atlas *atlas) {
 		// Calculate final world transform with the
 		// crosshair bone set to the mouse cursor
 		// position.
-		drawable.skeleton->updateWorldTransform(Physics::update);
+		drawable.skeleton->updateWorldTransform(Physics_Update);
 
 		window.clear();
 		window.draw(drawable);
@@ -246,7 +246,7 @@ void goblins(SkeletonData *skeletonData, Atlas *atlas) {
 	skeleton->setSkin("goblingirl");
 	skeleton->setSlotsToSetupPose();
 	skeleton->setPosition(320, 590);
-	skeleton->updateWorldTransform(Physics::update);
+	skeleton->updateWorldTransform(Physics_Update);
 
 	drawable.state->setAnimation(0, "walk", true);
 
@@ -281,7 +281,7 @@ void raptor(SkeletonData *skeletonData, Atlas *atlas) {
 
 	Skeleton *skeleton = drawable.skeleton;
 	skeleton->setPosition(320, 590);
-	skeleton->updateWorldTransform(Physics::update);
+	skeleton->updateWorldTransform(Physics_Update);
 
 	drawable.state->setAnimation(0, "walk", true);
 	drawable.state->addAnimation(1, "gun-grab", false, 2);
@@ -314,7 +314,7 @@ void tank(SkeletonData *skeletonData, Atlas *atlas) {
 
 	Skeleton *skeleton = drawable.skeleton;
 	skeleton->setPosition(500, 590);
-	skeleton->updateWorldTransform(Physics::update);
+	skeleton->updateWorldTransform(Physics_Update);
 
 	drawable.state->setAnimation(0, "drive", true);
 
@@ -345,7 +345,7 @@ void vine(SkeletonData *skeletonData, Atlas *atlas) {
 
 	Skeleton *skeleton = drawable.skeleton;
 	skeleton->setPosition(320, 590);
-	skeleton->updateWorldTransform(Physics::update);
+	skeleton->updateWorldTransform(Physics_Update);
 
 	drawable.state->setAnimation(0, "grow", true);
 
@@ -378,7 +378,7 @@ void stretchyman(SkeletonData *skeletonData, Atlas *atlas) {
 	Skeleton *skeleton = drawable.skeleton;
 
 	skeleton->setPosition(100, 590);
-	skeleton->updateWorldTransform(Physics::update);
+	skeleton->updateWorldTransform(Physics_Update);
 
 	drawable.state->setAnimation(0, "sneak", true);
 
@@ -411,7 +411,7 @@ void stretchymanStrechyIk(SkeletonData *skeletonData, Atlas *atlas) {
 	Skeleton *skeleton = drawable->skeleton;
 
 	skeleton->setPosition(100, 590);
-	skeleton->updateWorldTransform(Physics::update);
+	skeleton->updateWorldTransform(Physics_Update);
 
 	drawable->state->setAnimation(0, "sneak", true);
 
@@ -445,7 +445,7 @@ void coin(SkeletonData *skeletonData, Atlas *atlas) {
 
 	Skeleton *skeleton = drawable.skeleton;
 	skeleton->setPosition(320, 320);
-	skeleton->updateWorldTransform(Physics::update);
+	skeleton->updateWorldTransform(Physics_Update);
 
 	drawable.state->setAnimation(0, "animation", true);
 
@@ -479,7 +479,7 @@ void dragon(SkeletonData *skeletonData, Atlas *atlas) {
 
 	Skeleton *skeleton = drawable.skeleton;
 	skeleton->setPosition(320, 320);
-	skeleton->updateWorldTransform(Physics::update);
+	skeleton->updateWorldTransform(Physics_Update);
 
 	drawable.state->setAnimation(0, "flying", true);
 
@@ -513,7 +513,7 @@ void owl(SkeletonData *skeletonData, Atlas *atlas) {
 
 	Skeleton *skeleton = drawable.skeleton;
 	skeleton->setPosition(320, 400);
-	skeleton->updateWorldTransform(Physics::update);
+	skeleton->updateWorldTransform(Physics_Update);
 
 	drawable.state->setAnimation(0, "idle", true);
 	drawable.state->setAnimation(1, "blink", true);
@@ -588,7 +588,7 @@ void mixAndMatch(SkeletonData *skeletonData, Atlas *atlas) {
 	skeleton->setSlotsToSetupPose();
 
 	skeleton->setPosition(320, 590);
-	skeleton->updateWorldTransform(Physics::update);
+	skeleton->updateWorldTransform(Physics_Update);
 
 	drawable.state->setAnimation(0, "dance", true);
 
@@ -626,7 +626,7 @@ void test(SkeletonData *skeletonData, Atlas *atlas) {
 	for (int i = 0; i < 1; i++) {
 		animationState.update(d);
 		animationState.apply(skeleton);
-		skeleton.updateWorldTransform(Physics::update);
+		skeleton.updateWorldTransform(Physics_Update);
 		d += 0.1f;
 	}
 }

+ 1 - 2
spine-sfml/cpp/example/testbed.cpp

@@ -104,8 +104,7 @@ int main(void) {
 
 	AnimationStateData stateData(skeletonData);
 	SkeletonDrawable drawable(skeletonData, &stateData);
-    drawable.skeleton->getRootBone()->update(Physics::update);
-	drawable.skeleton->updateWorldTransform(Physics::update);
+	drawable.skeleton->updateWorldTransform(Physics_Update);
 	drawable.skeleton->setPosition(320, 960);
 	if (animation.length() > 0) drawable.state->setAnimation(0, animation, true);
 	if (skin.length() > 0) drawable.skeleton->setSkin(skin);

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

@@ -52,7 +52,7 @@ namespace spine {
 
 		~SkeletonDrawable();
 
-		void update(float deltaTime, Physics physics = Physics::update);
+		void update(float deltaTime, Physics physics = Physics_Update);
 
 		virtual void draw(sf::RenderTarget &target, sf::RenderStates states) const;