Keyable draw order and other fixes.

This commit is contained in:
NathanSweet 2013-08-19 23:10:11 +02:00
parent 9ffb689b49
commit f174b1a3cb
6 changed files with 122 additions and 21 deletions

View File

@ -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]));
}
}
}

View File

@ -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;

View File

@ -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);

View File

@ -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));
}

View File

@ -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);
}

View File

@ -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"));