Browse Source

[libgdx] Fixed applying a constraint reverting changes from other constraints.

Previously: When a bone in the update cache is updated, the local transform is used. This causes any applied transform to be lost, losing the pose from any previously applied constraints.

After this commit: Before processing the update cache, first all bones applied transform is set to the local transform. Next, when a bone in the update cache is updated, the applied transform is used. This keeps the pose from any previously applied constraints. Additionally, instead of using the `appliedValid` flag, the applied transform is always updated after making changes to the world transform.

Forum discussion:
http://esotericsoftware.com/forum/Editor-Parent-constraint-order-breaks-child-constraints-15774?p=69494#p69494
Nathan Sweet 4 years ago
parent
commit
4f73fbbb39

+ 0 - 1
spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/IKTest.java

@@ -118,7 +118,6 @@ public class IKTest extends ApplicationAdapter {
 		boneCoords.set(cameraCoords.x, cameraCoords.y);
 		crosshair.getParent().worldToLocal(boneCoords); // camera space to local bone space
 		crosshair.setPosition(boneCoords.x, boneCoords.y); // override the crosshair position
-		crosshair.setAppliedValid(false);
 
 		// Calculate final world transform with the
 		// crosshair bone set to the mouse cursor

+ 18 - 29
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java

@@ -51,7 +51,6 @@ public class Bone implements Updatable {
 	final Array<Bone> children = new Array();
 	float x, y, rotation, scaleX, scaleY, shearX, shearY;
 	float ax, ay, arotation, ascaleX, ascaleY, ashearX, ashearY;
-	boolean appliedValid;
 	float a, b, worldX;
 	float c, d, worldY;
 
@@ -82,9 +81,9 @@ public class Bone implements Updatable {
 		shearY = bone.shearY;
 	}
 
-	/** Same as {@link #updateWorldTransform()}. This method exists for Bone to implement {@link Updatable}. */
+	/** Computes the world transform using the parent bone and this bone's local applied transform. */
 	public void update () {
-		updateWorldTransform(x, y, rotation, scaleX, scaleY, shearX, shearY);
+		updateWorldTransform(ax, ay, arotation, ascaleX, ascaleY, ashearX, ashearY);
 	}
 
 	/** Computes the world transform using the parent bone and this bone's local transform.
@@ -94,7 +93,8 @@ public class Bone implements Updatable {
 		updateWorldTransform(x, y, rotation, scaleX, scaleY, shearX, shearY);
 	}
 
-	/** Computes the world transform using the parent bone and the specified local transform. Child bones are not updated.
+	/** Computes the world transform using the parent bone and the specified local transform. The applied transform is set to the
+	 * specified local transform. Child bones are not updated.
 	 * <p>
 	 * See <a href="http://esotericsoftware.com/spine-runtime-skeletons#World-transforms">World transforms</a> in the Spine
 	 * Runtimes Guide. */
@@ -106,7 +106,6 @@ public class Bone implements Updatable {
 		ascaleY = scaleY;
 		ashearX = shearX;
 		ashearY = shearY;
-		appliedValid = true;
 
 		Bone parent = this.parent;
 		if (parent == null) { // Root bone.
@@ -387,26 +386,15 @@ public class Bone implements Updatable {
 		this.ashearY = ashearY;
 	}
 
-	/** If true, the applied transform matches the world transform. If false, the world transform has been modified since it was
-	 * computed and {@link #updateAppliedTransform()} must be called before accessing the applied transform. */
-	public boolean isAppliedValid () {
-		return appliedValid;
-	}
-
-	public void setAppliedValid (boolean appliedValid) {
-		this.appliedValid = appliedValid;
-	}
-
-	/** Computes the applied transform values from the world transform. This allows the applied transform to be accessed after the
-	 * world transform has been modified (by a constraint, {@link #rotateWorld(float)}, etc).
+	/** Computes the applied transform values from the world transform.
 	 * <p>
-	 * If {@link #updateWorldTransform()} has been called for a bone and {@link #isAppliedValid()} is false, then
-	 * {@link #updateAppliedTransform()} must be called before accessing the applied transform.
+	 * If the world transform is modified (by a constraint, {@link #rotateWorld(float)}, etc) then this method should be called so
+	 * the applied transform matches the world transform. The applied transform may be needed by other code (eg to apply another
+	 * constraints).
 	 * <p>
 	 * Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation. The applied transform after
 	 * calling this method is equivalent to the local tranform used to compute the world transform, but may not be identical. */
 	public void updateAppliedTransform () {
-		appliedValid = true;
 		Bone parent = this.parent;
 		if (parent == null) {
 			ax = worldX;
@@ -448,7 +436,7 @@ public class Bone implements Updatable {
 
 	// -- World transform
 
-	/** Part of the world transform matrix for the X axis. If changed, {@link #setAppliedValid(boolean)} should be set to false. */
+	/** Part of the world transform matrix for the X axis. If changed, {@link #updateAppliedTransform()} should be called. */
 	public float getA () {
 		return a;
 	}
@@ -457,7 +445,7 @@ public class Bone implements Updatable {
 		this.a = a;
 	}
 
-	/** Part of the world transform matrix for the Y axis. If changed, {@link #setAppliedValid(boolean)} should be set to false. */
+	/** Part of the world transform matrix for the Y axis. If changed, {@link #updateAppliedTransform()} should be called. */
 	public float getB () {
 		return b;
 	}
@@ -466,7 +454,7 @@ public class Bone implements Updatable {
 		this.b = b;
 	}
 
-	/** Part of the world transform matrix for the X axis. If changed, {@link #setAppliedValid(boolean)} should be set to false. */
+	/** Part of the world transform matrix for the X axis. If changed, {@link #updateAppliedTransform()} should be called. */
 	public float getC () {
 		return c;
 	}
@@ -475,7 +463,7 @@ public class Bone implements Updatable {
 		this.c = c;
 	}
 
-	/** Part of the world transform matrix for the Y axis. If changed, {@link #setAppliedValid(boolean)} should be set to false. */
+	/** Part of the world transform matrix for the Y axis. If changed, {@link #updateAppliedTransform()} should be called. */
 	public float getD () {
 		return d;
 	}
@@ -484,7 +472,7 @@ public class Bone implements Updatable {
 		this.d = d;
 	}
 
-	/** The world X position. If changed, {@link #setAppliedValid(boolean)} should be set to false. */
+	/** The world X position. If changed, {@link #updateAppliedTransform()} should be called. */
 	public float getWorldX () {
 		return worldX;
 	}
@@ -493,7 +481,7 @@ public class Bone implements Updatable {
 		this.worldX = worldX;
 	}
 
-	/** The world Y position. If changed, {@link #setAppliedValid(boolean)} should be set to false. */
+	/** The world Y position. If changed, {@link #updateAppliedTransform()} should be called. */
 	public float getWorldY () {
 		return worldY;
 	}
@@ -569,15 +557,16 @@ public class Bone implements Updatable {
 		return atan2(cos * c + sin * d, cos * a + sin * b) * radDeg;
 	}
 
-	/** Rotates the world transform the specified amount and sets {@link #isAppliedValid()} to false.
-	 * {@link #updateWorldTransform()} will need to be called on any child bones, recursively, and any constraints reapplied. */
+	/** Rotates the world transform the specified amount.
+	 * <p>
+	 * After changes are made to the world transform, {@link #updateAppliedTransform()} should be called and {@link #update()} will
+	 * need to be called on any child bones, recursively. */
 	public void rotateWorld (float degrees) {
 		float cos = cosDeg(degrees), sin = sinDeg(degrees);
 		a = cos * a - sin * c;
 		b = cos * b - sin * d;
 		c = sin * a + cos * c;
 		d = sin * b + cos * d;
-		appliedValid = false;
 	}
 
 	// ---

+ 0 - 3
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java

@@ -177,7 +177,6 @@ public class IkConstraint implements Updatable {
 	static public void apply (Bone bone, float targetX, float targetY, boolean compress, boolean stretch, boolean uniform,
 		float alpha) {
 		if (bone == null) throw new IllegalArgumentException("bone cannot be null.");
-		if (!bone.appliedValid) bone.updateAppliedTransform();
 		Bone p = bone.parent;
 		float pa = p.a, pb = p.b, pc = p.c, pd = p.d;
 		float rotationIK = -bone.ashearX - bone.arotation, tx, ty;
@@ -230,8 +229,6 @@ public class IkConstraint implements Updatable {
 		float softness, float alpha) {
 		if (parent == null) throw new IllegalArgumentException("parent cannot be null.");
 		if (child == null) throw new IllegalArgumentException("child cannot be null.");
-		if (!parent.appliedValid) parent.updateAppliedTransform();
-		if (!child.appliedValid) child.updateAppliedTransform();
 		float px = parent.ax, py = parent.ay, psx = parent.ascaleX, psy = parent.ascaleY, sx = psx, sy = psy, csx = child.ascaleX;
 		int os1, os2, s2;
 		if (psx < 0) {

+ 1 - 1
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraint.java

@@ -215,7 +215,7 @@ public class PathConstraint implements Updatable {
 				bone.c = sin * a + cos * c;
 				bone.d = sin * b + cos * d;
 			}
-			bone.appliedValid = false;
+			bone.updateAppliedTransform();
 		}
 	}
 

+ 12 - 0
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java

@@ -342,6 +342,18 @@ public class Skeleton {
 	 * See <a href="http://esotericsoftware.com/spine-runtime-skeletons#World-transforms">World transforms</a> in the Spine
 	 * Runtimes Guide. */
 	public void updateWorldTransform () {
+		Object[] bones = this.bones.items;
+		for (int i = 0, n = this.bones.size; i < n; i++) {
+			Bone bone = (Bone)bones[i];
+			bone.ax = bone.x;
+			bone.ay = bone.y;
+			bone.arotation = bone.rotation;
+			bone.ascaleX = bone.scaleX;
+			bone.ascaleY = bone.scaleY;
+			bone.ashearX = bone.shearX;
+			bone.ashearY = bone.shearY;
+		}
+
 		Object[] updateCache = this.updateCache.items;
 		for (int i = 0, n = this.updateCache.size; i < n; i++)
 			((Updatable)updateCache[i]).update();

+ 2 - 6
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraint.java

@@ -159,7 +159,7 @@ public class TransformConstraint implements Updatable {
 				bone.d = sin(r) * s;
 			}
 
-			bone.appliedValid = false;
+			bone.updateAppliedTransform();
 		}
 	}
 
@@ -223,7 +223,7 @@ public class TransformConstraint implements Updatable {
 				bone.d = sin(r) * s;
 			}
 
-			bone.appliedValid = false;
+			bone.updateAppliedTransform();
 		}
 	}
 
@@ -232,12 +232,10 @@ public class TransformConstraint implements Updatable {
 			mixScaleY = this.mixScaleY, mixShearY = this.mixShearY;
 
 		Bone target = this.target;
-		if (!target.appliedValid) target.updateAppliedTransform();
 
 		Object[] bones = this.bones.items;
 		for (int i = 0, n = this.bones.size; i < n; i++) {
 			Bone bone = (Bone)bones[i];
-			if (!bone.appliedValid) bone.updateAppliedTransform();
 
 			float rotation = bone.arotation;
 			if (mixRotate != 0) {
@@ -272,12 +270,10 @@ public class TransformConstraint implements Updatable {
 			mixScaleY = this.mixScaleY, mixShearY = this.mixShearY;
 
 		Bone target = this.target;
-		if (!target.appliedValid) target.updateAppliedTransform();
 
 		Object[] bones = this.bones.items;
 		for (int i = 0, n = this.bones.size; i < n; i++) {
 			Bone bone = (Bone)bones[i];
-			if (!bone.appliedValid) bone.updateAppliedTransform();
 
 			float rotation = bone.arotation + (target.arotation + data.offsetRotation) * mixRotate;
 			float x = bone.ax + (target.ax + data.offsetX) * mixX;