From ac656fc8a9e2eff526a9d7a8fc0d8a6eaa56801c Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Tue, 22 Apr 2025 18:54:07 -0400 Subject: [PATCH] [libgdx] Added slider (time) timeline. --- .../com/esotericsoftware/spine/Animation.java | 110 ++++++++---------- .../spine/SkeletonBinary.java | 31 ++--- .../esotericsoftware/spine/SkeletonJson.java | 53 ++++----- 3 files changed, 88 insertions(+), 106 deletions(-) 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 52e9b90ac..94479cfa5 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java @@ -183,7 +183,7 @@ public class Animation { physicsConstraintInertia, physicsConstraintStrength, physicsConstraintDamping, physicsConstraintMass, // physicsConstraintWind, physicsConstraintGravity, physicsConstraintMix, physicsConstraintReset, // sequence, // - sliderMix + sliderTime, sliderMix } /** The base class for all timelines. */ @@ -512,16 +512,17 @@ public class Animation { } } - /** The base class for a {@link CurveTimeline} which sets two properties. */ - static abstract public class CurveTimeline2 extends CurveTimeline { + /** The base class for a {@link CurveTimeline} that is a {@link BoneTimeline} and sets two properties. */ + static abstract public class BoneTimeline2 extends CurveTimeline implements BoneTimeline { static public final int ENTRIES = 3; static final int VALUE1 = 1, VALUE2 = 2; - /** @param bezierCount The maximum number of Bezier curves. See {@link #shrink(int)}. - * @param propertyId1 Unique identifier for the first property the timeline modifies. - * @param propertyId2 Unique identifier for the second property the timeline modifies. */ - public CurveTimeline2 (int frameCount, int bezierCount, String propertyId1, String propertyId2) { - super(frameCount, bezierCount, propertyId1, propertyId2); + final int boneIndex; + + /** @param bezierCount The maximum number of Bezier curves. See {@link #shrink(int)}. */ + public BoneTimeline2 (int frameCount, int bezierCount, int boneIndex, Property property1, Property property2) { + super(frameCount, bezierCount, property1.ordinal() + "|" + boneIndex, property2.ordinal() + "|" + boneIndex); + this.boneIndex = boneIndex; } public int getFrameEntries () { @@ -537,21 +538,6 @@ public class Animation { frames[frame + VALUE1] = value1; frames[frame + VALUE2] = value2; } - } - - /** An interface for timelines which change the property of a bone. */ - static public interface BoneTimeline { - /** The index of the bone in {@link Skeleton#getBones()} that will be changed when this timeline is applied. */ - public int getBoneIndex (); - } - - static abstract public class BoneTimeline1 extends CurveTimeline1 implements BoneTimeline { - final int boneIndex; - - public BoneTimeline1 (int frameCount, int bezierCount, int boneIndex, Property property) { - super(frameCount, bezierCount, property.ordinal() + "|" + boneIndex); - this.boneIndex = boneIndex; - } public int getBoneIndex () { return boneIndex; @@ -568,11 +554,17 @@ public class Animation { MixDirection direction); } - static abstract public class BoneTimeline2 extends CurveTimeline2 implements BoneTimeline { + /** An interface for timelines which change the property of a bone. */ + static public interface BoneTimeline { + /** The index of the bone in {@link Skeleton#getBones()} that will be changed when this timeline is applied. */ + public int getBoneIndex (); + } + + static abstract public class BoneTimeline1 extends CurveTimeline1 implements BoneTimeline { final int boneIndex; - public BoneTimeline2 (int frameCount, int bezierCount, int boneIndex, Property property1, Property property2) { - super(frameCount, bezierCount, property1.ordinal() + "|" + boneIndex, property2.ordinal() + "|" + boneIndex); + public BoneTimeline1 (int frameCount, int bezierCount, int boneIndex, Property property) { + super(frameCount, bezierCount, property.ordinal() + "|" + boneIndex); this.boneIndex = boneIndex; } @@ -2165,18 +2157,24 @@ public class Animation { } } - /** Changes a path constraint's {@link PathConstraintPose#getPosition()}. */ - static public class PathConstraintPositionTimeline extends CurveTimeline1 implements ConstraintTimeline { + static abstract public class ConstraintTimeline1 extends CurveTimeline1 implements ConstraintTimeline { final int constraintIndex; - public PathConstraintPositionTimeline (int frameCount, int bezierCount, int constraintIndex) { - super(frameCount, bezierCount, Property.pathConstraintPosition.ordinal() + "|" + constraintIndex); + public ConstraintTimeline1 (int frameCount, int bezierCount, int constraintIndex, Property property) { + super(frameCount, bezierCount, property.ordinal() + "|" + constraintIndex); this.constraintIndex = constraintIndex; } public int getConstraintIndex () { return constraintIndex; } + } + + /** Changes a path constraint's {@link PathConstraintPose#getPosition()}. */ + static public class PathConstraintPositionTimeline extends ConstraintTimeline1 { + public PathConstraintPositionTimeline (int frameCount, int bezierCount, int constraintIndex) { + super(frameCount, bezierCount, constraintIndex, Property.pathConstraintPosition); + } public void apply (Skeleton skeleton, float lastTime, float time, @Null Array events, float alpha, MixBlend blend, MixDirection direction, boolean appliedPose) { @@ -2190,16 +2188,9 @@ public class Animation { } /** Changes a path constraint's {@link PathConstraintPose#getSpacing()}. */ - static public class PathConstraintSpacingTimeline extends CurveTimeline1 implements ConstraintTimeline { - final int constraintIndex; - + static public class PathConstraintSpacingTimeline extends ConstraintTimeline1 { public PathConstraintSpacingTimeline (int frameCount, int bezierCount, int constraintIndex) { - super(frameCount, bezierCount, Property.pathConstraintSpacing.ordinal() + "|" + constraintIndex); - this.constraintIndex = constraintIndex; - } - - public int getConstraintIndex () { - return constraintIndex; + super(frameCount, bezierCount, constraintIndex, Property.pathConstraintSpacing); } public void apply (Skeleton skeleton, float lastTime, float time, @Null Array events, float alpha, MixBlend blend, @@ -2307,19 +2298,10 @@ public class Animation { } /** The base class for most {@link PhysicsConstraint} timelines. */ - static abstract public class PhysicsConstraintTimeline extends CurveTimeline1 implements ConstraintTimeline { - final int constraintIndex; - + static abstract public class PhysicsConstraintTimeline extends ConstraintTimeline1 { /** @param constraintIndex -1 for all physics constraints in the skeleton. */ public PhysicsConstraintTimeline (int frameCount, int bezierCount, int constraintIndex, Property property) { - super(frameCount, bezierCount, property.ordinal() + "|" + constraintIndex); - this.constraintIndex = constraintIndex; - } - - /** The index of the physics constraint in {@link Skeleton#getConstraints()} that will be changed when this timeline is - * applied, or -1 if all physics constraints in the skeleton will be changed. */ - public int getConstraintIndex () { - return constraintIndex; + super(frameCount, bezierCount, constraintIndex, property); } public void apply (Skeleton skeleton, float lastTime, float time, @Null Array events, float alpha, MixBlend blend, @@ -2546,17 +2528,27 @@ public class Animation { } } - /** Changes a slider's {@link SliderPose#getMix()}. */ - static public class SliderMixTimeline extends CurveTimeline1 implements ConstraintTimeline { - final int constraintIndex; - - public SliderMixTimeline (int frameCount, int bezierCount, int constraintIndex) { - super(frameCount, bezierCount, Property.sliderMix.ordinal() + "|" + constraintIndex); - this.constraintIndex = constraintIndex; + /** Changes a slider's {@link SliderPose#getTime()}. */ + static public class SliderTimeline extends ConstraintTimeline1 { + public SliderTimeline (int frameCount, int bezierCount, int constraintIndex) { + super(frameCount, bezierCount, constraintIndex, Property.sliderTime); } - public int getConstraintIndex () { - return constraintIndex; + public void apply (Skeleton skeleton, float lastTime, float time, @Null Array events, float alpha, MixBlend blend, + MixDirection direction, boolean appliedPose) { + + var constraint = (Slider)skeleton.constraints.items[constraintIndex]; + if (constraint.active) { + SliderPose pose = appliedPose ? constraint.applied : constraint.pose; + pose.time = getAbsoluteValue(time, alpha, blend, pose.time, constraint.data.setup.time); + } + } + } + + /** Changes a slider's {@link SliderPose#getMix()}. */ + static public class SliderMixTimeline extends ConstraintTimeline1 { + public SliderMixTimeline (int frameCount, int bezierCount, int constraintIndex) { + super(frameCount, bezierCount, constraintIndex, Property.sliderMix); } public void apply (Skeleton skeleton, float lastTime, float time, @Null Array events, float alpha, MixBlend blend, 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 f7bb332f7..775e33841 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java @@ -45,9 +45,9 @@ import com.badlogic.gdx.utils.SerializationException; import com.esotericsoftware.spine.Animation.AlphaTimeline; import com.esotericsoftware.spine.Animation.AttachmentTimeline; +import com.esotericsoftware.spine.Animation.BoneTimeline2; import com.esotericsoftware.spine.Animation.CurveTimeline; import com.esotericsoftware.spine.Animation.CurveTimeline1; -import com.esotericsoftware.spine.Animation.CurveTimeline2; import com.esotericsoftware.spine.Animation.DeformTimeline; import com.esotericsoftware.spine.Animation.DrawOrderTimeline; import com.esotericsoftware.spine.Animation.EventTimeline; @@ -77,6 +77,7 @@ import com.esotericsoftware.spine.Animation.ShearTimeline; import com.esotericsoftware.spine.Animation.ShearXTimeline; import com.esotericsoftware.spine.Animation.ShearYTimeline; import com.esotericsoftware.spine.Animation.SliderMixTimeline; +import com.esotericsoftware.spine.Animation.SliderTimeline; import com.esotericsoftware.spine.Animation.Timeline; import com.esotericsoftware.spine.Animation.TransformConstraintTimeline; import com.esotericsoftware.spine.Animation.TranslateTimeline; @@ -160,7 +161,8 @@ public class SkeletonBinary extends SkeletonLoader { static public final int PHYSICS_MIX = 7; static public final int PHYSICS_RESET = 8; - static public final int SLIDER_MIX = 0; + static public final int SLIDER_TIME = 0; + static public final int SLIDER_MIX = 1; static public final int CURVE_LINEAR = 0; static public final int CURVE_STEPPED = 1; @@ -1055,24 +1057,11 @@ public class SkeletonBinary extends SkeletonLoader { int index = input.readInt(true); for (int ii = 0, nn = input.readInt(true); ii < nn; ii++) { int type = input.readByte(), frameCount = input.readInt(true), bezierCount = input.readInt(true); - switch (type) { - case SLIDER_MIX -> { - var timeline = new SliderMixTimeline(frameCount, bezierCount, index); - float time = input.readFloat(), mix = input.readFloat(); - for (int frame = 0, bezier = 0, frameLast = timeline.getFrameCount() - 1;; frame++) { - timeline.setFrame(frame, time, mix); - if (frame == frameLast) break; - float time2 = input.readFloat(), mix2 = input.readFloat(); - switch (input.readByte()) { - case CURVE_STEPPED -> timeline.setStepped(frame); - case CURVE_BEZIER -> setBezier(input, timeline, bezier++, frame, 0, time, time2, mix, mix2, 1); - } - time = time2; - mix = mix2; - } - timelines.add(timeline); - } - } + readTimeline(input, timelines, switch (type) { + case SLIDER_TIME -> new SliderTimeline(frameCount, bezierCount, index); + case SLIDER_MIX -> new SliderMixTimeline(frameCount, bezierCount, index); + default -> throw new SerializationException(); + }, 1); } } @@ -1221,7 +1210,7 @@ public class SkeletonBinary extends SkeletonLoader { timelines.add(timeline); } - private void readTimeline (SkeletonInput input, Array timelines, CurveTimeline2 timeline, float scale) + private void readTimeline (SkeletonInput input, Array timelines, BoneTimeline2 timeline, float scale) throws IOException { float time = input.readFloat(), value1 = input.readFloat() * scale, value2 = input.readFloat() * scale; for (int frame = 0, bezier = 0, frameLast = timeline.getFrameCount() - 1;; frame++) { 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 f85def428..cfeb6e9c5 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java @@ -46,9 +46,9 @@ import com.badlogic.gdx.utils.SerializationException; import com.esotericsoftware.spine.Animation.AlphaTimeline; import com.esotericsoftware.spine.Animation.AttachmentTimeline; +import com.esotericsoftware.spine.Animation.BoneTimeline2; import com.esotericsoftware.spine.Animation.CurveTimeline; import com.esotericsoftware.spine.Animation.CurveTimeline1; -import com.esotericsoftware.spine.Animation.CurveTimeline2; import com.esotericsoftware.spine.Animation.DeformTimeline; import com.esotericsoftware.spine.Animation.DrawOrderTimeline; import com.esotericsoftware.spine.Animation.EventTimeline; @@ -78,6 +78,7 @@ import com.esotericsoftware.spine.Animation.ShearTimeline; import com.esotericsoftware.spine.Animation.ShearXTimeline; import com.esotericsoftware.spine.Animation.ShearYTimeline; import com.esotericsoftware.spine.Animation.SliderMixTimeline; +import com.esotericsoftware.spine.Animation.SliderTimeline; import com.esotericsoftware.spine.Animation.Timeline; import com.esotericsoftware.spine.Animation.TransformConstraintTimeline; import com.esotericsoftware.spine.Animation.TranslateTimeline; @@ -763,7 +764,7 @@ public class SkeletonJson extends SkeletonLoader { } timelines.add(timeline); } - case "alpha" -> timelines.add(readTimeline(keyMap, new AlphaTimeline(frames, frames, slot.index), 0, 1)); + case "alpha" -> readTimeline(timelines, keyMap, new AlphaTimeline(frames, frames, slot.index), 0, 1); case "rgba2" -> { var timeline = new RGBA2Timeline(frames, frames * 7, slot.index); float time = keyMap.getFloat("time", 0); @@ -878,21 +879,17 @@ public class SkeletonJson extends SkeletonLoader { int frames = timelineMap.size; switch (timelineMap.name) { - case "rotate" -> timelines.add(readTimeline(keyMap, new RotateTimeline(frames, frames, bone.index), 0, 1)); + case "rotate" -> readTimeline(timelines, keyMap, new RotateTimeline(frames, frames, bone.index), 0, 1); case "translate" -> // - timelines.add(readTimeline(keyMap, new TranslateTimeline(frames, frames << 1, bone.index), "x", "y", 0, scale)); - case "translatex" -> // - timelines.add(readTimeline(keyMap, new TranslateXTimeline(frames, frames, bone.index), 0, scale)); - case "translatey" -> // - timelines.add(readTimeline(keyMap, new TranslateYTimeline(frames, frames, bone.index), 0, scale)); - case "scale" -> timelines - .add(readTimeline(keyMap, new ScaleTimeline(frames, frames << 1, bone.index), "x", "y", 1, 1)); - case "scalex" -> timelines.add(readTimeline(keyMap, new ScaleXTimeline(frames, frames, bone.index), 1, 1)); - case "scaley" -> timelines.add(readTimeline(keyMap, new ScaleYTimeline(frames, frames, bone.index), 1, 1)); - case "shear" -> // - timelines.add(readTimeline(keyMap, new ShearTimeline(frames, frames << 1, bone.index), "x", "y", 0, 1)); - case "shearx" -> timelines.add(readTimeline(keyMap, new ShearXTimeline(frames, frames, bone.index), 0, 1)); - case "sheary" -> timelines.add(readTimeline(keyMap, new ShearYTimeline(frames, frames, bone.index), 0, 1)); + readTimeline(timelines, keyMap, new TranslateTimeline(frames, frames << 1, bone.index), "x", "y", 0, scale); + case "translatex" -> readTimeline(timelines, keyMap, new TranslateXTimeline(frames, frames, bone.index), 0, scale); + case "translatey" -> readTimeline(timelines, keyMap, new TranslateYTimeline(frames, frames, bone.index), 0, scale); + case "scale" -> readTimeline(timelines, keyMap, new ScaleTimeline(frames, frames << 1, bone.index), "x", "y", 1, 1); + case "scalex" -> readTimeline(timelines, keyMap, new ScaleXTimeline(frames, frames, bone.index), 1, 1); + case "scaley" -> readTimeline(timelines, keyMap, new ScaleYTimeline(frames, frames, bone.index), 1, 1); + case "shear" -> readTimeline(timelines, keyMap, new ShearTimeline(frames, frames << 1, bone.index), "x", "y", 0, 1); + case "shearx" -> readTimeline(timelines, keyMap, new ShearXTimeline(frames, frames, bone.index), 0, 1); + case "sheary" -> readTimeline(timelines, keyMap, new ShearYTimeline(frames, frames, bone.index), 0, 1); case "inherit" -> { var timeline = new InheritTimeline(frames, bone.index); for (int frame = 0; keyMap != null; keyMap = keyMap.next, frame++) { @@ -997,12 +994,12 @@ public class SkeletonJson extends SkeletonLoader { switch (timelineMap.name) { case "position" -> { var timeline = new PathConstraintPositionTimeline(frames, frames, index); - timelines.add(readTimeline(keyMap, timeline, 0, constraint.positionMode == PositionMode.fixed ? scale : 1)); + readTimeline(timelines, keyMap, timeline, 0, constraint.positionMode == PositionMode.fixed ? scale : 1); } case "spacing" -> { var timeline = new PathConstraintSpacingTimeline(frames, frames, index); - timelines.add(readTimeline(keyMap, timeline, 0, - constraint.spacingMode == SpacingMode.length || constraint.spacingMode == SpacingMode.fixed ? scale : 1)); + readTimeline(timelines, keyMap, timeline, 0, + constraint.spacingMode == SpacingMode.length || constraint.spacingMode == SpacingMode.fixed ? scale : 1); } case "mix" -> { var timeline = new PathConstraintMixTimeline(frames, frames * 3, index); @@ -1074,7 +1071,7 @@ public class SkeletonJson extends SkeletonLoader { continue; } } - timelines.add(readTimeline(keyMap, timeline, defaultValue, 1)); + readTimeline(timelines, keyMap, timeline, defaultValue, 1); } } @@ -1089,7 +1086,8 @@ public class SkeletonJson extends SkeletonLoader { int frames = timelineMap.size; switch (timelineMap.name) { - case "mix" -> timelines.add(readTimeline(keyMap, new SliderMixTimeline(frames, frames, index), 1, 1)); + case "time" -> readTimeline(timelines, keyMap, new SliderTimeline(frames, frames, index), 1, 1); + case "mix" -> readTimeline(timelines, keyMap, new SliderMixTimeline(frames, frames, index), 1, 1); } } } @@ -1231,14 +1229,16 @@ public class SkeletonJson extends SkeletonLoader { skeletonData.animations.add(new Animation(name, timelines, duration)); } - private Timeline readTimeline (JsonValue keyMap, CurveTimeline1 timeline, float defaultValue, float scale) { + private void readTimeline (Array timelines, JsonValue keyMap, CurveTimeline1 timeline, float defaultValue, + float scale) { float time = keyMap.getFloat("time", 0), value = keyMap.getFloat("value", defaultValue) * scale; for (int frame = 0, bezier = 0;; frame++) { timeline.setFrame(frame, time, value); JsonValue nextMap = keyMap.next; if (nextMap == null) { timeline.shrink(bezier); - return timeline; + timelines.add(timeline); + return; } float time2 = nextMap.getFloat("time", 0); float value2 = nextMap.getFloat("value", defaultValue) * scale; @@ -1250,8 +1250,8 @@ public class SkeletonJson extends SkeletonLoader { } } - private Timeline readTimeline (JsonValue keyMap, CurveTimeline2 timeline, String name1, String name2, float defaultValue, - float scale) { + private void readTimeline (Array timelines, JsonValue keyMap, BoneTimeline2 timeline, String name1, String name2, + float defaultValue, float scale) { float time = keyMap.getFloat("time", 0); float value1 = keyMap.getFloat(name1, defaultValue) * scale, value2 = keyMap.getFloat(name2, defaultValue) * scale; for (int frame = 0, bezier = 0;; frame++) { @@ -1259,7 +1259,8 @@ public class SkeletonJson extends SkeletonLoader { JsonValue nextMap = keyMap.next; if (nextMap == null) { timeline.shrink(bezier); - return timeline; + timelines.add(timeline); + return; } float time2 = nextMap.getFloat("time", 0); float nvalue1 = nextMap.getFloat(name1, defaultValue) * scale, nvalue2 = nextMap.getFloat(name2, defaultValue) * scale;