[libgdx] Added DrawOrderFolderTimeline.

This commit is contained in:
Nathan Sweet 2026-03-13 20:20:59 -04:00
parent 516518b2d9
commit cf45806bdd
5 changed files with 250 additions and 73 deletions

View File

@ -1,26 +1,65 @@
package com.esotericsoftware.spine.utils; package com.esotericsoftware.spine.utils;
import com.esotericsoftware.spine.*; import java.util.HashMap;
import com.esotericsoftware.spine.Animation.*; import java.util.Map;
import com.esotericsoftware.spine.AnimationState.*;
import com.esotericsoftware.spine.BoneData.Inherit;
import com.esotericsoftware.spine.Skin.SkinEntry;
import com.esotericsoftware.spine.PathConstraintData.*;
import com.esotericsoftware.spine.TransformConstraintData.*;
import com.esotericsoftware.spine.attachments.*;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.IntArray;
import com.badlogic.gdx.utils.FloatArray; import com.badlogic.gdx.utils.FloatArray;
import com.badlogic.gdx.utils.IntArray;
import java.util.Locale; import com.esotericsoftware.spine.Animation;
import java.util.Map; import com.esotericsoftware.spine.Animation.Timeline;
import java.util.HashMap; import com.esotericsoftware.spine.AnimationState;
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;
import com.esotericsoftware.spine.Event;
import com.esotericsoftware.spine.EventData;
import com.esotericsoftware.spine.IkConstraint;
import com.esotericsoftware.spine.IkConstraintData;
import com.esotericsoftware.spine.IkConstraintPose;
import com.esotericsoftware.spine.PathConstraint;
import com.esotericsoftware.spine.PathConstraintData;
import com.esotericsoftware.spine.PathConstraintPose;
import com.esotericsoftware.spine.PhysicsConstraint;
import com.esotericsoftware.spine.PhysicsConstraintData;
import com.esotericsoftware.spine.PhysicsConstraintPose;
import com.esotericsoftware.spine.Skeleton;
import com.esotericsoftware.spine.SkeletonData;
import com.esotericsoftware.spine.Skin;
import com.esotericsoftware.spine.Skin.SkinEntry;
import com.esotericsoftware.spine.Slider;
import com.esotericsoftware.spine.SliderData;
import com.esotericsoftware.spine.SliderPose;
import com.esotericsoftware.spine.Slot;
import com.esotericsoftware.spine.SlotData;
import com.esotericsoftware.spine.SlotPose;
import com.esotericsoftware.spine.TransformConstraint;
import com.esotericsoftware.spine.TransformConstraintData;
import com.esotericsoftware.spine.TransformConstraintData.FromProperty;
import com.esotericsoftware.spine.TransformConstraintData.ToProperty;
import com.esotericsoftware.spine.TransformConstraintPose;
import com.esotericsoftware.spine.Update;
import com.esotericsoftware.spine.attachments.Attachment;
import com.esotericsoftware.spine.attachments.BoundingBoxAttachment;
import com.esotericsoftware.spine.attachments.ClippingAttachment;
import com.esotericsoftware.spine.attachments.MeshAttachment;
import com.esotericsoftware.spine.attachments.PathAttachment;
import com.esotericsoftware.spine.attachments.PointAttachment;
import com.esotericsoftware.spine.attachments.RegionAttachment;
import com.esotericsoftware.spine.attachments.Sequence;
import com.esotericsoftware.spine.attachments.VertexAttachment;
public class SkeletonSerializer { public class SkeletonSerializer {
private final Map<Object, String> visitedObjects = new HashMap<>(); private final Map<Object, String> visitedObjects = new HashMap();
private int nextId = 1; private int nextId = 1;
private JsonWriter json; private JsonWriter json;
@ -3271,7 +3310,7 @@ public class SkeletonSerializer {
json.writeValue("Skin"); json.writeValue("Skin");
json.writeName("attachments"); json.writeName("attachments");
Array<SkinEntry> sortedAttachments = new Array<>(obj.getAttachments()); var sortedAttachments = new Array<SkinEntry>(obj.getAttachments());
sortedAttachments.sort( (a, b) -> Integer.compare(a.getSlotIndex(), b.getSlotIndex())); sortedAttachments.sort( (a, b) -> Integer.compare(a.getSlotIndex(), b.getSlotIndex()));
json.writeArrayStart(); json.writeArrayStart();
for (SkinEntry item : sortedAttachments) { for (SkinEntry item : sortedAttachments) {

View File

@ -1880,8 +1880,7 @@ public class Animation {
/** Sets the time and draw order for the specified frame. /** Sets the time and draw order for the specified frame.
* @param frame Between 0 and <code>frameCount</code>, inclusive. * @param frame Between 0 and <code>frameCount</code>, inclusive.
* @param time The frame time in seconds. * @param time The frame time in seconds.
* @param drawOrder For each slot in {@link Skeleton#slots}, the index of the slot in the new draw order. May be null to use * @param drawOrder Ordered {@link Skeleton#slots} indices, or null to use setup pose order. */
* setup pose draw order. */
public void setFrame (int frame, float time, @Null int[] drawOrder) { public void setFrame (int frame, float time, @Null int[] drawOrder) {
frames[frame] = time; frames[frame] = time;
drawOrders[frame] = drawOrder; drawOrders[frame] = drawOrder;
@ -1913,6 +1912,87 @@ public class Animation {
} }
} }
/** Changes a subset of a skeleton's {@link Skeleton#getDrawOrder()}. */
static public class DrawOrderFolderTimeline extends Timeline {
private final int[] slots;
private final boolean[] inFolder;
private final int[][] drawOrders;
/** @param slots {@link Skeleton#getSlots()} indices controlled by this timeline, in setup order.
* @param slotCount The maximum number of slots in the skeleton. */
public DrawOrderFolderTimeline (int frameCount, int[] slots, int slotCount) {
super(frameCount, DrawOrderTimeline.propertyIds);
this.slots = slots;
drawOrders = new int[frameCount][];
inFolder = new boolean[slotCount];
for (int i : slots)
inFolder[i] = true;
}
public int getFrameCount () {
return frames.length;
}
/** The {@link Skeleton#getSlots()} indices that this timeline affects, in setup order. */
public int[] getSlots () {
return slots;
}
/** The draw order for each frame. See {@link #setFrame(int, float, int[])}. */
public int[][] getDrawOrders () {
return drawOrders;
}
/** Sets the time and draw order for the specified frame.
* @param frame Between 0 and <code>frameCount</code>, inclusive.
* @param time The frame time in seconds.
* @param drawOrder Ordered {@link #getSlots()} indices, or null to use setup pose order. */
public void setFrame (int frame, float time, @Null int[] drawOrder) {
frames[frame] = time;
drawOrders[frame] = drawOrder;
}
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
MixDirection direction, boolean appliedPose) {
if (direction == out) {
if (blend == setup) setup(skeleton);
} else if (time < frames[0]) {
if (blend == setup || blend == first) setup(skeleton);
} else {
int[] order = drawOrders[search(frames, time)];
if (order == null)
setup(skeleton);
else
apply(skeleton, order);
}
}
private void setup (Skeleton skeleton) {
boolean[] inFolder = this.inFolder;
Slot[] drawOrder = skeleton.drawOrder.items, allSlots = skeleton.slots.items;
int[] slots = this.slots;
for (int i = 0, found = 0, done = slots.length;; i++) {
if (inFolder[drawOrder[i].data.index]) {
drawOrder[i] = allSlots[slots[found]];
if (++found == done) break;
}
}
}
private void apply (Skeleton skeleton, int[] order) {
boolean[] inFolder = this.inFolder;
Slot[] drawOrder = skeleton.drawOrder.items, allSlots = skeleton.slots.items;
int[] slots = this.slots;
for (int i = 0, found = 0, done = slots.length;; i++) {
if (inFolder[drawOrder[i].data.index]) {
drawOrder[i] = allSlots[slots[order[found]]];
if (++found == done) break;
}
}
}
}
static public interface ConstraintTimeline { static public interface ConstraintTimeline {
/** The index of the constraint in {@link Skeleton#getConstraints()} that will be changed when this timeline is applied, or /** The index of the constraint in {@link Skeleton#getConstraints()} that will be changed when this timeline is applied, or
* -1 if a specific constraint will not be changed. */ * -1 if a specific constraint will not be changed. */

View File

@ -39,6 +39,7 @@ import com.badlogic.gdx.utils.Pool.Poolable;
import com.badlogic.gdx.utils.SnapshotArray; import com.badlogic.gdx.utils.SnapshotArray;
import com.esotericsoftware.spine.Animation.AttachmentTimeline; import com.esotericsoftware.spine.Animation.AttachmentTimeline;
import com.esotericsoftware.spine.Animation.DrawOrderFolderTimeline;
import com.esotericsoftware.spine.Animation.DrawOrderTimeline; import com.esotericsoftware.spine.Animation.DrawOrderTimeline;
import com.esotericsoftware.spine.Animation.EventTimeline; import com.esotericsoftware.spine.Animation.EventTimeline;
import com.esotericsoftware.spine.Animation.MixBlend; import com.esotericsoftware.spine.Animation.MixBlend;
@ -807,7 +808,8 @@ public class AnimationState {
if (!propertyIds.addAll(ids)) if (!propertyIds.addAll(ids))
timelineMode[i] = SUBSEQUENT; timelineMode[i] = SUBSEQUENT;
else if (to == null || timeline instanceof AttachmentTimeline || timeline instanceof DrawOrderTimeline else if (to == null || timeline instanceof AttachmentTimeline || timeline instanceof DrawOrderTimeline
|| timeline instanceof EventTimeline || !to.animation.hasTimeline(ids)) { || timeline instanceof DrawOrderFolderTimeline || timeline instanceof EventTimeline
|| !to.animation.hasTimeline(ids)) {
timelineMode[i] = FIRST; timelineMode[i] = FIRST;
} else { } else {
for (TrackEntry next = to.mixingTo; next != null; next = next.mixingTo) { for (TrackEntry next = to.mixingTo; next != null; next = next.mixingTo) {

View File

@ -32,6 +32,7 @@ package com.esotericsoftware.spine;
import java.io.EOFException; import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Arrays;
import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
@ -47,6 +48,7 @@ import com.esotericsoftware.spine.Animation.BoneTimeline2;
import com.esotericsoftware.spine.Animation.CurveTimeline; import com.esotericsoftware.spine.Animation.CurveTimeline;
import com.esotericsoftware.spine.Animation.CurveTimeline1; import com.esotericsoftware.spine.Animation.CurveTimeline1;
import com.esotericsoftware.spine.Animation.DeformTimeline; import com.esotericsoftware.spine.Animation.DeformTimeline;
import com.esotericsoftware.spine.Animation.DrawOrderFolderTimeline;
import com.esotericsoftware.spine.Animation.DrawOrderTimeline; import com.esotericsoftware.spine.Animation.DrawOrderTimeline;
import com.esotericsoftware.spine.Animation.EventTimeline; import com.esotericsoftware.spine.Animation.EventTimeline;
import com.esotericsoftware.spine.Animation.IkConstraintTimeline; import com.esotericsoftware.spine.Animation.IkConstraintTimeline;
@ -1160,34 +1162,26 @@ public class SkeletonBinary extends SkeletonLoader {
} }
// Draw order timeline. // Draw order timeline.
int slotCount = skeletonData.slots.size;
int drawOrderCount = input.readInt(true); int drawOrderCount = input.readInt(true);
if (drawOrderCount > 0) { if (drawOrderCount > 0) {
var timeline = new DrawOrderTimeline(drawOrderCount); var timeline = new DrawOrderTimeline(drawOrderCount);
int slotCount = skeletonData.slots.size; for (int i = 0; i < drawOrderCount; i++)
for (int i = 0; i < drawOrderCount; i++) { timeline.setFrame(i, input.readFloat(), readDrawOrder(input, slotCount));
float time = input.readFloat(); timelines.add(timeline);
int offsetCount = input.readInt(true); }
var drawOrder = new int[slotCount];
for (int ii = slotCount - 1; ii >= 0; ii--) // Draw order folder timelines.
drawOrder[ii] = -1; int folderCount = input.readInt(true);
var unchanged = new int[slotCount - offsetCount]; for (int i = 0; i < folderCount; i++) {
int originalIndex = 0, unchangedIndex = 0; int folderSlotCount = input.readInt(true);
for (int ii = 0; ii < offsetCount; ii++) { var folderSlots = new int[folderSlotCount];
int slotIndex = input.readInt(true); for (int ii = 0; ii < folderSlotCount; ii++)
// Collect unchanged items. folderSlots[ii] = input.readInt(true);
while (originalIndex != slotIndex) int keyCount = input.readInt(true);
unchanged[unchangedIndex++] = originalIndex++; var timeline = new DrawOrderFolderTimeline(keyCount, folderSlots, slotCount);
// Set changed items. for (int ii = 0; ii < keyCount; ii++)
drawOrder[originalIndex + input.readInt(true)] = originalIndex++; timeline.setFrame(ii, input.readFloat(), readDrawOrder(input, folderSlotCount));
}
// Collect remaining unchanged items.
while (originalIndex < slotCount)
unchanged[unchangedIndex++] = originalIndex++;
// Fill in unchanged items.
for (int ii = slotCount - 1; ii >= 0; ii--)
if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex];
timeline.setFrame(i, time, drawOrder);
}
timelines.add(timeline); timelines.add(timeline);
} }
@ -1257,6 +1251,30 @@ public class SkeletonBinary extends SkeletonLoader {
timelines.add(timeline); timelines.add(timeline);
} }
private @Null int[] readDrawOrder (SkeletonInput input, int slotCount) throws IOException {
int changeCount = input.readInt(true);
if (changeCount == 0) return null;
var drawOrder = new int[slotCount];
Arrays.fill(drawOrder, -1);
var unchanged = new int[slotCount - changeCount];
int originalIndex = 0, unchangedIndex = 0;
for (int i = 0; i < changeCount; i++) {
int slotIndex = input.readInt(true);
// Collect unchanged items.
while (originalIndex != slotIndex)
unchanged[unchangedIndex++] = originalIndex++;
// Set changed items.
drawOrder[originalIndex + input.readInt(true)] = originalIndex++;
}
// Collect remaining unchanged items.
while (originalIndex < slotCount)
unchanged[unchangedIndex++] = originalIndex++;
// Fill in unchanged items.
for (int i = slotCount - 1; i >= 0; i--)
if (drawOrder[i] == -1) drawOrder[i] = unchanged[--unchangedIndex];
return drawOrder;
}
void setBezier (SkeletonInput input, CurveTimeline timeline, int bezier, int frame, int value, float time1, float time2, void setBezier (SkeletonInput input, CurveTimeline timeline, int bezier, int frame, int value, float time1, float time2,
float value1, float value2, float scale) throws IOException { float value1, float value2, float scale) throws IOException {
timeline.setBezier(bezier, frame, value, time1, value1, input.readFloat(), input.readFloat() * scale, input.readFloat(), timeline.setBezier(bezier, frame, value, time1, value1, input.readFloat(), input.readFloat() * scale, input.readFloat(),

View File

@ -32,6 +32,7 @@ package com.esotericsoftware.spine;
import static com.esotericsoftware.spine.utils.SpineUtils.*; import static com.esotericsoftware.spine.utils.SpineUtils.*;
import java.io.InputStream; import java.io.InputStream;
import java.util.Arrays;
import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
@ -50,6 +51,7 @@ import com.esotericsoftware.spine.Animation.BoneTimeline2;
import com.esotericsoftware.spine.Animation.CurveTimeline; import com.esotericsoftware.spine.Animation.CurveTimeline;
import com.esotericsoftware.spine.Animation.CurveTimeline1; import com.esotericsoftware.spine.Animation.CurveTimeline1;
import com.esotericsoftware.spine.Animation.DeformTimeline; import com.esotericsoftware.spine.Animation.DeformTimeline;
import com.esotericsoftware.spine.Animation.DrawOrderFolderTimeline;
import com.esotericsoftware.spine.Animation.DrawOrderTimeline; import com.esotericsoftware.spine.Animation.DrawOrderTimeline;
import com.esotericsoftware.spine.Animation.EventTimeline; import com.esotericsoftware.spine.Animation.EventTimeline;
import com.esotericsoftware.spine.Animation.IkConstraintTimeline; import com.esotericsoftware.spine.Animation.IkConstraintTimeline;
@ -464,7 +466,7 @@ public class SkeletonJson extends SkeletonLoader {
skin.constraints.shrink(); skin.constraints.shrink();
for (JsonValue slotEntry = skinMap.getChild("attachments"); slotEntry != null; slotEntry = slotEntry.next) { for (JsonValue slotEntry = skinMap.getChild("attachments"); slotEntry != null; slotEntry = slotEntry.next) {
SlotData slot = skeletonData.findSlot(slotEntry.name); SlotData slot = skeletonData.findSlot(slotEntry.name);
if (slot == null) throw new SerializationException("Slot not found: " + slotEntry.name); if (slot == null) throw new SerializationException("Skin slot not found: " + slotEntry.name);
for (JsonValue entry = slotEntry.child; entry != null; entry = entry.next) { for (JsonValue entry = slotEntry.child; entry != null; entry = entry.next) {
try { try {
Attachment attachment = readAttachment(entry, skin, slot.index, entry.name, skeletonData); Attachment attachment = readAttachment(entry, skin, slot.index, entry.name, skeletonData);
@ -1136,7 +1138,7 @@ public class SkeletonJson extends SkeletonLoader {
if (skin == null) throw new SerializationException("Skin not found: " + attachmentsMap.name); if (skin == null) throw new SerializationException("Skin not found: " + attachmentsMap.name);
for (JsonValue slotMap = attachmentsMap.child; slotMap != null; slotMap = slotMap.next) { for (JsonValue slotMap = attachmentsMap.child; slotMap != null; slotMap = slotMap.next) {
SlotData slot = skeletonData.findSlot(slotMap.name); SlotData slot = skeletonData.findSlot(slotMap.name);
if (slot == null) throw new SerializationException("Slot not found: " + slotMap.name); if (slot == null) throw new SerializationException("Attachment slot not found: " + slotMap.name);
for (JsonValue attachmentMap = slotMap.child; attachmentMap != null; attachmentMap = attachmentMap.next) { for (JsonValue attachmentMap = slotMap.child; attachmentMap != null; attachmentMap = attachmentMap.next) {
Attachment attachment = skin.getAttachment(slot.index, attachmentMap.name); Attachment attachment = skin.getAttachment(slot.index, attachmentMap.name);
if (attachment == null) throw new SerializationException("Timeline attachment not found: " + attachmentMap.name); if (attachment == null) throw new SerializationException("Timeline attachment not found: " + attachmentMap.name);
@ -1206,38 +1208,35 @@ public class SkeletonJson extends SkeletonLoader {
JsonValue drawOrderMap = map.get("drawOrder"); JsonValue drawOrderMap = map.get("drawOrder");
if (drawOrderMap != null) { if (drawOrderMap != null) {
var timeline = new DrawOrderTimeline(drawOrderMap.size); var timeline = new DrawOrderTimeline(drawOrderMap.size);
int slotCount = skeletonData.slots.size; int slotCount = skeletonData.slots.size, frame = 0;
int frame = 0; for (JsonValue keyMap = drawOrderMap.child; keyMap != null; keyMap = keyMap.next, frame++)
for (JsonValue keyMap = drawOrderMap.child; keyMap != null; keyMap = keyMap.next, frame++) { timeline.setFrame(frame, keyMap.getFloat("time", 0), readDrawOrder(skeletonData, keyMap, slotCount, null));
int[] drawOrder = null;
JsonValue offsets = keyMap.get("offsets");
if (offsets != null) {
drawOrder = new int[slotCount];
for (int i = slotCount - 1; i >= 0; i--)
drawOrder[i] = -1;
var unchanged = new int[slotCount - offsets.size];
int originalIndex = 0, unchangedIndex = 0;
for (JsonValue offsetMap = offsets.child; offsetMap != null; offsetMap = offsetMap.next) {
SlotData slot = skeletonData.findSlot(offsetMap.getString("slot"));
if (slot == null) throw new SerializationException("Slot not found: " + offsetMap.getString("slot"));
// Collect unchanged items.
while (originalIndex != slot.index)
unchanged[unchangedIndex++] = originalIndex++;
// Set changed items.
drawOrder[originalIndex + offsetMap.getInt("offset")] = originalIndex++;
}
// Collect remaining unchanged items.
while (originalIndex < slotCount)
unchanged[unchangedIndex++] = originalIndex++;
// Fill in unchanged items.
for (int i = slotCount - 1; i >= 0; i--)
if (drawOrder[i] == -1) drawOrder[i] = unchanged[--unchangedIndex];
}
timeline.setFrame(frame, keyMap.getFloat("time", 0), drawOrder);
}
timelines.add(timeline); timelines.add(timeline);
} }
// Draw order folder timelines.
JsonValue drawOrderFoldersMap = map.get("drawOrderFolder");
if (drawOrderFoldersMap != null) {
for (JsonValue timelineMap = drawOrderFoldersMap.child; timelineMap != null; timelineMap = timelineMap.next) {
JsonValue slotEntry = timelineMap.get("slots");
var folderSlots = new int[slotEntry.size];
int ii = 0;
for (slotEntry = slotEntry.child; slotEntry != null; slotEntry = slotEntry.next, ii++) {
SlotData slot = skeletonData.findSlot(slotEntry.asString());
if (slot == null) throw new SerializationException("Draw order folder slot not found: " + slotEntry.asString());
folderSlots[ii] = slot.index;
}
JsonValue keyMap = timelineMap.get("keys");
var timeline = new DrawOrderFolderTimeline(keyMap.size, folderSlots, skeletonData.slots.size);
int frame = 0;
for (keyMap = keyMap.child; keyMap != null; keyMap = keyMap.next, frame++)
timeline.setFrame(frame, keyMap.getFloat("time", 0),
readDrawOrder(skeletonData, keyMap, folderSlots.length, folderSlots));
timelines.add(timeline);
}
}
// Event timeline. // Event timeline.
JsonValue eventsMap = map.get("events"); JsonValue eventsMap = map.get("events");
if (eventsMap != null) { if (eventsMap != null) {
@ -1314,6 +1313,45 @@ public class SkeletonJson extends SkeletonLoader {
} }
} }
/** @param folderSlots Slot names are resolved to positions within this array. If null, slot indices are used as positions. */
private @Null int[] readDrawOrder (SkeletonData skeletonData, JsonValue keyMap, int slotCount, @Null int[] folderSlots) {
JsonValue changes = keyMap.get("offsets");
if (changes == null) return null; // Setup draw order.
var drawOrder = new int[slotCount];
Arrays.fill(drawOrder, -1);
var unchanged = new int[slotCount - changes.size];
int originalIndex = 0, unchangedIndex = 0;
for (JsonValue offsetMap = changes.child; offsetMap != null; offsetMap = offsetMap.next) {
SlotData slot = skeletonData.findSlot(offsetMap.getString("slot"));
if (slot == null) throw new SerializationException("Draw order slot not found: " + offsetMap.getString("slot"));
int index;
if (folderSlots == null)
index = slot.index;
else {
index = -1;
for (int i = 0; i < slotCount; i++) {
if (folderSlots[i] == slot.index) {
index = i;
break;
}
}
if (index == -1) throw new SerializationException("Slot not in folder: " + offsetMap.getString("slot"));
}
// Collect unchanged items.
while (originalIndex != index)
unchanged[unchangedIndex++] = originalIndex++;
// Set changed items.
drawOrder[originalIndex + offsetMap.getInt("offset")] = originalIndex++;
}
// Collect remaining unchanged items.
while (originalIndex < slotCount)
unchanged[unchangedIndex++] = originalIndex++;
// Fill in unchanged items.
for (int i = slotCount - 1; i >= 0; i--)
if (drawOrder[i] == -1) drawOrder[i] = unchanged[--unchangedIndex];
return drawOrder;
}
int readCurve (JsonValue curve, CurveTimeline timeline, int bezier, int frame, int value, float time1, float time2, int readCurve (JsonValue curve, CurveTimeline timeline, int bezier, int frame, int value, float time1, float time2,
float value1, float value2, float scale) { float value1, float value2, float scale) {
if (curve.isString()) { if (curve.isString()) {