Browse Source

Keyable draw order and other fixes.

NathanSweet 12 years ago
parent
commit
f174b1a3cb

+ 49 - 0
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<Event> 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<Slot> drawOrder = skeleton.drawOrder;
+			Array<Slot> slots = skeleton.slots;
+			int[] drawOrderToSetupIndex = drawOrders[frameIndex];
+			for (int i = 0, n = drawOrderToSetupIndex.length; i < n; i++)
+				drawOrder.set(i, slots.get(drawOrderToSetupIndex[i]));
+		}
+	}
 }

+ 8 - 1
spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java

@@ -34,7 +34,7 @@ public class Skeleton {
 	final SkeletonData data;
 	final Array<Bone> bones;
 	final Array<Slot> slots;
-	final Array<Slot> drawOrder;
+	Array<Slot> drawOrder;
 	Skin skin;
 	final Color color;
 	float time;
@@ -112,6 +112,8 @@ public class Skeleton {
 	}
 
 	public void setSlotsToSetupPose () {
+		drawOrder.clear();
+		drawOrder.addAll(slots);
 		Array<Slot> 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<Slot> drawOrder) {
+		this.drawOrder = drawOrder;
+	}
+
 	/** @return May be null. */
 	public Skin getSkin () {
 		return skin;

+ 33 - 20
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<SlotData> 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);

+ 26 - 0
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<SlotData> 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));
 	}

+ 4 - 0
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<String> 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<Attachment> attachments) {
 		if (attachments == null) throw new IllegalArgumentException("attachments cannot be null.");
+		if (slotIndex < 0) throw new IllegalArgumentException("slotIndex must be >= 0.");
 		for (Entry<Key, Attachment> entry : this.attachments.entries())
 			if (entry.key.slotIndex == slotIndex) attachments.add(entry.value);
 	}

+ 2 - 0
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"));