Skip to content

Commit

Permalink
[libgdx] Fixed applying a constraint reverting changes from other con…
Browse files Browse the repository at this point in the history
…straints.

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
  • Loading branch information
NathanSweet committed May 25, 2021
1 parent bb239c3 commit 4f73fbb
Show file tree
Hide file tree
Showing 6 changed files with 33 additions and 40 deletions.
Expand Up @@ -118,7 +118,6 @@ public void render () {
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
Expand Down
47 changes: 18 additions & 29 deletions spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java
Expand Up @@ -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;

Expand Down Expand Up @@ -82,9 +81,9 @@ public Bone (Bone bone, Skeleton skeleton, @Null Bone parent) {
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.
Expand All @@ -94,7 +93,8 @@ public void updateWorldTransform () {
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. */
Expand All @@ -106,7 +106,6 @@ public void updateWorldTransform (float x, float y, float rotation, float scaleX
ascaleY = scaleY;
ashearX = shearX;
ashearY = shearY;
appliedValid = true;

Bone parent = this.parent;
if (parent == null) { // Root bone.
Expand Down Expand Up @@ -387,26 +386,15 @@ public void setAShearY (float ashearY) {
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. */

This comment has been minimized.

Copy link
@HaraldCsaszar

HaraldCsaszar May 27, 2021

Collaborator

found two minor typos above: tranform and eg to apply another <linebreak> constraints).

public void updateAppliedTransform () {
appliedValid = true;
Bone parent = this.parent;
if (parent == null) {
ax = worldX;
Expand Down Expand Up @@ -448,7 +436,7 @@ public void updateAppliedTransform () {

// -- 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;
}
Expand All @@ -457,7 +445,7 @@ public void setA (float a) {
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;
}
Expand All @@ -466,7 +454,7 @@ public void setB (float b) {
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;
}
Expand All @@ -475,7 +463,7 @@ public void setC (float c) {
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;
}
Expand All @@ -484,7 +472,7 @@ public void setD (float d) {
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;
}
Expand All @@ -493,7 +481,7 @@ public void setWorldX (float worldX) {
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;
}
Expand Down Expand Up @@ -569,15 +557,16 @@ public float localToWorldRotation (float localRotation) {
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;
}

// ---
Expand Down
Expand Up @@ -177,7 +177,6 @@ public String toString () {
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;
Expand Down Expand Up @@ -230,8 +229,6 @@ static public void apply (Bone parent, Bone child, float targetX, float targetY,
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) {
Expand Down
Expand Up @@ -215,7 +215,7 @@ else if (r < -SpineUtils.PI) //
bone.c = sin * a + cos * c;
bone.d = sin * b + cos * d;
}
bone.appliedValid = false;
bone.updateAppliedTransform();
}
}

Expand Down
Expand Up @@ -342,6 +342,18 @@ private void sortReset (Array<Bone> bones) {
* 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();
Expand Down
Expand Up @@ -159,7 +159,7 @@ else if (r < -PI) //
bone.d = sin(r) * s;
}

bone.appliedValid = false;
bone.updateAppliedTransform();
}
}

Expand Down Expand Up @@ -223,7 +223,7 @@ else if (r < -PI) //
bone.d = sin(r) * s;
}

bone.appliedValid = false;
bone.updateAppliedTransform();
}
}

Expand All @@ -232,12 +232,10 @@ private void applyAbsoluteLocal () {
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) {
Expand Down Expand Up @@ -272,12 +270,10 @@ private void applyRelativeLocal () {
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;
Expand Down

0 comments on commit 4f73fbb

Please sign in to comment.