[libgdx] Removed BoneLocal, use BonePose.

This commit is contained in:
Nathan Sweet 2026-03-24 17:58:37 -04:00
parent f79a7f7e76
commit dffbc043f1
14 changed files with 152 additions and 240 deletions

View File

@ -17,7 +17,6 @@ import com.esotericsoftware.spine.AnimationState.TrackEntry;
import com.esotericsoftware.spine.AnimationStateData;
import com.esotericsoftware.spine.Bone;
import com.esotericsoftware.spine.BoneData;
import com.esotericsoftware.spine.BoneLocal;
import com.esotericsoftware.spine.BonePose;
import com.esotericsoftware.spine.Constraint;
import com.esotericsoftware.spine.ConstraintData;
@ -1999,7 +1998,7 @@ public class SkeletonSerializer {
writeBoneData(obj.getData());
json.writeName("pose");
writeBoneLocal(obj.getPose());
writeBonePose(obj.getPose());
json.writeName("appliedPose");
writeBonePose(obj.getAppliedPose());
@ -2047,7 +2046,7 @@ public class SkeletonSerializer {
json.writeValue(obj.getName());
json.writeName("setupPose");
writeBoneLocal(obj.getSetupPose());
writeBonePose(obj.getSetupPose());
json.writeName("skinRequired");
json.writeValue(obj.getSkinRequired());
@ -2055,47 +2054,6 @@ public class SkeletonSerializer {
json.writeObjectEnd();
}
private void writeBoneLocal (BoneLocal obj) {
if (visitedObjects.containsKey(obj)) {
json.writeValue(visitedObjects.get(obj));
return;
}
String refString = "<BoneLocal-" + (nextId++) + ">";
visitedObjects.put(obj, refString);
json.writeObjectStart();
json.writeName("refString");
json.writeValue(refString);
json.writeName("type");
json.writeValue("BoneLocal");
json.writeName("x");
json.writeValue(obj.getX());
json.writeName("y");
json.writeValue(obj.getY());
json.writeName("rotation");
json.writeValue(obj.getRotation());
json.writeName("scaleX");
json.writeValue(obj.getScaleX());
json.writeName("scaleY");
json.writeValue(obj.getScaleY());
json.writeName("shearX");
json.writeValue(obj.getShearX());
json.writeName("shearY");
json.writeValue(obj.getShearY());
json.writeName("inherit");
json.writeValue(obj.getInherit().name());
json.writeObjectEnd();
}
private void writeBonePose (BonePose obj) {
if (visitedObjects.containsKey(obj)) {
json.writeValue(visitedObjects.get(obj));

View File

@ -517,7 +517,7 @@ public class Animation {
if (bone.active) apply(appliedPose ? bone.applied : bone.pose, bone.data.setup, time, alpha, fromSetup, add, out);
}
abstract protected void apply (BoneLocal pose, BoneLocal setup, float time, float alpha, boolean fromSetup, boolean add,
abstract protected void apply (BonePose pose, BonePose setup, float time, float alpha, boolean fromSetup, boolean add,
boolean out);
}
@ -559,30 +559,28 @@ public class Animation {
if (bone.active) apply(appliedPose ? bone.applied : bone.pose, bone.data.setup, time, alpha, fromSetup, add, out);
}
abstract protected void apply (BoneLocal pose, BoneLocal setup, float time, float alpha, boolean fromSetup, boolean add,
abstract protected void apply (BonePose pose, BonePose setup, float time, float alpha, boolean fromSetup, boolean add,
boolean out);
}
/** Changes {@link BoneLocal#getRotation()}. */
/** Changes {@link BonePose#getRotation()}. */
static public class RotateTimeline extends BoneTimeline1 {
public RotateTimeline (int frameCount, int bezierCount, int boneIndex) {
super(frameCount, bezierCount, boneIndex, Property.rotate);
}
protected void apply (BoneLocal pose, BoneLocal setup, float time, float alpha, boolean fromSetup, boolean add,
boolean out) {
protected void apply (BonePose pose, BonePose setup, float time, float alpha, boolean fromSetup, boolean add, boolean out) {
pose.rotation = getRelativeValue(time, alpha, fromSetup, add, pose.rotation, setup.rotation);
}
}
/** Changes {@link BoneLocal#getX()} and {@link BoneLocal#getY()}. */
/** Changes {@link BonePose#getX()} and {@link BonePose#getY()}. */
static public class TranslateTimeline extends BoneTimeline2 {
public TranslateTimeline (int frameCount, int bezierCount, int boneIndex) {
super(frameCount, bezierCount, boneIndex, Property.x, Property.y);
}
protected void apply (BoneLocal pose, BoneLocal setup, float time, float alpha, boolean fromSetup, boolean add,
boolean out) {
protected void apply (BonePose pose, BonePose setup, float time, float alpha, boolean fromSetup, boolean add, boolean out) {
float[] frames = this.frames;
if (time < frames[0]) {
if (fromSetup) {
@ -626,38 +624,35 @@ public class Animation {
}
}
/** Changes {@link BoneLocal#getX()}. */
/** Changes {@link BonePose#getX()}. */
static public class TranslateXTimeline extends BoneTimeline1 {
public TranslateXTimeline (int frameCount, int bezierCount, int boneIndex) {
super(frameCount, bezierCount, boneIndex, Property.x);
}
protected void apply (BoneLocal pose, BoneLocal setup, float time, float alpha, boolean fromSetup, boolean add,
boolean out) {
protected void apply (BonePose pose, BonePose setup, float time, float alpha, boolean fromSetup, boolean add, boolean out) {
pose.x = getRelativeValue(time, alpha, fromSetup, add, pose.x, setup.x);
}
}
/** Changes {@link BoneLocal#getY()}. */
/** Changes {@link BonePose#getY()}. */
static public class TranslateYTimeline extends BoneTimeline1 {
public TranslateYTimeline (int frameCount, int bezierCount, int boneIndex) {
super(frameCount, bezierCount, boneIndex, Property.y);
}
protected void apply (BoneLocal pose, BoneLocal setup, float time, float alpha, boolean fromSetup, boolean add,
boolean out) {
protected void apply (BonePose pose, BonePose setup, float time, float alpha, boolean fromSetup, boolean add, boolean out) {
pose.y = getRelativeValue(time, alpha, fromSetup, add, pose.y, setup.y);
}
}
/** Changes {@link BoneLocal#getScaleX()} and {@link BoneLocal#getScaleY()}. */
/** Changes {@link BonePose#getScaleX()} and {@link BonePose#getScaleY()}. */
static public class ScaleTimeline extends BoneTimeline2 {
public ScaleTimeline (int frameCount, int bezierCount, int boneIndex) {
super(frameCount, bezierCount, boneIndex, Property.scaleX, Property.scaleY);
}
protected void apply (BoneLocal pose, BoneLocal setup, float time, float alpha, boolean fromSetup, boolean add,
boolean out) {
protected void apply (BonePose pose, BonePose setup, float time, float alpha, boolean fromSetup, boolean add, boolean out) {
float[] frames = this.frames;
if (time < frames[0]) {
if (fromSetup) {
@ -718,38 +713,35 @@ public class Animation {
}
}
/** Changes {@link BoneLocal#getScaleX()}. */
/** Changes {@link BonePose#getScaleX()}. */
static public class ScaleXTimeline extends BoneTimeline1 {
public ScaleXTimeline (int frameCount, int bezierCount, int boneIndex) {
super(frameCount, bezierCount, boneIndex, Property.scaleX);
}
protected void apply (BoneLocal pose, BoneLocal setup, float time, float alpha, boolean fromSetup, boolean add,
boolean out) {
protected void apply (BonePose pose, BonePose setup, float time, float alpha, boolean fromSetup, boolean add, boolean out) {
pose.scaleX = getScaleValue(time, alpha, fromSetup, add, out, pose.scaleX, setup.scaleX);
}
}
/** Changes {@link BoneLocal#getScaleY()}. */
/** Changes {@link BonePose#getScaleY()}. */
static public class ScaleYTimeline extends BoneTimeline1 {
public ScaleYTimeline (int frameCount, int bezierCount, int boneIndex) {
super(frameCount, bezierCount, boneIndex, Property.scaleY);
}
protected void apply (BoneLocal pose, BoneLocal setup, float time, float alpha, boolean fromSetup, boolean add,
boolean out) {
protected void apply (BonePose pose, BonePose setup, float time, float alpha, boolean fromSetup, boolean add, boolean out) {
pose.scaleY = getScaleValue(time, alpha, fromSetup, add, out, pose.scaleY, setup.scaleY);
}
}
/** Changes {@link BoneLocal#getShearX()} and {@link BoneLocal#getShearY()}. */
/** Changes {@link BonePose#getShearX()} and {@link BonePose#getShearY()}. */
static public class ShearTimeline extends BoneTimeline2 {
public ShearTimeline (int frameCount, int bezierCount, int boneIndex) {
super(frameCount, bezierCount, boneIndex, Property.shearX, Property.shearY);
}
protected void apply (BoneLocal pose, BoneLocal setup, float time, float alpha, boolean fromSetup, boolean add,
boolean out) {
protected void apply (BonePose pose, BonePose setup, float time, float alpha, boolean fromSetup, boolean add, boolean out) {
float[] frames = this.frames;
if (time < frames[0]) {
if (fromSetup) {
@ -793,31 +785,29 @@ public class Animation {
}
}
/** Changes {@link BoneLocal#getShearX()}. */
/** Changes {@link BonePose#getShearX()}. */
static public class ShearXTimeline extends BoneTimeline1 {
public ShearXTimeline (int frameCount, int bezierCount, int boneIndex) {
super(frameCount, bezierCount, boneIndex, Property.shearX);
}
protected void apply (BoneLocal pose, BoneLocal setup, float time, float alpha, boolean fromSetup, boolean add,
boolean out) {
protected void apply (BonePose pose, BonePose setup, float time, float alpha, boolean fromSetup, boolean add, boolean out) {
pose.shearX = getRelativeValue(time, alpha, fromSetup, add, pose.shearX, setup.shearX);
}
}
/** Changes {@link BoneLocal#getShearY()}. */
/** Changes {@link BonePose#getShearY()}. */
static public class ShearYTimeline extends BoneTimeline1 {
public ShearYTimeline (int frameCount, int bezierCount, int boneIndex) {
super(frameCount, bezierCount, boneIndex, Property.shearY);
}
protected void apply (BoneLocal pose, BoneLocal setup, float time, float alpha, boolean fromSetup, boolean add,
boolean out) {
protected void apply (BonePose pose, BonePose setup, float time, float alpha, boolean fromSetup, boolean add, boolean out) {
pose.shearY = getRelativeValue(time, alpha, fromSetup, add, pose.shearY, setup.shearY);
}
}
/** Changes {@link BoneLocal#getInherit()}. */
/** Changes {@link BonePose#getInherit()}. */
static public class InheritTimeline extends Timeline implements BoneTimeline {
static public final int ENTRIES = 2;
static private final int INHERIT = 1;
@ -851,7 +841,7 @@ public class Animation {
boolean add, boolean out, boolean appliedPose) {
Bone bone = skeleton.bones.items[boneIndex];
if (!bone.active) return;
BoneLocal pose = appliedPose ? bone.applied : bone.pose;
BonePose pose = appliedPose ? bone.applied : bone.pose;
if (out) {
if (fromSetup) pose.inherit = bone.data.setup.inherit;

View File

@ -345,7 +345,7 @@ public class AnimationState {
Bone bone = skeleton.bones.items[timeline.boneIndex];
if (!bone.active) return;
BoneLocal pose = bone.pose, setup = bone.data.setup;
BonePose pose = bone.pose, setup = bone.data.setup;
float[] frames = timeline.frames;
if (time < frames[0]) { // Time is before first frame.
if (fromSetup) pose.rotation = setup.rotation;

View File

@ -42,7 +42,7 @@ import com.badlogic.gdx.utils.Null;
* {@link Skeleton#updateWorldTransform(Physics)} and {@link BonePose#updateWorldTransform(Skeleton)}.
* </ul>
*/
public class Bone extends PosedActive<BoneData, BoneLocal, BonePose> {
public class Bone extends PosedActive<BoneData, BonePose> {
@Null final Bone parent;
final Array<Bone> children = new Array(true, 4, Bone[]::new);
boolean sorted;

View File

@ -33,7 +33,7 @@ import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.utils.Null;
/** The setup pose for a bone. */
public class BoneData extends PosedData<BoneLocal> {
public class BoneData extends PosedData<BonePose> {
final int index;
@Null final BoneData parent;
float length;
@ -44,7 +44,7 @@ public class BoneData extends PosedData<BoneLocal> {
boolean visible;
public BoneData (int index, String name, @Null BoneData parent) {
super(name, new BoneLocal());
super(name, new BonePose());
if (index < 0) throw new IllegalArgumentException("index must be >= 0.");
if (name == null) throw new IllegalArgumentException("name cannot be null.");
this.index = index;

View File

@ -1,141 +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;
import com.esotericsoftware.spine.BoneData.Inherit;
/** Stores a bone's local pose. */
public class BoneLocal implements Pose<BoneLocal> {
float x, y, rotation, scaleX, scaleY, shearX, shearY;
Inherit inherit;
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;
}
/** Sets local x and y translation. */
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;
}
/** Sets local x and y scale. */
public void setScale (float scaleX, float scaleY) {
this.scaleX = scaleX;
this.scaleY = scaleY;
}
/** Sets local x and y scale to the same value. */
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;
}
}

View File

@ -18,12 +18,119 @@ import com.esotericsoftware.spine.BoneData.Inherit;
* After changing the world transform, call {@link #updateWorldTransform(Skeleton)} on every descendant bone. It may be more
* convenient to modify the local transform instead, then call {@link Skeleton#updateWorldTransform(Physics)} to update the world
* transforms for all bones and apply constraints. */
public class BonePose extends BoneLocal implements Update {
public class BonePose implements Pose<BonePose>, Update {
Bone bone;
float x, y, rotation, scaleX, scaleY, shearX, shearY;
Inherit inherit;
float a, b, worldX;
float c, d, worldY;
int world, local;
public void set (BonePose pose) {
if (pose == null) throw new IllegalArgumentException("pose cannot be null.");
x = pose.x;
y = pose.y;
rotation = pose.rotation;
scaleX = pose.scaleX;
scaleY = pose.scaleY;
shearX = pose.shearX;
shearY = pose.shearY;
inherit = pose.inherit;
}
/** The local x translation. */
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;
}
/** Sets local x and y translation. */
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;
}
/** Sets local x and y scale. */
public void setScale (float scaleX, float scaleY) {
this.scaleX = scaleX;
this.scaleY = scaleY;
}
/** Sets local x and y scale to the same value. */
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;
}
/** Called by {@link Skeleton#updateCache()} to compute the world transform, if needed. */
public void update (Skeleton skeleton, Physics physics) {
if (world != skeleton.update) updateWorldTransform(skeleton);

View File

@ -5,7 +5,7 @@ abstract public class Constraint< //
T extends Constraint<T, D, P>, //
D extends ConstraintData<T, P>, //
P extends Pose> //
extends PosedActive<D, P, P> implements Update {
extends PosedActive<D, P> implements Update {
public Constraint (D data, P pose, P constrained) {
super(data, pose, constrained);

View File

@ -10,15 +10,14 @@ package com.esotericsoftware.spine;
*/
abstract public class Posed< //
D extends PosedData<P>, //
P extends Pose, //
A extends P> {
P extends Pose> {
final D data;
final A pose;
final A constrained;
A applied;
final P pose;
final P constrained;
P applied;
public Posed (D data, A pose, A constrained) {
protected Posed (D data, P pose, P constrained) {
if (data == null) throw new IllegalArgumentException("data cannot be null.");
this.data = data;
this.pose = pose;
@ -43,7 +42,7 @@ abstract public class Posed< //
/** If no constraints modify this object, the applied pose is the same as the {@link #pose}. Otherwise it is a copy of the
* {@link #pose} modified by constraints. */
public A getAppliedPose () {
public P getAppliedPose () {
return applied;
}

View File

@ -4,13 +4,12 @@ package com.esotericsoftware.spine;
/** A posed object that may be active or inactive. */
abstract public class PosedActive< //
D extends PosedData<P>, //
P extends Pose, //
A extends P> //
extends Posed<D, P, A> {
P extends Pose> //
extends Posed<D, P> {
boolean active;
public PosedActive (D data, A pose, A constrained) {
protected PosedActive (D data, P pose, P constrained) {
super(data, pose, constrained);
setupPose();
}

View File

@ -35,7 +35,7 @@ abstract public class PosedData<P extends Pose> {
final P setup;
boolean skinRequired;
public PosedData (String name, P setup) {
protected PosedData (String name, P setup) {
if (name == null) throw new IllegalArgumentException("name cannot be null.");
this.name = name;
this.setup = setup;

View File

@ -230,7 +230,7 @@ public class SkeletonBinary extends SkeletonLoader {
String name = input.readString();
BoneData parent = i == 0 ? null : bones[input.readInt(true)];
var data = new BoneData(i, name, parent);
BoneLocal setup = data.setup;
BonePose setup = data.setup;
setup.rotation = input.readFloat();
setup.x = input.readFloat() * scale;
setup.y = input.readFloat() * scale;

View File

@ -189,7 +189,7 @@ public class SkeletonJson extends SkeletonLoader {
}
var data = new BoneData(skeletonData.bones.size, boneMap.getString("name"), parent);
data.length = boneMap.getFloat("length", 0) * scale;
BoneLocal setup = data.setup;
BonePose setup = data.setup;
setup.x = boneMap.getFloat("x", 0) * scale;
setup.y = boneMap.getFloat("y", 0) * scale;
setup.rotation = boneMap.getFloat("rotation", 0);

View File

@ -34,7 +34,7 @@ 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 extends Posed<SlotData, SlotPose, SlotPose> {
public class Slot extends Posed<SlotData, SlotPose> {
final Skeleton skeleton;
final Bone bone;
int attachmentState;