From fff1606b6dee88593fa24da3fbf58d058edb32cc Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Wed, 16 Apr 2025 23:09:38 -0400 Subject: [PATCH] [libgdx] Added Posed and other base classes. Renamed BoneLocal/Pose. Removed skeleton fields, prefer passing. --- .../esotericsoftware/spine/BonePlotting.java | 2 +- .../esotericsoftware/spine/Box2DExample.java | 2 +- .../spine/SkeletonAttachmentTest.java | 2 +- .../com/esotericsoftware/spine/Animation.java | 48 +- .../spine/AnimationState.java | 2 +- .../src/com/esotericsoftware/spine/Bone.java | 73 +-- .../esotericsoftware/spine/BoneApplied.java | 376 ---------------- .../com/esotericsoftware/spine/BoneData.java | 36 +- .../com/esotericsoftware/spine/BoneLocal.java | 141 ++++++ .../com/esotericsoftware/spine/BonePose.java | 414 ++++++++++++++---- .../esotericsoftware/spine/Constrained.java | 36 -- .../esotericsoftware/spine/Constraint.java | 10 + .../esotericsoftware/spine/IkConstraint.java | 101 ++--- .../spine/IkConstraintData.java | 9 +- .../spine/IkConstraintPose.java | 2 +- .../spine/PathConstraint.java | 102 +---- .../spine/PathConstraintData.java | 9 +- .../spine/PathConstraintPose.java | 2 +- .../spine/PhysicsConstraint.java | 85 +--- .../spine/PhysicsConstraintData.java | 9 +- .../spine/PhysicsConstraintPose.java | 2 +- .../src/com/esotericsoftware/spine/Pose.java | 6 + .../src/com/esotericsoftware/spine/Posed.java | 51 +++ .../esotericsoftware/spine/PosedActive.java | 21 + .../{ConstraintData.java => PosedData.java} | 29 +- .../com/esotericsoftware/spine/Skeleton.java | 48 +- .../spine/SkeletonBinary.java | 10 +- .../spine/SkeletonBounds.java | 2 +- .../esotericsoftware/spine/SkeletonJson.java | 2 +- .../spine/SkeletonRenderer.java | 8 +- .../spine/SkeletonRendererDebug.java | 8 +- .../src/com/esotericsoftware/spine/Skin.java | 8 +- .../com/esotericsoftware/spine/Slider.java | 62 +-- .../esotericsoftware/spine/SliderData.java | 9 +- .../esotericsoftware/spine/SliderPose.java | 2 +- .../src/com/esotericsoftware/spine/Slot.java | 52 +-- .../com/esotericsoftware/spine/SlotData.java | 20 +- .../com/esotericsoftware/spine/SlotPose.java | 5 +- .../spine/TransformConstraint.java | 78 +--- .../spine/TransformConstraintData.java | 37 +- .../spine/TransformConstraintPose.java | 2 +- .../com/esotericsoftware/spine/Update.java | 2 +- .../spine/attachments/MeshAttachment.java | 6 +- .../spine/attachments/PointAttachment.java | 6 +- .../spine/attachments/RegionAttachment.java | 4 +- .../spine/attachments/VertexAttachment.java | 16 +- .../spine/utils/SkeletonClipping.java | 5 +- 47 files changed, 793 insertions(+), 1169 deletions(-) delete mode 100644 spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneApplied.java create mode 100644 spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneLocal.java delete mode 100644 spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Constrained.java create mode 100644 spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Constraint.java create mode 100644 spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Pose.java create mode 100644 spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Posed.java create mode 100644 spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PosedActive.java rename spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/{ConstraintData.java => PosedData.java} (89%) diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/BonePlotting.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/BonePlotting.java index d4ab28113..eb5e0446e 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/BonePlotting.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/BonePlotting.java @@ -75,7 +75,7 @@ public class BonePlotting { SkeletonData skeletonData = json.readSkeletonData(new FileHandle("assets/spineboy/spineboy-ess.json")); Skeleton skeleton = new Skeleton(skeletonData); - BoneApplied bone = skeleton.findBone("gun-tip").getAppliedPose(); + BonePose bone = skeleton.findBone("gun-tip").getAppliedPose(); // Pose the skeleton at regular intervals throughout each animation. float fps = 1 / 15f; diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/Box2DExample.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/Box2DExample.java index 07f603ad0..e7cd72e2b 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/Box2DExample.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/Box2DExample.java @@ -161,7 +161,7 @@ public class Box2DExample extends ApplicationAdapter { if (!(slot.getAppliedPose().getAttachment() instanceof Box2dAttachment)) continue; Box2dAttachment attachment = (Box2dAttachment)slot.getAppliedPose().getAttachment(); if (attachment.body == null) continue; - BoneApplied bone = slot.getBone().getAppliedPose(); + BonePose bone = slot.getBone().getAppliedPose(); float x = bone.getWorldX(); float y = bone.getWorldY(); float rotation = bone.getWorldRotationX(); diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SkeletonAttachmentTest.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SkeletonAttachmentTest.java index 07756192d..b37fffbc5 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SkeletonAttachmentTest.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SkeletonAttachmentTest.java @@ -47,7 +47,7 @@ public class SkeletonAttachmentTest extends ApplicationAdapter { Skeleton spineboy, goblin; AnimationState spineboyState, goblinState; - BoneApplied attachmentBone; + BonePose attachmentBone; public void create () { camera = new OrthographicCamera(); 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 296e1b30a..efd4cb7d1 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 BonePose#getRotation()}. */ + /** Changes a bone's local {@link BoneLocal#getRotation()}. */ static public class RotateTimeline extends CurveTimeline1 implements BoneTimeline { final int boneIndex; @@ -562,13 +562,13 @@ public class Animation { Bone bone = skeleton.bones.get(boneIndex); if (bone.active) { - BonePose pose = appliedPose ? bone.applied : bone.pose; + BoneLocal pose = appliedPose ? bone.applied : bone.pose; pose.rotation = getRelativeValue(time, alpha, blend, pose.rotation, bone.data.setup.rotation); } } } - /** Changes a bone's local {@link BonePose#getX()} and {@link BonePose#getY()}. */ + /** Changes a bone's local {@link BoneLocal#getX()} and {@link BoneLocal#getY()}. */ static public class TranslateTimeline extends CurveTimeline2 implements BoneTimeline { final int boneIndex; @@ -588,7 +588,7 @@ public class Animation { Bone bone = skeleton.bones.get(boneIndex); if (!bone.active) return; - BonePose pose = appliedPose ? bone.applied : bone.pose, setup = bone.data.setup; + BoneLocal pose = appliedPose ? bone.applied : bone.pose, setup = bone.data.setup; float[] frames = this.frames; if (time < frames[0]) { @@ -641,7 +641,7 @@ public class Animation { } } - /** Changes a bone's local {@link BonePose#getX()}. */ + /** Changes a bone's local {@link BoneLocal#getX()}. */ static public class TranslateXTimeline extends CurveTimeline1 implements BoneTimeline { final int boneIndex; @@ -659,13 +659,13 @@ public class Animation { Bone bone = skeleton.bones.get(boneIndex); if (bone.active) { - BonePose pose = appliedPose ? bone.applied : bone.pose; + BoneLocal pose = appliedPose ? bone.applied : bone.pose; pose.x = getRelativeValue(time, alpha, blend, pose.x, bone.data.setup.x); } } } - /** Changes a bone's local {@link BonePose#getY()}. */ + /** Changes a bone's local {@link BoneLocal#getY()}. */ static public class TranslateYTimeline extends CurveTimeline1 implements BoneTimeline { final int boneIndex; @@ -683,13 +683,13 @@ public class Animation { Bone bone = skeleton.bones.get(boneIndex); if (bone.active) { - BonePose pose = appliedPose ? bone.applied : bone.pose; + BoneLocal pose = appliedPose ? bone.applied : bone.pose; pose.y = getRelativeValue(time, alpha, blend, pose.y, bone.data.setup.y); } } } - /** Changes a bone's local {@link BonePose#getScaleX()} and {@link BonePose#getScaleY()}. */ + /** Changes a bone's local {@link BoneLocal#getScaleX()} and {@link BoneLocal#getScaleY()}. */ static public class ScaleTimeline extends CurveTimeline2 implements BoneTimeline { final int boneIndex; @@ -709,7 +709,7 @@ public class Animation { Bone bone = skeleton.bones.get(boneIndex); if (!bone.active) return; - BonePose pose = appliedPose ? bone.applied : bone.pose, setup = bone.data.setup; + BoneLocal pose = appliedPose ? bone.applied : bone.pose, setup = bone.data.setup; float[] frames = this.frames; if (time < frames[0]) { @@ -801,7 +801,7 @@ public class Animation { } } - /** Changes a bone's local {@link BonePose#getScaleX()}. */ + /** Changes a bone's local {@link BoneLocal#getScaleX()}. */ static public class ScaleXTimeline extends CurveTimeline1 implements BoneTimeline { final int boneIndex; @@ -819,13 +819,13 @@ public class Animation { Bone bone = skeleton.bones.get(boneIndex); if (bone.active) { - BonePose pose = appliedPose ? bone.applied : bone.pose; + BoneLocal pose = appliedPose ? bone.applied : bone.pose; pose.scaleX = getScaleValue(time, alpha, blend, direction, pose.scaleX, bone.data.setup.scaleX); } } } - /** Changes a bone's local {@link BonePose#getScaleY()}. */ + /** Changes a bone's local {@link BoneLocal#getScaleY()}. */ static public class ScaleYTimeline extends CurveTimeline1 implements BoneTimeline { final int boneIndex; @@ -843,13 +843,13 @@ public class Animation { Bone bone = skeleton.bones.get(boneIndex); if (bone.active) { - BonePose pose = appliedPose ? bone.applied : bone.pose; + BoneLocal pose = appliedPose ? bone.applied : bone.pose; pose.scaleY = getScaleValue(time, alpha, blend, direction, pose.scaleY, bone.data.setup.scaleY); } } } - /** Changes a bone's local {@link BonePose#getShearX()} and {@link BonePose#getShearY()}. */ + /** Changes a bone's local {@link BoneLocal#getShearX()} and {@link BoneLocal#getShearY()}. */ static public class ShearTimeline extends CurveTimeline2 implements BoneTimeline { final int boneIndex; @@ -869,7 +869,7 @@ public class Animation { Bone bone = skeleton.bones.get(boneIndex); if (!bone.active) return; - BonePose pose = appliedPose ? bone.applied : bone.pose, setup = bone.data.setup; + BoneLocal pose = appliedPose ? bone.applied : bone.pose, setup = bone.data.setup; float[] frames = this.frames; if (time < frames[0]) { @@ -922,7 +922,7 @@ public class Animation { } } - /** Changes a bone's local {@link BonePose#getShearX()}. */ + /** Changes a bone's local {@link BoneLocal#getShearX()}. */ static public class ShearXTimeline extends CurveTimeline1 implements BoneTimeline { final int boneIndex; @@ -940,13 +940,13 @@ public class Animation { Bone bone = skeleton.bones.get(boneIndex); if (bone.active) { - BonePose pose = appliedPose ? bone.applied : bone.pose; + BoneLocal pose = appliedPose ? bone.applied : bone.pose; pose.shearX = getRelativeValue(time, alpha, blend, pose.shearX, bone.data.setup.shearX); } } } - /** Changes a bone's local {@link BonePose#getShearY()}. */ + /** Changes a bone's local {@link BoneLocal#getShearY()}. */ static public class ShearYTimeline extends CurveTimeline1 implements BoneTimeline { final int boneIndex; @@ -964,13 +964,13 @@ public class Animation { Bone bone = skeleton.bones.get(boneIndex); if (bone.active) { - BonePose pose = appliedPose ? bone.applied : bone.pose; + BoneLocal pose = appliedPose ? bone.applied : bone.pose; pose.shearY = getRelativeValue(time, alpha, blend, pose.shearY, bone.data.setup.shearY); } } } - /** Changes a bone's {@link BonePose#getInherit()}. */ + /** Changes a bone's {@link BoneLocal#getInherit()}. */ static public class InheritTimeline extends Timeline implements BoneTimeline { static public final int ENTRIES = 2; static private final int INHERIT = 1; @@ -1004,7 +1004,7 @@ public class Animation { Bone bone = skeleton.bones.get(boneIndex); if (!bone.active) return; - BonePose pose = appliedPose ? bone.applied : bone.pose; + BoneLocal pose = appliedPose ? bone.applied : bone.pose; if (direction == out) { if (blend == setup) pose.inherit = bone.data.setup.inherit; @@ -2572,12 +2572,12 @@ public class Animation { if (lastTime < frames[0] || time >= frames[search(frames, lastTime) + 1]) { if (constraint != null) - constraint.reset(); + constraint.reset(skeleton); else { Object[] constraints = skeleton.physicsConstraints.items; for (int i = 0, n = skeleton.physicsConstraints.size; i < n; i++) { constraint = (PhysicsConstraint)constraints[i]; - if (constraint.active) constraint.reset(); + if (constraint.active) constraint.reset(skeleton); } } } 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 8ca3d121c..4fb0c9681 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java @@ -417,7 +417,7 @@ public class AnimationState { Bone bone = skeleton.bones.get(timeline.boneIndex); if (!bone.active) return; - BonePose pose = bone.pose, setup = bone.data.setup; + BoneLocal pose = bone.pose, setup = bone.data.setup; float[] frames = timeline.frames; float r1, r2; if (time < frames[0]) { // Time is before 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 3c1acf9a0..8a3abd6dd 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java @@ -38,67 +38,24 @@ import com.badlogic.gdx.utils.Null; * 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 implements Constrained { - final BoneData data; - final Skeleton skeleton; +public class Bone extends PosedActive { @Null final Bone parent; final Array children = new Array(); - final BoneApplied pose = new BoneApplied(this), constrained = new BoneApplied(this); - BoneApplied applied = pose; - boolean sorted, active; + boolean sorted; - 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."); - this.data = data; - this.skeleton = skeleton; + public Bone (BoneData data, @Null Bone parent) { + super(data, new BonePose(), new BonePose()); this.parent = parent; - setupPose(); + applied.bone = this; + constrained.bone = this; } /** Copy constructor. Does not copy the {@link #getChildren()} bones. */ - public Bone (Bone bone, Skeleton skeleton, @Null Bone parent) { - this.data = bone.data; - this.skeleton = skeleton; - this.parent = parent; + public Bone (Bone bone, @Null Bone parent) { + this(bone.data, parent); pose.set(bone.pose); } - /** Sets this bone's local transform to the setup pose. */ - public void setupPose () { - pose.set(data.setup); - } - - /** The bone's setup pose data. */ - public BoneData getData () { - return data; - } - - public BonePose getPose () { - return pose; - } - - public BoneApplied getAppliedPose () { - return applied; - } - - public BoneApplied getConstrainedPose () { - return constrained; - } - - public void setConstrained (boolean constrained) { - applied = constrained ? this.constrained : pose; - } - - public void resetAppliedPose () { - applied.set(pose); - } - - /** The skeleton this bone belongs to. */ - public Skeleton getSkeleton () { - return skeleton; - } - /** The parent bone, or null if this is the root bone. */ public @Null Bone getParent () { return parent; @@ -108,18 +65,4 @@ public class Bone implements Constrained { public Array getChildren () { return children; } - - /** Returns false when this bone won't be updated by {@link Skeleton#updateWorldTransform(com.esotericsoftware.spine.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; - } - - public String toString () { - return data.name; - } } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneApplied.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneApplied.java deleted file mode 100644 index e3b8efdb6..000000000 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneApplied.java +++ /dev/null @@ -1,376 +0,0 @@ - -package com.esotericsoftware.spine; - -import static com.badlogic.gdx.math.Matrix3.*; -import static com.esotericsoftware.spine.utils.SpineUtils.*; - -import com.badlogic.gdx.math.Matrix3; -import com.badlogic.gdx.math.Vector2; - -import com.esotericsoftware.spine.BoneData.Inherit; - -/** The applied pose for a bone. This is the {@link Bone} pose with constraints applied and the world transform computed by - * {@link Skeleton#updateWorldTransform(Physics)}. */ -public class BoneApplied extends BonePose implements Update { - final Bone bone; - float a, b, worldX; - float c, d, worldY; - - BoneApplied (Bone bone) { - this.bone = bone; - } - - /** Computes the world transform using the parent bone and this bone's local applied transform. */ - public void updateWorldTransform () { - update(null); - } - - /** Computes the world transform using the parent bone and this bone's local transform. - *

- * See {@link #updateWorldTransform(float, float, float, float, float, float, float)}. */ - /** 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. - *

- * See World transforms in the Spine - * Runtimes Guide. */ - public void update (Physics physics) { - if (bone.parent == null) { // Root bone. - Skeleton skeleton = bone.skeleton; - float sx = skeleton.scaleX, sy = skeleton.scaleY; - float rx = (rotation + shearX) * degRad; - float ry = (rotation + 90 + shearY) * degRad; - a = cos(rx) * scaleX * sx; - b = cos(ry) * scaleY * sx; - c = sin(rx) * scaleX * sy; - d = sin(ry) * scaleY * sy; - worldX = x * sx + skeleton.x; - worldY = y * sy + skeleton.y; - return; - } - - BoneApplied parent = bone.parent.applied; - float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d; - worldX = pa * x + pb * y + parent.worldX; - worldY = pc * x + pd * y + parent.worldY; - - switch (inherit) { - case normal -> { - float rx = (rotation + shearX) * degRad; - float ry = (rotation + 90 + shearY) * degRad; - float la = cos(rx) * scaleX; - float lb = cos(ry) * scaleY; - float lc = sin(rx) * scaleX; - float ld = sin(ry) * scaleY; - a = pa * la + pb * lc; - b = pa * lb + pb * ld; - c = pc * la + pd * lc; - d = pc * lb + pd * ld; - return; - } - case onlyTranslation -> { - float rx = (rotation + shearX) * degRad; - float ry = (rotation + 90 + shearY) * degRad; - a = cos(rx) * scaleX; - b = cos(ry) * scaleY; - c = sin(rx) * scaleX; - d = sin(ry) * scaleY; - } - case noRotationOrReflection -> { - float sx = 1 / bone.skeleton.scaleX, sy = 1 / bone.skeleton.scaleY; - pa *= sx; - pc *= sy; - float s = pa * pa + pc * pc, prx; - if (s > 0.0001f) { - s = Math.abs(pa * pd * sy - pb * sx * pc) / s; - pb = pc * s; - pd = pa * s; - prx = atan2Deg(pc, pa); - } else { - pa = 0; - pc = 0; - prx = 90 - atan2Deg(pd, pb); - } - float rx = (rotation + shearX - prx) * degRad; - float ry = (rotation + shearY - prx + 90) * degRad; - float la = cos(rx) * scaleX; - float lb = cos(ry) * scaleY; - float lc = sin(rx) * scaleX; - float ld = sin(ry) * scaleY; - a = pa * la - pb * lc; - b = pa * lb - pb * ld; - c = pc * la + pd * lc; - d = pc * lb + pd * ld; - } - case noScale, noScaleOrReflection -> { - Skeleton skeleton = bone.skeleton; - rotation *= degRad; - float cos = cos(rotation), sin = sin(rotation); - float za = (pa * cos + pb * sin) / skeleton.scaleX; - float zc = (pc * cos + pd * sin) / skeleton.scaleY; - float s = (float)Math.sqrt(za * za + zc * zc); - if (s > 0.00001f) s = 1 / s; - za *= s; - zc *= s; - s = (float)Math.sqrt(za * za + zc * zc); - if (inherit == Inherit.noScale && (pa * pd - pb * pc < 0) != (skeleton.scaleX < 0 != skeleton.scaleY < 0)) s = -s; - rotation = PI / 2 + atan2(zc, za); - float zb = cos(rotation) * s; - float zd = sin(rotation) * s; - shearX *= degRad; - shearY = (90 + shearY) * degRad; - float la = cos(shearX) * scaleX; - float lb = cos(shearY) * scaleY; - float lc = sin(shearX) * scaleX; - float ld = sin(shearY) * scaleY; - a = za * la + zb * lc; - b = za * lb + zb * ld; - c = zc * la + zd * lc; - d = zc * lb + zd * ld; - } - } - Skeleton skeleton = bone.skeleton; - a *= skeleton.scaleX; - b *= skeleton.scaleX; - c *= skeleton.scaleY; - d *= skeleton.scaleY; - } - - /** 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 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 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 updateLocalTransform () { - BoneApplied parent = bone.parent.applied; - if (parent == null) { - x = worldX - bone.skeleton.x; - y = worldY - bone.skeleton.y; - float a = this.a, b = this.b, c = this.c, d = this.d; - rotation = atan2Deg(c, a); - scaleX = (float)Math.sqrt(a * a + c * c); - scaleY = (float)Math.sqrt(b * b + d * d); - shearX = 0; - shearY = atan2Deg(a * b + c * d, a * d - b * c); - return; - } - - float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d; - float pid = 1 / (pa * pd - pb * pc); - float ia = pd * pid, ib = pb * pid, ic = pc * pid, id = pa * pid; - float dx = worldX - parent.worldX, dy = worldY - parent.worldY; - x = (dx * ia - dy * ib); - y = (dy * id - dx * ic); - - float ra, rb, rc, rd; - if (inherit == Inherit.onlyTranslation) { - ra = a; - rb = b; - rc = c; - rd = d; - } else { - Skeleton skeleton = bone.skeleton; - switch (inherit) { - case noRotationOrReflection -> { - float s = Math.abs(pa * pd - pb * pc) / (pa * pa + pc * pc); - pb = -pc * skeleton.scaleX * s / skeleton.scaleY; - pd = pa * skeleton.scaleY * s / skeleton.scaleX; - pid = 1 / (pa * pd - pb * pc); - ia = pd * pid; - ib = pb * pid; - } - case noScale, noScaleOrReflection -> { - float r = rotation * degRad, cos = cos(r), sin = sin(r); - pa = (pa * cos + pb * sin) / skeleton.scaleX; - pc = (pc * cos + pd * sin) / skeleton.scaleY; - float s = (float)Math.sqrt(pa * pa + pc * pc); - if (s > 0.00001f) s = 1 / s; - pa *= s; - pc *= s; - s = (float)Math.sqrt(pa * pa + pc * pc); - if (inherit == Inherit.noScale && pid < 0 != (skeleton.scaleX < 0 != skeleton.scaleY < 0)) s = -s; - r = PI / 2 + atan2(pc, pa); - pb = cos(r) * s; - pd = sin(r) * s; - pid = 1 / (pa * pd - pb * pc); - ia = pd * pid; - ib = pb * pid; - ic = pc * pid; - id = pa * pid; - } - } - ra = ia * a - ib * c; - rb = ia * b - ib * d; - rc = id * c - ic * a; - rd = id * d - ic * b; - } - - shearX = 0; - scaleX = (float)Math.sqrt(ra * ra + rc * rc); - if (scaleX > 0.0001f) { - float det = ra * rd - rb * rc; - scaleY = det / scaleX; - shearY = -atan2Deg(ra * rb + rc * rd, det); - rotation = atan2Deg(rc, ra); - } else { - scaleX = 0; - scaleY = (float)Math.sqrt(rb * rb + rd * rd); - shearY = 0; - rotation = 90 - atan2Deg(rd, rb); - } - } - - /** Part of the world transform matrix for the X axis. If changed, {@link #updateLocalTransform()} should be called. */ - public float getA () { - return a; - } - - public void setA (float a) { - this.a = a; - } - - /** Part of the world transform matrix for the Y axis. If changed, {@link #updateLocalTransform()} should be called. */ - public float getB () { - return b; - } - - public void setB (float b) { - this.b = b; - } - - /** Part of the world transform matrix for the X axis. If changed, {@link #updateLocalTransform()} should be called. */ - public float getC () { - return c; - } - - public void setC (float c) { - this.c = c; - } - - /** Part of the world transform matrix for the Y axis. If changed, {@link #updateLocalTransform()} should be called. */ - public float getD () { - return d; - } - - public void setD (float d) { - this.d = d; - } - - /** The world X position. If changed, {@link #updateLocalTransform()} should be called. */ - public float getWorldX () { - return worldX; - } - - public void setWorldX (float worldX) { - this.worldX = worldX; - } - - /** The world Y position. If changed, {@link #updateLocalTransform()} should be called. */ - public float getWorldY () { - return worldY; - } - - public void setWorldY (float worldY) { - this.worldY = worldY; - } - - /** The world rotation for the X axis, calculated using {@link #a} and {@link #c}. */ - public float getWorldRotationX () { - return atan2Deg(c, a); - } - - /** The world rotation for the Y axis, calculated using {@link #b} and {@link #d}. */ - public float getWorldRotationY () { - return atan2Deg(d, b); - } - - /** The magnitude (always positive) of the world scale X, calculated using {@link #a} and {@link #c}. */ - public float getWorldScaleX () { - return (float)Math.sqrt(a * a + c * c); - } - - /** The magnitude (always positive) of the world scale Y, calculated using {@link #b} and {@link #d}. */ - public float getWorldScaleY () { - return (float)Math.sqrt(b * b + d * d); - } - - public Matrix3 getWorldTransform (Matrix3 worldTransform) { - if (worldTransform == null) throw new IllegalArgumentException("worldTransform cannot be null."); - float[] val = worldTransform.val; - val[M00] = a; - val[M01] = b; - val[M10] = c; - val[M11] = d; - val[M02] = worldX; - val[M12] = worldY; - val[M20] = 0; - val[M21] = 0; - val[M22] = 1; - return worldTransform; - } - - /** Transforms a point from world coordinates to the bone's local coordinates. */ - public Vector2 worldToLocal (Vector2 world) { - if (world == null) throw new IllegalArgumentException("world cannot be null."); - float det = a * d - b * c; - float x = world.x - worldX, y = world.y - worldY; - world.x = (x * d - y * b) / det; - world.y = (y * a - x * c) / det; - return world; - } - - /** Transforms a point from the bone's local coordinates to world coordinates. */ - public Vector2 localToWorld (Vector2 local) { - if (local == null) throw new IllegalArgumentException("local cannot be null."); - float x = local.x, y = local.y; - local.x = x * a + y * b + worldX; - local.y = x * c + y * d + worldY; - return local; - } - - /** 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 bone.parent == null ? world : bone.parent.applied.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 bone.parent == null ? world : bone.parent.applied.localToWorld(world); - } - - /** Transforms a world rotation to a local rotation. */ - public float worldToLocalRotation (float worldRotation) { - worldRotation *= degRad; - float sin = sin(worldRotation), cos = cos(worldRotation); - return atan2Deg(a * sin - c * cos, d * cos - b * sin) + rotation - shearX; - } - - /** Transforms a local rotation to a world rotation. */ - public float localToWorldRotation (float localRotation) { - localRotation = (localRotation - rotation - shearX) * degRad; - float sin = sin(localRotation), cos = cos(localRotation); - return atan2Deg(cos * c + sin * d, cos * a + sin * b); - } - - /** Rotates the world transform the specified amount. - *

- * 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); - float ra = a, rb = b; - a = cos * ra - sin * c; - b = cos * rb - sin * d; - c = sin * ra + cos * c; - d = sin * rb + cos * d; - } - - public String toString () { - return bone.data.name; - } -} 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 8432bc5cb..4cb908305 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneData.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneData.java @@ -33,13 +33,10 @@ import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.utils.Null; /** The setup pose for a bone. */ -public class BoneData { +public class BoneData extends PosedData { final int index; - final String name; @Null final BoneData parent; - final BonePose setup = new BonePose(); float length; - boolean skinRequired; // Nonessential. final Color color = new Color(0.61f, 0.61f, 0.61f, 1); // 9b9b9bff @@ -47,18 +44,16 @@ public class BoneData { boolean visible; public BoneData (int index, String name, @Null BoneData parent) { + super(name, new BoneLocal()); if (index < 0) throw new IllegalArgumentException("index must be >= 0."); if (name == null) throw new IllegalArgumentException("name cannot be null."); this.index = index; - this.name = name; this.parent = parent; } /** Copy constructor. */ public BoneData (BoneData data, @Null BoneData parent) { - index = data.index; - name = data.name; - this.parent = parent; + this(data.index, data.name, parent); length = data.length; setup.set(data.setup); } @@ -68,19 +63,10 @@ public class BoneData { return index; } - /** The name of the bone, which is unique across all bones in the skeleton. */ - public String getName () { - return name; - } - public @Null BoneData getParent () { return parent; } - public BonePose getSetupPose () { - return setup; - } - /** The bone's length. */ public float getLength () { return length; @@ -90,18 +76,6 @@ public class BoneData { this.length = length; } - /** When true, {@link Skeleton#updateWorldTransform(Physics)} only updates this bone if the {@link Skeleton#getSkin()} contains - * this bone. - *

- * See {@link Skin#getBones()}. */ - public boolean getSkinRequired () { - return skinRequired; - } - - public void setSkinRequired (boolean skinRequired) { - this.skinRequired = skinRequired; - } - /** The color of the bone as it was in Spine, or a default color if nonessential data was not exported. Bones are not usually * rendered at runtime. */ public Color getColor () { @@ -126,10 +100,6 @@ public class BoneData { this.visible = visible; } - public String toString () { - return name; - } - /** Determines how a bone inherits world transforms from parent bones. */ static public enum Inherit { normal, onlyTranslation, noRotationOrReflection, noScale, noScaleOrReflection; diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneLocal.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneLocal.java new file mode 100644 index 000000000..bf04928bb --- /dev/null +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneLocal.java @@ -0,0 +1,141 @@ +/****************************************************************************** + * 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 local pose. */ +public class BoneLocal implements Pose { + float x, y, rotation, scaleX, scaleY, shearX, shearY; + Inherit inherit; + + BoneLocal () { + } + + public void set (BoneLocal 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. */ + 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/BonePose.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BonePose.java index a74d76969..6887b8211 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BonePose.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BonePose.java @@ -1,141 +1,371 @@ -/****************************************************************************** - * 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 static com.badlogic.gdx.math.Matrix3.*; +import static com.esotericsoftware.spine.utils.SpineUtils.*; + +import com.badlogic.gdx.math.Matrix3; +import com.badlogic.gdx.math.Vector2; + import com.esotericsoftware.spine.BoneData.Inherit; -/** Stores a bone's local pose. */ -public class BonePose { - float x, y, rotation, scaleX, scaleY, shearX, shearY; - Inherit inherit; +/** The applied pose for a bone. This is the {@link Bone} pose with constraints applied and the world transform computed by + * {@link Skeleton#updateWorldTransform(Physics)}. */ +public class BonePose extends BoneLocal implements Update { + Bone bone; + float a, b, worldX; + float c, d, worldY; BonePose () { } - 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; + /** Computes the world transform using the parent bone and this bone's local applied transform. */ + public void updateWorldTransform (Skeleton skeleton) { + update(skeleton, null); } - /** The local x translation. */ - public float getX () { - return x; + /** Computes the world transform using the parent bone and this bone's local transform. + *

+ * See {@link #updateWorldTransform(float, float, float, float, float, float, float)}. */ + /** 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. + *

+ * See World transforms in the Spine + * Runtimes Guide. */ + public void update (Skeleton skeleton, Physics physics) { + if (bone.parent == null) { // Root bone. + float sx = skeleton.scaleX, sy = skeleton.scaleY; + float rx = (rotation + shearX) * degRad; + float ry = (rotation + 90 + shearY) * degRad; + a = cos(rx) * scaleX * sx; + b = cos(ry) * scaleY * sx; + c = sin(rx) * scaleX * sy; + d = sin(ry) * scaleY * sy; + worldX = x * sx + skeleton.x; + worldY = y * sy + skeleton.y; + return; + } + + BonePose parent = bone.parent.applied; + float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d; + worldX = pa * x + pb * y + parent.worldX; + worldY = pc * x + pd * y + parent.worldY; + + switch (inherit) { + case normal -> { + float rx = (rotation + shearX) * degRad; + float ry = (rotation + 90 + shearY) * degRad; + float la = cos(rx) * scaleX; + float lb = cos(ry) * scaleY; + float lc = sin(rx) * scaleX; + float ld = sin(ry) * scaleY; + a = pa * la + pb * lc; + b = pa * lb + pb * ld; + c = pc * la + pd * lc; + d = pc * lb + pd * ld; + return; + } + case onlyTranslation -> { + float rx = (rotation + shearX) * degRad; + float ry = (rotation + 90 + shearY) * degRad; + a = cos(rx) * scaleX; + b = cos(ry) * scaleY; + c = sin(rx) * scaleX; + d = sin(ry) * scaleY; + } + case noRotationOrReflection -> { + float sx = 1 / skeleton.scaleX, sy = 1 / skeleton.scaleY; + pa *= sx; + pc *= sy; + float s = pa * pa + pc * pc, prx; + if (s > 0.0001f) { + s = Math.abs(pa * pd * sy - pb * sx * pc) / s; + pb = pc * s; + pd = pa * s; + prx = atan2Deg(pc, pa); + } else { + pa = 0; + pc = 0; + prx = 90 - atan2Deg(pd, pb); + } + float rx = (rotation + shearX - prx) * degRad; + float ry = (rotation + shearY - prx + 90) * degRad; + float la = cos(rx) * scaleX; + float lb = cos(ry) * scaleY; + float lc = sin(rx) * scaleX; + float ld = sin(ry) * scaleY; + a = pa * la - pb * lc; + b = pa * lb - pb * ld; + c = pc * la + pd * lc; + d = pc * lb + pd * ld; + } + case noScale, noScaleOrReflection -> { + rotation *= degRad; + float cos = cos(rotation), sin = sin(rotation); + float za = (pa * cos + pb * sin) / skeleton.scaleX; + float zc = (pc * cos + pd * sin) / skeleton.scaleY; + float s = (float)Math.sqrt(za * za + zc * zc); + if (s > 0.00001f) s = 1 / s; + za *= s; + zc *= s; + s = (float)Math.sqrt(za * za + zc * zc); + if (inherit == Inherit.noScale && (pa * pd - pb * pc < 0) != (skeleton.scaleX < 0 != skeleton.scaleY < 0)) s = -s; + rotation = PI / 2 + atan2(zc, za); + float zb = cos(rotation) * s; + float zd = sin(rotation) * s; + shearX *= degRad; + shearY = (90 + shearY) * degRad; + float la = cos(shearX) * scaleX; + float lb = cos(shearY) * scaleY; + float lc = sin(shearX) * scaleX; + float ld = sin(shearY) * scaleY; + a = za * la + zb * lc; + b = za * lb + zb * ld; + c = zc * la + zd * lc; + d = zc * lb + zd * ld; + } + } + a *= skeleton.scaleX; + b *= skeleton.scaleX; + c *= skeleton.scaleY; + d *= skeleton.scaleY; } - public void setX (float x) { - this.x = x; + /** 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 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 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 updateLocalTransform (Skeleton skeleton) { + if (bone.parent == null) { + x = worldX - skeleton.x; + y = worldY - skeleton.y; + float a = this.a, b = this.b, c = this.c, d = this.d; + rotation = atan2Deg(c, a); + scaleX = (float)Math.sqrt(a * a + c * c); + scaleY = (float)Math.sqrt(b * b + d * d); + shearX = 0; + shearY = atan2Deg(a * b + c * d, a * d - b * c); + return; + } + + BonePose parent = bone.parent.applied; + float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d; + float pid = 1 / (pa * pd - pb * pc); + float ia = pd * pid, ib = pb * pid, ic = pc * pid, id = pa * pid; + float dx = worldX - parent.worldX, dy = worldY - parent.worldY; + x = (dx * ia - dy * ib); + y = (dy * id - dx * ic); + + float ra, rb, rc, rd; + if (inherit == Inherit.onlyTranslation) { + ra = a; + rb = b; + rc = c; + rd = d; + } else { + switch (inherit) { + case noRotationOrReflection -> { + float s = Math.abs(pa * pd - pb * pc) / (pa * pa + pc * pc); + pb = -pc * skeleton.scaleX * s / skeleton.scaleY; + pd = pa * skeleton.scaleY * s / skeleton.scaleX; + pid = 1 / (pa * pd - pb * pc); + ia = pd * pid; + ib = pb * pid; + } + case noScale, noScaleOrReflection -> { + float r = rotation * degRad, cos = cos(r), sin = sin(r); + pa = (pa * cos + pb * sin) / skeleton.scaleX; + pc = (pc * cos + pd * sin) / skeleton.scaleY; + float s = (float)Math.sqrt(pa * pa + pc * pc); + if (s > 0.00001f) s = 1 / s; + pa *= s; + pc *= s; + s = (float)Math.sqrt(pa * pa + pc * pc); + if (inherit == Inherit.noScale && pid < 0 != (skeleton.scaleX < 0 != skeleton.scaleY < 0)) s = -s; + r = PI / 2 + atan2(pc, pa); + pb = cos(r) * s; + pd = sin(r) * s; + pid = 1 / (pa * pd - pb * pc); + ia = pd * pid; + ib = pb * pid; + ic = pc * pid; + id = pa * pid; + } + } + ra = ia * a - ib * c; + rb = ia * b - ib * d; + rc = id * c - ic * a; + rd = id * d - ic * b; + } + + shearX = 0; + scaleX = (float)Math.sqrt(ra * ra + rc * rc); + if (scaleX > 0.0001f) { + float det = ra * rd - rb * rc; + scaleY = det / scaleX; + shearY = -atan2Deg(ra * rb + rc * rd, det); + rotation = atan2Deg(rc, ra); + } else { + scaleX = 0; + scaleY = (float)Math.sqrt(rb * rb + rd * rd); + shearY = 0; + rotation = 90 - atan2Deg(rd, rb); + } } - /** The local y translation. */ - public float getY () { - return y; + /** Part of the world transform matrix for the X axis. If changed, {@link #updateLocalTransform(Skeleton)} should be called. */ + public float getA () { + return a; } - public void setY (float y) { - this.y = y; + public void setA (float a) { + this.a = a; } - public void setPosition (float x, float y) { - this.x = x; - this.y = y; + /** Part of the world transform matrix for the Y axis. If changed, {@link #updateLocalTransform(Skeleton)} should be called. */ + public float getB () { + return b; } - /** The local rotation in degrees, counter clockwise. */ - public float getRotation () { - return rotation; + public void setB (float b) { + this.b = b; } - public void setRotation (float rotation) { - this.rotation = rotation; + /** Part of the world transform matrix for the X axis. If changed, {@link #updateLocalTransform(Skeleton)} should be called. */ + public float getC () { + return c; } - /** The local scaleX. */ - public float getScaleX () { - return scaleX; + public void setC (float c) { + this.c = c; } - public void setScaleX (float scaleX) { - this.scaleX = scaleX; + /** Part of the world transform matrix for the Y axis. If changed, {@link #updateLocalTransform(Skeleton)} should be called. */ + public float getD () { + return d; } - /** The local scaleY. */ - public float getScaleY () { - return scaleY; + public void setD (float d) { + this.d = d; } - public void setScaleY (float scaleY) { - this.scaleY = scaleY; + /** The world X position. If changed, {@link #updateLocalTransform(Skeleton)} should be called. */ + public float getWorldX () { + return worldX; } - public void setScale (float scaleX, float scaleY) { - this.scaleX = scaleX; - this.scaleY = scaleY; + public void setWorldX (float worldX) { + this.worldX = worldX; } - public void setScale (float scale) { - scaleX = scale; - scaleY = scale; + /** The world Y position. If changed, {@link #updateLocalTransform(Skeleton)} should be called. */ + public float getWorldY () { + return worldY; } - /** The local shearX. */ - public float getShearX () { - return shearX; + public void setWorldY (float worldY) { + this.worldY = worldY; } - public void setShearX (float shearX) { - this.shearX = shearX; + /** The world rotation for the X axis, calculated using {@link #a} and {@link #c}. */ + public float getWorldRotationX () { + return atan2Deg(c, a); } - /** The local shearY. */ - public float getShearY () { - return shearY; + /** The world rotation for the Y axis, calculated using {@link #b} and {@link #d}. */ + public float getWorldRotationY () { + return atan2Deg(d, b); } - public void setShearY (float shearY) { - this.shearY = shearY; + /** The magnitude (always positive) of the world scale X, calculated using {@link #a} and {@link #c}. */ + public float getWorldScaleX () { + return (float)Math.sqrt(a * a + c * c); } - /** Determines how parent world transforms affect this bone. */ - public Inherit getInherit () { - return inherit; + /** The magnitude (always positive) of the world scale Y, calculated using {@link #b} and {@link #d}. */ + public float getWorldScaleY () { + return (float)Math.sqrt(b * b + d * d); } - public void setInherit (Inherit inherit) { - if (inherit == null) throw new IllegalArgumentException("inherit cannot be null."); - this.inherit = inherit; + public Matrix3 getWorldTransform (Matrix3 worldTransform) { + if (worldTransform == null) throw new IllegalArgumentException("worldTransform cannot be null."); + float[] val = worldTransform.val; + val[M00] = a; + val[M01] = b; + val[M10] = c; + val[M11] = d; + val[M02] = worldX; + val[M12] = worldY; + val[M20] = 0; + val[M21] = 0; + val[M22] = 1; + return worldTransform; + } + + /** Transforms a point from world coordinates to the bone's local coordinates. */ + public Vector2 worldToLocal (Vector2 world) { + if (world == null) throw new IllegalArgumentException("world cannot be null."); + float det = a * d - b * c; + float x = world.x - worldX, y = world.y - worldY; + world.x = (x * d - y * b) / det; + world.y = (y * a - x * c) / det; + return world; + } + + /** Transforms a point from the bone's local coordinates to world coordinates. */ + public Vector2 localToWorld (Vector2 local) { + if (local == null) throw new IllegalArgumentException("local cannot be null."); + float x = local.x, y = local.y; + local.x = x * a + y * b + worldX; + local.y = x * c + y * d + worldY; + return local; + } + + /** 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 bone.parent == null ? world : bone.parent.applied.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 bone.parent == null ? world : bone.parent.applied.localToWorld(world); + } + + /** Transforms a world rotation to a local rotation. */ + public float worldToLocalRotation (float worldRotation) { + worldRotation *= degRad; + float sin = sin(worldRotation), cos = cos(worldRotation); + return atan2Deg(a * sin - c * cos, d * cos - b * sin) + rotation - shearX; + } + + /** Transforms a local rotation to a world rotation. */ + public float localToWorldRotation (float localRotation) { + localRotation = (localRotation - rotation - shearX) * degRad; + float sin = sin(localRotation), cos = cos(localRotation); + return atan2Deg(cos * c + sin * d, cos * a + sin * b); + } + + /** Rotates the world transform the specified amount. + *

+ * After changes are made to the world transform, {@link #updateLocalTransform(Skeleton)} should be called and + * {@link #update(Skeleton, 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); + float ra = a, rb = b; + a = cos * ra - sin * c; + b = cos * rb - sin * d; + c = sin * ra + cos * c; + d = sin * rb + cos * d; + } + + public String toString () { + return bone.data.name; } } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Constrained.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Constrained.java deleted file mode 100644 index ac8e8100d..000000000 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Constrained.java +++ /dev/null @@ -1,36 +0,0 @@ -/****************************************************************************** - * 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; - -public interface Constrained { - public void setConstrained (boolean constrained); - - public void resetAppliedPose (); -} diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Constraint.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Constraint.java new file mode 100644 index 000000000..e131b3f4e --- /dev/null +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Constraint.java @@ -0,0 +1,10 @@ + +package com.esotericsoftware.spine; + +abstract public class Constraint, P extends Pose> extends PosedActive implements Update { + public Constraint (D data, P pose, P constrained) { + super(data, pose, constrained); + } + + abstract public void sort (); +} 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 4e4f843a6..11a3bb114 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java @@ -39,26 +39,19 @@ import com.esotericsoftware.spine.BoneData.Inherit; * the last bone is as close to the target bone as possible. *

* See IK constraints in the Spine User Guide. */ -public class IkConstraint implements Constrained, Update { - final IkConstraintData data; - final Array bones; +public class IkConstraint extends Constraint { + final Array bones; Bone target; - final IkConstraintPose pose = new IkConstraintPose(), constrained = new IkConstraintPose(); - IkConstraintPose applied = pose; - boolean active; public IkConstraint (IkConstraintData data, Skeleton skeleton) { - if (data == null) throw new IllegalArgumentException("data cannot be null."); + super(data, new IkConstraintPose(), new IkConstraintPose()); if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null."); - this.data = data; bones = new Array(data.bones.size); for (BoneData boneData : data.bones) bones.add(skeleton.bones.get(boneData.index).constrained); target = skeleton.bones.get(data.target.index); - - setupPose(); } /** Copy constructor. */ @@ -67,26 +60,25 @@ public class IkConstraint implements Constrained, Update { pose.set(constraint.pose); } - public void setupPose () { - pose.set(data.setup); - } - /** Applies the constraint to the constrained bones. */ - public void update (Physics physics) { + public void update (Skeleton skeleton, Physics physics) { IkConstraintPose a = applied; if (a.mix == 0) return; - BoneApplied target = this.target.applied; + BonePose target = this.target.applied; Object[] bones = this.bones.items; switch (this.bones.size) { - case 1 -> apply((BoneApplied)bones[0], target.worldX, target.worldY, a.compress, a.stretch, data.uniform, a.mix); - case 2 -> // - apply((BoneApplied)bones[0], (BoneApplied)bones[1], target.worldX, target.worldY, a.bendDirection, a.stretch, - data.uniform, a.softness, a.mix); + case 1 -> apply(skeleton, (BonePose)bones[0], target.worldX, target.worldY, a.compress, a.stretch, data.uniform, a.mix); + case 2 -> apply(skeleton, (BonePose)bones[0], (BonePose)bones[1], target.worldX, target.worldY, a.bendDirection, a.stretch, + data.uniform, a.softness, a.mix); } } + public void sort () { + // BOZO + } + /** The 1 or 2 bones that will be modified by this IK constraint. */ - public Array getBones () { + public Array getBones () { return bones; } @@ -100,61 +92,20 @@ public class IkConstraint implements Constrained, Update { this.target = target; } - public IkConstraintPose getPose () { - return pose; - } - - public IkConstraintPose getAppliedPose () { - return applied; - } - - public IkConstraintPose getConstrainedPose () { - return constrained; - } - - public void setConstrained (boolean constrained) { - applied = constrained ? this.constrained : pose; - } - - public void resetAppliedPose () { - applied.set(pose); - } - - /** Returns false when this constraint won't be updated by - * {@link Skeleton#updateWorldTransform(com.esotericsoftware.spine.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; - } - - /** The IK constraint's setup pose data. */ - public IkConstraintData getData () { - return data; - } - - public String toString () { - return data.name; - } - /** Applies 1 bone IK. The target is specified in the world coordinate system. */ - static public void apply (BoneApplied bone, float targetX, float targetY, boolean compress, boolean stretch, boolean uniform, - float alpha) { + static public void apply (Skeleton skeleton, BonePose 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.bone.parent.applied; + BonePose p = bone.bone.parent.applied; 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.bone.skeleton.scaleX); - ty = (targetY - bone.worldY) * Math.signum(bone.bone.skeleton.scaleY); + tx = (targetX - bone.worldX) * Math.signum(skeleton.scaleX); + ty = (targetY - bone.worldY) * Math.signum(skeleton.scaleY); break; case noRotationOrReflection: float s = Math.abs(pa * pd - pb * pc) / Math.max(0.0001f, pa * pa + pc * pc); - Skeleton skeleton = bone.bone.skeleton; float sa = pa / skeleton.scaleX; float sc = pc / skeleton.scaleY; pb = -sc * s * skeleton.scaleX; @@ -196,13 +147,13 @@ public class IkConstraint implements Constrained, Update { } } } - bone.updateWorldTransform(); + bone.updateWorldTransform(skeleton); } /** Applies 2 bone IK. The target is specified in the world coordinate system. * @param child A direct descendant of the parent bone. */ - static public void apply (BoneApplied parent, BoneApplied child, float targetX, float targetY, int bendDir, boolean stretch, - boolean uniform, float softness, float alpha) { + static public void apply (Skeleton skeleton, BonePose parent, BonePose child, float targetX, float targetY, int bendDir, + boolean stretch, boolean uniform, 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.inherit != Inherit.normal || child.inherit != Inherit.normal) return; @@ -235,7 +186,7 @@ public class IkConstraint implements Constrained, Update { cwx = a * child.x + b * child.y + parent.worldX; cwy = c * child.x + d * child.y + parent.worldY; } - BoneApplied pp = parent.bone.parent.applied; + BonePose pp = parent.bone.parent.applied; a = pp.a; b = pp.b; c = pp.c; @@ -245,9 +196,9 @@ public class IkConstraint implements Constrained, Update { 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.bone.data.length * csx, a1, a2; if (l1 < 0.0001f) { - apply(parent, targetX, targetY, false, stretch, false, alpha); + apply(skeleton, parent, targetX, targetY, false, stretch, false, alpha); child.rotation = 0; - child.updateWorldTransform(); + child.updateWorldTransform(skeleton); return; } x = targetX - pp.worldX; @@ -342,13 +293,13 @@ public class IkConstraint implements Constrained, Update { else if (a1 < -180) // a1 += 360; parent.rotation += a1 * alpha; - parent.updateWorldTransform(); + parent.updateWorldTransform(skeleton); a2 = ((a2 + os) * radDeg - child.shearX) * s2 + os2 - child.rotation; if (a2 > 180) a2 -= 360; else if (a2 < -180) // a2 += 360; child.rotation += a2 * alpha; - child.updateWorldTransform(); + child.updateWorldTransform(skeleton); } } 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 81ea32789..f590e90d4 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraintData.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraintData.java @@ -34,18 +34,13 @@ import com.badlogic.gdx.utils.Array; /** Stores the setup pose for an {@link IkConstraint}. *

* See IK constraints in the Spine User Guide. */ -public class IkConstraintData extends ConstraintData { - final IkConstraintPose setup = new IkConstraintPose(); +public class IkConstraintData extends PosedData { final Array bones = new Array(); BoneData target; boolean uniform; public IkConstraintData (String name) { - super(name); - } - - public IkConstraintPose getSetupPose () { - return setup; + super(name, new IkConstraintPose()); } /** The bones that are constrained by this IK constraint. */ diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraintPose.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraintPose.java index b1dddc3c4..ec7ac38fa 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraintPose.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraintPose.java @@ -30,7 +30,7 @@ package com.esotericsoftware.spine; /** Stores the current pose for an IK constraint. */ -public class IkConstraintPose { +public class IkConstraintPose implements Pose { int bendDirection; boolean compress, stretch; float mix, softness; 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 f2120ec89..f5751cc14 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraint.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraint.java @@ -45,39 +45,26 @@ import com.esotericsoftware.spine.attachments.PathAttachment; * constrained bones so they follow a {@link PathAttachment}. *

* See Path constraints in the Spine User Guide. */ -public class PathConstraint implements Constrained, Update { +public class PathConstraint extends Constraint { static final int NONE = -1, BEFORE = -2, AFTER = -3; static final float epsilon = 0.00001f; - final PathConstraintData data; - final Array bones; + final Array bones; Slot slot; - final PathConstraintPose pose = new PathConstraintPose(), constrained = new PathConstraintPose(); - PathConstraintPose applied = pose; - boolean active; private final FloatArray spaces = new FloatArray(), positions = new FloatArray(); private final FloatArray world = new FloatArray(), curves = new FloatArray(), lengths = new FloatArray(); private final float[] segments = new float[10]; - public PathConstraint (PathConstraintData data, Array bones, Slot slot) { - this.data = data; - this.bones = bones; - this.slot = slot; - } - public PathConstraint (PathConstraintData data, Skeleton skeleton) { - if (data == null) throw new IllegalArgumentException("data cannot be null."); + super(data, new PathConstraintPose(), new PathConstraintPose()); if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null."); - this.data = data; bones = new Array(data.bones.size); for (BoneData boneData : data.bones) bones.add(skeleton.bones.get(boneData.index).constrained); slot = skeleton.slots.get(data.slot.index); - - setupPose(); } /** Copy constructor. */ @@ -86,12 +73,8 @@ public class PathConstraint implements Constrained, Update { pose.set(constraint.pose); } - public void setupPose () { - pose.set(data.setup); - } - /** Applies the constraint to the constrained bones. */ - public void update (Physics physics) { + public void update (Skeleton skeleton, Physics physics) { if (!(slot.applied.attachment instanceof PathAttachment pathAttachment)) return; PathConstraintPose pose = applied; @@ -109,7 +92,7 @@ public class PathConstraint implements Constrained, Update { case percent -> { if (scale) { for (int i = 0, n = spacesCount - 1; i < n; i++) { - var bone = (BoneApplied)bones[i]; + var bone = (BonePose)bones[i]; float setupLength = bone.bone.data.length; float x = setupLength * bone.a, y = setupLength * bone.c; lengths[i] = (float)Math.sqrt(x * x + y * y); @@ -120,7 +103,7 @@ public class PathConstraint implements Constrained, Update { case proportional -> { float sum = 0; for (int i = 0, n = spacesCount - 1; i < n;) { - var bone = (BoneApplied)bones[i]; + var bone = (BonePose)bones[i]; float setupLength = bone.bone.data.length; if (setupLength < epsilon) { if (scale) lengths[i] = 0; @@ -142,7 +125,7 @@ public class PathConstraint implements Constrained, Update { default -> { boolean lengthSpacing = data.spacingMode == SpacingMode.length; for (int i = 0, n = spacesCount - 1; i < n;) { - var bone = (BoneApplied)bones[i]; + var bone = (BonePose)bones[i]; float setupLength = bone.bone.data.length; if (setupLength < epsilon) { if (scale) lengths[i] = 0; @@ -157,18 +140,18 @@ public class PathConstraint implements Constrained, Update { } } - float[] positions = computeWorldPositions(pathAttachment, spacesCount, tangents); + float[] positions = computeWorldPositions(skeleton, pathAttachment, spacesCount, tangents); float boneX = positions[0], boneY = positions[1], offsetRotation = data.offsetRotation; boolean tip; if (offsetRotation == 0) tip = data.rotateMode == RotateMode.chain; else { tip = false; - BoneApplied p = slot.bone.applied; + BonePose p = slot.bone.applied; offsetRotation *= p.a * p.d - p.b * p.c > 0 ? degRad : -degRad; } for (int i = 0, p = 3; i < boneCount; i++, p += 3) { - var bone = (BoneApplied)bones[i]; + var bone = (BonePose)bones[i]; bone.worldX += (boneX - bone.worldX) * mixX; bone.worldY += (boneY - bone.worldY) * mixY; float x = positions[p], y = positions[p + 1], dx = x - boneX, dy = y - boneY; @@ -211,11 +194,11 @@ public class PathConstraint implements Constrained, Update { bone.c = sin * a + cos * c; bone.d = sin * b + cos * d; } - bone.updateLocalTransform(); + bone.updateLocalTransform(skeleton); } } - float[] computeWorldPositions (PathAttachment path, int spacesCount, boolean tangents) { + float[] computeWorldPositions (Skeleton skeleton, PathAttachment path, int spacesCount, boolean tangents) { Slot slot = this.slot; float position = applied.position; float[] spaces = this.spaces.items, out = this.positions.setSize(spacesCount * 3 + 2), world; @@ -248,14 +231,14 @@ public class PathConstraint implements Constrained, Update { } else if (p < 0) { if (prevCurve != BEFORE) { prevCurve = BEFORE; - path.computeWorldVertices(slot, 2, 4, world, 0, 2); + path.computeWorldVertices(skeleton, slot, 2, 4, world, 0, 2); } addBeforePosition(p, world, 0, out, o); continue; } else if (p > pathLength) { if (prevCurve != AFTER) { prevCurve = AFTER; - path.computeWorldVertices(slot, verticesLength - 6, 4, world, 0, 2); + path.computeWorldVertices(skeleton, slot, verticesLength - 6, 4, world, 0, 2); } addAfterPosition(p - pathLength, world, 0, out, o); continue; @@ -276,10 +259,10 @@ public class PathConstraint implements Constrained, Update { if (curve != prevCurve) { prevCurve = curve; if (closed && curve == curveCount) { - path.computeWorldVertices(slot, verticesLength - 4, 4, world, 0, 2); - path.computeWorldVertices(slot, 0, 4, world, 4, 2); + path.computeWorldVertices(skeleton, slot, verticesLength - 4, 4, world, 0, 2); + path.computeWorldVertices(skeleton, slot, 0, 4, world, 4, 2); } else - path.computeWorldVertices(slot, curve * 6 + 2, 8, world, 0, 2); + path.computeWorldVertices(skeleton, slot, curve * 6 + 2, 8, world, 0, 2); } addCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], out, o, tangents || (i > 0 && space < epsilon)); @@ -291,15 +274,15 @@ public class PathConstraint implements Constrained, Update { if (closed) { verticesLength += 2; world = this.world.setSize(verticesLength); - path.computeWorldVertices(slot, 2, verticesLength - 4, world, 0, 2); - path.computeWorldVertices(slot, 0, 2, world, verticesLength - 4, 2); + path.computeWorldVertices(skeleton, slot, 2, verticesLength - 4, world, 0, 2); + path.computeWorldVertices(skeleton, slot, 0, 2, world, verticesLength - 4, 2); world[verticesLength - 2] = world[0]; world[verticesLength - 1] = world[1]; } else { curveCount--; verticesLength -= 4; world = this.world.setSize(verticesLength); - path.computeWorldVertices(slot, 2, verticesLength, world, 0, 2); + path.computeWorldVertices(skeleton, slot, 2, verticesLength, world, 0, 2); } // Curve lengths. @@ -473,8 +456,11 @@ public class PathConstraint implements Constrained, Update { } } + public void sort () { + } + /** The bones that will be modified by this path constraint. */ - public Array getBones () { + public Array getBones () { return bones; } @@ -487,44 +473,4 @@ public class PathConstraint implements Constrained, Update { if (slot == null) throw new IllegalArgumentException("slot cannot be null."); this.slot = slot; } - - public PathConstraintPose getPose () { - return pose; - } - - public PathConstraintPose getAppliedPose () { - return applied; - } - - public PathConstraintPose getConstrainedPose () { - return constrained; - } - - public void setConstrained (boolean constrained) { - applied = constrained ? this.constrained : pose; - } - - public void resetAppliedPose () { - applied.set(pose); - } - - /** Returns false when this constraint won't be updated by - * {@link Skeleton#updateWorldTransform(com.esotericsoftware.spine.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; - } - - /** The path constraint's setup pose data. */ - public PathConstraintData getData () { - return data; - } - - public String toString () { - return data.name; - } } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraintData.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraintData.java index 455a40672..0e866f463 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraintData.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraintData.java @@ -34,8 +34,7 @@ import com.badlogic.gdx.utils.Array; /** Stores the setup pose for a {@link PathConstraint}. *

* See Path constraints in the Spine User Guide. */ -public class PathConstraintData extends ConstraintData { - final PathConstraintPose setup = new PathConstraintPose(); +public class PathConstraintData extends PosedData { final Array bones = new Array(); SlotData slot; PositionMode positionMode; @@ -44,11 +43,7 @@ public class PathConstraintData extends ConstraintData { float offsetRotation; public PathConstraintData (String name) { - super(name); - } - - public PathConstraintPose getSetupPose () { - return setup; + super(name, new PathConstraintPose()); } /** The bones that will be modified by this path constraint. */ diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraintPose.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraintPose.java index e21293f90..b7938bdd1 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraintPose.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraintPose.java @@ -30,7 +30,7 @@ package com.esotericsoftware.spine; /** Stores a pose for a path constraint. */ -public class PathConstraintPose { +public class PathConstraintPose implements Pose { float position, spacing, mixRotate, mixX, mixY; public void set (PathConstraintPose pose) { 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 e5b7ccba4..59942b18c 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PhysicsConstraint.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PhysicsConstraint.java @@ -34,13 +34,8 @@ import static com.esotericsoftware.spine.utils.SpineUtils.*; /** Stores the current pose for a physics constraint. A physics constraint applies physics to bones. *

* See Physics constraints in the Spine User Guide. */ -public class PhysicsConstraint implements Constrained, Update { - final PhysicsConstraintData data; - final Skeleton skeleton; - BoneApplied bone; - final PhysicsConstraintPose pose = new PhysicsConstraintPose(), constrained = new PhysicsConstraintPose(); - PhysicsConstraintPose applied = pose; - boolean active; +public class PhysicsConstraint extends Constraint { + BonePose bone; boolean reset = true; float ux, uy, cx, cy, tx, ty; @@ -51,14 +46,10 @@ public class PhysicsConstraint implements Constrained, Update { float remaining, lastTime; public PhysicsConstraint (PhysicsConstraintData data, Skeleton skeleton) { - if (data == null) throw new IllegalArgumentException("data cannot be null."); + super(data, new PhysicsConstraintPose(), new PhysicsConstraintPose()); if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null."); - this.data = data; - this.skeleton = skeleton; bone = skeleton.bones.get(data.bone.index).constrained; - - setupPose(); } /** Copy constructor. */ @@ -67,7 +58,7 @@ public class PhysicsConstraint implements Constrained, Update { pose.set(constraint.pose); } - public void reset () { + public void reset (Skeleton skeleton) { remaining = 0; lastTime = skeleton.time; reset = true; @@ -81,12 +72,8 @@ public class PhysicsConstraint implements Constrained, Update { scaleVelocity = 0; } - public void setupPose () { - pose.set(data.setup); - } - - /** Translates the physics constraint so next {@link #update(Physics)} forces are applied as if the bone moved an additional - * amount in world space. */ + /** Translates the physics constraint so next {@link #update(Skeleton, Physics)} forces are applied as if the bone moved an + * additional amount in world space. */ public void translate (float x, float y) { ux -= x; uy -= y; @@ -94,8 +81,8 @@ public class PhysicsConstraint implements Constrained, Update { cy -= y; } - /** Rotates the physics constraint so next {@link #update(Physics)} forces are applied as if the bone rotated around the - * specified point in world space. */ + /** Rotates the physics constraint so next {@link #update(Skeleton, Physics)} forces are applied as if the bone rotated around + * the specified point in world space. */ public void rotate (float x, float y, float degrees) { float r = degrees * degRad, cos = cos(r), sin = sin(r); float dx = cx - x, dy = cy - y; @@ -103,23 +90,22 @@ public class PhysicsConstraint implements Constrained, Update { } /** Applies the constraint to the constrained bones. */ - public void update (Physics physics) { + public void update (Skeleton skeleton, Physics physics) { PhysicsConstraintPose pose = applied; float mix = pose.mix; if (mix == 0) return; boolean x = data.x > 0, y = data.y > 0, rotateOrShearX = data.rotate > 0 || data.shearX > 0, scaleX = data.scaleX > 0; - BoneApplied bone = this.bone; + BonePose bone = this.bone; float l = bone.bone.data.length; switch (physics) { case none: return; case reset: - reset(); + reset(skeleton); // Fall through. case update: - Skeleton skeleton = this.skeleton; float delta = Math.max(skeleton.time - lastTime, 0); remaining += delta; lastTime = skeleton.time; @@ -266,59 +252,18 @@ public class PhysicsConstraint implements Constrained, Update { tx = l * bone.a; ty = l * bone.c; } - bone.updateLocalTransform(); + bone.updateLocalTransform(skeleton); } - /** The physics constraint's setup pose data. */ - public PhysicsConstraintData getData () { - return data; - } - - public Skeleton getSkeleton () { - return skeleton; + public void sort () { } /** The bone constrained by this physics constraint. */ - public BoneApplied getBone () { + public BonePose getBone () { return bone; } - public void setBone (BoneApplied bone) { + public void setBone (BonePose bone) { this.bone = bone; } - - public PhysicsConstraintPose getPose () { - return pose; - } - - public PhysicsConstraintPose getAppliedPose () { - return applied; - } - - public PhysicsConstraintPose getConstrainedPose () { - return constrained; - } - - public void setConstrained (boolean constrained) { - applied = constrained ? this.constrained : pose; - } - - public void resetAppliedPose () { - applied.set(pose); - } - - /** Returns false when this constraint won't be updated by - * {@link Skeleton#updateWorldTransform(com.esotericsoftware.spine.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; - } - - public String toString () { - return data.name; - } } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PhysicsConstraintData.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PhysicsConstraintData.java index 9ee259251..97fc2a5ad 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PhysicsConstraintData.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PhysicsConstraintData.java @@ -32,18 +32,13 @@ package com.esotericsoftware.spine; /** Stores the setup pose for a {@link PhysicsConstraint}. *

* See Physics constraints in the Spine User Guide. */ -public class PhysicsConstraintData extends ConstraintData { - final PhysicsConstraintPose setup = new PhysicsConstraintPose(); +public class PhysicsConstraintData extends PosedData { BoneData bone; float x, y, rotate, scaleX, shearX, limit, step; boolean inertiaGlobal, strengthGlobal, dampingGlobal, massGlobal, windGlobal, gravityGlobal, mixGlobal; public PhysicsConstraintData (String name) { - super(name); - } - - public PhysicsConstraintPose getSetupPose () { - return setup; + super(name, new PhysicsConstraintPose()); } /** The bone constrained by this physics constraint. */ diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PhysicsConstraintPose.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PhysicsConstraintPose.java index 2bf89ecc4..9ec735b7b 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PhysicsConstraintPose.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PhysicsConstraintPose.java @@ -30,7 +30,7 @@ package com.esotericsoftware.spine; /** Stores a pose for a physics constraint. */ -public class PhysicsConstraintPose { +public class PhysicsConstraintPose implements Pose { float inertia, strength, damping, massInverse, wind, gravity, mix; public void set (PhysicsConstraintPose pose) { diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Pose.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Pose.java new file mode 100644 index 000000000..3f3c3ea3c --- /dev/null +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Pose.java @@ -0,0 +1,6 @@ + +package com.esotericsoftware.spine; + +public interface Pose

{ + public void set (P pose); +} diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Posed.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Posed.java new file mode 100644 index 000000000..9c47f317e --- /dev/null +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Posed.java @@ -0,0 +1,51 @@ + +package com.esotericsoftware.spine; + +abstract public class Posed, P extends Pose, A extends P> { + final D data; + final P pose; + final A constrained; + A applied; + + public Posed (D data, P pose, A constrained) { + if (data == null) throw new IllegalArgumentException("data cannot be null."); + this.data = data; + this.pose = pose; + this.constrained = constrained; + applied = (A)pose; + setupPose(); + } + + public void setupPose () { + pose.set(data.setup); + } + + /** The constraint's setup pose data. */ + public D getData () { + return data; + } + + public P getPose () { + return pose; + } + + public A getAppliedPose () { + return applied; + } + + public A getConstrainedPose () { + return constrained; + } + + public void setConstrained (boolean constrained) { + applied = constrained ? this.constrained : (A)pose; + } + + public void resetAppliedPose () { + applied.set(pose); + } + + public String toString () { + return data.name; + } +} diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PosedActive.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PosedActive.java new file mode 100644 index 000000000..fee548886 --- /dev/null +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PosedActive.java @@ -0,0 +1,21 @@ + +package com.esotericsoftware.spine; + +abstract public class PosedActive, P extends Pose, A extends P> extends Posed { + boolean active; + + public PosedActive (D data, P pose, A constrained) { + super(data, pose, constrained); + } + + /** Returns false when this constraint won't be updated by + * {@link Skeleton#updateWorldTransform(com.esotericsoftware.spine.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 PosedData#getSkinRequired() + * @see Skeleton#updateCache() */ + public boolean isActive () { + return active; + } +} diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/ConstraintData.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PosedData.java similarity index 89% rename from spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/ConstraintData.java rename to spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PosedData.java index a0d99caa3..f8f652d0c 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/ConstraintData.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PosedData.java @@ -29,15 +29,26 @@ package com.esotericsoftware.spine; -/** The base class for all constraint datas. */ -abstract public class ConstraintData { +/** The base class for all constrained datas. */ +abstract public class PosedData

{ final String name; - int order; + final P setup; boolean skinRequired; - public ConstraintData (String name) { + int order; // BOZO - Remove order. + + public int getOrder () { + return order; + } + + public void setOrder (int order) { + this.order = order; + } + + public PosedData (String name, P setup) { if (name == null) throw new IllegalArgumentException("name cannot be null."); this.name = name; + this.setup = setup; } /** The constraint's name, which is unique across all constraints in the skeleton of the same type. */ @@ -45,14 +56,8 @@ abstract public class ConstraintData { return name; } - /** The ordinal of this constraint for the order a skeleton's constraints will be applied by - * {@link Skeleton#updateWorldTransform(Physics)}. */ - public int getOrder () { - return order; - } - - public void setOrder (int order) { - this.order = order; + public P getSetupPose () { + return setup; } /** When true, {@link Skeleton#updateWorldTransform(Physics)} only updates this constraint if the {@link Skeleton#getSkin()} 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 795bcebd8..240f19bf5 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java @@ -64,7 +64,7 @@ public class Skeleton { final Array pathConstraints; final Array physicsConstraints; final Array updateCache = new Array(); - final Array resetCache = new Array(); + final Array resetCache = new Array(); @Null Skin skin; final Color color; float x, y, scaleX = 1, scaleY = 1, time; @@ -78,10 +78,10 @@ public class Skeleton { for (BoneData boneData : data.bones) { Bone bone; if (boneData.parent == null) - bone = new Bone(boneData, this, null); + bone = new Bone(boneData, null); else { var parent = (Bone)bones[boneData.parent.index]; - bone = new Bone(boneData, this, parent); + bone = new Bone(boneData, parent); parent.children.add(bone); } this.bones.add(bone); @@ -97,7 +97,7 @@ public class Skeleton { sliders = new Array(data.sliders.size); for (SliderData constraint : data.sliders) - sliders.add(new Slider(constraint, this)); + sliders.add(new Slider(constraint)); ikConstraints = new Array(data.ikConstraints.size); for (IkConstraintData constraint : data.ikConstraints) @@ -129,10 +129,10 @@ public class Skeleton { for (Bone bone : skeleton.bones) { Bone newBone; if (bone.parent == null) - newBone = new Bone(bone, this, null); + newBone = new Bone(bone, null); else { Bone parent = bones.get(bone.parent.data.index); - newBone = new Bone(bone, this, parent); + newBone = new Bone(bone, parent); parent.children.add(newBone); } bones.add(newBone); @@ -150,7 +150,7 @@ public class Skeleton { sliders = new Array(skeleton.sliders.size); for (Slider constraint : skeleton.sliders) - sliders.add(new Slider(constraint, skeleton)); + sliders.add(new Slider(constraint)); ikConstraints = new Array(skeleton.ikConstraints.size); for (IkConstraint constraint : skeleton.ikConstraints) @@ -276,7 +276,7 @@ public class Skeleton { sortBone(constraint.target); - Array constrained = constraint.bones; + Array constrained = constraint.bones; Bone parent = constrained.first().bone; sortBone(parent); resetCache(parent); @@ -306,14 +306,14 @@ public class Skeleton { int boneCount = constraint.bones.size; if (constraint.data.localSource) { for (int i = 0; i < boneCount; i++) { - Bone child = ((BoneApplied)constrained[i]).bone; + Bone child = ((BonePose)constrained[i]).bone; resetCache(child); sortBone(child.parent); sortBone(child); } } else { for (int i = 0; i < boneCount; i++) { - Bone bone = ((BoneApplied)constrained[i]).bone; + Bone bone = ((BonePose)constrained[i]).bone; resetCache(bone); sortBone(bone); } @@ -322,9 +322,9 @@ public class Skeleton { updateCache.add(constraint); for (int i = 0; i < boneCount; i++) - sortReset(((BoneApplied)constrained[i]).bone.children); + sortReset(((BonePose)constrained[i]).bone.children); for (int i = 0; i < boneCount; i++) - ((BoneApplied)constrained[i]).bone.sorted = true; + ((BonePose)constrained[i]).bone.sorted = true; } private void sortPathConstraint (PathConstraint constraint) { @@ -344,7 +344,7 @@ public class Skeleton { Object[] constrained = constraint.bones.items; int boneCount = constraint.bones.size; for (int i = 0; i < boneCount; i++) { - Bone bone = ((BoneApplied)constrained[i]).bone; + Bone bone = ((BonePose)constrained[i]).bone; resetCache(bone); sortBone(bone); } @@ -352,9 +352,9 @@ public class Skeleton { updateCache.add(constraint); for (int i = 0; i < boneCount; i++) - sortReset(((BoneApplied)constrained[i]).bone.children); + sortReset(((BonePose)constrained[i]).bone.children); for (int i = 0; i < boneCount; i++) - ((BoneApplied)constrained[i]).bone.sorted = true; + ((BonePose)constrained[i]).bone.sorted = true; } private void sortPathConstraintAttachment (Skin skin, int slotIndex, Bone slotBone) { @@ -423,7 +423,7 @@ public class Skeleton { if (timelines[i] instanceof BoneTimeline boneTimeline) sortBone((Bone)bones[boneTimeline.getBoneIndex()]); } - private void resetCache (Constrained object) { + private void resetCache (Posed object) { if (!resetCache.contains(object, true)) { resetCache.add(object); object.setConstrained(true); @@ -455,11 +455,11 @@ public class Skeleton { public void updateWorldTransform (Physics physics) { Object[] resetCache = this.resetCache.items; for (int i = 0, n = this.resetCache.size; i < n; i++) - ((Constrained)resetCache[i]).resetAppliedPose(); + ((Posed)resetCache[i]).resetAppliedPose(); Object[] updateCache = this.updateCache.items; for (int i = 0, n = this.updateCache.size; i < n; i++) - ((Update)updateCache[i]).update(physics); + ((Update)updateCache[i]).update(this, physics); } /** Temporarily sets the root bone as a child of the specified bone, then updates the world transform for each bone and applies @@ -467,15 +467,15 @@ public class Skeleton { *

* See World transforms in the Spine * Runtimes Guide. */ - public void updateWorldTransform (Physics physics, BoneApplied parent) { + public void updateWorldTransform (Physics physics, BonePose parent) { if (parent == null) throw new IllegalArgumentException("parent cannot be null."); Object[] resetCache = this.resetCache.items; for (int i = 0, n = this.resetCache.size; i < n; i++) - ((Constrained)resetCache[i]).resetAppliedPose(); + ((Posed)resetCache[i]).resetAppliedPose(); // Apply the parent bone transform to the root bone. The root bone always inherits scale, rotation and reflection. - BoneApplied rootBone = getRootBone().applied; + BonePose rootBone = getRootBone().applied; float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d; rootBone.worldX = pa * x + pb * y + parent.worldX; rootBone.worldY = pc * x + pd * y + parent.worldY; @@ -495,7 +495,7 @@ public class Skeleton { Object[] updateCache = this.updateCache.items; for (int i = 0, n = this.updateCache.size; i < n; i++) { var updatable = (Update)updateCache[i]; - if (updatable != rootBone) updatable.update(physics); + if (updatable != rootBone) updatable.update(this, physics); } } @@ -804,10 +804,10 @@ public class Skeleton { } else if (attachment instanceof MeshAttachment mesh) { verticesLength = mesh.getWorldVerticesLength(); vertices = temp.setSize(verticesLength); - mesh.computeWorldVertices(slot, 0, verticesLength, vertices, 0, 2); + mesh.computeWorldVertices(this, slot, 0, verticesLength, vertices, 0, 2); triangles = mesh.getTriangles(); } else if (attachment instanceof ClippingAttachment clip && clipper != null) { - clipper.clipStart(slot, clip); + clipper.clipStart(this, slot, clip); continue; } if (vertices != null) { 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 bf055ba6b..635041d3a 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java @@ -217,7 +217,7 @@ 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); - BonePose setup = data.setup; + BoneLocal setup = data.setup; setup.rotation = input.readFloat(); setup.x = input.readFloat() * scale; setup.y = input.readFloat() * scale; @@ -493,16 +493,16 @@ public class SkeletonBinary extends SkeletonLoader { items = skeletonData.ikConstraints.items; for (int i = 0, n = input.readInt(true); i < n; i++) - skin.constraints.add((ConstraintData)items[input.readInt(true)]); + skin.constraints.add((PosedData)items[input.readInt(true)]); items = skeletonData.transformConstraints.items; for (int i = 0, n = input.readInt(true); i < n; i++) - skin.constraints.add((ConstraintData)items[input.readInt(true)]); + skin.constraints.add((PosedData)items[input.readInt(true)]); items = skeletonData.pathConstraints.items; for (int i = 0, n = input.readInt(true); i < n; i++) - skin.constraints.add((ConstraintData)items[input.readInt(true)]); + skin.constraints.add((PosedData)items[input.readInt(true)]); items = skeletonData.physicsConstraints.items; for (int i = 0, n = input.readInt(true); i < n; i++) - skin.constraints.add((ConstraintData)items[input.readInt(true)]); + skin.constraints.add((PosedData)items[input.readInt(true)]); skin.constraints.shrink(); slotCount = input.readInt(true); diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBounds.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBounds.java index c382b3cb0..d014f4707 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBounds.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBounds.java @@ -73,7 +73,7 @@ public class SkeletonBounds { FloatArray polygon = polygonPool.obtain(); polygons.add(polygon); - boundingBox.computeWorldVertices(slot, 0, boundingBox.getWorldVerticesLength(), + boundingBox.computeWorldVertices(skeleton, slot, 0, boundingBox.getWorldVerticesLength(), polygon.setSize(boundingBox.getWorldVerticesLength()), 0, 2); } } 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 ca14c5e06..eb0b195a1 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java @@ -174,7 +174,7 @@ public class SkeletonJson extends SkeletonLoader { } var data = new BoneData(skeletonData.bones.size, boneMap.getString("name"), parent); data.length = boneMap.getFloat("length", 0) * scale; - BonePose setup = data.setup; + BoneLocal setup = data.setup; setup.x = boneMap.getFloat("x", 0) * scale; setup.y = boneMap.getFloat("y", 0) * scale; setup.rotation = boneMap.getFloat("rotation", 0); diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java index 4c5c73143..a335d2699 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java @@ -165,14 +165,14 @@ public class SkeletonRenderer { int count = mesh.getWorldVerticesLength(); verticesLength = (count >> 1) * 5; vertices = this.vertices.setSize(verticesLength); - mesh.computeWorldVertices(slot, 0, count, vertices, 0, 5); + mesh.computeWorldVertices(skeleton, slot, 0, count, vertices, 0, 5); triangles = mesh.getTriangles(); texture = mesh.getRegion().getTexture(); uvs = mesh.getUVs(); color = mesh.getColor(); } else if (attachment instanceof ClippingAttachment clip) { - clipper.clipStart(slot, clip); + clipper.clipStart(skeleton, slot, clip); continue; } else if (attachment instanceof SkeletonAttachment skeletonAttachment) { @@ -260,14 +260,14 @@ public class SkeletonRenderer { int count = mesh.getWorldVerticesLength(); verticesLength = count * 3; vertices = this.vertices.setSize(verticesLength); - mesh.computeWorldVertices(slot, 0, count, vertices, 0, 6); + mesh.computeWorldVertices(skeleton, slot, 0, count, vertices, 0, 6); triangles = mesh.getTriangles(); texture = mesh.getRegion().getTexture(); uvs = mesh.getUVs(); color = mesh.getColor(); } else if (attachment instanceof ClippingAttachment clip) { - clipper.clipStart(slot, clip); + clipper.clipStart(skeleton, slot, clip); continue; } else if (attachment instanceof SkeletonAttachment skeletonAttachment) { diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRendererDebug.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRendererDebug.java index 5b345a9e2..b9ae5743b 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRendererDebug.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRendererDebug.java @@ -95,7 +95,7 @@ public class SkeletonRendererDebug { shapes.setColor(boneOriginColor); } else shapes.setColor(boneLineColor); - BoneApplied applied = bone.applied; + BonePose applied = bone.applied; float x = length * applied.a + applied.worldX; float y = length * applied.c + applied.worldY; shapes.rectLine(applied.worldX, applied.worldY, x, y, width * scale); @@ -140,7 +140,7 @@ public class SkeletonRendererDebug { if (!slot.bone.active) continue; if (!(slot.pose.attachment instanceof MeshAttachment mesh)) continue; float[] vertices = this.vertices.setSize(mesh.getWorldVerticesLength()); - mesh.computeWorldVertices(slot, 0, mesh.getWorldVerticesLength(), vertices, 0, 2); + mesh.computeWorldVertices(skeleton, slot, 0, mesh.getWorldVerticesLength(), vertices, 0, 2); short[] triangles = mesh.getTriangles(); int hullLength = mesh.getHullLength(); if (drawMeshTriangles) { @@ -187,7 +187,7 @@ public class SkeletonRendererDebug { if (!(slot.pose.attachment instanceof ClippingAttachment clip)) continue; int nn = clip.getWorldVerticesLength(); float[] vertices = this.vertices.setSize(nn); - clip.computeWorldVertices(slot, 0, nn, vertices, 0, 2); + clip.computeWorldVertices(skeleton, slot, 0, nn, vertices, 0, 2); shapes.setColor(clip.getColor()); for (int ii = 2; ii < nn; ii += 2) shapes.line(vertices[ii - 2], vertices[ii - 1], vertices[ii], vertices[ii + 1]); @@ -202,7 +202,7 @@ public class SkeletonRendererDebug { if (!(slot.pose.attachment instanceof PathAttachment path)) continue; int nn = path.getWorldVerticesLength(); float[] vertices = this.vertices.setSize(nn); - path.computeWorldVertices(slot, 0, nn, vertices, 0, 2); + path.computeWorldVertices(skeleton, slot, 0, nn, vertices, 0, 2); Color color = path.getColor(); float x1 = vertices[2], y1 = vertices[3], x2 = 0, y2 = 0; if (path.getClosed()) { diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skin.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skin.java index a4c6f2d49..6246155ca 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skin.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skin.java @@ -45,7 +45,7 @@ public class Skin { final String name; final OrderedSet attachments = new OrderedSet(); final Array bones = new Array(0); - final Array constraints = new Array(0); + final Array constraints = new Array(0); private final SkinEntry lookup = new SkinEntry(0, "", null); // Nonessential. @@ -71,7 +71,7 @@ public class Skin { for (BoneData data : skin.bones) if (!bones.contains(data, true)) bones.add(data); - for (ConstraintData data : skin.constraints) + for (PosedData data : skin.constraints) if (!constraints.contains(data, true)) constraints.add(data); for (SkinEntry entry : skin.attachments.orderedItems()) @@ -86,7 +86,7 @@ public class Skin { for (BoneData data : skin.bones) if (!bones.contains(data, true)) bones.add(data); - for (ConstraintData data : skin.constraints) + for (PosedData data : skin.constraints) if (!constraints.contains(data, true)) constraints.add(data); for (SkinEntry entry : skin.attachments.orderedItems()) { @@ -134,7 +134,7 @@ public class Skin { return bones; } - public Array getConstraints () { + public Array getConstraints () { return constraints; } 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 f3ca5dd2b..6040b234e 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Slider.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Slider.java @@ -35,70 +35,22 @@ import com.esotericsoftware.spine.Animation.MixDirection; /** Stores the setup pose for a {@link PhysicsConstraint}. *

* See Physics constraints in the Spine User Guide. */ -public class Slider implements Constrained, Update { - final SliderData data; - final Skeleton skeleton; - final SliderPose pose = new SliderPose(), constrained = new SliderPose(); - SliderPose applied = pose; - - boolean active; - - public Slider (SliderData data, Skeleton skeleton) { - if (data == null) throw new IllegalArgumentException("data cannot be null."); - if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null."); - this.data = data; - this.skeleton = skeleton; - - setupPose(); +public class Slider extends Constraint { + public Slider (SliderData data) { + super(data, new SliderPose(), new SliderPose()); } /** Copy constructor. */ - public Slider (Slider slider, Skeleton skeleton) { - this(slider.data, skeleton); + public Slider (Slider slider) { + this(slider.data); pose.set(slider.pose); } - public void update (Physics physics) { + public void update (Skeleton skeleton, Physics physics) { SliderPose pose = applied; data.animation.apply(skeleton, pose.time, pose.time, false, null, pose.mix, MixBlend.replace, MixDirection.in, true); } - public void setupPose () { - pose.set(data.setup); - } - - public SliderPose getPose () { - return pose; - } - - public SliderPose getAppliedPose () { - return applied; - } - - public SliderPose getConstrainedPose () { - return constrained; - } - - public void setConstrained (boolean constrained) { - applied = constrained ? this.constrained : pose; - } - - public void resetAppliedPose () { - applied.set(pose); - } - - /** Returns false when this constraint won't be updated by - * {@link Skeleton#updateWorldTransform(com.esotericsoftware.spine.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; - } - - public String toString () { - return data.name; + public void sort () { } } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SliderData.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SliderData.java index 38f91740b..199fe0ade 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SliderData.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SliderData.java @@ -32,16 +32,11 @@ package com.esotericsoftware.spine; /** Stores the setup pose for a {@link PhysicsConstraint}. *

* See Physics constraints in the Spine User Guide. */ -public class SliderData extends ConstraintData { - final SliderPose setup = new SliderPose(); +public class SliderData extends PosedData { Animation animation; public SliderData (String name) { - super(name); - } - - public SliderPose getSetupPose () { - return setup; + super(name, new SliderPose()); } public Animation getAnimation () { diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SliderPose.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SliderPose.java index 039029584..6fe29820d 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SliderPose.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SliderPose.java @@ -30,7 +30,7 @@ package com.esotericsoftware.spine; /** Stores a pose for a slider. */ -public class SliderPose { +public class SliderPose implements Pose { float time, mix; public void set (SliderPose pose) { 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 168a40236..caea1f964 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Slot.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Slot.java @@ -34,17 +34,13 @@ import com.badlogic.gdx.graphics.Color; /** Stores a slot's current pose. Slots organize attachments for {@link Skeleton#drawOrder} purposes and provide a place to store * state for an attachment. State cannot be stored in an attachment itself because attachments are stateless and may be shared * across multiple skeletons. */ -public class Slot implements Constrained { - final SlotData data; +public class Slot extends Posed { final Bone bone; - final SlotPose pose = new SlotPose(), constrained = new SlotPose(); - SlotPose applied = pose; int attachmentState; public Slot (SlotData data, Skeleton skeleton) { - if (data == null) throw new IllegalArgumentException("slot cannot be null."); + super(data, new SlotPose(), new SlotPose()); if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null."); - this.data = data; bone = skeleton.bones.get(data.boneData.index); if (data.setup.darkColor != null) { pose.darkColor = new Color(); @@ -55,9 +51,7 @@ public class Slot implements Constrained { /** Copy constructor. */ public Slot (Slot slot, Bone bone) { - if (slot == null) throw new IllegalArgumentException("slot cannot be null."); - if (bone == null) throw new IllegalArgumentException("bone cannot be null."); - data = slot.data; + super(slot.data, new SlotPose(), new SlotPose()); this.bone = bone; if (data.setup.darkColor != null) { pose.darkColor = new Color(); @@ -66,48 +60,8 @@ public class Slot implements Constrained { pose.set(slot.pose); } - /** Sets this slot to the setup pose. */ - public void setupPose () { - pose.set(data.setup); - if (data.attachmentName != null) pose.setAttachment(bone.skeleton.getAttachment(data.index, data.attachmentName)); - } - - /** The slot's setup pose data. */ - public SlotData getData () { - return data; - } - - public SlotPose getPose () { - return pose; - } - - public SlotPose getAppliedPose () { - return applied; - } - - public SlotPose getConstrainedPose () { - return constrained; - } - - public void setConstrained (boolean constrained) { - applied = constrained ? this.constrained : pose; - } - - public void resetAppliedPose () { - applied.set(pose); - } - /** The bone this slot belongs to. */ public Bone getBone () { return bone; } - - /** The skeleton this slot belongs to. */ - public Skeleton getSkeleton () { - return bone.skeleton; - } - - public String toString () { - return data.name; - } } 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 4cb2bfb5b..b6484c9b5 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SlotData.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SlotData.java @@ -32,11 +32,9 @@ package com.esotericsoftware.spine; import com.badlogic.gdx.utils.Null; /** Stores the setup pose for a {@link Slot}. */ -public class SlotData { +public class SlotData extends PosedData { final int index; - final String name; final BoneData boneData; - final SlotPose setup = new SlotPose(); @Null String attachmentName; BlendMode blendMode; @@ -44,11 +42,10 @@ public class SlotData { boolean visible = true; public SlotData (int index, String name, BoneData boneData) { + super(name, new SlotPose()); if (index < 0) throw new IllegalArgumentException("index must be >= 0."); - if (name == null) throw new IllegalArgumentException("name cannot be null."); if (boneData == null) throw new IllegalArgumentException("boneData cannot be null."); this.index = index; - this.name = name; this.boneData = boneData; } @@ -57,20 +54,11 @@ public class SlotData { return index; } - /** The name of the slot, which is unique across all slots in the skeleton. */ - public String getName () { - return name; - } - /** The bone this slot belongs to. */ public BoneData getBoneData () { return boneData; } - public SlotPose getSetupPose () { - return setup; - } - public void setAttachmentName (@Null String attachmentName) { this.attachmentName = attachmentName; } @@ -98,8 +86,4 @@ public class SlotData { public void setVisible (boolean visible) { this.visible = visible; } - - public String toString () { - return name; - } } 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 1044bec40..c2f83fcf8 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SlotPose.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SlotPose.java @@ -41,7 +41,7 @@ import com.esotericsoftware.spine.attachments.VertexAttachment; /** Stores a slot's pose. Slots organize attachments for {@link Skeleton#drawOrder} purposes and provide a place to store state * for an attachment. State cannot be stored in an attachment itself because attachments are stateless and may be shared across * multiple skeletons. */ -public class SlotPose { +public class SlotPose implements Pose { final Color color = new Color(); @Null Color darkColor; @Null Attachment attachment; // Not used in setup pose. @@ -104,7 +104,8 @@ public class SlotPose { /** Values to deform the slot's attachment. For an unweighted mesh, the entries are local positions for each vertex. For a * weighted mesh, the entries are an offset for each vertex which will be added to the mesh's local vertex positions. *

- * See {@link VertexAttachment#computeWorldVertices(Slot, int, int, float[], int, int)} and {@link DeformTimeline}. */ + * See {@link VertexAttachment#computeWorldVertices(Skeleton, Slot, int, int, float[], int, int)} and + * {@link DeformTimeline}. */ public FloatArray getDeform () { return deform; } 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 54cb02596..f4a9d5b05 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraint.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraint.java @@ -40,32 +40,19 @@ import com.esotericsoftware.spine.TransformConstraintData.ToProperty; * bones to match that of the source bone. *

* See Transform constraints in the Spine User Guide. */ -public class TransformConstraint implements Constrained, Update { - final TransformConstraintData data; - final Array bones; +public class TransformConstraint extends Constraint { + final Array bones; Bone source; - final TransformConstraintPose pose = new TransformConstraintPose(), constrained = new TransformConstraintPose(); - TransformConstraintPose applied = pose; - boolean active; - - public TransformConstraint (TransformConstraintData data, Array bones, Bone source) { - this.data = data; - this.bones = bones; - this.source = source; - } public TransformConstraint (TransformConstraintData data, Skeleton skeleton) { - if (data == null) throw new IllegalArgumentException("data cannot be null."); + super(data, new TransformConstraintPose(), new TransformConstraintPose()); if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null."); - this.data = data; bones = new Array(data.bones.size); for (BoneData boneData : data.bones) bones.add(skeleton.bones.get(boneData.index).constrained); source = skeleton.bones.get(data.source.index); - - setupPose(); } /** Copy constructor. */ @@ -74,24 +61,20 @@ public class TransformConstraint implements Constrained, Update { pose.set(constraint.pose); } - public void setupPose () { - pose.set(data.setup); - } - /** Applies the constraint to the constrained bones. */ - public void update (Physics physics) { + public void update (Skeleton skeleton, Physics physics) { TransformConstraintPose pose = applied; if (pose.mixRotate == 0 && pose.mixX == 0 && pose.mixY == 0 && pose.mixScaleX == 0 && pose.mixScaleY == 0 && pose.mixShearY == 0) return; TransformConstraintData data = this.data; boolean localFrom = data.localSource, localTarget = data.localTarget, additive = data.additive, clamp = data.clamp; - BoneApplied source = this.source.applied; + BonePose source = this.source.applied; Object[] fromItems = data.properties.items; int fn = data.properties.size; Object[] bones = this.bones.items; for (int i = 0, n = this.bones.size; i < n; i++) { - var bone = (BoneApplied)bones[i]; + var bone = (BonePose)bones[i]; if (bone.bone.applied != bone.bone.constrained) System.out.println(); for (int f = 0; f < fn; f++) { var from = (FromProperty)fromItems[f]; @@ -112,14 +95,17 @@ public class TransformConstraint implements Constrained, Update { } } if (localTarget) - bone.update(null); + bone.update(skeleton, null); else - bone.updateLocalTransform(); + bone.updateLocalTransform(skeleton); } } + public void sort () { + } + /** The bones that will be modified by this transform constraint. */ - public Array getBones () { + public Array getBones () { return bones; } @@ -132,44 +118,4 @@ public class TransformConstraint implements Constrained, Update { if (source == null) throw new IllegalArgumentException("source cannot be null."); this.source = source; } - - public TransformConstraintPose getPose () { - return pose; - } - - public TransformConstraintPose getAppliedPose () { - return applied; - } - - public TransformConstraintPose getConstrainedPose () { - return constrained; - } - - public void setConstrained (boolean constrained) { - applied = constrained ? this.constrained : pose; - } - - public void resetAppliedPose () { - applied.set(pose); - } - - /** Returns false when this constraint won't be updated by - * {@link Skeleton#updateWorldTransform(com.esotericsoftware.spine.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; - } - - /** The transform constraint's setup pose data. */ - public TransformConstraintData getData () { - return data; - } - - public String toString () { - return data.name; - } } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraintData.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraintData.java index 547ceb699..c1ee72781 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraintData.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraintData.java @@ -36,8 +36,7 @@ import com.badlogic.gdx.utils.Array; /** Stores the setup pose for a {@link TransformConstraint}. *

* See Transform constraints in the Spine User Guide. */ -public class TransformConstraintData extends ConstraintData { - final TransformConstraintPose setup = new TransformConstraintPose(); +public class TransformConstraintData extends PosedData { final Array bones = new Array(); BoneData source; float offsetRotation, offsetX, offsetY, offsetScaleX, offsetScaleY, offsetShearY; @@ -45,11 +44,7 @@ public class TransformConstraintData extends ConstraintData { final Array properties = new Array(); public TransformConstraintData (String name) { - super(name); - } - - public TransformConstraintPose getSetupPose () { - return setup; + super(name, new TransformConstraintPose()); } /** The bones that will be modified by this transform constraint. */ @@ -171,7 +166,7 @@ public class TransformConstraintData extends ConstraintData { public final Array to = new Array(); /** Reads this property from the specified bone. */ - abstract public float value (TransformConstraintData data, BoneApplied source, boolean local); + abstract public float value (TransformConstraintData data, BonePose source, boolean local); } /** Constrained property for a {@link TransformConstraint}. */ @@ -189,11 +184,11 @@ public class TransformConstraintData extends ConstraintData { abstract public float mix (TransformConstraintPose pose); /** Applies the value to this property. */ - abstract public void apply (TransformConstraintPose pose, BoneApplied bone, float value, boolean local, boolean additive); + abstract public void apply (TransformConstraintPose pose, BonePose bone, float value, boolean local, boolean additive); } static public class FromRotate extends FromProperty { - public float value (TransformConstraintData data, BoneApplied source, boolean local) { + public float value (TransformConstraintData data, BonePose source, boolean local) { if (local) return source.rotation + data.offsetRotation; float value = atan2(source.c, source.a) * radDeg + (source.a * source.d - source.b * source.c > 0 ? data.offsetRotation : -data.offsetRotation); @@ -207,7 +202,7 @@ public class TransformConstraintData extends ConstraintData { return pose.mixRotate; } - public void apply (TransformConstraintPose pose, BoneApplied bone, float value, boolean local, boolean additive) { + public void apply (TransformConstraintPose pose, BonePose bone, float value, boolean local, boolean additive) { if (local) { if (!additive) value -= bone.rotation; bone.rotation += value * pose.mixRotate; @@ -230,7 +225,7 @@ public class TransformConstraintData extends ConstraintData { } static public class FromX extends FromProperty { - public float value (TransformConstraintData data, BoneApplied source, boolean local) { + public float value (TransformConstraintData data, BonePose source, boolean local) { return local ? source.x + data.offsetX : data.offsetX * source.a + data.offsetY * source.b + source.worldX; } } @@ -240,7 +235,7 @@ public class TransformConstraintData extends ConstraintData { return pose.mixX; } - public void apply (TransformConstraintPose pose, BoneApplied bone, float value, boolean local, boolean additive) { + public void apply (TransformConstraintPose pose, BonePose bone, float value, boolean local, boolean additive) { if (local) { if (!additive) value -= bone.x; bone.x += value * pose.mixX; @@ -252,7 +247,7 @@ public class TransformConstraintData extends ConstraintData { } static public class FromY extends FromProperty { - public float value (TransformConstraintData data, BoneApplied source, boolean local) { + public float value (TransformConstraintData data, BonePose source, boolean local) { return local ? source.y + data.offsetY : data.offsetX * source.c + data.offsetY * source.d + source.worldY; } } @@ -262,7 +257,7 @@ public class TransformConstraintData extends ConstraintData { return pose.mixY; } - public void apply (TransformConstraintPose pose, BoneApplied bone, float value, boolean local, boolean additive) { + public void apply (TransformConstraintPose pose, BonePose bone, float value, boolean local, boolean additive) { if (local) { if (!additive) value -= bone.y; bone.y += value * pose.mixY; @@ -274,7 +269,7 @@ public class TransformConstraintData extends ConstraintData { } static public class FromScaleX extends FromProperty { - public float value (TransformConstraintData data, BoneApplied source, boolean local) { + public float value (TransformConstraintData data, BonePose source, boolean local) { return (local ? source.scaleX : (float)Math.sqrt(source.a * source.a + source.c * source.c)) + data.offsetScaleX; } } @@ -284,7 +279,7 @@ public class TransformConstraintData extends ConstraintData { return pose.mixScaleX; } - public void apply (TransformConstraintPose pose, BoneApplied bone, float value, boolean local, boolean additive) { + public void apply (TransformConstraintPose pose, BonePose bone, float value, boolean local, boolean additive) { if (local) { if (additive) bone.scaleX *= 1 + ((value - 1) * pose.mixScaleX); @@ -305,7 +300,7 @@ public class TransformConstraintData extends ConstraintData { } static public class FromScaleY extends FromProperty { - public float value (TransformConstraintData data, BoneApplied source, boolean local) { + public float value (TransformConstraintData data, BonePose source, boolean local) { return (local ? source.scaleY : (float)Math.sqrt(source.b * source.b + source.d * source.d)) + data.offsetScaleY; } } @@ -315,7 +310,7 @@ public class TransformConstraintData extends ConstraintData { return pose.mixScaleY; } - public void apply (TransformConstraintPose pose, BoneApplied bone, float value, boolean local, boolean additive) { + public void apply (TransformConstraintPose pose, BonePose bone, float value, boolean local, boolean additive) { if (local) { if (additive) bone.scaleY *= 1 + ((value - 1) * pose.mixScaleY); @@ -336,7 +331,7 @@ public class TransformConstraintData extends ConstraintData { } static public class FromShearY extends FromProperty { - public float value (TransformConstraintData data, BoneApplied source, boolean local) { + public float value (TransformConstraintData data, BonePose source, boolean local) { return (local ? source.shearY : (atan2(source.d, source.b) - atan2(source.c, source.a)) * radDeg - 90) + data.offsetShearY; } @@ -347,7 +342,7 @@ public class TransformConstraintData extends ConstraintData { return pose.mixShearY; } - public void apply (TransformConstraintPose pose, BoneApplied bone, float value, boolean local, boolean additive) { + public void apply (TransformConstraintPose pose, BonePose bone, float value, boolean local, boolean additive) { if (local) { if (!additive) value -= bone.shearY; bone.shearY += value * pose.mixShearY; diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraintPose.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraintPose.java index 5d234addb..8bf7cd603 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraintPose.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraintPose.java @@ -30,7 +30,7 @@ package com.esotericsoftware.spine; /** Stores a pose for a transform constraint. */ -public class TransformConstraintPose { +public class TransformConstraintPose implements Pose { float mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY; public void set (TransformConstraintPose pose) { diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Update.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Update.java index f2e933e3a..85c291e4c 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Update.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Update.java @@ -32,5 +32,5 @@ package com.esotericsoftware.spine; /** The interface for items updated by {@link Skeleton#updateWorldTransform(Physics)}. */ public interface Update { /** @param physics Determines how physics and other non-deterministic updates are applied. */ - public void update (Physics physics); + public void update (Skeleton skeleton, Physics physics); } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/MeshAttachment.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/MeshAttachment.java index dda206ff0..3b9e583f7 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/MeshAttachment.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/MeshAttachment.java @@ -36,6 +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.Skeleton; import com.esotericsoftware.spine.Slot; /** An attachment that displays a textured mesh. A mesh has hull vertices and internal vertices within the hull. Holes are not @@ -164,9 +165,10 @@ public class MeshAttachment extends VertexAttachment implements HasTextureRegion } /** If the attachment has a {@link #sequence}, the region may be changed. */ - public void computeWorldVertices (Slot slot, int start, int count, float[] worldVertices, int offset, int stride) { + public void computeWorldVertices (Skeleton skeleton, Slot slot, int start, int count, float[] worldVertices, int offset, + int stride) { if (sequence != null) sequence.apply(slot.getAppliedPose(), this); - super.computeWorldVertices(slot, start, count, worldVertices, offset, stride); + super.computeWorldVertices(skeleton, slot, start, count, worldVertices, offset, stride); } /** Triplets of vertex indices which describe the mesh's triangulation. */ diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/PointAttachment.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/PointAttachment.java index adaf3d05c..2bdc36138 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/PointAttachment.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/PointAttachment.java @@ -34,7 +34,7 @@ import static com.esotericsoftware.spine.utils.SpineUtils.*; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.math.Vector2; -import com.esotericsoftware.spine.BoneApplied; +import com.esotericsoftware.spine.BonePose; /** An attachment which is a single point and a rotation. This can be used to spawn projectiles, particles, etc. A bone can be * used in similar ways, but a PointAttachment is slightly less expensive to compute and can be hidden, shown, and placed in a @@ -90,13 +90,13 @@ public class PointAttachment extends Attachment { return color; } - public Vector2 computeWorldPosition (BoneApplied bone, Vector2 point) { + public Vector2 computeWorldPosition (BonePose bone, Vector2 point) { point.x = x * bone.getA() + y * bone.getB() + bone.getWorldX(); point.y = x * bone.getC() + y * bone.getD() + bone.getWorldY(); return point; } - public float computeWorldRotation (BoneApplied bone) { + public float computeWorldRotation (BonePose bone) { float r = rotation * degRad, cos = cos(r), sin = sin(r); float x = cos * bone.getA() + sin * bone.getB(); float y = cos * bone.getC() + sin * bone.getD(); 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 6bf813813..d48e37907 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.BoneApplied; +import com.esotericsoftware.spine.BonePose; import com.esotericsoftware.spine.Slot; /** An attachment that displays a textured quadrilateral. @@ -179,7 +179,7 @@ public class RegionAttachment extends Attachment implements HasTextureRegion { if (sequence != null) sequence.apply(slot.getAppliedPose(), this); float[] vertexOffset = this.offset; - BoneApplied bone = slot.getBone().getAppliedPose(); + BonePose bone = slot.getBone().getAppliedPose(); float x = bone.getWorldX(), y = bone.getWorldY(); float a = bone.getA(), b = bone.getB(), c = bone.getC(), d = bone.getD(); float offsetX, offsetY; 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 1789cf1ca..49440254f 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 @@ -35,7 +35,7 @@ import com.badlogic.gdx.utils.FloatArray; import com.badlogic.gdx.utils.Null; import com.esotericsoftware.spine.Bone; -import com.esotericsoftware.spine.BoneApplied; +import com.esotericsoftware.spine.BonePose; import com.esotericsoftware.spine.Skeleton; import com.esotericsoftware.spine.Slot; import com.esotericsoftware.spine.SlotPose; @@ -86,14 +86,16 @@ abstract public class VertexAttachment extends Attachment { * stride / 2. * @param offset The worldVertices index to begin writing values. * @param stride The number of worldVertices entries between the value pairs written. */ - public void computeWorldVertices (Slot slot, int start, int count, float[] worldVertices, int offset, int stride) { + public void computeWorldVertices (Skeleton skeleton, Slot slot, int start, int count, float[] worldVertices, int offset, + int stride) { + count = offset + (count >> 1) * stride; FloatArray deformArray = slot.getAppliedPose().getDeform(); float[] vertices = this.vertices; int[] bones = this.bones; if (bones == null) { if (deformArray.size > 0) vertices = deformArray.items; - BoneApplied bone = slot.getBone().getAppliedPose(); + BonePose bone = slot.getBone().getAppliedPose(); float x = bone.getWorldX(), y = bone.getWorldY(); float a = bone.getA(), b = bone.getB(), c = bone.getC(), d = bone.getD(); for (int v = start, w = offset; w < count; v += 2, w += stride) { @@ -109,14 +111,14 @@ abstract public class VertexAttachment extends Attachment { v += n + 1; skip += n; } - Object[] skeletonBones = slot.getSkeleton().getBones().items; + Object[] skeletonBones = skeleton.getBones().items; if (deformArray.size == 0) { for (int w = offset, b = skip * 3; w < count; w += stride) { float wx = 0, wy = 0; int n = bones[v++]; n += v; for (; v < n; v++, b += 3) { - BoneApplied bone = ((Bone)skeletonBones[bones[v]]).getAppliedPose(); + BonePose bone = ((Bone)skeletonBones[bones[v]]).getAppliedPose(); float vx = vertices[b], vy = vertices[b + 1], weight = vertices[b + 2]; wx += (vx * bone.getA() + vy * bone.getB() + bone.getWorldX()) * weight; wy += (vx * bone.getC() + vy * bone.getD() + bone.getWorldY()) * weight; @@ -131,7 +133,7 @@ abstract public class VertexAttachment extends Attachment { int n = bones[v++]; n += v; for (; v < n; v++, b += 3, f += 2) { - BoneApplied bone = ((Bone)skeletonBones[bones[v]]).getAppliedPose(); + BonePose bone = ((Bone)skeletonBones[bones[v]]).getAppliedPose(); float vx = vertices[b] + deform[f], vy = vertices[b + 1] + deform[f + 1], weight = vertices[b + 2]; wx += (vx * bone.getA() + vy * bone.getB() + bone.getWorldX()) * weight; wy += (vx * bone.getC() + vy * bone.getD() + bone.getWorldY()) * weight; @@ -166,7 +168,7 @@ abstract public class VertexAttachment extends Attachment { } /** The maximum number of world vertex values that can be output by - * {@link #computeWorldVertices(Slot, int, int, float[], int, int)} using the count parameter. */ + * {@link #computeWorldVertices(Skeleton, Slot, int, int, float[], int, int)} using the count parameter. */ public int getWorldVerticesLength () { return worldVerticesLength; } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/SkeletonClipping.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/SkeletonClipping.java index b88a788ac..07a59c648 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/SkeletonClipping.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/SkeletonClipping.java @@ -33,6 +33,7 @@ import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.FloatArray; import com.badlogic.gdx.utils.ShortArray; +import com.esotericsoftware.spine.Skeleton; import com.esotericsoftware.spine.Slot; import com.esotericsoftware.spine.attachments.ClippingAttachment; @@ -48,14 +49,14 @@ public class SkeletonClipping { private ClippingAttachment clipAttachment; private Array clippingPolygons; - public void clipStart (Slot slot, ClippingAttachment clip) { + public void clipStart (Skeleton skeleton, Slot slot, ClippingAttachment clip) { if (clipAttachment != null) return; int n = clip.getWorldVerticesLength(); if (n < 6) return; clipAttachment = clip; float[] vertices = clippingPolygon.setSize(n); - clip.computeWorldVertices(slot, 0, n, vertices, 0, 2); + clip.computeWorldVertices(skeleton, slot, 0, n, vertices, 0, 2); makeClockwise(clippingPolygon); ShortArray triangles = triangulator.triangulate(clippingPolygon); clippingPolygons = triangulator.decompose(clippingPolygon, triangles);