From f174b1a3cb202b6cf4bfff227bc2336cf05d2e81 Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Mon, 19 Aug 2013 23:10:11 +0200 Subject: [PATCH] Keyable draw order and other fixes. --- .../com/esotericsoftware/spine/Animation.java | 49 +++++++++++++++++ .../com/esotericsoftware/spine/Skeleton.java | 9 +++- .../spine/SkeletonBinary.java | 53 ++++++++++++------- .../esotericsoftware/spine/SkeletonJson.java | 26 +++++++++ .../src/com/esotericsoftware/spine/Skin.java | 4 ++ .../esotericsoftware/spine/SkeletonTest.java | 2 + 6 files changed, 122 insertions(+), 21 deletions(-) diff --git a/spine-libgdx/src/com/esotericsoftware/spine/Animation.java b/spine-libgdx/src/com/esotericsoftware/spine/Animation.java index 43c435120..866b0883a 100644 --- a/spine-libgdx/src/com/esotericsoftware/spine/Animation.java +++ b/spine-libgdx/src/com/esotericsoftware/spine/Animation.java @@ -522,6 +522,10 @@ public class Animation { return frames; } + public Event[] getEvents () { + return events; + } + /** Sets the time of the specified keyframe. */ public void setFrame (int frameIndex, float time, Event event) { frames[frameIndex] = time; @@ -548,4 +552,49 @@ public class Animation { firedEvents.add(events[frameIndex]); } } + + static public class DrawOrderTimeline implements Timeline { + private final float[] frames; // time, ... + private final int[][] drawOrders; + + public DrawOrderTimeline (int frameCount) { + frames = new float[frameCount]; + drawOrders = new int[frameCount][]; + } + + public int getFrameCount () { + return frames.length; + } + + public float[] getFrames () { + return frames; + } + + public int[][] getDrawOrders () { + return drawOrders; + } + + /** Sets the time of the specified keyframe. */ + public void setFrame (int frameIndex, float time, int[] drawOrder) { + frames[frameIndex] = time; + drawOrders[frameIndex] = drawOrder; + } + + public void apply (Skeleton skeleton, float lastTime, float time, float alpha, Array firedEvents) { + float[] frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + int frameIndex; + if (time >= frames[frames.length - 1]) // Time is after last frame. + frameIndex = frames.length - 1; + else + frameIndex = binarySearch(frames, time, 1) - 1; + + Array drawOrder = skeleton.drawOrder; + Array slots = skeleton.slots; + int[] drawOrderToSetupIndex = drawOrders[frameIndex]; + for (int i = 0, n = drawOrderToSetupIndex.length; i < n; i++) + drawOrder.set(i, slots.get(drawOrderToSetupIndex[i])); + } + } } diff --git a/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java b/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java index 70b73f1f6..be5dac6eb 100644 --- a/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java +++ b/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java @@ -34,7 +34,7 @@ public class Skeleton { final SkeletonData data; final Array bones; final Array slots; - final Array drawOrder; + Array drawOrder; Skin skin; final Color color; float time; @@ -112,6 +112,8 @@ public class Skeleton { } public void setSlotsToSetupPose () { + drawOrder.clear(); + drawOrder.addAll(slots); Array slots = this.slots; for (int i = 0, n = slots.size; i < n; i++) slots.get(i).setToSetupPose(i); @@ -180,6 +182,11 @@ public class Skeleton { return drawOrder; } + /** Sets the slots and the order they will be drawn. */ + public void setDrawOrder (Array drawOrder) { + this.drawOrder = drawOrder; + } + /** @return May be null. */ public Skin getSkin () { return skin; diff --git a/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java b/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java index e5d8b9b81..12a43c802 100644 --- a/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java +++ b/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java @@ -28,6 +28,7 @@ package com.esotericsoftware.spine; import com.esotericsoftware.spine.Animation.AttachmentTimeline; import com.esotericsoftware.spine.Animation.ColorTimeline; import com.esotericsoftware.spine.Animation.CurveTimeline; +import com.esotericsoftware.spine.Animation.DrawOrderTimeline; import com.esotericsoftware.spine.Animation.EventTimeline; import com.esotericsoftware.spine.Animation.RotateTimeline; import com.esotericsoftware.spine.Animation.ScaleTimeline; @@ -46,6 +47,7 @@ import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.g2d.TextureAtlas; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.DataInput; +import com.badlogic.gdx.utils.IntArray; import com.badlogic.gdx.utils.SerializationException; import java.io.IOException; @@ -57,6 +59,7 @@ public class SkeletonBinary { static public final int TIMELINE_ATTACHMENT = 3; static public final int TIMELINE_COLOR = 4; static public final int TIMELINE_EVENT = 5; + static public final int TIMELINE_DRAWORDER = 6; static public final int CURVE_LINEAR = 0; static public final int CURVE_STEPPED = 1; @@ -96,11 +99,8 @@ public class SkeletonBinary { for (int i = 0, n = input.readInt(true); i < n; i++) { String name = input.readString(); BoneData parent = null; - String parentName = input.readString(); - if (parentName != null) { - parent = skeletonData.findBone(parentName); - if (parent == null) throw new SerializationException("Parent bone not found: " + parentName); - } + int parentIndex = input.readInt(true) - 1; + if (parentIndex != -1) parent = skeletonData.bones.get(parentIndex); BoneData boneData = new BoneData(name, parent); boneData.x = input.readFloat() * scale; boneData.y = input.readFloat() * scale; @@ -116,9 +116,7 @@ public class SkeletonBinary { // Slots. for (int i = 0, n = input.readInt(true); i < n; i++) { String slotName = input.readString(); - String boneName = input.readString(); - BoneData boneData = skeletonData.findBone(boneName); - if (boneData == null) throw new SerializationException("Bone not found: " + boneName); + BoneData boneData = skeletonData.bones.get(input.readInt(true)); SlotData slotData = new SlotData(slotName, boneData); Color.rgba8888ToColor(slotData.getColor(), input.readInt()); slotData.attachmentName = input.readString(); @@ -215,9 +213,7 @@ public class SkeletonBinary { try { int boneCount = input.readInt(true); for (int i = 0; i < boneCount; i++) { - String boneName = input.readString(); - int boneIndex = skeletonData.findBoneIndex(boneName); - if (boneIndex == -1) throw new SerializationException("Bone not found: " + boneName); + int boneIndex = input.readInt(true); int itemCount = input.readInt(true); for (int ii = 0; ii < itemCount; ii++) { int timelineType = input.readByte(); @@ -253,16 +249,13 @@ public class SkeletonBinary { timelines.add(timeline); duration = Math.max(duration, timeline.getFrames()[keyCount * 3 - 3]); break; - default: - throw new RuntimeException("Invalid timeline type for a bone: " + timelineType + " (" + boneName + ")"); } } } int slotCount = input.readInt(true); for (int i = 0; i < slotCount; i++) { - String slotName = input.readString(); - int slotIndex = skeletonData.findSlotIndex(slotName); + int slotIndex = input.readInt(true); int itemCount = input.readInt(true); for (int ii = 0; ii < itemCount; ii++) { int timelineType = input.readByte(); @@ -289,8 +282,6 @@ public class SkeletonBinary { timelines.add(timeline); duration = Math.max(duration, timeline.getFrames()[keyCount - 1]); break; - default: - throw new RuntimeException("Invalid timeline type for a slot: " + timelineType + " (" + slotName + ")"); } } } @@ -300,15 +291,37 @@ public class SkeletonBinary { EventTimeline timeline = new EventTimeline(eventCount); for (int i = 0; i < eventCount; i++) { float time = input.readFloat(); - String eventName = input.readString(); - EventData eventData = skeletonData.findEvent(eventName); - if (eventData == null) throw new SerializationException("Event not found: " + eventName); + EventData eventData = skeletonData.eventDatas.get(input.readInt(true)); Event event = new Event(eventData); event.intValue = input.readInt(false); event.floatValue = input.readFloat(); event.stringValue = input.readBoolean() ? input.readString() : eventData.stringValue; timeline.setFrame(i, time, event); } + timelines.add(timeline); + duration = Math.max(duration, timeline.getFrames()[eventCount - 1]); + } + + int drawOrderCount = input.readInt(true); + if (drawOrderCount > 0) { + Array slots = skeletonData.slots; + DrawOrderTimeline timeline = new DrawOrderTimeline(drawOrderCount); + for (int i = 0; i < drawOrderCount; i++) { + IntArray drawOrder = new IntArray(slots.size); + for (int ii = 0, n = slots.size; ii < n; ii++) + drawOrder.add(ii); + + int offsetCount = input.readInt(true); + for (int ii = 0; ii < offsetCount; ii++) { + int slotIndex = input.readInt(true); + int index = drawOrder.indexOf(slotIndex); + drawOrder.removeIndex(index); + drawOrder.insert(index + input.readInt(true), slotIndex); + } + timeline.setFrame(i, input.readFloat(), drawOrder.toArray()); + } + timelines.add(timeline); + duration = Math.max(duration, timeline.getFrames()[drawOrderCount - 1]); } } catch (IOException ex) { throw new SerializationException("Error reading skeleton file.", ex); diff --git a/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java b/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java index 3054158ee..b6f2a7f36 100644 --- a/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java +++ b/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java @@ -28,6 +28,7 @@ package com.esotericsoftware.spine; import com.esotericsoftware.spine.Animation.AttachmentTimeline; import com.esotericsoftware.spine.Animation.ColorTimeline; import com.esotericsoftware.spine.Animation.CurveTimeline; +import com.esotericsoftware.spine.Animation.DrawOrderTimeline; import com.esotericsoftware.spine.Animation.EventTimeline; import com.esotericsoftware.spine.Animation.RotateTimeline; import com.esotericsoftware.spine.Animation.ScaleTimeline; @@ -45,6 +46,7 @@ import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.g2d.TextureAtlas; import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.IntArray; import com.badlogic.gdx.utils.JsonReader; import com.badlogic.gdx.utils.JsonValue; import com.badlogic.gdx.utils.SerializationException; @@ -127,6 +129,7 @@ public class SkeletonJson { Skin skin = new Skin(skinMap.name()); for (JsonValue slotEntry = skinMap.child(); slotEntry != null; slotEntry = slotEntry.next()) { int slotIndex = skeletonData.findSlotIndex(slotEntry.name()); + if (slotIndex == -1) throw new SerializationException("Slot not found: " + slotEntry.name()); for (JsonValue entry = slotEntry.child(); entry != null; entry = entry.next()) { Attachment attachment = readAttachment(skin, entry.name(), entry); if (attachment != null) skin.addAttachment(slotIndex, entry.name(), attachment); @@ -240,6 +243,7 @@ public class SkeletonJson { for (JsonValue slotMap = map.getChild("slots"); slotMap != null; slotMap = slotMap.next()) { int slotIndex = skeletonData.findSlotIndex(slotMap.name()); + if (slotIndex == -1) throw new SerializationException("Slot not found: " + slotMap.name()); for (JsonValue timelineMap = slotMap.child(); timelineMap != null; timelineMap = timelineMap.next()) { String timelineName = timelineMap.name(); @@ -292,6 +296,28 @@ public class SkeletonJson { duration = Math.max(duration, timeline.getFrames()[timeline.getFrameCount() - 1]); } + JsonValue drawOrdersMap = map.get("draworder"); + if (drawOrdersMap != null) { + DrawOrderTimeline timeline = new DrawOrderTimeline(drawOrdersMap.size); + Array slots = skeletonData.slots; + int frameIndex = 0; + for (JsonValue drawOrderMap = drawOrdersMap.child; drawOrderMap != null; drawOrderMap = drawOrderMap.next()) { + IntArray drawOrder = new IntArray(slots.size); + for (int i = 0, n = slots.size; i < n; i++) + drawOrder.add(i); + for (JsonValue offsetMap = drawOrderMap.getChild("offsets"); offsetMap != null; offsetMap = offsetMap.next()) { + int slotIndex = skeletonData.findSlotIndex(offsetMap.getString("slot")); + if (slotIndex == -1) throw new SerializationException("Slot not found: " + offsetMap.getString("slot")); + int index = drawOrder.indexOf(slotIndex); + drawOrder.removeIndex(index); + drawOrder.insert(index + offsetMap.getInt("offset"), slotIndex); + } + timeline.setFrame(frameIndex++, drawOrderMap.getFloat("time"), drawOrder.toArray()); + } + timelines.add(timeline); + duration = Math.max(duration, timeline.getFrames()[timeline.getFrameCount() - 1]); + } + timelines.shrink(); skeletonData.addAnimation(new Animation(name, timelines, duration)); } diff --git a/spine-libgdx/src/com/esotericsoftware/spine/Skin.java b/spine-libgdx/src/com/esotericsoftware/spine/Skin.java index 5e7505e98..e788e8a4c 100644 --- a/spine-libgdx/src/com/esotericsoftware/spine/Skin.java +++ b/spine-libgdx/src/com/esotericsoftware/spine/Skin.java @@ -45,6 +45,7 @@ public class Skin { public void addAttachment (int slotIndex, String name, Attachment attachment) { if (attachment == null) throw new IllegalArgumentException("attachment cannot be null."); + if (slotIndex < 0) throw new IllegalArgumentException("slotIndex must be >= 0."); Key key = new Key(); key.set(slotIndex, name); attachments.put(key, attachment); @@ -52,18 +53,21 @@ public class Skin { /** @return May be null. */ public Attachment getAttachment (int slotIndex, String name) { + if (slotIndex < 0) throw new IllegalArgumentException("slotIndex must be >= 0."); lookup.set(slotIndex, name); return attachments.get(lookup); } public void findNamesForSlot (int slotIndex, Array names) { if (names == null) throw new IllegalArgumentException("names cannot be null."); + if (slotIndex < 0) throw new IllegalArgumentException("slotIndex must be >= 0."); for (Key key : attachments.keys()) if (key.slotIndex == slotIndex) names.add(key.name); } public void findAttachmentsForSlot (int slotIndex, Array attachments) { if (attachments == null) throw new IllegalArgumentException("attachments cannot be null."); + if (slotIndex < 0) throw new IllegalArgumentException("slotIndex must be >= 0."); for (Entry entry : this.attachments.entries()) if (entry.key.slotIndex == slotIndex) attachments.add(entry.value); } diff --git a/spine-libgdx/test/com/esotericsoftware/spine/SkeletonTest.java b/spine-libgdx/test/com/esotericsoftware/spine/SkeletonTest.java index c9504ab4f..18c6ebe96 100644 --- a/spine-libgdx/test/com/esotericsoftware/spine/SkeletonTest.java +++ b/spine-libgdx/test/com/esotericsoftware/spine/SkeletonTest.java @@ -75,10 +75,12 @@ public class SkeletonTest extends ApplicationAdapter { }; if (true) { + System.out.println("JSON"); SkeletonJson json = new SkeletonJson(atlas); // json.setScale(2); skeletonData = json.readSkeletonData(Gdx.files.internal(name + ".json")); } else { + System.out.println("Binary"); SkeletonBinary binary = new SkeletonBinary(atlas); // binary.setScale(2); skeletonData = binary.readSkeletonData(Gdx.files.internal(name + ".skel"));