diff --git a/spine-csharp/src/Animation.cs b/spine-csharp/src/Animation.cs
index d1502af04..c45a263dc 100644
--- a/spine-csharp/src/Animation.cs
+++ b/spine-csharp/src/Animation.cs
@@ -2018,7 +2018,7 @@ namespace Spine {
/// Changes a skeleton's .
public class DrawOrderTimeline : Timeline {
- static readonly string[] propertyIds = { ((int)Property.DrawOrder).ToString() };
+ internal static readonly string[] propertyIds = { ((int)Property.DrawOrder).ToString() };
readonly int[][] drawOrders;
@@ -2034,16 +2034,13 @@ namespace Spine {
/// The draw order for each frame.
/// .
public int[][] DrawOrders {
- get {
- return drawOrders;
- }
+ get { return drawOrders; }
}
/// Sets the time and draw order for the specified frame.
/// Between 0 and frameCount, inclusive.
/// The frame time in seconds.
- /// For each slot in , the index of the slot in the new draw order. May be null to use
- /// setup pose draw order.
+ /// Ordered indices, or null to use setup pose order.
public void SetFrame (int frame, float time, int[] drawOrder) {
frames[frame] = time;
drawOrders[frame] = drawOrder;
@@ -2075,6 +2072,89 @@ namespace Spine {
}
}
+
+ /// Changes a subset of a skeleton's .
+ public class DrawOrderFolderTimeline : Timeline {
+ private readonly int[] slots;
+ private readonly bool[] inFolder;
+ private readonly int[][] drawOrders;
+
+ /// indices controlled by this timeline, in setup order.
+ /// The maximum number of slots in the skeleton.
+ public DrawOrderFolderTimeline (int frameCount, int[] slots, int slotCount)
+ : base(frameCount, DrawOrderTimeline.propertyIds) {
+ this.slots = slots;
+ drawOrders = new int[frameCount][];
+ inFolder = new bool[slotCount];
+ foreach (int i in slots)
+ inFolder[i] = true;
+ }
+
+ override public int FrameCount {
+ get { return frames.Length; }
+ }
+
+ /// The indices that this timeline affects, in setup order.
+ public int[] Slots {
+ get { return slots; }
+ }
+
+ /// The draw order for each frame.
+ /// .
+ public int[][] DrawOrders {
+ get { return drawOrders; }
+ }
+
+ /// Sets the time and draw order for the specified frame.
+ /// Between 0 and frameCount, inclusive.
+ /// The frame time in seconds.
+ /// Ordered indices, or null to use setup pose order.
+ public void SetFrame (int frame, float time, int[] drawOrder) {
+ frames[frame] = time;
+ drawOrders[frame] = drawOrder;
+ }
+
+ override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList events, float alpha, MixBlend blend,
+ MixDirection direction, bool appliedPose) {
+
+ if (direction == MixDirection.Out) {
+ if (blend == MixBlend.Setup) setup(skeleton);
+ } else if (time < frames[0]) {
+ if (blend == MixBlend.Setup || blend == MixBlend.First) setup(skeleton);
+ } else {
+ int[] order = drawOrders[Search(frames, time)];
+ if (order == null)
+ setup(skeleton);
+ else
+ Apply(skeleton, order);
+ }
+ }
+
+ private void setup (Skeleton skeleton) {
+ bool[] 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) {
+ bool[] 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;
+ }
+ }
+ }
+ }
+
public interface IConstraintTimeline {
///
/// The index of the constraint in that will be changed when this timeline is applied.
diff --git a/spine-csharp/src/AnimationState.cs b/spine-csharp/src/AnimationState.cs
index 7ba72ec01..9f310a0b8 100644
--- a/spine-csharp/src/AnimationState.cs
+++ b/spine-csharp/src/AnimationState.cs
@@ -919,7 +919,8 @@ namespace Spine {
if (!propertyIds.AddAll(ids))
timelineMode[i] = AnimationState.Subsequent;
else if (to == null || timeline is AttachmentTimeline || timeline is DrawOrderTimeline
- || timeline is EventTimeline || !to.animation.HasTimeline(ids)) {
+ || timeline is DrawOrderFolderTimeline || timeline is EventTimeline
+ || !to.animation.HasTimeline(ids)) {
timelineMode[i] = AnimationState.First;
} else {
for (TrackEntry next = to.mixingTo; next != null; next = next.mixingTo) {
diff --git a/spine-csharp/src/SkeletonBinary.cs b/spine-csharp/src/SkeletonBinary.cs
index 944232aa3..997608c4d 100644
--- a/spine-csharp/src/SkeletonBinary.cs
+++ b/spine-csharp/src/SkeletonBinary.cs
@@ -1216,34 +1216,26 @@ namespace Spine {
}
// Draw order timeline.
+ int slotCount = skeletonData.slots.Count;
int drawOrderCount = input.ReadInt(true);
if (drawOrderCount > 0) {
var timeline = new DrawOrderTimeline(drawOrderCount);
- int slotCount = skeletonData.slots.Count;
- for (int i = 0; i < drawOrderCount; i++) {
- float time = input.ReadFloat();
- int offsetCount = input.ReadInt(true);
- var drawOrder = new int[slotCount];
- for (int ii = slotCount - 1; ii >= 0; ii--)
- drawOrder[ii] = -1;
- var unchanged = new int[slotCount - offsetCount];
- int originalIndex = 0, unchangedIndex = 0;
- for (int ii = 0; ii < offsetCount; ii++) {
- 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 ii = slotCount - 1; ii >= 0; ii--)
- if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex];
- timeline.SetFrame(i, time, drawOrder);
- }
+ for (int i = 0; i < drawOrderCount; i++)
+ timeline.SetFrame(i, input.ReadFloat(), ReadDrawOrder(input, slotCount));
+ timelines.Add(timeline);
+ }
+
+ // Draw order folder timelines.
+ int folderCount = input.ReadInt(true);
+ for (int i = 0; i < folderCount; i++) {
+ int folderSlotCount = input.ReadInt(true);
+ var folderSlots = new int[folderSlotCount];
+ for (int ii = 0; ii < folderSlotCount; ii++)
+ folderSlots[ii] = input.ReadInt(true);
+ int keyCount = input.ReadInt(true);
+ var timeline = new DrawOrderFolderTimeline(keyCount, folderSlots, slotCount);
+ for (int ii = 0; ii < keyCount; ii++)
+ timeline.SetFrame(ii, input.ReadFloat(), ReadDrawOrder(input, folderSlotCount));
timelines.Add(timeline);
}
@@ -1319,6 +1311,32 @@ namespace Spine {
timelines.Add(timeline);
}
+ /// Throws IOException when a read operation fails.
+ private int[] ReadDrawOrder (SkeletonInput input, int slotCount) {
+ int changeCount = input.ReadInt(true);
+ if (changeCount == 0) return null;
+ var drawOrder = new int[slotCount];
+ for (int ii = slotCount - 1; ii >= 0; ii--)
+ drawOrder[ii] = -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;
+ }
+
/// Throws IOException when a read operation fails.
void SetBezier (SkeletonInput input, CurveTimeline timeline, int bezier, int frame, int value, float time1, float time2,
float value1, float value2, float scale) {
diff --git a/spine-csharp/src/SkeletonJson.cs b/spine-csharp/src/SkeletonJson.cs
index 8720d71ce..42281476c 100644
--- a/spine-csharp/src/SkeletonJson.cs
+++ b/spine-csharp/src/SkeletonJson.cs
@@ -469,7 +469,9 @@ namespace Spine {
skin.constraints.TrimExcess();
if (skinMap.ContainsKey("attachments")) {
foreach (KeyValuePair slotEntry in (Dictionary)skinMap["attachments"]) {
- int slotIndex = FindSlotIndex(skeletonData, slotEntry.Key);
+ SlotData slot = skeletonData.FindSlot(slotEntry.Key);
+ if (slot == null) throw new Exception("Skin slot not found: " + slotEntry.Key);
+ int slotIndex = slot.Index;
foreach (KeyValuePair entry in ((Dictionary)slotEntry.Value)) {
try {
Attachment attachment = ReadAttachment((Dictionary)entry.Value, skin, slotIndex, entry.Key, skeletonData);
@@ -728,13 +730,6 @@ namespace Spine {
attachment.vertices = weights.ToArray();
}
- private int FindSlotIndex (SkeletonData skeletonData, string slotName) {
- SlotData[] slots = skeletonData.slots.Items;
- for (int i = 0, n = skeletonData.slots.Count; i < n; i++)
- if (slots[i].name == slotName) return i;
- throw new Exception("Slot not found: " + slotName);
- }
-
private void ReadAnimation (Dictionary map, string name, SkeletonData skeletonData) {
float scale = this.scale;
var timelines = new ExposedList();
@@ -743,7 +738,9 @@ namespace Spine {
if (map.ContainsKey("slots")) {
foreach (KeyValuePair entry in (Dictionary)map["slots"]) {
string slotName = entry.Key;
- int slotIndex = FindSlotIndex(skeletonData, slotName);
+ SlotData slot = skeletonData.FindSlot(slotName);
+ if (slot == null) throw new SerializationException("Slot not found: " + slotName);
+ int slotIndex = slot.Index;
Dictionary timelineMap = (Dictionary)entry.Value;
foreach (KeyValuePair timelineEntry in timelineMap) {
List