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 15418d4ce..ba17bb8b3 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java @@ -563,7 +563,7 @@ public class Animation { Bone bone = skeleton.bones.get(boneIndex); if (bone.active) { BonePose pose = appliedPose ? bone.applied : bone.pose; - pose.rotation = getRelativeValue(time, alpha, blend, pose.rotation, bone.data.rotation); + pose.rotation = getRelativeValue(time, alpha, blend, pose.rotation, bone.data.setup.rotation); } } } @@ -588,18 +588,18 @@ public class Animation { Bone bone = skeleton.bones.get(boneIndex); if (!bone.active) return; - BonePose pose = appliedPose ? bone.applied : bone.pose; + BonePose pose = appliedPose ? bone.applied : bone.pose, setup = bone.data.setup; float[] frames = this.frames; if (time < frames[0]) { switch (blend) { case setup: - pose.x = bone.data.x; - pose.y = bone.data.y; + pose.x = setup.x; + pose.y = setup.y; return; case first: - pose.x += (bone.data.x - pose.x) * alpha; - pose.y += (bone.data.y - pose.y) * alpha; + pose.x += (setup.x - pose.x) * alpha; + pose.y += (setup.y - pose.y) * alpha; } return; } @@ -626,13 +626,13 @@ public class Animation { switch (blend) { case setup: - pose.x = bone.data.x + x * alpha; - pose.y = bone.data.y + y * alpha; + pose.x = setup.x + x * alpha; + pose.y = setup.y + y * alpha; break; case first: case replace: - pose.x += (bone.data.x + x - pose.x) * alpha; - pose.y += (bone.data.y + y - pose.y) * alpha; + pose.x += (setup.x + x - pose.x) * alpha; + pose.y += (setup.y + y - pose.y) * alpha; break; case add: pose.x += x * alpha; @@ -660,7 +660,7 @@ public class Animation { Bone bone = skeleton.bones.get(boneIndex); if (bone.active) { BonePose pose = appliedPose ? bone.applied : bone.pose; - pose.x = getRelativeValue(time, alpha, blend, pose.x, bone.data.x); + pose.x = getRelativeValue(time, alpha, blend, pose.x, bone.data.setup.x); } } } @@ -684,7 +684,7 @@ public class Animation { Bone bone = skeleton.bones.get(boneIndex); if (bone.active) { BonePose pose = appliedPose ? bone.applied : bone.pose; - pose.y = getRelativeValue(time, alpha, blend, pose.y, bone.data.y); + pose.y = getRelativeValue(time, alpha, blend, pose.y, bone.data.setup.y); } } } @@ -709,18 +709,18 @@ public class Animation { Bone bone = skeleton.bones.get(boneIndex); if (!bone.active) return; - BonePose pose = appliedPose ? bone.applied : bone.pose; + BonePose pose = appliedPose ? bone.applied : bone.pose, setup = bone.data.setup; float[] frames = this.frames; if (time < frames[0]) { switch (blend) { case setup: - pose.scaleX = bone.data.scaleX; - pose.scaleY = bone.data.scaleY; + pose.scaleX = setup.scaleX; + pose.scaleY = setup.scaleY; return; case first: - pose.scaleX += (bone.data.scaleX - pose.scaleX) * alpha; - pose.scaleY += (bone.data.scaleY - pose.scaleY) * alpha; + pose.scaleX += (setup.scaleX - pose.scaleX) * alpha; + pose.scaleY += (setup.scaleY - pose.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 *= setup.scaleX; + y *= setup.scaleY; if (alpha == 1) { if (blend == add) { - pose.scaleX += x - bone.data.scaleX; - pose.scaleY += y - bone.data.scaleY; + pose.scaleX += x - setup.scaleX; + pose.scaleY += y - setup.scaleY; } else { pose.scaleX = x; pose.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 = setup.scaleX; + by = setup.scaleY; pose.scaleX = bx + (Math.abs(x) * Math.signum(bx) - bx) * alpha; pose.scaleY = by + (Math.abs(y) * Math.signum(by) - by) * alpha; break; @@ -774,14 +774,14 @@ public class Animation { pose.scaleY = by + (Math.abs(y) * Math.signum(by) - by) * alpha; break; case add: - pose.scaleX += (x - bone.data.scaleX) * alpha; - pose.scaleY += (y - bone.data.scaleY) * alpha; + pose.scaleX += (x - setup.scaleX) * alpha; + pose.scaleY += (y - setup.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(setup.scaleX) * Math.signum(x); + by = Math.abs(setup.scaleY) * Math.signum(y); pose.scaleX = bx + (x - bx) * alpha; pose.scaleY = by + (y - by) * alpha; break; @@ -793,8 +793,8 @@ public class Animation { pose.scaleY = by + (y - by) * alpha; break; case add: - pose.scaleX += (x - bone.data.scaleX) * alpha; - pose.scaleY += (y - bone.data.scaleY) * alpha; + pose.scaleX += (x - setup.scaleX) * alpha; + pose.scaleY += (y - setup.scaleY) * alpha; } } } @@ -820,7 +820,7 @@ public class Animation { Bone bone = skeleton.bones.get(boneIndex); if (bone.active) { BonePose pose = appliedPose ? bone.applied : bone.pose; - pose.scaleX = getScaleValue(time, alpha, blend, direction, pose.scaleX, bone.data.scaleX); + pose.scaleX = getScaleValue(time, alpha, blend, direction, pose.scaleX, bone.data.setup.scaleX); } } } @@ -844,7 +844,7 @@ public class Animation { Bone bone = skeleton.bones.get(boneIndex); if (bone.active) { BonePose pose = appliedPose ? bone.applied : bone.pose; - pose.scaleY = getScaleValue(time, alpha, blend, direction, pose.scaleY, bone.data.scaleY); + pose.scaleY = getScaleValue(time, alpha, blend, direction, pose.scaleY, bone.data.setup.scaleY); } } } @@ -869,18 +869,18 @@ public class Animation { Bone bone = skeleton.bones.get(boneIndex); if (!bone.active) return; - BonePose pose = appliedPose ? bone.applied : bone.pose; + BonePose pose = appliedPose ? bone.applied : bone.pose, setup = bone.data.setup; float[] frames = this.frames; if (time < frames[0]) { switch (blend) { case setup: - pose.shearX = bone.data.shearX; - pose.shearY = bone.data.shearY; + pose.shearX = setup.shearX; + pose.shearY = setup.shearY; return; case first: - pose.shearX += (bone.data.shearX - pose.shearX) * alpha; - pose.shearY += (bone.data.shearY - pose.shearY) * alpha; + pose.shearX += (setup.shearX - pose.shearX) * alpha; + pose.shearY += (setup.shearY - pose.shearY) * alpha; } return; } @@ -907,13 +907,13 @@ public class Animation { switch (blend) { case setup: - pose.shearX = bone.data.shearX + x * alpha; - pose.shearY = bone.data.shearY + y * alpha; + pose.shearX = setup.shearX + x * alpha; + pose.shearY = setup.shearY + y * alpha; break; case first: case replace: - pose.shearX += (bone.data.shearX + x - pose.shearX) * alpha; - pose.shearY += (bone.data.shearY + y - pose.shearY) * alpha; + pose.shearX += (setup.shearX + x - pose.shearX) * alpha; + pose.shearY += (setup.shearY + y - pose.shearY) * alpha; break; case add: pose.shearX += x * alpha; @@ -941,7 +941,7 @@ public class Animation { Bone bone = skeleton.bones.get(boneIndex); if (bone.active) { BonePose pose = appliedPose ? bone.applied : bone.pose; - pose.shearX = getRelativeValue(time, alpha, blend, pose.shearX, bone.data.shearX); + pose.shearX = getRelativeValue(time, alpha, blend, pose.shearX, bone.data.setup.shearX); } } } @@ -965,7 +965,7 @@ public class Animation { Bone bone = skeleton.bones.get(boneIndex); if (bone.active) { BonePose pose = appliedPose ? bone.applied : bone.pose; - pose.shearY = getRelativeValue(time, alpha, blend, pose.shearY, bone.data.shearY); + pose.shearY = getRelativeValue(time, alpha, blend, pose.shearY, bone.data.setup.shearY); } } } @@ -1007,13 +1007,13 @@ public class Animation { BonePose pose = appliedPose ? bone.applied : bone.pose; if (direction == out) { - if (blend == setup) pose.inherit = bone.data.inherit; + if (blend == setup) pose.inherit = bone.data.setup.inherit; return; } float[] frames = this.frames; if (time < frames[0]) { - if (blend == setup || blend == first) pose.inherit = bone.data.inherit; + if (blend == setup || blend == first) pose.inherit = bone.data.setup.inherit; return; } pose.inherit = Inherit.values[(int)frames[search(frames, time, ENTRIES) + INHERIT]]; @@ -1064,7 +1064,7 @@ public class Animation { float[] frames = this.frames; Color color = pose.color; if (time < frames[0]) { - Color setup = slot.data.color; + Color setup = slot.data.setup.color; switch (blend) { case setup: color.set(setup); @@ -1107,7 +1107,7 @@ public class Animation { if (alpha == 1) color.set(r, g, b, a); else { - if (blend == setup) color.set(slot.data.color); + if (blend == setup) color.set(slot.data.setup.color); color.add((r - color.r) * alpha, (g - color.g) * alpha, (b - color.b) * alpha, (a - color.a) * alpha); } } @@ -1154,7 +1154,7 @@ public class Animation { float[] frames = this.frames; Color color = pose.color; if (time < frames[0]) { - Color setup = slot.data.color; + Color setup = slot.data.setup.color; switch (blend) { case setup: color.r = setup.r; @@ -1199,7 +1199,7 @@ public class Animation { color.b = b; } else { if (blend == setup) { - Color setup = slot.data.color; + Color setup = slot.data.setup.color; color.r = setup.r; color.g = setup.g; color.b = setup.b; @@ -1234,7 +1234,7 @@ public class Animation { float[] frames = this.frames; Color color = pose.color; if (time < frames[0]) { - Color setup = slot.data.color; + Color setup = slot.data.setup.color; switch (blend) { case setup: color.a = setup.a; @@ -1249,7 +1249,7 @@ public class Animation { if (alpha == 1) color.a = a; else { - if (blend == setup) color.a = slot.data.color.a; + if (blend == setup) color.a = slot.data.setup.color.a; color.a += (a - color.a) * alpha; } } @@ -1305,7 +1305,8 @@ public class Animation { float[] frames = this.frames; Color light = pose.color, dark = pose.darkColor; if (time < frames[0]) { - Color setupLight = slot.data.color, setupDark = slot.data.darkColor; + SlotPose setup = slot.data.setup; + Color setupLight = setup.color, setupDark = setup.darkColor; switch (blend) { case setup: light.set(setupLight); @@ -1370,8 +1371,9 @@ public class Animation { dark.b = b2; } else { if (blend == setup) { - light.set(slot.data.color); - Color setupDark = slot.data.darkColor; + SlotPose setup = slot.data.setup; + light.set(setup.color); + Color setupDark = setup.darkColor; dark.r = setupDark.r; dark.g = setupDark.g; dark.b = setupDark.b; @@ -1432,7 +1434,8 @@ public class Animation { float[] frames = this.frames; Color light = pose.color, dark = pose.darkColor; if (time < frames[0]) { - Color setupLight = slot.data.color, setupDark = slot.data.darkColor; + SlotPose setup = slot.data.setup; + Color setupLight = setup.color, setupDark = setup.darkColor; switch (blend) { case setup: light.r = setupLight.r; @@ -1498,7 +1501,8 @@ public class Animation { dark.b = b2; } else { if (blend == setup) { - Color setupLight = slot.data.color, setupDark = slot.data.darkColor; + SlotPose setup = slot.data.setup; + Color setupLight = setup.color, setupDark = setup.darkColor; light.r = setupLight.r; light.g = setupLight.g; light.b = setupLight.b; @@ -1958,8 +1962,9 @@ public class Animation { } } - /** Changes an IK constraint's {@link IkConstraint#getMix()}, {@link IkConstraint#getSoftness()}, - * {@link IkConstraint#getBendDirection()}, {@link IkConstraint#getStretch()}, and {@link IkConstraint#getCompress()}. */ + /** Changes an IK constraint's {@link IkConstraintPose#getMix()}, {@link IkConstraintPose#getSoftness()}, + * {@link IkConstraintPose#getBendDirection()}, {@link IkConstraintPose#getStretch()}, and + * {@link IkConstraintPose#getCompress()}. */ static public class IkConstraintTimeline extends CurveTimeline { static public final int ENTRIES = 6; static private final int MIX = 1, SOFTNESS = 2, BEND_DIRECTION = 3, COMPRESS = 4, STRETCH = 5; @@ -2001,24 +2006,24 @@ public class Animation { IkConstraint constraint = skeleton.ikConstraints.get(constraintIndex); if (!constraint.active) return; - if (appliedPose) constraint = constraint.applied; + IkConstraintPose pose = appliedPose ? constraint.applied : constraint.pose, setup = constraint.data.setup; float[] frames = this.frames; if (time < frames[0]) { switch (blend) { case setup: - constraint.mix = constraint.data.mix; - constraint.softness = constraint.data.softness; - constraint.bendDirection = constraint.data.bendDirection; - constraint.compress = constraint.data.compress; - constraint.stretch = constraint.data.stretch; + pose.mix = setup.mix; + pose.softness = setup.softness; + pose.bendDirection = setup.bendDirection; + pose.compress = setup.compress; + pose.stretch = setup.stretch; return; case first: - constraint.mix += (constraint.data.mix - constraint.mix) * alpha; - constraint.softness += (constraint.data.softness - constraint.softness) * alpha; - constraint.bendDirection = constraint.data.bendDirection; - constraint.compress = constraint.data.compress; - constraint.stretch = constraint.data.stretch; + pose.mix += (setup.mix - pose.mix) * alpha; + pose.softness += (setup.softness - pose.softness) * alpha; + pose.bendDirection = setup.bendDirection; + pose.compress = setup.compress; + pose.stretch = setup.stretch; } return; } @@ -2043,25 +2048,25 @@ public class Animation { softness = getBezierValue(time, i, SOFTNESS, curveType + BEZIER_SIZE - BEZIER); } - if (blend == setup) { - constraint.mix = constraint.data.mix + (mix - constraint.data.mix) * alpha; - constraint.softness = constraint.data.softness + (softness - constraint.data.softness) * alpha; + if (blend == MixBlend.setup) { + pose.mix = setup.mix + (mix - setup.mix) * alpha; + pose.softness = setup.softness + (softness - setup.softness) * alpha; if (direction == out) { - constraint.bendDirection = constraint.data.bendDirection; - constraint.compress = constraint.data.compress; - constraint.stretch = constraint.data.stretch; + pose.bendDirection = setup.bendDirection; + pose.compress = setup.compress; + pose.stretch = setup.stretch; } else { - constraint.bendDirection = (int)frames[i + BEND_DIRECTION]; - constraint.compress = frames[i + COMPRESS] != 0; - constraint.stretch = frames[i + STRETCH] != 0; + pose.bendDirection = (int)frames[i + BEND_DIRECTION]; + pose.compress = frames[i + COMPRESS] != 0; + pose.stretch = frames[i + STRETCH] != 0; } } else { - constraint.mix += (mix - constraint.mix) * alpha; - constraint.softness += (softness - constraint.softness) * alpha; + pose.mix += (mix - pose.mix) * alpha; + pose.softness += (softness - pose.softness) * alpha; if (direction == in) { - constraint.bendDirection = (int)frames[i + BEND_DIRECTION]; - constraint.compress = frames[i + COMPRESS] != 0; - constraint.stretch = frames[i + STRETCH] != 0; + pose.bendDirection = (int)frames[i + BEND_DIRECTION]; + pose.compress = frames[i + COMPRESS] != 0; + pose.stretch = frames[i + STRETCH] != 0; } } } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java index 35ded5575..0097bdc77 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java @@ -417,23 +417,23 @@ public class AnimationState { Bone bone = skeleton.bones.get(timeline.boneIndex); if (!bone.active) return; - BonePose pose = bone.getPose(); + BonePose pose = bone.getPose(), setup = bone.data.setup; float[] frames = timeline.frames; float r1, r2; if (time < frames[0]) { // Time is before first frame. switch (blend) { case setup: - pose.rotation = bone.data.rotation; + pose.rotation = setup.rotation; // Fall through. default: return; case first: r1 = pose.rotation; - r2 = bone.data.rotation; + r2 = setup.rotation; } } else { - r1 = blend == MixBlend.setup ? bone.data.rotation : pose.rotation; - r2 = bone.data.rotation + timeline.getCurveValue(time); + r1 = blend == MixBlend.setup ? setup.rotation : pose.rotation; + r2 = setup.rotation + timeline.getCurveValue(time); } // Mix between rotations using the direction of the shortest route on the first frame. 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 a85604bba..9fcc49a6c 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java @@ -66,7 +66,7 @@ public class Bone { /** Sets this bone's local transform to the setup pose. */ public void setupPose () { - pose.set(data); + pose.set(data.setup); } /** The bone's setup pose data. */ 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 166be533b..8432bc5cb 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneData.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneData.java @@ -33,10 +33,11 @@ import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.utils.Null; /** The setup pose for a bone. */ -public class BoneData extends BonePose { +public class BoneData { final int index; final String name; @Null final BoneData parent; + final BonePose setup = new BonePose(); float length; boolean skinRequired; @@ -54,12 +55,12 @@ public class BoneData extends BonePose { } /** Copy constructor. */ - public BoneData (BoneData bone, @Null BoneData parent) { - index = bone.index; - name = bone.name; + public BoneData (BoneData data, @Null BoneData parent) { + index = data.index; + name = data.name; this.parent = parent; - length = bone.length; - set(bone); + length = data.length; + setup.set(data.setup); } /** The index of the bone in {@link Skeleton#getBones()}. */ @@ -76,6 +77,10 @@ public class BoneData extends BonePose { return parent; } + public BonePose getSetupPose () { + return setup; + } + /** The bone's length. */ public float getLength () { return length; diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BonePose.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BonePose.java index 8d5b1e8fd..a74d76969 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BonePose.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BonePose.java @@ -39,16 +39,16 @@ public class BonePose { BonePose () { } - public void set (BonePose bone) { - if (bone == null) throw new IllegalArgumentException("bone cannot be null."); - x = bone.x; - y = bone.y; - rotation = bone.rotation; - scaleX = bone.scaleX; - scaleY = bone.scaleY; - shearX = bone.shearX; - shearY = bone.shearY; - inherit = bone.inherit; + public void set (BonePose pose) { + if (pose == null) throw new IllegalArgumentException("pose cannot be null."); + x = pose.x; + y = pose.y; + rotation = pose.rotation; + scaleX = pose.scaleX; + scaleY = pose.scaleY; + shearX = pose.shearX; + shearY = pose.shearY; + inherit = pose.inherit; } /** The local x translation. */ 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 a53c35c6e..323718227 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java @@ -43,19 +43,9 @@ public class IkConstraint implements Updatable { final IkConstraintData data; final Array bones; BoneApplied target; - IkConstraint applied; + final IkConstraintPose pose = new IkConstraintPose(), applied = new IkConstraintPose(); boolean active; - int bendDirection; - boolean compress, stretch; - float mix = 1, softness; - - private IkConstraint (IkConstraintData data, Array bones, BoneApplied target) { - this.data = data; - this.bones = bones; - this.target = target; - } - public IkConstraint (IkConstraintData data, Skeleton skeleton) { if (data == null) throw new IllegalArgumentException("data cannot be null."); if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null."); @@ -67,8 +57,6 @@ public class IkConstraint implements Updatable { target = skeleton.bones.get(data.target.index).applied; - applied = new IkConstraint(data, bones, target); - setupPose(); } @@ -79,24 +67,20 @@ public class IkConstraint implements Updatable { } public void setupPose () { - IkConstraintData data = this.data; - mix = data.mix; - softness = data.softness; - bendDirection = data.bendDirection; - compress = data.compress; - stretch = data.stretch; + pose.set(data.setup); } /** Applies the constraint to the constrained bones. */ public void update (Physics physics) { - if (mix == 0) return; + IkConstraintPose pose = applied; + if (pose.mix == 0) return; BoneApplied target = this.target; Object[] bones = this.bones.items; switch (this.bones.size) { - case 1 -> apply((BoneApplied)bones[0], target.worldX, target.worldY, compress, stretch, data.uniform, mix); + case 1 -> apply((BoneApplied)bones[0], target.worldX, target.worldY, pose.compress, pose.stretch, data.uniform, pose.mix); case 2 -> // - apply((BoneApplied)bones[0], (BoneApplied)bones[1], target.worldX, target.worldY, bendDirection, stretch, data.uniform, - softness, mix); + apply((BoneApplied)bones[0], (BoneApplied)bones[1], target.worldX, target.worldY, pose.bendDirection, pose.stretch, + data.uniform, pose.softness, pose.mix); } } @@ -115,55 +99,12 @@ public class IkConstraint implements Updatable { this.target = target; } - /** A percentage (0-1) that controls the mix between the constrained and unconstrained rotation. - *

- * For two bone IK: if the parent bone has local nonuniform scale, the child bone's local Y translation is set to 0. */ - public float getMix () { - return mix; + public IkConstraintPose getPose () { + return pose; } - public void setMix (float mix) { - this.mix = mix; - } - - /** For two bone IK, the target bone's distance from the maximum reach of the bones where rotation begins to slow. The bones - * will not straighten completely until the target is this far out of range. */ - public float getSoftness () { - return softness; - } - - public void setSoftness (float softness) { - this.softness = softness; - } - - /** For two bone IK, controls the bend direction of the IK bones, either 1 or -1. */ - public int getBendDirection () { - return bendDirection; - } - - public void setBendDirection (int bendDirection) { - this.bendDirection = bendDirection; - } - - /** For one bone IK, when true and the target is too close, the bone is scaled to reach it. */ - public boolean getCompress () { - return compress; - } - - public void setCompress (boolean compress) { - this.compress = compress; - } - - /** When true and the target is out of range, the parent bone is scaled to reach it. - *

- * For two bone IK: 1) the child bone's local Y translation is set to 0, 2) stretch is not applied if {@link #getSoftness()} is - * > 0, and 3) if the parent bone has local nonuniform scale, stretch is not applied. */ - public boolean getStretch () { - return stretch; - } - - public void setStretch (boolean stretch) { - this.stretch = stretch; + public IkConstraintPose getAppliedPose () { + return applied; } /** Returns false when this constraint won't be updated by diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraintData.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraintData.java index 572b7b410..f04a5f9a2 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraintData.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraintData.java @@ -37,9 +37,8 @@ import com.badlogic.gdx.utils.Array; public class IkConstraintData extends ConstraintData { final Array bones = new Array(); BoneData target; - int bendDirection; - boolean compress, stretch, uniform; - float mix, softness; + final IkConstraintPose setup = new IkConstraintPose(); + boolean uniform; public IkConstraintData (String name) { super(name); @@ -60,58 +59,12 @@ public class IkConstraintData extends ConstraintData { this.target = target; } - /** A percentage (0-1) that controls the mix between the constrained and unconstrained rotation. - *

- * For two bone IK: if the parent bone has local nonuniform scale, the child bone's local Y translation is set to 0. */ - public float getMix () { - return mix; + public IkConstraintPose getSetupPose () { + return setup; } - public void setMix (float mix) { - this.mix = mix; - } - - /** For two bone IK, the target bone's distance from the maximum reach of the bones where rotation begins to slow. The bones - * will not straighten completely until the target is this far out of range. */ - public float getSoftness () { - return softness; - } - - public void setSoftness (float softness) { - this.softness = softness; - } - - /** For two bone IK, controls the bend direction of the IK bones, either 1 or -1. */ - public int getBendDirection () { - return bendDirection; - } - - public void setBendDirection (int bendDirection) { - this.bendDirection = bendDirection; - } - - /** For one bone IK, when true and the target is too close, the bone is scaled to reach it. */ - public boolean getCompress () { - return compress; - } - - public void setCompress (boolean compress) { - this.compress = compress; - } - - /** When true and the target is out of range, the parent bone is scaled to reach it. - *

- * For two bone IK: 1) the child bone's local Y translation is set to 0, 2) stretch is not applied if {@link #getSoftness()} is - * > 0, and 3) if the parent bone has local nonuniform scale, stretch is not applied. */ - public boolean getStretch () { - return stretch; - } - - public void setStretch (boolean stretch) { - this.stretch = stretch; - } - - /** When true and {@link #getCompress()} or {@link #getStretch()} is used, the bone is scaled on both the X and Y axes. */ + /** When true and {@link IkConstraintPose#getCompress()} or {@link IkConstraintPose#getStretch()} is used, the bone is scaled + * on both the X and Y axes. */ public boolean getUniform () { return uniform; } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraintPose.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraintPose.java new file mode 100644 index 000000000..b1dddc3c4 --- /dev/null +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraintPose.java @@ -0,0 +1,96 @@ +/****************************************************************************** + * 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; + +/** Stores the current pose for an IK constraint. */ +public class IkConstraintPose { + int bendDirection; + boolean compress, stretch; + float mix, softness; + + public void set (IkConstraintPose pose) { + mix = pose.mix; + softness = pose.softness; + bendDirection = pose.bendDirection; + compress = pose.compress; + stretch = pose.stretch; + } + + /** A percentage (0-1) that controls the mix between the constrained and unconstrained rotation. + *

+ * For two bone IK: if the parent bone has local nonuniform scale, the child bone's local Y translation is set to 0. */ + public float getMix () { + return mix; + } + + public void setMix (float mix) { + this.mix = mix; + } + + /** For two bone IK, the target bone's distance from the maximum reach of the bones where rotation begins to slow. The bones + * will not straighten completely until the target is this far out of range. */ + public float getSoftness () { + return softness; + } + + public void setSoftness (float softness) { + this.softness = softness; + } + + /** For two bone IK, controls the bend direction of the IK bones, either 1 or -1. */ + public int getBendDirection () { + return bendDirection; + } + + public void setBendDirection (int bendDirection) { + this.bendDirection = bendDirection; + } + + /** For one bone IK, when true and the target is too close, the bone is scaled to reach it. */ + public boolean getCompress () { + return compress; + } + + public void setCompress (boolean compress) { + this.compress = compress; + } + + /** When true and the target is out of range, the parent bone is scaled to reach it. + *

+ * For two bone IK: 1) the child bone's local Y translation is set to 0, 2) stretch is not applied if {@link #getSoftness()} is + * > 0, and 3) if the parent bone has local nonuniform scale, stretch is not applied. */ + public boolean getStretch () { + return stretch; + } + + public void setStretch (boolean stretch) { + this.stretch = stretch; + } +} 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 13d7e39cb..434f0081c 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java @@ -53,6 +53,7 @@ import com.esotericsoftware.spine.utils.SkeletonClipping; * Runtimes Guide. */ public class Skeleton { static private final short[] quadTriangles = {0, 1, 2, 2, 3, 0}; + private static final Object IkConstraint = null; final SkeletonData data; final Array bones; final Array slots; @@ -412,16 +413,22 @@ public class Skeleton { * See World transforms in the Spine * Runtimes Guide. */ public void updateWorldTransform (Physics physics) { - Object[] bones = this.bones.items; + Object[] objects = this.bones.items; for (int i = 0, n = this.bones.size; i < n; i++) { - var bone = (Bone)bones[i]; + var bone = (Bone)objects[i]; if (bone.active) bone.applied.set(bone.pose); } - Object[] slots = this.slots.items; + objects = this.slots.items; for (int i = 0, n = this.slots.size; i < n; i++) { - var slot = (Slot)slots[i]; + var slot = (Slot)objects[i]; if (slot.bone.active) slot.applied.set(slot.pose); } + objects = ikConstraints.items; + for (int i = 0, n = ikConstraints.size; i < n; i++) { + var constraint = (IkConstraint)objects[i]; + if (constraint.active) constraint.applied.set(constraint.pose); + } + // BOZO! - Reset the rest. Object[] updateCache = this.updateCache.items; diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java index 55c66e84b..b25b6d930 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java @@ -217,15 +217,16 @@ public class SkeletonBinary extends SkeletonLoader { String name = input.readString(); BoneData parent = i == 0 ? null : (BoneData)bones[input.readInt(true)]; var data = new BoneData(i, name, parent); - data.rotation = input.readFloat(); - data.x = input.readFloat() * scale; - data.y = input.readFloat() * scale; - data.scaleX = input.readFloat(); - data.scaleY = input.readFloat(); - data.shearX = input.readFloat(); - data.shearY = input.readFloat(); + BonePose setup = data.setup; + setup.rotation = input.readFloat(); + setup.x = input.readFloat() * scale; + setup.y = input.readFloat() * scale; + setup.scaleX = input.readFloat(); + setup.scaleY = input.readFloat(); + setup.shearX = input.readFloat(); + setup.shearY = input.readFloat(); + setup.inherit = Inherit.values[input.readByte()]; data.length = input.readFloat() * scale; - data.inherit = Inherit.values[input.readByte()]; data.skinRequired = input.readBoolean(); if (nonessential) { Color.rgba8888ToColor(data.color, input.readInt()); @@ -241,10 +242,10 @@ public class SkeletonBinary extends SkeletonLoader { String slotName = input.readString(); var boneData = (BoneData)bones[input.readInt(true)]; var data = new SlotData(i, slotName, boneData); - Color.rgba8888ToColor(data.color, input.readInt()); + Color.rgba8888ToColor(data.setup.color, input.readInt()); int darkColor = input.readInt(); - if (darkColor != -1) Color.rgb888ToColor(data.darkColor = new Color(), darkColor); + if (darkColor != -1) Color.rgb888ToColor(data.setup.darkColor = new Color(), darkColor); data.attachmentName = input.readStringRef(); data.blendMode = BlendMode.values[input.readInt(true)]; @@ -263,12 +264,13 @@ public class SkeletonBinary extends SkeletonLoader { data.target = (BoneData)bones[input.readInt(true)]; int flags = input.read(); data.skinRequired = (flags & 1) != 0; - data.bendDirection = (flags & 2) != 0 ? 1 : -1; - data.compress = (flags & 4) != 0; - data.stretch = (flags & 8) != 0; - data.uniform = (flags & 16) != 0; - if ((flags & 32) != 0) data.mix = (flags & 64) != 0 ? input.readFloat() : 1; - if ((flags & 128) != 0) data.softness = input.readFloat() * scale; + data.uniform = (flags & 2) != 0; + IkConstraintPose setup = data.setup; + setup.bendDirection = (flags & 4) != 0 ? 1 : -1; + setup.compress = (flags & 8) != 0; + setup.stretch = (flags & 16) != 0; + if ((flags & 32) != 0) setup.mix = (flags & 64) != 0 ? input.readFloat() : 1; + if ((flags & 128) != 0) setup.softness = input.readFloat() * scale; o[i] = data; } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java index 01f26c89b..17fad77cb 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java @@ -174,14 +174,15 @@ public class SkeletonJson extends SkeletonLoader { } var data = new BoneData(skeletonData.bones.size, boneMap.getString("name"), parent); data.length = boneMap.getFloat("length", 0) * scale; - data.x = boneMap.getFloat("x", 0) * scale; - data.y = boneMap.getFloat("y", 0) * scale; - data.rotation = boneMap.getFloat("rotation", 0); - data.scaleX = boneMap.getFloat("scaleX", 1); - data.scaleY = boneMap.getFloat("scaleY", 1); - data.shearX = boneMap.getFloat("shearX", 0); - data.shearY = boneMap.getFloat("shearY", 0); - data.inherit = Inherit.valueOf(boneMap.getString("inherit", Inherit.normal.name())); + BonePose setup = data.setup; + setup.x = boneMap.getFloat("x", 0) * scale; + setup.y = boneMap.getFloat("y", 0) * scale; + setup.rotation = boneMap.getFloat("rotation", 0); + setup.scaleX = boneMap.getFloat("scaleX", 1); + setup.scaleY = boneMap.getFloat("scaleY", 1); + setup.shearX = boneMap.getFloat("shearX", 0); + setup.shearY = boneMap.getFloat("shearY", 0); + setup.inherit = Inherit.valueOf(boneMap.getString("inherit", Inherit.normal.name())); data.skinRequired = boneMap.getBoolean("skin", false); String color = boneMap.getString("color", null); @@ -203,10 +204,10 @@ public class SkeletonJson extends SkeletonLoader { var data = new SlotData(skeletonData.slots.size, slotName, boneData); String color = slotMap.getString("color", null); - if (color != null) Color.valueOf(color, data.getColor()); + if (color != null) Color.valueOf(color, data.getSetupPose().getColor()); String dark = slotMap.getString("dark", null); - if (dark != null) data.setDarkColor(Color.valueOf(dark)); + if (dark != null) Color.valueOf(dark, data.getSetupPose().getDarkColor()); data.attachmentName = slotMap.getString("attachment", null); data.blendMode = BlendMode.valueOf(slotMap.getString("blend", BlendMode.normal.name())); @@ -230,12 +231,13 @@ public class SkeletonJson extends SkeletonLoader { data.target = skeletonData.findBone(targetName); if (data.target == null) throw new SerializationException("IK target bone not found: " + targetName); - data.mix = constraintMap.getFloat("mix", 1); - data.softness = constraintMap.getFloat("softness", 0) * scale; - data.bendDirection = constraintMap.getBoolean("bendPositive", true) ? 1 : -1; - data.compress = constraintMap.getBoolean("compress", false); - data.stretch = constraintMap.getBoolean("stretch", false); data.uniform = constraintMap.getBoolean("uniform", false); + IkConstraintPose setup = data.setup; + setup.mix = constraintMap.getFloat("mix", 1); + setup.softness = constraintMap.getFloat("softness", 0) * scale; + setup.bendDirection = constraintMap.getBoolean("bendPositive", true) ? 1 : -1; + setup.compress = constraintMap.getBoolean("compress", false); + setup.stretch = constraintMap.getBoolean("stretch", false); skeletonData.ikConstraints.add(data); } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Slot.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Slot.java index 46dbd4931..14893dc37 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Slot.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Slot.java @@ -45,7 +45,7 @@ public class Slot { if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null."); this.data = data; bone = skeleton.bones.get(data.boneData.index); - if (data.darkColor != null) { + if (data.setup.darkColor != null) { pose.darkColor = new Color(); applied.darkColor = new Color(); } @@ -58,7 +58,7 @@ public class Slot { if (bone == null) throw new IllegalArgumentException("bone cannot be null."); data = slot.data; this.bone = bone; - if (data.darkColor != null) { + if (data.setup.darkColor != null) { pose.darkColor = new Color(); applied.darkColor = new Color(); } @@ -67,7 +67,7 @@ public class Slot { /** Sets this slot to the setup pose. */ public void setupPose () { - pose.set(data); + pose.set(data.setup); if (data.attachmentName != null) pose.setAttachment(bone.skeleton.getAttachment(data.index, data.attachmentName)); } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SlotData.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SlotData.java index c7707e19a..4cb2bfb5b 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SlotData.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SlotData.java @@ -29,14 +29,14 @@ package com.esotericsoftware.spine; -import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.utils.Null; /** Stores the setup pose for a {@link Slot}. */ -public class SlotData extends SlotPose { +public class SlotData { final int index; final String name; final BoneData boneData; + final SlotPose setup = new SlotPose(); @Null String attachmentName; BlendMode blendMode; @@ -67,20 +67,8 @@ public class SlotData extends SlotPose { return boneData; } - /** The color used to tint the slot's attachment. If {@link #getDarkColor()} is set, this is used as the light color for two - * color tinting. */ - public Color getColor () { - return color; - } - - /** The dark color used to tint the slot's attachment for two color tinting, or null if two color tinting is not used. The dark - * color's alpha is not used. */ - public @Null Color getDarkColor () { - return darkColor; - } - - public void setDarkColor (@Null Color darkColor) { - this.darkColor = darkColor; + public SlotPose getSetupPose () { + return setup; } public void setAttachmentName (@Null String attachmentName) { diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SlotPose.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SlotPose.java index ab4264c82..1044bec40 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SlotPose.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SlotPose.java @@ -44,21 +44,21 @@ import com.esotericsoftware.spine.attachments.VertexAttachment; public class SlotPose { final Color color = new Color(); @Null Color darkColor; - @Null Attachment attachment; + @Null Attachment attachment; // Not used in setup pose. int sequenceIndex; final FloatArray deform = new FloatArray(0); SlotPose () { } - public void set (SlotPose slot) { - if (slot == null) throw new IllegalArgumentException("slot cannot be null."); - color.set(slot.color); - if (slot.darkColor != null) darkColor.set(slot.darkColor); - attachment = slot.attachment; - sequenceIndex = slot.sequenceIndex; + public void set (SlotPose pose) { + if (pose == null) throw new IllegalArgumentException("pose cannot be null."); + color.set(pose.color); + if (darkColor != null) darkColor.set(pose.darkColor); + attachment = pose.attachment; + sequenceIndex = pose.sequenceIndex; deform.size = 0; - deform.addAll(slot.deform); + deform.addAll(pose.deform); } /** The color used to tint the slot's attachment. If {@link #getDarkColor()} is set, this is used as the light color for two