From 515d238886b2860249f75860cc21d24f20510dde Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Mon, 4 Oct 2021 00:57:25 -1000 Subject: [PATCH] JSON and binary loading for sequences. --- .../spine/SkeletonBinary.java | 134 +++++++++++------- .../esotericsoftware/spine/SkeletonJson.java | 122 ++++++++++------ .../spine/attachments/MeshAttachment.java | 3 +- .../spine/attachments/RegionAttachment.java | 3 +- 4 files changed, 161 insertions(+), 101 deletions(-) diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java index 9d12696e7..de09803a9 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java @@ -63,6 +63,7 @@ import com.esotericsoftware.spine.Animation.RotateTimeline; import com.esotericsoftware.spine.Animation.ScaleTimeline; import com.esotericsoftware.spine.Animation.ScaleXTimeline; import com.esotericsoftware.spine.Animation.ScaleYTimeline; +import com.esotericsoftware.spine.Animation.SequenceTimeline; import com.esotericsoftware.spine.Animation.ShearTimeline; import com.esotericsoftware.spine.Animation.ShearXTimeline; import com.esotericsoftware.spine.Animation.ShearYTimeline; @@ -85,6 +86,8 @@ 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.Sequence.SequenceMode; import com.esotericsoftware.spine.attachments.VertexAttachment; /** Loads skeleton data in the Spine binary format. @@ -111,6 +114,9 @@ public class SkeletonBinary extends SkeletonLoader { static public final int SLOT_RGB2 = 4; static public final int SLOT_ALPHA = 5; + static public final int ATTACHMENT_DEFORM = 0; + static public final int ATTACHMENT_SEQUENCE = 1; + static public final int PATH_POSITION = 0; static public final int PATH_SPACING = 1; static public final int PATH_MIX = 2; @@ -302,7 +308,7 @@ public class SkeletonBinary extends SkeletonLoader { if (parent == null) throw new SerializationException("Parent mesh not found: " + linkedMesh.parent); linkedMesh.mesh.setTimelineAttachment(linkedMesh.inheritTimelines ? (VertexAttachment)parent : linkedMesh.mesh); linkedMesh.mesh.setParentMesh((MeshAttachment)parent); - linkedMesh.mesh.updateRegion(); + if (linkedMesh.mesh.getSequence() == null) linkedMesh.mesh.updateRegion(); } linkedMeshes.clear(); @@ -395,11 +401,10 @@ public class SkeletonBinary extends SkeletonLoader { float width = input.readFloat(); float height = input.readFloat(); int color = input.readInt(); - - // BOZO! - Read sequence. + Sequence sequence = readSequence(input); if (path == null) path = name; - RegionAttachment region = attachmentLoader.newRegionAttachment(skin, name, path, null); + RegionAttachment region = attachmentLoader.newRegionAttachment(skin, name, path, sequence); if (region == null) return null; region.setPath(path); region.setX(x * scale); @@ -410,7 +415,8 @@ public class SkeletonBinary extends SkeletonLoader { region.setWidth(width * scale); region.setHeight(height * scale); Color.rgba8888ToColor(region.getColor(), color); - region.updateRegion(); + region.setSequence(sequence); + if (sequence == null) region.updateRegion(); return region; } case boundingbox: { @@ -434,6 +440,7 @@ public class SkeletonBinary extends SkeletonLoader { short[] triangles = readShortArray(input); Vertices vertices = readVertices(input, vertexCount); int hullLength = input.readInt(true); + Sequence sequence = readSequence(input); short[] edges = null; float width = 0, height = 0; if (nonessential) { @@ -442,10 +449,8 @@ public class SkeletonBinary extends SkeletonLoader { height = input.readFloat(); } - // BOZO! - Read sequence. - if (path == null) path = name; - MeshAttachment mesh = attachmentLoader.newMeshAttachment(skin, name, path, null); + MeshAttachment mesh = attachmentLoader.newMeshAttachment(skin, name, path, sequence); if (mesh == null) return null; mesh.setPath(path); Color.rgba8888ToColor(mesh.getColor(), color); @@ -454,8 +459,9 @@ public class SkeletonBinary extends SkeletonLoader { mesh.setWorldVerticesLength(vertexCount << 1); mesh.setTriangles(triangles); mesh.setRegionUVs(uvs); - mesh.updateRegion(); + if (sequence == null) mesh.updateRegion(); mesh.setHullLength(hullLength << 1); + mesh.setSequence(sequence); if (nonessential) { mesh.setEdges(edges); mesh.setWidth(width * scale); @@ -469,19 +475,19 @@ public class SkeletonBinary extends SkeletonLoader { String skinName = input.readStringRef(); String parent = input.readStringRef(); boolean inheritTimelines = input.readBoolean(); + Sequence sequence = readSequence(input); float width = 0, height = 0; if (nonessential) { width = input.readFloat(); height = input.readFloat(); } - // BOZO! - Read sequence. - if (path == null) path = name; - MeshAttachment mesh = attachmentLoader.newMeshAttachment(skin, name, path, null); + MeshAttachment mesh = attachmentLoader.newMeshAttachment(skin, name, path, sequence); if (mesh == null) return null; mesh.setPath(path); Color.rgba8888ToColor(mesh.getColor(), color); + mesh.setSequence(sequence); if (nonessential) { mesh.setWidth(width * scale); mesh.setHeight(height * scale); @@ -542,6 +548,15 @@ public class SkeletonBinary extends SkeletonLoader { return null; } + private Sequence readSequence (SkeletonInput input) throws IOException { + if (!input.readBoolean()) return null; + Sequence sequence = new Sequence(input.readInt(true)); + sequence.setStart(input.readInt(true)); + sequence.setDigits(input.readInt(true)); + sequence.setSetupIndex(input.readInt(true)); + return sequence; + } + private Vertices readVertices (SkeletonInput input, int vertexCount) throws IOException { float scale = this.scale; int verticesLength = vertexCount << 1; @@ -751,7 +766,6 @@ public class SkeletonBinary extends SkeletonLoader { a = a2; } timelines.add(timeline); - break; } } } @@ -897,57 +911,73 @@ public class SkeletonBinary extends SkeletonLoader { } } - // Deform timelines. + // Attachment timelines. for (int i = 0, n = input.readInt(true); i < n; i++) { Skin skin = skeletonData.skins.get(input.readInt(true)); for (int ii = 0, nn = input.readInt(true); ii < nn; ii++) { int slotIndex = input.readInt(true); for (int iii = 0, nnn = input.readInt(true); iii < nnn; iii++) { String attachmentName = input.readStringRef(); - VertexAttachment attachment = (VertexAttachment)skin.getAttachment(slotIndex, attachmentName); - if (attachment == null) throw new SerializationException("Vertex attachment not found: " + attachmentName); - boolean weighted = attachment.getBones() != null; - float[] vertices = attachment.getVertices(); - int deformLength = weighted ? (vertices.length / 3) << 1 : vertices.length; + Attachment attachment = skin.getAttachment(slotIndex, attachmentName); + if (attachment == null) throw new SerializationException("Timeline attachment not found: " + attachmentName); - int frameCount = input.readInt(true), frameLast = frameCount - 1; - DeformTimeline timeline = new DeformTimeline(frameCount, input.readInt(true), slotIndex, attachment); + int timelineType = input.readByte(), frameCount = input.readInt(true), frameLast = frameCount - 1; + switch (timelineType) { + case ATTACHMENT_DEFORM: { + VertexAttachment vertexAttachment = (VertexAttachment)attachment; + boolean weighted = vertexAttachment.getBones() != null; + float[] vertices = vertexAttachment.getVertices(); + int deformLength = weighted ? (vertices.length / 3) << 1 : vertices.length; - float time = input.readFloat(); - for (int frame = 0, bezier = 0;; frame++) { - float[] deform; - int end = input.readInt(true); - if (end == 0) - deform = weighted ? new float[deformLength] : vertices; - else { - deform = new float[deformLength]; - int start = input.readInt(true); - end += start; - if (scale == 1) { - for (int v = start; v < end; v++) - deform[v] = input.readFloat(); - } else { - for (int v = start; v < end; v++) - deform[v] = input.readFloat() * scale; + DeformTimeline timeline = new DeformTimeline(frameCount, input.readInt(true), slotIndex, vertexAttachment); + + float time = input.readFloat(); + for (int frame = 0, bezier = 0;; frame++) { + float[] deform; + int end = input.readInt(true); + if (end == 0) + deform = weighted ? new float[deformLength] : vertices; + else { + deform = new float[deformLength]; + int start = input.readInt(true); + end += start; + if (scale == 1) { + for (int v = start; v < end; v++) + deform[v] = input.readFloat(); + } else { + for (int v = start; v < end; v++) + deform[v] = input.readFloat() * scale; + } + if (!weighted) { + for (int v = 0, vn = deform.length; v < vn; v++) + deform[v] += vertices[v]; + } } - if (!weighted) { - for (int v = 0, vn = deform.length; v < vn; v++) - deform[v] += vertices[v]; + timeline.setFrame(frame, time, deform); + if (frame == frameLast) break; + float time2 = input.readFloat(); + switch (input.readByte()) { + case CURVE_STEPPED: + timeline.setStepped(frame); + break; + case CURVE_BEZIER: + setBezier(input, timeline, bezier++, frame, 0, time, time2, 0, 1, 1); } + time = time2; } - timeline.setFrame(frame, time, deform); - if (frame == frameLast) break; - float time2 = input.readFloat(); - switch (input.readByte()) { - case CURVE_STEPPED: - timeline.setStepped(frame); - break; - case CURVE_BEZIER: - setBezier(input, timeline, bezier++, frame, 0, time, time2, 0, 1, 1); - } - time = time2; + timelines.add(timeline); + break; + } + case ATTACHMENT_SEQUENCE: + SequenceTimeline timeline = new SequenceTimeline(frameCount, slotIndex, attachment); + for (int frame = 0; frame < frameCount; frame++) { + float time = input.readFloat(); + int modeAndIndex = input.readInt(); + timeline.setFrame(frame, time, SequenceMode.values[modeAndIndex & 0xf], modeAndIndex >> 4, + input.readFloat()); + } + timelines.add(timeline); } - timelines.add(timeline); } } } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java index b154e314a..3dda14efc 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java @@ -41,6 +41,7 @@ import com.badlogic.gdx.utils.FloatArray; import com.badlogic.gdx.utils.IntArray; import com.badlogic.gdx.utils.JsonReader; import com.badlogic.gdx.utils.JsonValue; +import com.badlogic.gdx.utils.Null; import com.badlogic.gdx.utils.SerializationException; import com.esotericsoftware.spine.Animation.AlphaTimeline; @@ -63,6 +64,7 @@ import com.esotericsoftware.spine.Animation.RotateTimeline; import com.esotericsoftware.spine.Animation.ScaleTimeline; import com.esotericsoftware.spine.Animation.ScaleXTimeline; import com.esotericsoftware.spine.Animation.ScaleYTimeline; +import com.esotericsoftware.spine.Animation.SequenceTimeline; import com.esotericsoftware.spine.Animation.ShearTimeline; import com.esotericsoftware.spine.Animation.ShearXTimeline; import com.esotericsoftware.spine.Animation.ShearYTimeline; @@ -84,6 +86,8 @@ 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.Sequence.SequenceMode; import com.esotericsoftware.spine.attachments.VertexAttachment; /** Loads skeleton data in the Spine JSON format. @@ -323,7 +327,7 @@ public class SkeletonJson extends SkeletonLoader { if (parent == null) throw new SerializationException("Parent mesh not found: " + linkedMesh.parent); linkedMesh.mesh.setTimelineAttachment(linkedMesh.inheritTimelines ? (VertexAttachment)parent : linkedMesh.mesh); linkedMesh.mesh.setParentMesh((MeshAttachment)parent); - linkedMesh.mesh.updateRegion(); + if (linkedMesh.mesh.getSequence() == null) linkedMesh.mesh.updateRegion(); } linkedMeshes.clear(); @@ -366,8 +370,8 @@ public class SkeletonJson extends SkeletonLoader { switch (AttachmentType.valueOf(map.getString("type", AttachmentType.region.name()))) { case region: { String path = map.getString("path", name); - // BOZO! - Read sequence. - RegionAttachment region = attachmentLoader.newRegionAttachment(skin, name, path, null); + Sequence sequence = readSequence(map.get("sequence")); + RegionAttachment region = attachmentLoader.newRegionAttachment(skin, name, path, sequence); if (region == null) return null; region.setPath(path); region.setX(map.getFloat("x", 0) * scale); @@ -377,11 +381,12 @@ public class SkeletonJson extends SkeletonLoader { region.setRotation(map.getFloat("rotation", 0)); region.setWidth(map.getFloat("width") * scale); region.setHeight(map.getFloat("height") * scale); + region.setSequence(sequence); String color = map.getString("color", null); if (color != null) Color.valueOf(color, region.getColor()); - region.updateRegion(); + if (region.getSequence() == null) region.updateRegion(); return region; } case boundingbox: { @@ -396,8 +401,8 @@ public class SkeletonJson extends SkeletonLoader { case mesh: case linkedmesh: { String path = map.getString("path", name); - // BOZO! - Read sequence. - MeshAttachment mesh = attachmentLoader.newMeshAttachment(skin, name, path, null); + Sequence sequence = readSequence(map.get("sequence")); + MeshAttachment mesh = attachmentLoader.newMeshAttachment(skin, name, path, sequence); if (mesh == null) return null; mesh.setPath(path); @@ -406,6 +411,7 @@ public class SkeletonJson extends SkeletonLoader { mesh.setWidth(map.getFloat("width", 0) * scale); mesh.setHeight(map.getFloat("height", 0) * scale); + mesh.setSequence(sequence); String parent = map.getString("parent", null); if (parent != null) { @@ -418,7 +424,7 @@ public class SkeletonJson extends SkeletonLoader { readVertices(map, mesh, uvs.length); mesh.setTriangles(map.require("triangles").asShortArray()); mesh.setRegionUVs(uvs); - mesh.updateRegion(); + if (mesh.getSequence() == null) mesh.updateRegion(); if (map.has("hull")) mesh.setHullLength(map.require("hull").asInt() << 1); if (map.has("edges")) mesh.setEdges(map.require("edges").asShortArray()); @@ -474,6 +480,15 @@ public class SkeletonJson extends SkeletonLoader { return null; } + private Sequence readSequence (@Null JsonValue map) { + if (map == null) return null; + Sequence sequence = new Sequence(map.getInt("count")); + sequence.setStart(map.getInt("start", 1)); + sequence.setDigits(map.getInt("digits", 0)); + sequence.setSetupIndex(map.getInt("setup", 0)); + return sequence; + } + private void readVertices (JsonValue map, VertexAttachment attachment, int verticesLength) { attachment.setWorldVerticesLength(verticesLength); float[] vertices = map.require("vertices").asFloatArray(); @@ -862,8 +877,8 @@ public class SkeletonJson extends SkeletonLoader { } } - // Deform timelines. - for (JsonValue deformMap = map.getChild("deform"); deformMap != null; deformMap = deformMap.next) { + // Attachment timelines. + for (JsonValue deformMap = map.getChild("attachments"); deformMap != null; deformMap = deformMap.next) { Skin skin = skeletonData.findSkin(deformMap.name); if (skin == null) throw new SerializationException("Skin not found: " + deformMap.name); for (JsonValue slotMap = deformMap.child; slotMap != null; slotMap = slotMap.next) { @@ -873,46 +888,63 @@ public class SkeletonJson extends SkeletonLoader { JsonValue keyMap = timelineMap.child; if (keyMap == null) continue; - VertexAttachment attachment = (VertexAttachment)skin.getAttachment(slot.index, timelineMap.name); - if (attachment == null) throw new SerializationException("Deform attachment not found: " + timelineMap.name); - boolean weighted = attachment.getBones() != null; - float[] vertices = attachment.getVertices(); - int deformLength = weighted ? (vertices.length / 3) << 1 : vertices.length; + Attachment attachment = skin.getAttachment(slot.index, timelineMap.name); + if (attachment == null) throw new SerializationException("Timeline attachment not found: " + timelineMap.name); - DeformTimeline timeline = new DeformTimeline(timelineMap.size, timelineMap.size, slot.index, attachment); - float time = keyMap.getFloat("time", 0); - for (int frame = 0, bezier = 0;; frame++) { - float[] deform; - JsonValue verticesValue = keyMap.get("vertices"); - if (verticesValue == null) - deform = weighted ? new float[deformLength] : vertices; - else { - deform = new float[deformLength]; - int start = keyMap.getInt("offset", 0); - arraycopy(verticesValue.asFloatArray(), 0, deform, start, verticesValue.size); - if (scale != 1) { - for (int i = start, n = i + verticesValue.size; i < n; i++) - deform[i] *= scale; - } - if (!weighted) { - for (int i = 0; i < deformLength; i++) - deform[i] += vertices[i]; - } - } + int frames = keyMap.size; + String timelineName = keyMap.name; + keyMap = keyMap.child; + if (timelineName.equals("deform")) { + VertexAttachment vertexAttachment = (VertexAttachment)attachment; + boolean weighted = vertexAttachment.getBones() != null; + float[] vertices = vertexAttachment.getVertices(); + int deformLength = weighted ? (vertices.length / 3) << 1 : vertices.length; - timeline.setFrame(frame, time, deform); - JsonValue nextMap = keyMap.next; - if (nextMap == null) { - timeline.shrink(bezier); - break; + DeformTimeline timeline = new DeformTimeline(frames, frames, slot.index, vertexAttachment); + float time = keyMap.getFloat("time", 0); + for (int frame = 0, bezier = 0;; frame++) { + float[] deform; + JsonValue verticesValue = keyMap.get("vertices"); + if (verticesValue == null) + deform = weighted ? new float[deformLength] : vertices; + else { + deform = new float[deformLength]; + int start = keyMap.getInt("offset", 0); + arraycopy(verticesValue.asFloatArray(), 0, deform, start, verticesValue.size); + if (scale != 1) { + for (int i = start, n = i + verticesValue.size; i < n; i++) + deform[i] *= scale; + } + if (!weighted) { + for (int i = 0; i < deformLength; i++) + deform[i] += vertices[i]; + } + } + + timeline.setFrame(frame, time, deform); + JsonValue nextMap = keyMap.next; + if (nextMap == null) { + timeline.shrink(bezier); + break; + } + float time2 = nextMap.getFloat("time", 0); + JsonValue curve = keyMap.get("curve"); + if (curve != null) bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, 0, 1, 1); + time = time2; + keyMap = nextMap; } - float time2 = nextMap.getFloat("time", 0); - JsonValue curve = keyMap.get("curve"); - if (curve != null) bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, 0, 1, 1); - time = time2; - keyMap = nextMap; + timelines.add(timeline); + } else if (timelineName.equals("sequence")) { + SequenceTimeline timeline = new SequenceTimeline(frames, slot.index, attachment); + float lastDelay = 0; + for (int frame = 0; keyMap != null; keyMap = keyMap.next, frame++) { + float delay = keyMap.getFloat("delay", lastDelay); + timeline.setFrame(frame, keyMap.getFloat("time", 0), SequenceMode.valueOf(keyMap.getString("mode", "stop")), + keyMap.getInt("index", 0), delay); + delay = lastDelay; + } + timelines.add(timeline); } - timelines.add(timeline); } } } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/MeshAttachment.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/MeshAttachment.java index c1eae3fc0..01cbcb491 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/MeshAttachment.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/MeshAttachment.java @@ -95,8 +95,7 @@ public class MeshAttachment extends VertexAttachment implements HasTextureRegion this.region = region; } - public TextureRegion getRegion () { - if (region == null) throw new IllegalStateException("Region has not been set: " + this); + public @Null TextureRegion getRegion () { return region; } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/RegionAttachment.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/RegionAttachment.java index 0301cdc1d..1ad4e5997 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/RegionAttachment.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/RegionAttachment.java @@ -156,8 +156,7 @@ public class RegionAttachment extends Attachment implements HasTextureRegion { this.region = region; } - public TextureRegion getRegion () { - if (region == null) throw new IllegalStateException("Region has not been set: " + name); + public @Null TextureRegion getRegion () { return region; }