From cab6f73396f91cb4114ec26217420025ffe20579 Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Sat, 12 Apr 2025 22:08:54 -0400 Subject: [PATCH] [libgdx] BonePose/Applied classes. --- .../com/esotericsoftware/spine/Animation.java | 170 +++++++++--------- .../src/com/esotericsoftware/spine/Bone.java | 124 ++----------- .../esotericsoftware/spine/BoneApplied.java | 46 ++--- .../com/esotericsoftware/spine/BoneData.java | 2 +- .../com/esotericsoftware/spine/BonePose.java | 145 +++++++++++++++ .../esotericsoftware/spine/IkConstraint.java | 30 ++-- .../spine/PathConstraint.java | 17 +- .../spine/PhysicsConstraint.java | 11 +- .../com/esotericsoftware/spine/Skeleton.java | 6 +- .../com/esotericsoftware/spine/Slider.java | 9 +- .../spine/TransformConstraint.java | 9 +- .../com/esotericsoftware/spine/Updatable.java | 9 - .../spine/attachments/RegionAttachment.java | 2 +- .../spine/attachments/VertexAttachment.java | 3 +- 14 files changed, 327 insertions(+), 256 deletions(-) create mode 100644 spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BonePose.java diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java index 008bac261..647d63e7c 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java @@ -544,7 +544,7 @@ public class Animation { } } - /** Changes a bone's local {@link Bone#getRotation()}. */ + /** Changes a bone's local {@link BonePose#getRotation()}. */ static public class RotateTimeline extends CurveTimeline1 implements BoneTimeline { final int boneIndex; @@ -560,15 +560,15 @@ public class Animation { public void apply (Skeleton skeleton, float lastTime, float time, @Null Array events, float alpha, MixBlend blend, MixDirection direction, boolean appliedPose) { - Bone bone = skeleton.bones.get(boneIndex); - if (bone.active) { - if (appliedPose) bone = bone.applied; - bone.rotation = getRelativeValue(time, alpha, blend, bone.rotation, bone.data.rotation); + Bone pose = skeleton.bones.get(boneIndex); + if (pose.active) { + BonePose bone = appliedPose ? pose.applied : pose; + bone.rotation = getRelativeValue(time, alpha, blend, bone.rotation, pose.data.rotation); } } } - /** Changes a bone's local {@link Bone#getX()} and {@link Bone#getY()}. */ + /** Changes a bone's local {@link BonePose#getX()} and {@link BonePose#getY()}. */ static public class TranslateTimeline extends CurveTimeline2 implements BoneTimeline { final int boneIndex; @@ -586,20 +586,20 @@ public class Animation { public void apply (Skeleton skeleton, float lastTime, float time, @Null Array events, float alpha, MixBlend blend, MixDirection direction, boolean appliedPose) { - Bone bone = skeleton.bones.get(boneIndex); - if (!bone.active) return; - if (appliedPose) bone = bone.applied; + Bone pose = skeleton.bones.get(boneIndex); + if (!pose.active) return; + BonePose bone = appliedPose ? pose.applied : pose; float[] frames = this.frames; if (time < frames[0]) { switch (blend) { case setup: - bone.x = bone.data.x; - bone.y = bone.data.y; + bone.x = pose.data.x; + bone.y = pose.data.y; return; case first: - bone.x += (bone.data.x - bone.x) * alpha; - bone.y += (bone.data.y - bone.y) * alpha; + bone.x += (pose.data.x - bone.x) * alpha; + bone.y += (pose.data.y - bone.y) * alpha; } return; } @@ -626,13 +626,13 @@ public class Animation { switch (blend) { case setup: - bone.x = bone.data.x + x * alpha; - bone.y = bone.data.y + y * alpha; + bone.x = pose.data.x + x * alpha; + bone.y = pose.data.y + y * alpha; break; case first: case replace: - bone.x += (bone.data.x + x - bone.x) * alpha; - bone.y += (bone.data.y + y - bone.y) * alpha; + bone.x += (pose.data.x + x - bone.x) * alpha; + bone.y += (pose.data.y + y - bone.y) * alpha; break; case add: bone.x += x * alpha; @@ -641,7 +641,7 @@ public class Animation { } } - /** Changes a bone's local {@link Bone#getX()}. */ + /** Changes a bone's local {@link BonePose#getX()}. */ static public class TranslateXTimeline extends CurveTimeline1 implements BoneTimeline { final int boneIndex; @@ -657,15 +657,15 @@ public class Animation { public void apply (Skeleton skeleton, float lastTime, float time, @Null Array events, float alpha, MixBlend blend, MixDirection direction, boolean appliedPose) { - Bone bone = skeleton.bones.get(boneIndex); - if (bone.active) { - if (appliedPose) bone = bone.applied; - bone.x = getRelativeValue(time, alpha, blend, bone.x, bone.data.x); + Bone pose = skeleton.bones.get(boneIndex); + if (pose.active) { + BonePose bone = appliedPose ? pose.applied : pose; + bone.x = getRelativeValue(time, alpha, blend, bone.x, pose.data.x); } } } - /** Changes a bone's local {@link Bone#getY()}. */ + /** Changes a bone's local {@link BonePose#getY()}. */ static public class TranslateYTimeline extends CurveTimeline1 implements BoneTimeline { final int boneIndex; @@ -681,15 +681,15 @@ public class Animation { public void apply (Skeleton skeleton, float lastTime, float time, @Null Array events, float alpha, MixBlend blend, MixDirection direction, boolean appliedPose) { - Bone bone = skeleton.bones.get(boneIndex); - if (bone.active) { - if (appliedPose) bone = bone.applied; - bone.y = getRelativeValue(time, alpha, blend, bone.y, bone.data.y); + Bone pose = skeleton.bones.get(boneIndex); + if (pose.active) { + BonePose bone = appliedPose ? pose.applied : pose; + bone.y = getRelativeValue(time, alpha, blend, bone.y, pose.data.y); } } } - /** Changes a bone's local {@link Bone#getScaleX()} and {@link Bone#getScaleY()}. */ + /** Changes a bone's local {@link BonePose#getScaleX()} and {@link BonePose#getScaleY()}. */ static public class ScaleTimeline extends CurveTimeline2 implements BoneTimeline { final int boneIndex; @@ -707,20 +707,20 @@ public class Animation { public void apply (Skeleton skeleton, float lastTime, float time, @Null Array events, float alpha, MixBlend blend, MixDirection direction, boolean appliedPose) { - Bone bone = skeleton.bones.get(boneIndex); - if (!bone.active) return; - if (appliedPose) bone = bone.applied; + Bone pose = skeleton.bones.get(boneIndex); + if (!pose.active) return; + BonePose bone = appliedPose ? pose.applied : pose; float[] frames = this.frames; if (time < frames[0]) { switch (blend) { case setup: - bone.scaleX = bone.data.scaleX; - bone.scaleY = bone.data.scaleY; + bone.scaleX = pose.data.scaleX; + bone.scaleY = pose.data.scaleY; return; case first: - bone.scaleX += (bone.data.scaleX - bone.scaleX) * alpha; - bone.scaleY += (bone.data.scaleY - bone.scaleY) * alpha; + bone.scaleX += (pose.data.scaleX - bone.scaleX) * alpha; + bone.scaleY += (pose.data.scaleY - bone.scaleY) * alpha; } return; } @@ -744,13 +744,13 @@ public class Animation { x = getBezierValue(time, i, VALUE1, curveType - BEZIER); y = getBezierValue(time, i, VALUE2, curveType + BEZIER_SIZE - BEZIER); } - x *= bone.data.scaleX; - y *= bone.data.scaleY; + x *= pose.data.scaleX; + y *= pose.data.scaleY; if (alpha == 1) { if (blend == add) { - bone.scaleX += x - bone.data.scaleX; - bone.scaleY += y - bone.data.scaleY; + bone.scaleX += x - pose.data.scaleX; + bone.scaleY += y - pose.data.scaleY; } else { bone.scaleX = x; bone.scaleY = y; @@ -761,8 +761,8 @@ public class Animation { if (direction == out) { switch (blend) { case setup: - bx = bone.data.scaleX; - by = bone.data.scaleY; + bx = pose.data.scaleX; + by = pose.data.scaleY; bone.scaleX = bx + (Math.abs(x) * Math.signum(bx) - bx) * alpha; bone.scaleY = by + (Math.abs(y) * Math.signum(by) - by) * alpha; break; @@ -774,14 +774,14 @@ public class Animation { bone.scaleY = by + (Math.abs(y) * Math.signum(by) - by) * alpha; break; case add: - bone.scaleX += (x - bone.data.scaleX) * alpha; - bone.scaleY += (y - bone.data.scaleY) * alpha; + bone.scaleX += (x - pose.data.scaleX) * alpha; + bone.scaleY += (y - pose.data.scaleY) * alpha; } } else { switch (blend) { case setup: - bx = Math.abs(bone.data.scaleX) * Math.signum(x); - by = Math.abs(bone.data.scaleY) * Math.signum(y); + bx = Math.abs(pose.data.scaleX) * Math.signum(x); + by = Math.abs(pose.data.scaleY) * Math.signum(y); bone.scaleX = bx + (x - bx) * alpha; bone.scaleY = by + (y - by) * alpha; break; @@ -793,15 +793,15 @@ public class Animation { bone.scaleY = by + (y - by) * alpha; break; case add: - bone.scaleX += (x - bone.data.scaleX) * alpha; - bone.scaleY += (y - bone.data.scaleY) * alpha; + bone.scaleX += (x - pose.data.scaleX) * alpha; + bone.scaleY += (y - pose.data.scaleY) * alpha; } } } } } - /** Changes a bone's local {@link Bone#getScaleX()}. */ + /** Changes a bone's local {@link BonePose#getScaleX()}. */ static public class ScaleXTimeline extends CurveTimeline1 implements BoneTimeline { final int boneIndex; @@ -817,15 +817,15 @@ public class Animation { public void apply (Skeleton skeleton, float lastTime, float time, @Null Array events, float alpha, MixBlend blend, MixDirection direction, boolean appliedPose) { - Bone bone = skeleton.bones.get(boneIndex); - if (bone.active) { - if (appliedPose) bone = bone.applied; - bone.scaleX = getScaleValue(time, alpha, blend, direction, bone.scaleX, bone.data.scaleX); + Bone pose = skeleton.bones.get(boneIndex); + if (pose.active) { + BonePose bone = appliedPose ? pose.applied : pose; + bone.scaleX = getScaleValue(time, alpha, blend, direction, bone.scaleX, pose.data.scaleX); } } } - /** Changes a bone's local {@link Bone#getScaleY()}. */ + /** Changes a bone's local {@link BonePose#getScaleY()}. */ static public class ScaleYTimeline extends CurveTimeline1 implements BoneTimeline { final int boneIndex; @@ -841,15 +841,15 @@ public class Animation { public void apply (Skeleton skeleton, float lastTime, float time, @Null Array events, float alpha, MixBlend blend, MixDirection direction, boolean appliedPose) { - Bone bone = skeleton.bones.get(boneIndex); - if (bone.active) { - if (appliedPose) bone = bone.applied; - bone.scaleY = getScaleValue(time, alpha, blend, direction, bone.scaleY, bone.data.scaleY); + Bone pose = skeleton.bones.get(boneIndex); + if (pose.active) { + BonePose bone = appliedPose ? pose.applied : pose; + bone.scaleY = getScaleValue(time, alpha, blend, direction, bone.scaleY, pose.data.scaleY); } } } - /** Changes a bone's local {@link Bone#getShearX()} and {@link Bone#getShearY()}. */ + /** Changes a bone's local {@link BonePose#getShearX()} and {@link BonePose#getShearY()}. */ static public class ShearTimeline extends CurveTimeline2 implements BoneTimeline { final int boneIndex; @@ -867,20 +867,20 @@ public class Animation { public void apply (Skeleton skeleton, float lastTime, float time, @Null Array events, float alpha, MixBlend blend, MixDirection direction, boolean appliedPose) { - Bone bone = skeleton.bones.get(boneIndex); - if (!bone.active) return; - if (appliedPose) bone = bone.applied; + Bone pose = skeleton.bones.get(boneIndex); + if (!pose.active) return; + BonePose bone = appliedPose ? pose.applied : pose; float[] frames = this.frames; if (time < frames[0]) { switch (blend) { case setup: - bone.shearX = bone.data.shearX; - bone.shearY = bone.data.shearY; + bone.shearX = pose.data.shearX; + bone.shearY = pose.data.shearY; return; case first: - bone.shearX += (bone.data.shearX - bone.shearX) * alpha; - bone.shearY += (bone.data.shearY - bone.shearY) * alpha; + bone.shearX += (pose.data.shearX - bone.shearX) * alpha; + bone.shearY += (pose.data.shearY - bone.shearY) * alpha; } return; } @@ -907,13 +907,13 @@ public class Animation { switch (blend) { case setup: - bone.shearX = bone.data.shearX + x * alpha; - bone.shearY = bone.data.shearY + y * alpha; + bone.shearX = pose.data.shearX + x * alpha; + bone.shearY = pose.data.shearY + y * alpha; break; case first: case replace: - bone.shearX += (bone.data.shearX + x - bone.shearX) * alpha; - bone.shearY += (bone.data.shearY + y - bone.shearY) * alpha; + bone.shearX += (pose.data.shearX + x - bone.shearX) * alpha; + bone.shearY += (pose.data.shearY + y - bone.shearY) * alpha; break; case add: bone.shearX += x * alpha; @@ -922,7 +922,7 @@ public class Animation { } } - /** Changes a bone's local {@link Bone#getShearX()}. */ + /** Changes a bone's local {@link BonePose#getShearX()}. */ static public class ShearXTimeline extends CurveTimeline1 implements BoneTimeline { final int boneIndex; @@ -938,15 +938,15 @@ public class Animation { public void apply (Skeleton skeleton, float lastTime, float time, @Null Array events, float alpha, MixBlend blend, MixDirection direction, boolean appliedPose) { - Bone bone = skeleton.bones.get(boneIndex); - if (bone.active) { - if (appliedPose) bone = bone.applied; - bone.shearX = getRelativeValue(time, alpha, blend, bone.shearX, bone.data.shearX); + Bone pose = skeleton.bones.get(boneIndex); + if (pose.active) { + BonePose bone = appliedPose ? pose.applied : pose; + bone.shearX = getRelativeValue(time, alpha, blend, bone.shearX, pose.data.shearX); } } } - /** Changes a bone's local {@link Bone#getShearY()}. */ + /** Changes a bone's local {@link BonePose#getShearY()}. */ static public class ShearYTimeline extends CurveTimeline1 implements BoneTimeline { final int boneIndex; @@ -962,15 +962,15 @@ public class Animation { public void apply (Skeleton skeleton, float lastTime, float time, @Null Array events, float alpha, MixBlend blend, MixDirection direction, boolean appliedPose) { - Bone bone = skeleton.bones.get(boneIndex); - if (bone.active) { - if (appliedPose) bone = bone.applied; - bone.shearY = getRelativeValue(time, alpha, blend, bone.shearY, bone.data.shearY); + Bone pose = skeleton.bones.get(boneIndex); + if (pose.active) { + BonePose bone = appliedPose ? pose.applied : pose; + bone.shearY = getRelativeValue(time, alpha, blend, bone.shearY, pose.data.shearY); } } } - /** Changes a bone's {@link Bone#getInherit()}. */ + /** Changes a bone's {@link BonePose#getInherit()}. */ static public class InheritTimeline extends Timeline implements BoneTimeline { static public final int ENTRIES = 2; static private final int INHERIT = 1; @@ -1002,18 +1002,18 @@ public class Animation { public void apply (Skeleton skeleton, float lastTime, float time, @Null Array events, float alpha, MixBlend blend, MixDirection direction, boolean appliedPose) { - Bone bone = skeleton.bones.get(boneIndex); - if (!bone.active) return; - if (appliedPose) bone = bone.applied; + Bone pose = skeleton.bones.get(boneIndex); + if (!pose.active) return; + BonePose bone = appliedPose ? pose.applied : pose; if (direction == out) { - if (blend == setup) bone.inherit = bone.data.inherit; + if (blend == setup) bone.inherit = pose.data.inherit; return; } float[] frames = this.frames; if (time < frames[0]) { - if (blend == setup || blend == first) bone.inherit = bone.data.inherit; + if (blend == setup || blend == first) bone.inherit = pose.data.inherit; return; } bone.inherit = Inherit.values[(int)frames[search(frames, time, ENTRIES) + INHERIT]]; diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java index a55b32737..eb4a7ceb0 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java @@ -32,32 +32,20 @@ package com.esotericsoftware.spine; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Null; -import com.esotericsoftware.spine.BoneData.Inherit; - /** Stores a bone's current pose. *

* A bone has a local transform which is used to compute its world transform. A bone also has an applied transform, which is a * local transform that can be applied to compute the world transform. The local transform and applied transform may differ if a * constraint or application code modifies the world transform after it was computed from the local transform. */ -public class Bone { +public class Bone extends BonePose { final BoneData data; final Skeleton skeleton; @Null final Bone parent; final Array children; - BoneApplied applied; - - float x, y, rotation, scaleX, scaleY, shearX, shearY; - Inherit inherit; + final BoneApplied applied = new BoneApplied(this); boolean sorted, active; - Bone (Bone bone) { - this.data = bone.data; - this.skeleton = bone.skeleton; - this.parent = bone.parent; - this.children = bone.children; - } - public Bone (BoneData data, Skeleton skeleton, @Null Bone parent) { if (data == null) throw new IllegalArgumentException("data cannot be null."); if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null."); @@ -66,27 +54,16 @@ public class Bone { this.parent = parent; children = new Array(); - applied = new BoneApplied(this); - setToSetupPose(); } /** Copy constructor. Does not copy the {@link #getChildren()} bones. */ public Bone (Bone bone, Skeleton skeleton, @Null Bone parent) { - if (bone == null) throw new IllegalArgumentException("bone cannot be null."); - if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null."); + super(bone); this.skeleton = skeleton; this.parent = parent; children = new Array(); data = bone.data; - x = bone.x; - y = bone.y; - rotation = bone.rotation; - scaleX = bone.scaleX; - scaleY = bone.scaleY; - shearX = bone.shearX; - shearY = bone.shearY; - inherit = bone.inherit; } /** Sets this bone's local transform to the setup pose. */ @@ -122,98 +99,17 @@ public class Bone { return children; } + /** Returns false when this bone won't be updated by + * {@link Skeleton#updateWorldTransform(com.esotericsoftware.spine.Skeleton.Physics)} because a skin is required and the + * {@link Skeleton#getSkin() active skin} does not contain this item. + * @see Skin#getBones() + * @see Skin#getConstraints() + * @see BoneData#getSkinRequired() + * @see Skeleton#updateCache() */ public boolean isActive () { return active; } - /** The local x translation. */ - public float getX () { - return x; - } - - public void setX (float x) { - this.x = x; - } - - /** The local y translation. */ - public float getY () { - return y; - } - - public void setY (float y) { - this.y = y; - } - - public void setPosition (float x, float y) { - this.x = x; - this.y = y; - } - - /** The local rotation in degrees, counter clockwise. */ - public float getRotation () { - return rotation; - } - - public void setRotation (float rotation) { - this.rotation = rotation; - } - - /** The local scaleX. */ - public float getScaleX () { - return scaleX; - } - - public void setScaleX (float scaleX) { - this.scaleX = scaleX; - } - - /** The local scaleY. */ - public float getScaleY () { - return scaleY; - } - - public void setScaleY (float scaleY) { - this.scaleY = scaleY; - } - - public void setScale (float scaleX, float scaleY) { - this.scaleX = scaleX; - this.scaleY = scaleY; - } - - public void setScale (float scale) { - scaleX = scale; - scaleY = scale; - } - - /** The local shearX. */ - public float getShearX () { - return shearX; - } - - public void setShearX (float shearX) { - this.shearX = shearX; - } - - /** The local shearY. */ - public float getShearY () { - return shearY; - } - - public void setShearY (float shearY) { - this.shearY = shearY; - } - - /** Determines how parent world transforms affect this bone. */ - public Inherit getInherit () { - return inherit; - } - - public void setInherit (Inherit inherit) { - if (inherit == null) throw new IllegalArgumentException("inherit cannot be null."); - this.inherit = inherit; - } - /** Returns the bone for applied pose. */ public BoneApplied getApplied () { return applied; diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneApplied.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneApplied.java index 43fff06ca..6195c5b2b 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneApplied.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneApplied.java @@ -11,21 +11,20 @@ import com.badlogic.gdx.utils.Null; import com.esotericsoftware.spine.BoneData.Inherit; import com.esotericsoftware.spine.Skeleton.Physics; -public class BoneApplied extends Bone implements Updatable { +public class BoneApplied extends BonePose implements Updatable { final Bone pose; - @Null final BoneApplied parentApplied; + @Null final BoneApplied parent; float a, b, worldX; float c, d, worldY; BoneApplied (Bone bone) { - super(bone); pose = bone; - parentApplied = parent == null ? null : parent.applied; + parent = bone.parent == null ? null : bone.parent.applied; } /** Computes the world transform using the parent bone and this bone's local applied transform. */ public void updateWorldTransform () { - updateWorldTransform(); + update(null); } /** Computes the world transform using the parent bone and this bone's local transform. @@ -37,9 +36,10 @@ public class BoneApplied extends Bone implements Updatable { * See World transforms in the Spine * Runtimes Guide. */ public void update (Physics physics) { - BoneApplied parent = parentApplied; + Skeleton skeleton = pose.skeleton; + + BoneApplied parent = this.parent; if (parent == null) { // Root bone. - Skeleton skeleton = this.skeleton; float sx = skeleton.scaleX, sy = skeleton.scaleY; float rx = (rotation + shearX) * degRad; float ry = (rotation + 90 + shearY) * degRad; @@ -136,16 +136,18 @@ public class BoneApplied extends Bone implements Updatable { d *= skeleton.scaleY; } - /** Computes the applied transform values from the world transform. + /** Computes the local transform values from the world 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 + * the local transform matches the world transform. The local transform may be needed by other code (eg to apply another * constraint). *

- * Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation. The applied transform after + * Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation. The local transform after * calling this method is equivalent to the local transform used to compute the world transform, but may not be identical. */ - public void updateAppliedTransform () { - BoneApplied parent = parentApplied; + public void updateLocalTransform () { + Skeleton skeleton = pose.skeleton; + + BoneApplied parent = this.parent; if (parent == null) { x = worldX - skeleton.x; y = worldY - skeleton.y; @@ -222,7 +224,7 @@ public class BoneApplied extends Bone implements Updatable { } } - /** Part of the world transform matrix for the X axis. If changed, {@link #updateAppliedTransform()} should be called. */ + /** Part of the world transform matrix for the X axis. If changed, {@link #updateLocalTransform()} should be called. */ public float getA () { return a; } @@ -231,7 +233,7 @@ public class BoneApplied extends Bone implements Updatable { this.a = a; } - /** Part of the world transform matrix for the Y axis. If changed, {@link #updateAppliedTransform()} should be called. */ + /** Part of the world transform matrix for the Y axis. If changed, {@link #updateLocalTransform()} should be called. */ public float getB () { return b; } @@ -240,7 +242,7 @@ public class BoneApplied extends Bone implements Updatable { this.b = b; } - /** Part of the world transform matrix for the X axis. If changed, {@link #updateAppliedTransform()} should be called. */ + /** Part of the world transform matrix for the X axis. If changed, {@link #updateLocalTransform()} should be called. */ public float getC () { return c; } @@ -249,7 +251,7 @@ public class BoneApplied extends Bone implements Updatable { this.c = c; } - /** Part of the world transform matrix for the Y axis. If changed, {@link #updateAppliedTransform()} should be called. */ + /** Part of the world transform matrix for the Y axis. If changed, {@link #updateLocalTransform()} should be called. */ public float getD () { return d; } @@ -258,7 +260,7 @@ public class BoneApplied extends Bone implements Updatable { this.d = d; } - /** The world X position. If changed, {@link #updateAppliedTransform()} should be called. */ + /** The world X position. If changed, {@link #updateLocalTransform()} should be called. */ public float getWorldX () { return worldX; } @@ -267,7 +269,7 @@ public class BoneApplied extends Bone implements Updatable { this.worldX = worldX; } - /** The world Y position. If changed, {@link #updateAppliedTransform()} should be called. */ + /** The world Y position. If changed, {@link #updateLocalTransform()} should be called. */ public float getWorldY () { return worldY; } @@ -333,13 +335,13 @@ public class BoneApplied extends Bone implements Updatable { /** Transforms a point from world coordinates to the parent bone's local coordinates. */ public Vector2 worldToParent (Vector2 world) { if (world == null) throw new IllegalArgumentException("world cannot be null."); - return parent == null ? world : parentApplied.worldToLocal(world); + return parent == null ? world : parent.worldToLocal(world); } /** Transforms a point from the parent bone's coordinates to world coordinates. */ public Vector2 parentToWorld (Vector2 world) { if (world == null) throw new IllegalArgumentException("world cannot be null."); - return parent == null ? world : parentApplied.localToWorld(world); + return parent == null ? world : parent.localToWorld(world); } /** Transforms a world rotation to a local rotation. */ @@ -358,8 +360,8 @@ public class BoneApplied extends Bone implements Updatable { /** Rotates the world transform the specified amount. *

- * After changes are made to the world transform, {@link #updateAppliedTransform()} should be called and - * {@link #update(Physics)} will need to be called on any child bones, recursively. */ + * After changes are made to the world transform, {@link #updateLocalTransform()} should be called and {@link #update(Physics)} + * will need to be called on any child bones, recursively. */ public void rotateWorld (float degrees) { degrees *= degRad; float sin = sin(degrees), cos = cos(degrees); diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneData.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneData.java index 3790231d7..dd9eb9c36 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneData.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneData.java @@ -34,7 +34,7 @@ import com.badlogic.gdx.utils.Null; import com.esotericsoftware.spine.Skeleton.Physics; -/** Stores the setup pose for a {@link Bone}. */ +/** Stores the setup pose for a {@link BonePose}. */ public class BoneData { final int index; final String name; diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BonePose.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BonePose.java new file mode 100644 index 000000000..0f1a70230 --- /dev/null +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BonePose.java @@ -0,0 +1,145 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated April 5, 2025. Replaces all prior versions. + * + * Copyright (c) 2013-2025, 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. + *****************************************************************************/ + +package com.esotericsoftware.spine; + +import com.esotericsoftware.spine.BoneData.Inherit; + +/** Stores a bone's current pose. + *

+ * A bone has a local transform which is used to compute its world transform. A bone also has an applied transform, which is a + * local transform that can be applied to compute the world transform. The local transform and applied transform may differ if a + * constraint or application code modifies the world transform after it was computed from the local transform. */ +public class BonePose { + float x, y, rotation, scaleX, scaleY, shearX, shearY; + Inherit inherit; + + BonePose () { + } + + /** Copy constructor. */ + public BonePose (BonePose bone) { + x = bone.x; + y = bone.y; + rotation = bone.rotation; + scaleX = bone.scaleX; + scaleY = bone.scaleY; + shearX = bone.shearX; + shearY = bone.shearY; + inherit = bone.inherit; + } + + /** The local x translation. */ + public float getX () { + return x; + } + + public void setX (float x) { + this.x = x; + } + + /** The local y translation. */ + public float getY () { + return y; + } + + public void setY (float y) { + this.y = y; + } + + public void setPosition (float x, float y) { + this.x = x; + this.y = y; + } + + /** The local rotation in degrees, counter clockwise. */ + public float getRotation () { + return rotation; + } + + public void setRotation (float rotation) { + this.rotation = rotation; + } + + /** The local scaleX. */ + public float getScaleX () { + return scaleX; + } + + public void setScaleX (float scaleX) { + this.scaleX = scaleX; + } + + /** The local scaleY. */ + public float getScaleY () { + return scaleY; + } + + public void setScaleY (float scaleY) { + this.scaleY = scaleY; + } + + public void setScale (float scaleX, float scaleY) { + this.scaleX = scaleX; + this.scaleY = scaleY; + } + + public void setScale (float scale) { + scaleX = scale; + scaleY = scale; + } + + /** The local shearX. */ + public float getShearX () { + return shearX; + } + + public void setShearX (float shearX) { + this.shearX = shearX; + } + + /** The local shearY. */ + public float getShearY () { + return shearY; + } + + public void setShearY (float shearY) { + this.shearY = shearY; + } + + /** Determines how parent world transforms affect this bone. */ + public Inherit getInherit () { + return inherit; + } + + public void setInherit (Inherit inherit) { + if (inherit == null) throw new IllegalArgumentException("inherit cannot be null."); + this.inherit = inherit; + } +} diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java index 256637a5b..33ecfa55a 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java @@ -107,7 +107,7 @@ public class IkConstraint implements Updatable { } /** The bone that is the IK target. */ - public Bone getTarget () { + public BoneApplied getTarget () { return target; } @@ -167,6 +167,13 @@ public class IkConstraint implements Updatable { this.stretch = stretch; } + /** Returns false when this constraint won't be updated by + * {@link Skeleton#updateWorldTransform(com.esotericsoftware.spine.Skeleton.Physics)} because a skin is required and the + * {@link Skeleton#getSkin() active skin} does not contain this item. + * @see Skin#getBones() + * @see Skin#getConstraints() + * @see ConstraintData#getSkinRequired() + * @see Skeleton#updateCache() */ public boolean isActive () { return active; } @@ -184,20 +191,21 @@ public class IkConstraint implements Updatable { static public void apply (BoneApplied bone, float targetX, float targetY, boolean compress, boolean stretch, boolean uniform, float alpha) { if (bone == null) throw new IllegalArgumentException("bone cannot be null."); - BoneApplied p = bone.parentApplied; + BoneApplied p = bone.parent; float pa = p.a, pb = p.b, pc = p.c, pd = p.d; float rotationIK = -bone.shearX - bone.rotation, tx, ty; switch (bone.inherit) { case onlyTranslation: - tx = (targetX - bone.worldX) * Math.signum(bone.skeleton.scaleX); - ty = (targetY - bone.worldY) * Math.signum(bone.skeleton.scaleY); + tx = (targetX - bone.worldX) * Math.signum(bone.pose.skeleton.scaleX); + ty = (targetY - bone.worldY) * Math.signum(bone.pose.skeleton.scaleY); break; case noRotationOrReflection: float s = Math.abs(pa * pd - pb * pc) / Math.max(0.0001f, pa * pa + pc * pc); - float sa = pa / bone.skeleton.scaleX; - float sc = pc / bone.skeleton.scaleY; - pb = -sc * s * bone.skeleton.scaleX; - pd = sa * s * bone.skeleton.scaleY; + Skeleton skeleton = bone.pose.skeleton; + float sa = pa / skeleton.scaleX; + float sc = pc / skeleton.scaleY; + pb = -sc * s * skeleton.scaleX; + pd = sa * s * skeleton.scaleY; rotationIK += atan2Deg(sc, sa); // Fall through. default: @@ -225,7 +233,7 @@ public class IkConstraint implements Updatable { ty = targetY - bone.worldY; } } - float b = bone.data.length * bone.scaleX; + float b = bone.pose.data.length * bone.scaleX; if (b > 0.0001f) { float dd = tx * tx + ty * ty; if ((compress && dd < b * b) || (stretch && dd > b * b)) { @@ -274,7 +282,7 @@ public class IkConstraint implements Updatable { cwx = a * child.x + b * child.y + parent.worldX; cwy = c * child.x + d * child.y + parent.worldY; } - BoneApplied pp = parent.parentApplied; + BoneApplied pp = parent.parent; a = pp.a; b = pp.b; c = pp.c; @@ -282,7 +290,7 @@ public class IkConstraint implements Updatable { float id = a * d - b * c, x = cwx - pp.worldX, y = cwy - pp.worldY; id = Math.abs(id) <= 0.0001f ? 0 : 1 / id; float dx = (x * d - y * b) * id - px, dy = (y * a - x * c) * id - py; - float l1 = (float)Math.sqrt(dx * dx + dy * dy), l2 = child.data.length * csx, a1, a2; + float l1 = (float)Math.sqrt(dx * dx + dy * dy), l2 = child.pose.data.length * csx, a1, a2; if (l1 < 0.0001f) { apply(parent, targetX, targetY, false, stretch, false, alpha); child.rotation = 0; diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraint.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraint.java index ba0f1ba7b..2a3f399e1 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraint.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraint.java @@ -119,7 +119,7 @@ public class PathConstraint implements Updatable { if (scale) { for (int i = 0, n = spacesCount - 1; i < n; i++) { var bone = (BoneApplied)bones[i]; - float setupLength = bone.data.length; + float setupLength = bone.pose.data.length; float x = setupLength * bone.a, y = setupLength * bone.c; lengths[i] = (float)Math.sqrt(x * x + y * y); } @@ -130,7 +130,7 @@ public class PathConstraint implements Updatable { float sum = 0; for (int i = 0, n = spacesCount - 1; i < n;) { var bone = (BoneApplied)bones[i]; - float setupLength = bone.data.length; + float setupLength = bone.pose.data.length; if (setupLength < epsilon) { if (scale) lengths[i] = 0; spaces[++i] = spacing; @@ -152,7 +152,7 @@ public class PathConstraint implements Updatable { boolean lengthSpacing = data.spacingMode == SpacingMode.length; for (int i = 0, n = spacesCount - 1; i < n;) { var bone = (BoneApplied)bones[i]; - float setupLength = bone.data.length; + float setupLength = bone.pose.data.length; if (setupLength < epsilon) { if (scale) lengths[i] = 0; spaces[++i] = spacing; @@ -203,7 +203,7 @@ public class PathConstraint implements Updatable { if (tip) { cos = cos(r); sin = sin(r); - float length = bone.data.length; + float length = bone.pose.data.length; boneX += (length * (cos * a - sin * c) - dx) * mixRotate; boneY += (length * (sin * a + cos * c) - dy) * mixRotate; } else @@ -220,7 +220,7 @@ public class PathConstraint implements Updatable { bone.c = sin * a + cos * c; bone.d = sin * b + cos * d; } - bone.updateAppliedTransform(); + bone.updateLocalTransform(); } } @@ -542,6 +542,13 @@ public class PathConstraint implements Updatable { this.slot = slot; } + /** Returns false when this constraint won't be updated by + * {@link Skeleton#updateWorldTransform(com.esotericsoftware.spine.Skeleton.Physics)} because a skin is required and the + * {@link Skeleton#getSkin() active skin} does not contain this item. + * @see Skin#getBones() + * @see Skin#getConstraints() + * @see ConstraintData#getSkinRequired() + * @see Skeleton#updateCache() */ public boolean isActive () { return active; } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PhysicsConstraint.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PhysicsConstraint.java index 1f8feb3e1..6eef08afd 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PhysicsConstraint.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PhysicsConstraint.java @@ -127,7 +127,7 @@ public class PhysicsConstraint implements Updatable { boolean x = data.x > 0, y = data.y > 0, rotateOrShearX = data.rotate > 0 || data.shearX > 0, scaleX = data.scaleX > 0; BoneApplied bone = this.bone; - float l = bone.data.length; + float l = bone.pose.data.length; switch (physics) { case none: @@ -282,7 +282,7 @@ public class PhysicsConstraint implements Updatable { tx = l * bone.a; ty = l * bone.c; } - bone.updateAppliedTransform(); + bone.updateLocalTransform(); } /** The bone constrained by this physics constraint. */ @@ -351,6 +351,13 @@ public class PhysicsConstraint implements Updatable { this.mix = mix; } + /** Returns false when this constraint won't be updated by + * {@link Skeleton#updateWorldTransform(com.esotericsoftware.spine.Skeleton.Physics)} because a skin is required and the + * {@link Skeleton#getSkin() active skin} does not contain this item. + * @see Skin#getBones() + * @see Skin#getConstraints() + * @see ConstraintData#getSkinRequired() + * @see Skeleton#updateCache() */ public boolean isActive () { return active; } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java index e07d73978..462f78649 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java @@ -273,7 +273,7 @@ public class Skeleton { } private void sortIkConstraint (IkConstraint constraint) { - constraint.active = constraint.target.active + constraint.active = constraint.target.pose.active && (!constraint.data.skinRequired || (skin != null && skin.constraints.contains(constraint.data, true))); if (!constraint.active) return; @@ -297,7 +297,7 @@ public class Skeleton { } private void sortTransformConstraint (TransformConstraint constraint) { - constraint.active = constraint.source.active + constraint.active = constraint.source.pose.active && (!constraint.data.skinRequired || (skin != null && skin.constraints.contains(constraint.data, true))); if (!constraint.active) return; @@ -319,7 +319,7 @@ public class Skeleton { updateCache.add(constraint); for (int i = 0; i < boneCount; i++) - sortReset(((BoneApplied)constrained[i]).children); + sortReset(((BoneApplied)constrained[i]).pose.children); for (int i = 0; i < boneCount; i++) ((BoneApplied)constrained[i]).pose.sorted = true; } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Slider.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Slider.java index d3ce3997a..be1eb3a58 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Slider.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Slider.java @@ -70,8 +70,15 @@ public class Slider implements Updatable { mix = data.mix; } + /** Returns false when this constraint won't be updated by + * {@link Skeleton#updateWorldTransform(com.esotericsoftware.spine.Skeleton.Physics)} because a skin is required and the + * {@link Skeleton#getSkin() active skin} does not contain this item. + * @see Skin#getBones() + * @see Skin#getConstraints() + * @see ConstraintData#getSkinRequired() + * @see Skeleton#updateCache() */ public boolean isActive () { - return true; + return active; } public Animation getAnimation () { diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraint.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraint.java index 82e5534b4..daf1e921f 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraint.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraint.java @@ -122,7 +122,7 @@ public class TransformConstraint implements Updatable { if (localTarget) bone.update(null); else - bone.updateAppliedTransform(); + bone.updateLocalTransform(); } } @@ -195,6 +195,13 @@ public class TransformConstraint implements Updatable { this.mixShearY = mixShearY; } + /** Returns false when this constraint won't be updated by + * {@link Skeleton#updateWorldTransform(com.esotericsoftware.spine.Skeleton.Physics)} because a skin is required and the + * {@link Skeleton#getSkin() active skin} does not contain this item. + * @see Skin#getBones() + * @see Skin#getConstraints() + * @see ConstraintData#getSkinRequired() + * @see Skeleton#updateCache() */ public boolean isActive () { return active; } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Updatable.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Updatable.java index 2efaa2c03..a1e9a584a 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Updatable.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Updatable.java @@ -35,13 +35,4 @@ import com.esotericsoftware.spine.Skeleton.Physics; public interface Updatable { /** @param physics Determines how physics and other non-deterministic updates are applied. */ public void update (Physics physics); - - /** Returns false when this item won't be updated by - * {@link Skeleton#updateWorldTransform(com.esotericsoftware.spine.Skeleton.Physics)} because a skin is required and the - * {@link Skeleton#getSkin() active skin} does not contain this item. - * @see Skin#getBones() - * @see Skin#getConstraints() - * @see BoneData#getSkinRequired() - * @see ConstraintData#getSkinRequired() */ - public boolean isActive (); } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/RegionAttachment.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/RegionAttachment.java index 7ee8b85ad..bb0636fc6 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/RegionAttachment.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/RegionAttachment.java @@ -36,7 +36,7 @@ import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion; import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.utils.Null; -import com.esotericsoftware.spine.Bone; +import com.esotericsoftware.spine.BonePose; import com.esotericsoftware.spine.BoneApplied; import com.esotericsoftware.spine.Slot; diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/VertexAttachment.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/VertexAttachment.java index 5d39629a1..1560b9b9e 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/VertexAttachment.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/VertexAttachment.java @@ -34,8 +34,9 @@ import static com.esotericsoftware.spine.utils.SpineUtils.*; import com.badlogic.gdx.utils.FloatArray; import com.badlogic.gdx.utils.Null; -import com.esotericsoftware.spine.Bone; +import com.esotericsoftware.spine.BonePose; import com.esotericsoftware.spine.BoneApplied; +import com.esotericsoftware.spine.Bone; import com.esotericsoftware.spine.Skeleton; import com.esotericsoftware.spine.Slot;