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 8afa3318c..52e9b90ac 100644
--- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java
+++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java
@@ -182,7 +182,8 @@ public class Animation {
pathConstraintPosition, pathConstraintSpacing, pathConstraintMix, //
physicsConstraintInertia, physicsConstraintStrength, physicsConstraintDamping, physicsConstraintMass, //
physicsConstraintWind, physicsConstraintGravity, physicsConstraintMix, physicsConstraintReset, //
- sequence
+ sequence, //
+ sliderMix
}
/** The base class for all timelines. */
@@ -1711,6 +1712,107 @@ public class Animation {
}
}
+ /** Changes a slot's {@link SlotPose#getSequenceIndex()} for an attachment's {@link Sequence}. */
+ static public class SequenceTimeline extends Timeline implements SlotTimeline {
+ static public final int ENTRIES = 3;
+ static private final int MODE = 1, DELAY = 2;
+
+ final int slotIndex;
+ final HasTextureRegion attachment;
+
+ public SequenceTimeline (int frameCount, int slotIndex, Attachment attachment) {
+ super(frameCount,
+ Property.sequence.ordinal() + "|" + slotIndex + "|" + ((HasTextureRegion)attachment).getSequence().getId());
+ this.slotIndex = slotIndex;
+ this.attachment = (HasTextureRegion)attachment;
+ }
+
+ public int getFrameEntries () {
+ return ENTRIES;
+ }
+
+ public int getSlotIndex () {
+ return slotIndex;
+ }
+
+ public Attachment getAttachment () {
+ return (Attachment)attachment;
+ }
+
+ /** Sets the time, mode, index, and frame time for the specified frame.
+ * @param frame Between 0 and frameCount, inclusive.
+ * @param time Seconds between frames. */
+ public void setFrame (int frame, float time, SequenceMode mode, int index, float delay) {
+ frame *= ENTRIES;
+ frames[frame] = time;
+ frames[frame + MODE] = mode.ordinal() | (index << 4);
+ frames[frame + DELAY] = delay;
+ }
+
+ public void apply (Skeleton skeleton, float lastTime, float time, @Null Array events, float alpha, MixBlend blend,
+ MixDirection direction, boolean appliedPose) {
+
+ Slot slot = skeleton.slots.items[slotIndex];
+ if (!slot.bone.active) return;
+ SlotPose pose = appliedPose ? slot.applied : slot.pose;
+
+ Attachment slotAttachment = pose.attachment;
+ if (slotAttachment != attachment) {
+ if (!(slotAttachment instanceof VertexAttachment vertexAttachment)
+ || vertexAttachment.getTimelineAttachment() != attachment) return;
+ }
+ Sequence sequence = ((HasTextureRegion)slotAttachment).getSequence();
+ if (sequence == null) return;
+
+ if (direction == out) {
+ if (blend == setup) pose.setSequenceIndex(-1);
+ return;
+ }
+
+ float[] frames = this.frames;
+ if (time < frames[0]) {
+ if (blend == setup || blend == first) pose.setSequenceIndex(-1);
+ return;
+ }
+
+ int i = search(frames, time, ENTRIES);
+ float before = frames[i];
+ int modeAndIndex = (int)frames[i + MODE];
+ float delay = frames[i + DELAY];
+
+ int index = modeAndIndex >> 4, count = sequence.getRegions().length;
+ SequenceMode mode = SequenceMode.values[modeAndIndex & 0xf];
+ if (mode != SequenceMode.hold) {
+ index += (time - before) / delay + 0.0001f;
+ switch (mode) {
+ case once:
+ index = Math.min(count - 1, index);
+ break;
+ case loop:
+ index %= count;
+ break;
+ case pingpong: {
+ int n = (count << 1) - 2;
+ index = n == 0 ? 0 : index % n;
+ if (index >= count) index = n - index;
+ break;
+ }
+ case onceReverse:
+ index = Math.max(count - 1 - index, 0);
+ break;
+ case loopReverse:
+ index = count - 1 - (index % count);
+ break;
+ case pingpongReverse:
+ int n = (count << 1) - 2;
+ index = n == 0 ? 0 : (index + count - 1) % n;
+ if (index >= count) index = n - index;
+ }
+ }
+ pose.setSequenceIndex(index);
+ }
+ }
+
/** Fires an {@link Event} when specific animation times are reached. */
static public class EventTimeline extends Timeline {
static private final String[] propertyIds = {Integer.toString(Property.event.ordinal())};
@@ -1827,6 +1929,7 @@ public class Animation {
}
static public interface ConstraintTimeline {
+ /** The index of the constraint in {@link Skeleton#getConstraints()} that will be changed when this timeline is applied. */
public int getConstraintIndex ();
}
@@ -1848,8 +1951,6 @@ public class Animation {
return ENTRIES;
}
- /** The index of the IK constraint in {@link Skeleton#getConstraints()} that will be changed when this timeline is
- * applied. */
public int getConstraintIndex () {
return constraintIndex;
}
@@ -1960,8 +2061,6 @@ public class Animation {
return ENTRIES;
}
- /** The index of the transform constraint in {@link Skeleton#getConstraints()} that will be changed when this timeline is
- * applied. */
public int getConstraintIndex () {
return constraintIndex;
}
@@ -2075,8 +2174,6 @@ public class Animation {
this.constraintIndex = constraintIndex;
}
- /** The index of the path constraint in {@link Skeleton#getConstraints()} that will be changed when this timeline is
- * applied. */
public int getConstraintIndex () {
return constraintIndex;
}
@@ -2101,8 +2198,6 @@ public class Animation {
this.constraintIndex = constraintIndex;
}
- /** The index of the path constraint in {@link Skeleton#getConstraints()} that will be changed when this timeline is
- * applied. */
public int getConstraintIndex () {
return constraintIndex;
}
@@ -2135,8 +2230,6 @@ public class Animation {
return ENTRIES;
}
- /** The index of the path constraint in {@link Skeleton#getConstraints()} that will be changed when this timeline is
- * applied. */
public int getConstraintIndex () {
return constraintIndex;
}
@@ -2223,8 +2316,8 @@ public class Animation {
this.constraintIndex = constraintIndex;
}
- /** The index of the physics constraint in {@link Skeleton#getPhysicsConstraints()} that will be changed when this timeline
- * is applied, or -1 if all physics constraints in the skeleton will be changed. */
+ /** 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;
}
@@ -2404,7 +2497,7 @@ public class Animation {
this.constraintIndex = constraintIndex;
}
- /** The index of the physics constraint in {@link Skeleton#getPhysicsConstraints()} that will be reset when this timeline is
+ /** The index of the physics constraint in {@link Skeleton#getConstraints()} that will be reset when this timeline is
* applied, or -1 if all physics constraints in the skeleton will be reset. */
public int getConstraintIndex () {
return constraintIndex;
@@ -2453,104 +2546,27 @@ public class Animation {
}
}
- /** Changes a slot's {@link SlotPose#getSequenceIndex()} for an attachment's {@link Sequence}. */
- static public class SequenceTimeline extends Timeline implements SlotTimeline {
- static public final int ENTRIES = 3;
- static private final int MODE = 1, DELAY = 2;
+ /** Changes a slider's {@link SliderPose#getMix()}. */
+ static public class SliderMixTimeline extends CurveTimeline1 implements ConstraintTimeline {
+ final int constraintIndex;
- final int slotIndex;
- final HasTextureRegion attachment;
-
- public SequenceTimeline (int frameCount, int slotIndex, Attachment attachment) {
- super(frameCount,
- Property.sequence.ordinal() + "|" + slotIndex + "|" + ((HasTextureRegion)attachment).getSequence().getId());
- this.slotIndex = slotIndex;
- this.attachment = (HasTextureRegion)attachment;
+ public SliderMixTimeline (int frameCount, int bezierCount, int constraintIndex) {
+ super(frameCount, bezierCount, Property.sliderMix.ordinal() + "|" + constraintIndex);
+ this.constraintIndex = constraintIndex;
}
- public int getFrameEntries () {
- return ENTRIES;
- }
-
- public int getSlotIndex () {
- return slotIndex;
- }
-
- public Attachment getAttachment () {
- return (Attachment)attachment;
- }
-
- /** Sets the time, mode, index, and frame time for the specified frame.
- * @param frame Between 0 and frameCount, inclusive.
- * @param time Seconds between frames. */
- public void setFrame (int frame, float time, SequenceMode mode, int index, float delay) {
- frame *= ENTRIES;
- frames[frame] = time;
- frames[frame + MODE] = mode.ordinal() | (index << 4);
- frames[frame + DELAY] = delay;
+ 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) {
- Slot slot = skeleton.slots.items[slotIndex];
- if (!slot.bone.active) return;
- SlotPose pose = appliedPose ? slot.applied : slot.pose;
-
- Attachment slotAttachment = pose.attachment;
- if (slotAttachment != attachment) {
- if (!(slotAttachment instanceof VertexAttachment vertexAttachment)
- || vertexAttachment.getTimelineAttachment() != attachment) return;
+ var constraint = (Slider)skeleton.constraints.items[constraintIndex];
+ if (constraint.active) {
+ SliderPose pose = appliedPose ? constraint.applied : constraint.pose;
+ pose.mix = getAbsoluteValue(time, alpha, blend, pose.mix, constraint.data.setup.mix);
}
- Sequence sequence = ((HasTextureRegion)slotAttachment).getSequence();
- if (sequence == null) return;
-
- if (direction == out) {
- if (blend == setup) pose.setSequenceIndex(-1);
- return;
- }
-
- float[] frames = this.frames;
- if (time < frames[0]) {
- if (blend == setup || blend == first) pose.setSequenceIndex(-1);
- return;
- }
-
- int i = search(frames, time, ENTRIES);
- float before = frames[i];
- int modeAndIndex = (int)frames[i + MODE];
- float delay = frames[i + DELAY];
-
- int index = modeAndIndex >> 4, count = sequence.getRegions().length;
- SequenceMode mode = SequenceMode.values[modeAndIndex & 0xf];
- if (mode != SequenceMode.hold) {
- index += (time - before) / delay + 0.0001f;
- switch (mode) {
- case once:
- index = Math.min(count - 1, index);
- break;
- case loop:
- index %= count;
- break;
- case pingpong: {
- int n = (count << 1) - 2;
- index = n == 0 ? 0 : index % n;
- if (index >= count) index = n - index;
- break;
- }
- case onceReverse:
- index = Math.max(count - 1 - index, 0);
- break;
- case loopReverse:
- index = count - 1 - (index % count);
- break;
- case pingpongReverse:
- int n = (count << 1) - 2;
- index = n == 0 ? 0 : (index + count - 1) % n;
- if (index >= count) index = n - index;
- }
- }
- pose.setSequenceIndex(index);
}
}
}
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 6eedba5d9..f7bb332f7 100644
--- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java
+++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java
@@ -76,6 +76,7 @@ import com.esotericsoftware.spine.Animation.SequenceTimeline;
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.Timeline;
import com.esotericsoftware.spine.Animation.TransformConstraintTimeline;
import com.esotericsoftware.spine.Animation.TranslateTimeline;
@@ -141,6 +142,7 @@ public class SkeletonBinary extends SkeletonLoader {
static public final int CONSTRAINT_PATH = 1;
static public final int CONSTRAINT_TRANSFORM = 2;
static public final int CONSTRAINT_PHYSICS = 3;
+ static public final int CONSTRAINT_SLIDER = 4;
static public final int ATTACHMENT_DEFORM = 0;
static public final int ATTACHMENT_SEQUENCE = 1;
@@ -158,6 +160,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 CURVE_LINEAR = 0;
static public final int CURVE_STEPPED = 1;
static public final int CURVE_BEZIER = 2;
@@ -259,12 +263,15 @@ public class SkeletonBinary extends SkeletonLoader {
}
// Constraints.
- o = skeletonData.constraints.setSize(n = input.readInt(true));
- for (int i = 0, nn; i < n; i++) {
+ int constraintCount = input.readInt(true);
+ ConstraintData[] constraints = skeletonData.constraints.setSize(constraintCount);
+ for (int i = 0; i < constraintCount; i++) {
+ String name = input.readString();
+ int nn = input.readInt(true);
switch (input.readByte()) {
case CONSTRAINT_IK -> {
- var data = new IkConstraintData(input.readString());
- BoneData[] constraintBones = data.bones.setSize(nn = input.readInt(true));
+ var data = new IkConstraintData(name);
+ BoneData[] constraintBones = data.bones.setSize(nn);
for (int ii = 0; ii < nn; ii++)
constraintBones[ii] = bones[input.readInt(true)];
data.target = bones[input.readInt(true)];
@@ -277,11 +284,11 @@ public class SkeletonBinary extends SkeletonLoader {
setup.stretch = (flags & 16) != 0;
if ((flags & 32) != 0) setup.mix = (flags & 64) != 0 ? input.readFloat() : 1;
if ((flags & 128) != 0) setup.softness = input.readFloat() * scale;
- o[i] = data;
+ constraints[i] = data;
}
case CONSTRAINT_TRANSFORM -> {
- var data = new TransformConstraintData(input.readString());
- BoneData[] constraintBones = data.bones.setSize(nn = input.readInt(true));
+ var data = new TransformConstraintData(name);
+ BoneData[] constraintBones = data.bones.setSize(nn);
for (int ii = 0; ii < nn; ii++)
constraintBones[ii] = bones[input.readInt(true)];
data.source = bones[input.readInt(true)];
@@ -352,11 +359,11 @@ public class SkeletonBinary extends SkeletonLoader {
if ((flags & 8) != 0) setup.mixScaleX = input.readFloat();
if ((flags & 16) != 0) setup.mixScaleY = input.readFloat();
if ((flags & 32) != 0) setup.mixShearY = input.readFloat();
- o[i] = data;
+ constraints[i] = data;
}
case CONSTRAINT_PATH -> {
- var data = new PathConstraintData(input.readString());
- BoneData[] constraintBones = data.bones.setSize(nn = input.readInt(true));
+ var data = new PathConstraintData(name);
+ BoneData[] constraintBones = data.bones.setSize(nn);
for (int ii = 0; ii < nn; ii++)
constraintBones[ii] = bones[input.readInt(true)];
data.slot = slots[input.readInt(true)];
@@ -374,11 +381,11 @@ public class SkeletonBinary extends SkeletonLoader {
setup.mixRotate = input.readFloat();
setup.mixX = input.readFloat();
setup.mixY = input.readFloat();
- o[i] = data;
+ constraints[i] = data;
}
case CONSTRAINT_PHYSICS -> {
- var data = new PhysicsConstraintData(input.readString());
- data.bone = bones[input.readInt(true)];
+ var data = new PhysicsConstraintData(name);
+ data.bone = bones[nn];
int flags = input.read();
data.skinRequired = (flags & 1) != 0;
if ((flags & 2) != 0) data.x = input.readFloat();
@@ -404,7 +411,14 @@ public class SkeletonBinary extends SkeletonLoader {
if ((flags & 32) != 0) data.gravityGlobal = true;
if ((flags & 64) != 0) data.mixGlobal = true;
setup.mix = (flags & 128) != 0 ? input.readFloat() : 1;
- o[i] = data;
+ constraints[i] = data;
+ }
+ case CONSTRAINT_SLIDER -> {
+ var data = new SliderData(name);
+ data.skinRequired = (nn & 1) != 0;
+ if ((nn & 2) != 0) data.setup.mix = (nn & 4) != 0 ? input.readFloat() : 1;
+ if ((nn & 8) != 0) data.setup.time = input.readFloat();
+ constraints[i] = data;
}
}
}
@@ -454,10 +468,12 @@ public class SkeletonBinary extends SkeletonLoader {
}
// Animations.
- o = skeletonData.animations.setSize(n = input.readInt(true));
+ Animation[] animations = skeletonData.animations.setSize(n = input.readInt(true));
for (int i = 0; i < n; i++)
- o[i] = readAnimation(input, input.readString(), skeletonData);
+ animations[i] = readAnimation(input, input.readString(), skeletonData);
+ for (int i = 0; i < constraintCount; i++)
+ if (constraints[i] instanceof SliderData data) data.animation = animations[input.readInt(true)];
} catch (IOException ex) {
throw new SerializationException("Error reading skeleton file.", ex);
} finally {
@@ -1034,6 +1050,32 @@ public class SkeletonBinary extends SkeletonLoader {
}
}
+ // Slider timelines.
+ for (int i = 0, n = input.readInt(true); i < n; i++) {
+ 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);
+ }
+ }
+ }
+ }
+
// Attachment timelines.
for (int i = 0, n = input.readInt(true); i < n; i++) {
Skin skin = skeletonData.skins.items[input.readInt(true)];
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 f93fc24f6..f85def428 100644
--- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java
+++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java
@@ -77,6 +77,7 @@ import com.esotericsoftware.spine.Animation.SequenceTimeline;
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.Timeline;
import com.esotericsoftware.spine.Animation.TransformConstraintTimeline;
import com.esotericsoftware.spine.Animation.TranslateTimeline;
@@ -217,10 +218,12 @@ public class SkeletonJson extends SkeletonLoader {
// Constraints.
for (JsonValue constraintMap = root.getChild("constraints"); constraintMap != null; constraintMap = constraintMap.next) {
+ String name = constraintMap.getString("name");
+ boolean skinRequired = constraintMap.getBoolean("skin", false);
switch (constraintMap.getString("type")) {
case "ik" -> {
- var data = new IkConstraintData(constraintMap.getString("name"));
- data.skinRequired = constraintMap.getBoolean("skin", false);
+ var data = new IkConstraintData(name);
+ data.skinRequired = skinRequired;
for (JsonValue entry = constraintMap.getChild("bones"); entry != null; entry = entry.next) {
BoneData bone = skeletonData.findBone(entry.asString());
@@ -243,8 +246,8 @@ public class SkeletonJson extends SkeletonLoader {
skeletonData.constraints.add(data);
}
case "transform" -> {
- var data = new TransformConstraintData(constraintMap.getString("name"));
- data.skinRequired = constraintMap.getBoolean("skin", false);
+ var data = new TransformConstraintData(name);
+ data.skinRequired = skinRequired;
for (JsonValue entry = constraintMap.getChild("bones"); entry != null; entry = entry.next) {
BoneData bone = skeletonData.findBone(entry.asString());
@@ -340,8 +343,8 @@ public class SkeletonJson extends SkeletonLoader {
skeletonData.constraints.add(data);
}
case "path" -> {
- var data = new PathConstraintData(constraintMap.getString("name"));
- data.skinRequired = constraintMap.getBoolean("skin", false);
+ var data = new PathConstraintData(name);
+ data.skinRequired = skinRequired;
for (JsonValue entry = constraintMap.getChild("bones"); entry != null; entry = entry.next) {
BoneData bone = skeletonData.findBone(entry.asString());
@@ -369,8 +372,8 @@ public class SkeletonJson extends SkeletonLoader {
skeletonData.constraints.add(data);
}
case "physics" -> {
- var data = new PhysicsConstraintData(constraintMap.getString("name"));
- data.skinRequired = constraintMap.getBoolean("skin", false);
+ var data = new PhysicsConstraintData(name);
+ data.skinRequired = skinRequired;
String boneName = constraintMap.getString("bone");
data.bone = skeletonData.findBone(boneName);
@@ -384,9 +387,9 @@ public class SkeletonJson extends SkeletonLoader {
data.limit = constraintMap.getFloat("limit", 5000) * scale;
data.step = 1f / constraintMap.getInt("fps", 60);
PhysicsConstraintPose setup = data.setup;
- setup.inertia = constraintMap.getFloat("inertia", 1);
+ setup.inertia = constraintMap.getFloat("inertia", 0.5f);
setup.strength = constraintMap.getFloat("strength", 100);
- setup.damping = constraintMap.getFloat("damping", 1);
+ setup.damping = constraintMap.getFloat("damping", 0.85f);
setup.massInverse = 1 / constraintMap.getFloat("mass", 1);
setup.wind = constraintMap.getFloat("wind", 0);
setup.gravity = constraintMap.getFloat("gravity", 0);
@@ -401,7 +404,13 @@ public class SkeletonJson extends SkeletonLoader {
skeletonData.constraints.add(data);
}
- // BOZO! - Sliders.
+ case "slider" -> {
+ var data = new SliderData(name);
+ data.skinRequired = skinRequired;
+ data.setup.time = constraintMap.getFloat("time", 0);
+ data.setup.mix = constraintMap.getFloat("mix", 1);
+ skeletonData.constraints.add(data);
+ }
}
}
@@ -492,6 +501,16 @@ public class SkeletonJson extends SkeletonLoader {
}
}
+ // Slider animations.
+ for (JsonValue constraintMap = root.getChild("constraints"); constraintMap != null; constraintMap = constraintMap.next) {
+ if (constraintMap.getString("type").equals("slider")) {
+ SliderData data = skeletonData.findConstraint(constraintMap.getString("name"), SliderData.class);
+ String animationName = constraintMap.getString("animation");
+ data.animation = skeletonData.findAnimation(animationName);
+ if (data.animation == null) throw new SerializationException("Slider animation not found: " + animationName);
+ }
+ }
+
skeletonData.bones.shrink();
skeletonData.slots.shrink();
skeletonData.skins.shrink();
@@ -1032,6 +1051,7 @@ public class SkeletonJson extends SkeletonLoader {
int frames = timelineMap.size;
CurveTimeline1 timeline;
+ float defaultValue = 0;
switch (timelineMap.name) {
case "reset" -> {
var resetTimeline = new PhysicsConstraintResetTimeline(frames, index);
@@ -1046,12 +1066,31 @@ public class SkeletonJson extends SkeletonLoader {
case "mass" -> timeline = new PhysicsConstraintMassTimeline(frames, frames, index);
case "wind" -> timeline = new PhysicsConstraintWindTimeline(frames, frames, index);
case "gravity" -> timeline = new PhysicsConstraintGravityTimeline(frames, frames, index);
- case "mix" -> timeline = new PhysicsConstraintMixTimeline(frames, frames, index);
+ case "mix" -> {
+ defaultValue = 1;
+ timeline = new PhysicsConstraintMixTimeline(frames, frames, index);
+ }
default -> {
continue;
}
}
- timelines.add(readTimeline(keyMap, timeline, 0, 1));
+ timelines.add(readTimeline(keyMap, timeline, defaultValue, 1));
+ }
+ }
+
+ // Slider timelines.
+ for (JsonValue constraintMap = map.getChild("slider"); constraintMap != null; constraintMap = constraintMap.next) {
+ SliderData constraint = skeletonData.findConstraint(constraintMap.name, SliderData.class);
+ if (constraint == null) throw new SerializationException("Slider not found: " + constraintMap.name);
+ int index = skeletonData.constraints.indexOf(constraint, true);
+ for (JsonValue timelineMap = constraintMap.child; timelineMap != null; timelineMap = timelineMap.next) {
+ JsonValue keyMap = timelineMap.child;
+ if (keyMap == null) continue;
+
+ int frames = timelineMap.size;
+ switch (timelineMap.name) {
+ case "mix" -> timelines.add(readTimeline(keyMap, new SliderMixTimeline(frames, frames, index), 1, 1));
+ }
}
}