diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/AnimationStateTests.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/AnimationStateTests.java index e74745f43..4df486d51 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/AnimationStateTests.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/AnimationStateTests.java @@ -36,6 +36,7 @@ import com.badlogic.gdx.Files.FileType; import com.badlogic.gdx.backends.lwjgl.LwjglFileHandle; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.Null; import com.badlogic.gdx.utils.Pool; import com.esotericsoftware.spine.AnimationState.AnimationStateListener; @@ -47,19 +48,15 @@ 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.SequenceAttachment; +import com.esotericsoftware.spine.attachments.Sequence; public class AnimationStateTests { final SkeletonJson json = new SkeletonJson(new AttachmentLoader() { - public RegionAttachment newRegionAttachment (Skin skin, String name, String path) { + public RegionAttachment newRegionAttachment (Skin skin, String name, String path, @Null Sequence sequence) { return null; } - public MeshAttachment newMeshAttachment (Skin skin, String name, String path) { - return null; - } - - public SequenceAttachment newSequenceAttachment (Skin skin, String name, String path, int frameCount) { + public MeshAttachment newMeshAttachment (Skin skin, String name, String path, @Null Sequence sequence) { return null; } @@ -893,7 +890,6 @@ public class AnimationStateTests { state.apply(skeleton); while (time < endTime) { time += incr; - skeleton.update(incr); state.update(incr); // Reduce float discrepancies for tests. diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/BonePlotting.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/BonePlotting.java index c5a1e93f7..e492bbf94 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/BonePlotting.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/BonePlotting.java @@ -30,6 +30,7 @@ package com.esotericsoftware.spine; import com.badlogic.gdx.files.FileHandle; +import com.badlogic.gdx.utils.Null; import com.esotericsoftware.spine.Animation.MixBlend; import com.esotericsoftware.spine.Animation.MixDirection; @@ -40,21 +41,17 @@ 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.SequenceAttachment; +import com.esotericsoftware.spine.attachments.Sequence; public class BonePlotting { static public void main (String[] args) throws Exception { // This example shows how to load skeleton data and plot a bone transform for each animation. SkeletonJson json = new SkeletonJson(new AttachmentLoader() { - public RegionAttachment newRegionAttachment (Skin skin, String name, String path) { + public RegionAttachment newRegionAttachment (Skin skin, String name, String path, @Null Sequence sequence) { return null; } - public MeshAttachment newMeshAttachment (Skin skin, String name, String path) { - return null; - } - - public SequenceAttachment newSequenceAttachment (Skin skin, String name, String path, int frameCount) { + public MeshAttachment newMeshAttachment (Skin skin, String name, String path, @Null Sequence sequence) { return null; } diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/Box2DExample.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/Box2DExample.java index a5d81f1dc..a32408a2a 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/Box2DExample.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/Box2DExample.java @@ -49,12 +49,14 @@ import com.badlogic.gdx.physics.box2d.FixtureDef; import com.badlogic.gdx.physics.box2d.PolygonShape; import com.badlogic.gdx.physics.box2d.World; import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.Null; import com.badlogic.gdx.utils.ScreenUtils; import com.esotericsoftware.spine.Animation.MixBlend; import com.esotericsoftware.spine.Animation.MixDirection; import com.esotericsoftware.spine.attachments.AtlasAttachmentLoader; import com.esotericsoftware.spine.attachments.RegionAttachment; +import com.esotericsoftware.spine.attachments.Sequence; public class Box2DExample extends ApplicationAdapter { SpriteBatch batch; @@ -85,7 +87,7 @@ public class Box2DExample extends ApplicationAdapter { // This loader creates Box2dAttachments instead of RegionAttachments for an easy way to keep // track of the Box2D body for each attachment. AtlasAttachmentLoader atlasLoader = new AtlasAttachmentLoader(atlas) { - public RegionAttachment newRegionAttachment (Skin skin, String name, String path) { + public RegionAttachment newRegionAttachment (Skin skin, String name, String path, @Null Sequence sequence) { Box2dAttachment attachment = new Box2dAttachment(name); AtlasRegion region = atlas.findRegion(attachment.getName()); if (region == null) throw new RuntimeException("Region not found in atlas: " + attachment); diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/MixTest.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/MixTest.java index 6f39a6eae..cee706c5a 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/MixTest.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/MixTest.java @@ -125,7 +125,6 @@ public class MixTest extends ApplicationAdapter { } skeleton.updateWorldTransform(); - skeleton.update(Gdx.graphics.getDeltaTime()); batch.begin(); renderer.draw(batch, skeleton); diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/NormalMapTest.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/NormalMapTest.java index 999cb130f..900018b67 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/NormalMapTest.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/NormalMapTest.java @@ -134,7 +134,6 @@ public class NormalMapTest extends ApplicationAdapter { time += Gdx.graphics.getDeltaTime(); if (animation != null) animation.apply(skeleton, lastTime, time, true, null, 1, MixBlend.first, MixDirection.in); skeleton.updateWorldTransform(); - skeleton.update(Gdx.graphics.getDeltaTime()); lightPosition.x = Gdx.input.getX(); lightPosition.y = (Gdx.graphics.getHeight() - 1 - Gdx.input.getY()); diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/VertexEffectTest.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/VertexEffectTest.java index e759a5ae0..4fc136f75 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/VertexEffectTest.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/VertexEffectTest.java @@ -85,7 +85,6 @@ public class VertexEffectTest extends ApplicationAdapter { public void render () { // Update the skeleton and animation time. float delta = Gdx.graphics.getDeltaTime(); - skeleton.update(delta); state.update(delta); swirlTime += delta; diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java index 3ba5122b9..805e58c75 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java @@ -40,6 +40,9 @@ import com.badlogic.gdx.utils.Null; import com.badlogic.gdx.utils.ObjectSet; import com.esotericsoftware.spine.attachments.Attachment; +import com.esotericsoftware.spine.attachments.HasTextureRegion; +import com.esotericsoftware.spine.attachments.Sequence; +import com.esotericsoftware.spine.attachments.Sequence.SequenceMode; import com.esotericsoftware.spine.attachments.VertexAttachment; /** Stores a list of timelines to animate a skeleton's pose over time. */ @@ -175,7 +178,8 @@ public class Animation { attachment, deform, // event, drawOrder, // ikConstraint, transformConstraint, // - pathConstraintPosition, pathConstraintSpacing, pathConstraintMix + pathConstraintPosition, pathConstraintSpacing, pathConstraintMix, // + sequence } /** The base class for all timelines. */ @@ -1646,7 +1650,7 @@ public class Animation { /** The attachment that will be deformed. *
- * See {@link VertexAttachment#getDeformAttachment()}. */
+ * See {@link VertexAttachment#getTimelineAttachment()}. */
public VertexAttachment getAttachment () {
return attachment;
}
@@ -1724,9 +1728,9 @@ public class Animation {
if (!slot.bone.active) return;
Attachment slotAttachment = slot.attachment;
if (!(slotAttachment instanceof VertexAttachment)
- || ((VertexAttachment)slotAttachment).getDeformAttachment() != attachment) return;
+ || ((VertexAttachment)slotAttachment).getTimelineAttachment() != attachment) return;
- FloatArray deformArray = slot.getDeform();
+ FloatArray deformArray = slot.deform;
if (deformArray.size == 0) blend = setup;
float[][] vertices = this.vertices;
@@ -1734,7 +1738,6 @@ public class Animation {
float[] frames = this.frames;
if (time < frames[0]) { // Time is before first frame.
- VertexAttachment vertexAttachment = (VertexAttachment)slotAttachment;
switch (blend) {
case setup:
deformArray.clear();
@@ -1745,6 +1748,7 @@ public class Animation {
return;
}
float[] deform = deformArray.setSize(vertexCount);
+ VertexAttachment vertexAttachment = (VertexAttachment)slotAttachment;
if (vertexAttachment.getBones() == null) {
// Unweighted vertex positions.
float[] setupVertices = vertexAttachment.getVertices();
@@ -2419,4 +2423,90 @@ public class Animation {
}
}
}
+
+ /** Changes a slot's {@link Slot#getSequenceIndex()} for an attachment's {@link Sequence}. */
+ static public class SequenceTimeline extends Timeline implements SlotTimeline {
+ static public final int ENTRIES = 3;
+ static private final int MODE = 1, DELAY = 2;
+
+ final int slotIndex;
+ final HasTextureRegion attachment;
+
+ public
+ * The
@@ -60,7 +59,6 @@ public class Skeleton {
final Array
- * See {@link #update(float)}. */
- public float getTime () {
- return time;
- }
-
- public void setTime (float time) {
- this.time = time;
- }
-
- /** Increments the skeleton's {@link #time}. */
- public void update (float delta) {
- time += delta;
- }
-
public String toString () {
return data.name != null ? data.name : super.toString();
}
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 050805e1f..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,9 +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.SequenceAttachment;
-import com.esotericsoftware.spine.attachments.SequenceAttachment.SequenceMode;
-import com.esotericsoftware.spine.attachments.HasTextureRegion;
+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.
@@ -114,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;
@@ -303,9 +306,9 @@ public class SkeletonBinary extends SkeletonLoader {
if (skin == null) throw new SerializationException("Skin not found: " + linkedMesh.skin);
Attachment parent = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent);
if (parent == null) throw new SerializationException("Parent mesh not found: " + linkedMesh.parent);
- linkedMesh.mesh.setDeformAttachment(linkedMesh.inheritDeform ? (VertexAttachment)parent : linkedMesh.mesh);
+ 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();
@@ -398,9 +401,10 @@ public class SkeletonBinary extends SkeletonLoader {
float width = input.readFloat();
float height = input.readFloat();
int color = input.readInt();
+ Sequence sequence = readSequence(input);
if (path == null) path = name;
- RegionAttachment region = attachmentLoader.newRegionAttachment(skin, name, path);
+ RegionAttachment region = attachmentLoader.newRegionAttachment(skin, name, path, sequence);
if (region == null) return null;
region.setPath(path);
region.setX(x * scale);
@@ -411,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: {
@@ -435,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) {
@@ -444,7 +450,7 @@ public class SkeletonBinary extends SkeletonLoader {
}
if (path == null) path = name;
- MeshAttachment mesh = attachmentLoader.newMeshAttachment(skin, name, path);
+ MeshAttachment mesh = attachmentLoader.newMeshAttachment(skin, name, path, sequence);
if (mesh == null) return null;
mesh.setPath(path);
Color.rgba8888ToColor(mesh.getColor(), color);
@@ -453,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);
@@ -467,7 +474,8 @@ public class SkeletonBinary extends SkeletonLoader {
int color = input.readInt();
String skinName = input.readStringRef();
String parent = input.readStringRef();
- boolean inheritDeform = input.readBoolean();
+ boolean inheritTimelines = input.readBoolean();
+ Sequence sequence = readSequence(input);
float width = 0, height = 0;
if (nonessential) {
width = input.readFloat();
@@ -475,15 +483,16 @@ public class SkeletonBinary extends SkeletonLoader {
}
if (path == null) path = name;
- MeshAttachment mesh = attachmentLoader.newMeshAttachment(skin, name, path);
+ 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);
}
- linkedMeshes.add(new LinkedMesh(mesh, skinName, slotIndex, parent, inheritDeform));
+ linkedMeshes.add(new LinkedMesh(mesh, skinName, slotIndex, parent, inheritTimelines));
return mesh;
}
case path: {
@@ -521,7 +530,7 @@ public class SkeletonBinary extends SkeletonLoader {
if (nonessential) Color.rgba8888ToColor(point.getColor(), color);
return point;
}
- case clipping: {
+ case clipping:
int endSlotIndex = input.readInt(true);
int vertexCount = input.readInt(true);
Vertices vertices = readVertices(input, vertexCount);
@@ -536,28 +545,18 @@ public class SkeletonBinary extends SkeletonLoader {
if (nonessential) Color.rgba8888ToColor(clip.getColor(), color);
return clip;
}
- case sequence:
- Attachment attachment = readAttachment(input, skeletonData, skin, slotIndex, attachmentName, nonessential);
- int frameCount = input.readInt(true);
- float frameTime = input.readFloat();
- SequenceMode mode = SequenceMode.values[input.readInt(true)];
-
- if (attachment == null) return null;
- String path = ((HasTextureRegion)attachment).getPath();
-
- SequenceAttachment sequence = attachmentLoader.newSequenceAttachment(skin, name, path, frameCount);
- if (sequence == null) return null;
-
- sequence.setAttachment(attachment);
- sequence.setPath(path);
- sequence.setFrameCount(frameCount);
- sequence.setFrameTime(frameTime);
- sequence.setMode(mode);
- return sequence;
- }
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;
@@ -767,7 +766,6 @@ public class SkeletonBinary extends SkeletonLoader {
a = a2;
}
timelines.add(timeline);
- break;
}
}
}
@@ -913,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 d32534b13..e17396922 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,9 +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.SequenceAttachment;
-import com.esotericsoftware.spine.attachments.SequenceAttachment.SequenceMode;
-import com.esotericsoftware.spine.attachments.HasTextureRegion;
+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.
@@ -324,9 +325,9 @@ public class SkeletonJson extends SkeletonLoader {
if (skin == null) throw new SerializationException("Skin not found: " + linkedMesh.skin);
Attachment parent = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent);
if (parent == null) throw new SerializationException("Parent mesh not found: " + linkedMesh.parent);
- linkedMesh.mesh.setDeformAttachment(linkedMesh.inheritDeform ? (VertexAttachment)parent : linkedMesh.mesh);
+ linkedMesh.mesh.setTimelineAttachment(linkedMesh.inheritTimelines ? (VertexAttachment)parent : linkedMesh.mesh);
linkedMesh.mesh.setParentMesh((MeshAttachment)parent);
- linkedMesh.mesh.updateRegion();
+ if (linkedMesh.mesh.getRegion() != null) linkedMesh.mesh.updateRegion();
}
linkedMeshes.clear();
@@ -369,7 +370,8 @@ public class SkeletonJson extends SkeletonLoader {
switch (AttachmentType.valueOf(map.getString("type", AttachmentType.region.name()))) {
case region: {
String path = map.getString("path", name);
- RegionAttachment region = attachmentLoader.newRegionAttachment(skin, name, path);
+ 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);
@@ -379,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.getRegion() != null) region.updateRegion();
return region;
}
case boundingbox: {
@@ -398,7 +401,8 @@ public class SkeletonJson extends SkeletonLoader {
case mesh:
case linkedmesh: {
String path = map.getString("path", name);
- MeshAttachment mesh = attachmentLoader.newMeshAttachment(skin, name, path);
+ Sequence sequence = readSequence(map.get("sequence"));
+ MeshAttachment mesh = attachmentLoader.newMeshAttachment(skin, name, path, sequence);
if (mesh == null) return null;
mesh.setPath(path);
@@ -407,11 +411,12 @@ 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) {
linkedMeshes
- .add(new LinkedMesh(mesh, map.getString("skin", null), slotIndex, parent, map.getBoolean("deform", true)));
+ .add(new LinkedMesh(mesh, map.getString("skin", null), slotIndex, parent, map.getBoolean("timelines", true)));
return mesh;
}
@@ -419,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.getRegion() != null) mesh.updateRegion();
if (map.has("hull")) mesh.setHullLength(map.require("hull").asInt() << 1);
if (map.has("edges")) mesh.setEdges(map.require("edges").asShortArray());
@@ -455,7 +460,7 @@ public class SkeletonJson extends SkeletonLoader {
if (color != null) Color.valueOf(color, point.getColor());
return point;
}
- case clipping: {
+ case clipping:
ClippingAttachment clip = attachmentLoader.newClippingAttachment(skin, name);
if (clip == null) return null;
@@ -472,23 +477,18 @@ public class SkeletonJson extends SkeletonLoader {
if (color != null) Color.valueOf(color, clip.getColor());
return clip;
}
- case sequence:
- Attachment attachment = readAttachment(map.getChild("attachment"), skin, slotIndex, name, skeletonData);
- if (attachment == null) return null;
- String path = ((HasTextureRegion)attachment).getPath();
- int frameCount = map.getInt("count");
- SequenceAttachment sequence = attachmentLoader.newSequenceAttachment(skin, name, path, frameCount);
- if (sequence == null) return null;
- sequence.setAttachment(attachment);
- sequence.setPath(path);
- sequence.setFrameCount(frameCount);
- sequence.setFrameTime(map.getInt("time"));
- sequence.setMode(SequenceMode.valueOf(map.getString("mode", SequenceMode.forward.name())));
- return sequence;
- }
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();
@@ -533,7 +533,7 @@ public class SkeletonJson extends SkeletonLoader {
if (timelineName.equals("attachment")) {
AttachmentTimeline timeline = new AttachmentTimeline(frames, slot.index);
for (int frame = 0; keyMap != null; keyMap = keyMap.next, frame++)
- timeline.setFrame(frame, keyMap.getFloat("time", 0), keyMap.getString("name"));
+ timeline.setFrame(frame, keyMap.getFloat("time", 0), keyMap.getString("name", null));
timelines.add(timeline);
} else if (timelineName.equals("rgba")) {
@@ -877,57 +877,72 @@ public class SkeletonJson extends SkeletonLoader {
}
}
- // Deform timelines.
- for (JsonValue deformMap = map.getChild("deform"); 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) {
+ // Attachment timelines.
+ for (JsonValue attachmentsMap = map.getChild("attachments"); attachmentsMap != null; attachmentsMap = attachmentsMap.next) {
+ Skin skin = skeletonData.findSkin(attachmentsMap.name);
+ if (skin == null) throw new SerializationException("Skin not found: " + attachmentsMap.name);
+ for (JsonValue slotMap = attachmentsMap.child; slotMap != null; slotMap = slotMap.next) {
SlotData slot = skeletonData.findSlot(slotMap.name);
if (slot == null) throw new SerializationException("Slot not found: " + slotMap.name);
- for (JsonValue timelineMap = slotMap.child; timelineMap != null; timelineMap = timelineMap.next) {
- JsonValue keyMap = timelineMap.child;
- if (keyMap == null) continue;
+ for (JsonValue attachmentMap = slotMap.child; attachmentMap != null; attachmentMap = attachmentMap.next) {
+ Attachment attachment = skin.getAttachment(slot.index, attachmentMap.name);
+ if (attachment == null) throw new SerializationException("Timeline attachment not found: " + attachmentMap.name);
+ for (JsonValue timelineMap = attachmentMap.child; timelineMap != null; timelineMap = timelineMap.next) {
+ JsonValue keyMap = timelineMap.child;
+ int frames = timelineMap.size;
+ String timelineName = timelineMap.name;
+ 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;
- 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;
+ 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];
+ }
+ }
- 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;
+ 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;
}
- if (!weighted) {
- for (int i = 0; i < deformLength; i++)
- deform[i] += vertices[i];
+ 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);
+ lastDelay = delay;
}
+ timelines.add(timeline);
}
-
- 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;
}
- timelines.add(timeline);
}
}
}
@@ -1068,14 +1083,14 @@ public class SkeletonJson extends SkeletonLoader {
String parent, skin;
int slotIndex;
MeshAttachment mesh;
- boolean inheritDeform;
+ boolean inheritTimelines;
- public LinkedMesh (MeshAttachment mesh, String skin, int slotIndex, String parent, boolean inheritDeform) {
+ public LinkedMesh (MeshAttachment mesh, String skin, int slotIndex, String parent, boolean inheritTimelines) {
this.mesh = mesh;
this.skin = skin;
this.slotIndex = slotIndex;
this.parent = parent;
- this.inheritDeform = inheritDeform;
+ this.inheritTimelines = inheritTimelines;
}
}
}
diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java
index a93f7e333..4f66bef09 100644
--- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java
+++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java
@@ -98,7 +98,7 @@ public class SkeletonRenderer {
Attachment attachment = slot.attachment;
if (attachment instanceof RegionAttachment) {
RegionAttachment region = (RegionAttachment)attachment;
- region.computeWorldVertices(slot.getBone(), vertices, 0, 5);
+ region.computeWorldVertices(slot, vertices, 0, 5);
Color color = region.getColor(), slotColor = slot.getColor();
float alpha = a * slotColor.a * color.a * 255;
float multiplier = pmaColors ? alpha : 255;
@@ -183,7 +183,7 @@ public class SkeletonRenderer {
RegionAttachment region = (RegionAttachment)attachment;
verticesLength = vertexSize << 2;
vertices = this.vertices.items;
- region.computeWorldVertices(slot.getBone(), vertices, 0, vertexSize);
+ region.computeWorldVertices(slot, vertices, 0, vertexSize);
triangles = quadTriangles;
texture = region.getRegion().getTexture();
uvs = region.getUVs();
@@ -309,7 +309,7 @@ public class SkeletonRenderer {
RegionAttachment region = (RegionAttachment)attachment;
verticesLength = vertexSize << 2;
vertices = this.vertices.items;
- region.computeWorldVertices(slot.getBone(), vertices, 0, vertexSize);
+ region.computeWorldVertices(slot, vertices, 0, vertexSize);
triangles = quadTriangles;
texture = region.getRegion().getTexture();
uvs = region.getUVs();
diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRendererDebug.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRendererDebug.java
index be2a67da5..f50e89c02 100644
--- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRendererDebug.java
+++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRendererDebug.java
@@ -127,7 +127,7 @@ public class SkeletonRendererDebug {
if (attachment instanceof RegionAttachment) {
RegionAttachment region = (RegionAttachment)attachment;
float[] vertices = this.vertices.items;
- region.computeWorldVertices(slot.getBone(), vertices, 0, 2);
+ region.computeWorldVertices(slot, vertices, 0, 2);
shapes.line(vertices[0], vertices[1], vertices[2], vertices[3]);
shapes.line(vertices[2], vertices[3], vertices[4], vertices[5]);
shapes.line(vertices[4], vertices[5], vertices[6], vertices[7]);
diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Slot.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Slot.java
index 2dd1fb348..13c75c2fd 100644
--- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Slot.java
+++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Slot.java
@@ -35,6 +35,7 @@ import com.badlogic.gdx.utils.Null;
import com.esotericsoftware.spine.Animation.DeformTimeline;
import com.esotericsoftware.spine.attachments.Attachment;
+import com.esotericsoftware.spine.attachments.Sequence;
import com.esotericsoftware.spine.attachments.VertexAttachment;
/** Stores a slot's current pose. Slots organize attachments for {@link Skeleton#drawOrder} purposes and provide a place to store
@@ -46,8 +47,8 @@ public class Slot {
final Color color = new Color();
@Null final Color darkColor;
@Null Attachment attachment;
- private float attachmentTime;
- private FloatArray deform = new FloatArray();
+ int sequenceIndex;
+ FloatArray deform = new FloatArray();
int attachmentState;
@@ -69,7 +70,7 @@ public class Slot {
color.set(slot.color);
darkColor = slot.darkColor == null ? null : new Color(slot.darkColor);
attachment = slot.attachment;
- attachmentTime = slot.attachmentTime;
+ sequenceIndex = slot.sequenceIndex;
deform.addAll(slot.deform);
}
@@ -105,27 +106,28 @@ public class Slot {
return attachment;
}
- /** Sets the slot's attachment and, if the attachment changed, resets {@link #attachmentTime} and clears the {@link #deform}.
- * The deform is not cleared if the old attachment has the same {@link VertexAttachment#getDeformAttachment()} as the specified
- * attachment. */
+ /** Sets the slot's attachment and, if the attachment changed, resets {@link #sequenceIndex} and clears the {@link #deform}.
+ * The deform is not cleared if the old attachment has the same {@link VertexAttachment#getTimelineAttachment()} as the
+ * specified attachment. */
public void setAttachment (@Null Attachment attachment) {
if (this.attachment == attachment) return;
if (!(attachment instanceof VertexAttachment) || !(this.attachment instanceof VertexAttachment)
- || ((VertexAttachment)attachment).getDeformAttachment() != ((VertexAttachment)this.attachment).getDeformAttachment()) {
+ || ((VertexAttachment)attachment).getTimelineAttachment() != ((VertexAttachment)this.attachment)
+ .getTimelineAttachment()) {
deform.clear();
}
this.attachment = attachment;
- attachmentTime = bone.skeleton.time;
+ sequenceIndex = -1;
}
- /** The time that has elapsed since the last time the attachment was set or cleared. Relies on Skeleton
- * {@link Skeleton#time}. */
- public float getAttachmentTime () {
- return bone.skeleton.time - attachmentTime;
+ /** The index of the texture region to display when the slot's attachment has a {@link Sequence}. -1 represents the
+ * {@link Sequence#getSetupIndex()}. */
+ public int getSequenceIndex () {
+ return sequenceIndex;
}
- public void setAttachmentTime (float time) {
- attachmentTime = bone.skeleton.time - time;
+ public void setSequenceIndex (int sequenceIndex) {
+ this.sequenceIndex = sequenceIndex;
}
/** Values to deform the slot's attachment. For an unweighted mesh, the entries are local positions for each vertex. For a
diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/AtlasAttachmentLoader.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/AtlasAttachmentLoader.java
index e5ba26041..384baabd4 100644
--- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/AtlasAttachmentLoader.java
+++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/AtlasAttachmentLoader.java
@@ -31,6 +31,8 @@ package com.esotericsoftware.spine.attachments;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;
+import com.badlogic.gdx.graphics.g2d.TextureRegion;
+import com.badlogic.gdx.utils.Null;
import com.esotericsoftware.spine.Skin;
@@ -47,32 +49,39 @@ public class AtlasAttachmentLoader implements AttachmentLoader {
this.atlas = atlas;
}
- public RegionAttachment newRegionAttachment (Skin skin, String name, String path) {
- AtlasRegion region = atlas.findRegion(path);
- if (region == null) throw new RuntimeException("Region not found in atlas: " + path + " (region attachment: " + name + ")");
- RegionAttachment attachment = new RegionAttachment(name);
- attachment.setRegion(region);
- return attachment;
- }
-
- public MeshAttachment newMeshAttachment (Skin skin, String name, String path) {
- AtlasRegion region = atlas.findRegion(path);
- if (region == null) throw new RuntimeException("Region not found in atlas: " + path + " (mesh attachment: " + name + ")");
- MeshAttachment attachment = new MeshAttachment(name);
- attachment.setRegion(region);
- return attachment;
- }
-
- public SequenceAttachment newSequenceAttachment (Skin skin, String name, String path, int frameCount) {
- AtlasRegion[] regions = new AtlasRegion[frameCount];
- for (int i = 0; i < frameCount; i++) {
- AtlasRegion region = atlas.findRegion(path + frameCount); // BOZO - Zero pad?
- if (region == null)
- throw new RuntimeException("Region not found in atlas: " + path + frameCount + " (sequence: " + name + ")");
+ private void loadSequence (String name, String basePath, Sequence sequence) {
+ TextureRegion[] regions = sequence.getRegions();
+ for (int i = 0, n = regions.length; i < n; i++) {
+ String path = sequence.getPath(basePath, i);
+ regions[i] = atlas.findRegion(path);
+ if (regions[i] == null) throw new RuntimeException("Region not found in atlas: " + path + " (sequence: " + name + ")");
}
- SequenceAttachment sequence = new SequenceAttachment(name);
- sequence.setRegions(regions);
- return sequence;
+ }
+
+ public RegionAttachment newRegionAttachment (Skin skin, String name, String path, @Null Sequence sequence) {
+ RegionAttachment attachment = new RegionAttachment(name);
+ if (sequence != null)
+ loadSequence(name, path, sequence);
+ else {
+ AtlasRegion region = atlas.findRegion(path);
+ if (region == null)
+ throw new RuntimeException("Region not found in atlas: " + path + " (region attachment: " + name + ")");
+ attachment.setRegion(region);
+ }
+ return attachment;
+ }
+
+ public MeshAttachment newMeshAttachment (Skin skin, String name, String path, @Null Sequence sequence) {
+ MeshAttachment attachment = new MeshAttachment(name);
+ if (sequence != null)
+ loadSequence(name, path, sequence);
+ else {
+ AtlasRegion region = atlas.findRegion(path);
+ if (region == null)
+ throw new RuntimeException("Region not found in atlas: " + path + " (mesh attachment: " + name + ")");
+ attachment.setRegion(region);
+ }
+ return attachment;
}
public BoundingBoxAttachment newBoundingBoxAttachment (Skin skin, String name) {
diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/Attachment.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/Attachment.java
index 0ecc20f47..b76fc6bbc 100644
--- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/Attachment.java
+++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/Attachment.java
@@ -31,13 +31,18 @@ package com.esotericsoftware.spine.attachments;
/** The base class for all attachments. */
abstract public class Attachment {
- String name;
+ final String name;
public Attachment (String name) {
if (name == null) throw new IllegalArgumentException("name cannot be null.");
this.name = name;
}
+ /** Copy constructor. */
+ protected Attachment (Attachment other) {
+ name = other.name;
+ }
+
/** The attachment's name. */
public String getName () {
return name;
diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/AttachmentLoader.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/AttachmentLoader.java
index 47084c130..cbe0bd55e 100644
--- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/AttachmentLoader.java
+++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/AttachmentLoader.java
@@ -39,13 +39,10 @@ import com.esotericsoftware.spine.Skin;
* Runtimes Guide. */
public interface AttachmentLoader {
/** @return May be null to not load the attachment. */
- public @Null RegionAttachment newRegionAttachment (Skin skin, String name, String path);
+ public @Null RegionAttachment newRegionAttachment (Skin skin, String name, String path, @Null Sequence sequence);
/** @return May be null to not load the attachment. In that case null should also be returned for child meshes. */
- public @Null MeshAttachment newMeshAttachment (Skin skin, String name, String path);
-
- /** @return May be null to not load the attachment. */
- public @Null SequenceAttachment newSequenceAttachment (Skin skin, String name, String path, int frameCount);
+ public @Null MeshAttachment newMeshAttachment (Skin skin, String name, String path, @Null Sequence sequence);
/** @return May be null to not load the attachment. */
public @Null BoundingBoxAttachment newBoundingBoxAttachment (Skin skin, String name);
diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/BoundingBoxAttachment.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/BoundingBoxAttachment.java
index 7bb2a1ba8..99346857b 100644
--- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/BoundingBoxAttachment.java
+++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/BoundingBoxAttachment.java
@@ -46,16 +46,19 @@ public class BoundingBoxAttachment extends VertexAttachment {
super(name);
}
+ /** Copy constructor. */
+ protected BoundingBoxAttachment (BoundingBoxAttachment other) {
+ super(other);
+ color.set(other.color);
+ }
+
/** The color of the bounding box as it was in Spine, or a default color if nonessential data was not exported. Bounding boxes
* are not usually rendered at runtime. */
public Color getColor () {
return color;
}
- public Attachment copy () {
- BoundingBoxAttachment copy = new BoundingBoxAttachment(name);
- copyTo(copy);
- copy.color.set(color);
- return copy;
+ public BoundingBoxAttachment copy () {
+ return new BoundingBoxAttachment(name);
}
}
diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/ClippingAttachment.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/ClippingAttachment.java
index cd804cfc8..3b2825a80 100644
--- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/ClippingAttachment.java
+++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/ClippingAttachment.java
@@ -45,6 +45,13 @@ public class ClippingAttachment extends VertexAttachment {
super(name);
}
+ /** Copy constructor. */
+ protected ClippingAttachment (ClippingAttachment other) {
+ super(other);
+ endSlot = other.endSlot;
+ color.set(other.color);
+ }
+
/** Clipping is performed between the clipping attachment's slot and the end slot. If null clipping is done until the end of
* the skeleton's rendering. */
public @Null SlotData getEndSlot () {
@@ -61,11 +68,7 @@ public class ClippingAttachment extends VertexAttachment {
return color;
}
- public Attachment copy () {
- ClippingAttachment copy = new ClippingAttachment(name);
- copyTo(copy);
- copy.endSlot = endSlot;
- copy.color.set(color);
- return copy;
+ public ClippingAttachment copy () {
+ return new ClippingAttachment(name);
}
}
diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/HasTextureRegion.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/HasTextureRegion.java
index 32247e169..6c37b6f48 100644
--- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/HasTextureRegion.java
+++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/HasTextureRegion.java
@@ -3,6 +3,7 @@ package com.esotericsoftware.spine.attachments;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
+import com.badlogic.gdx.utils.Null;
public interface HasTextureRegion {
/** The name used to find the {@link #getRegion()}. */
@@ -10,16 +11,20 @@ public interface HasTextureRegion {
public void setPath (String path);
- /** Sets the region used to draw the attachment. If the region or its properties are changed, {@link #updateRegion()} must be
- * called. */
- public void setRegion (TextureRegion region);
-
public TextureRegion getRegion ();
- /** Updates any values the attachment calculates using the {@link #getRegion()}. Must be called after changing the region or
- * the region's properties. */
+ /** Sets the region used to draw the attachment. After setting the region or if the region's properties are changed,
+ * {@link #updateRegion()} must be called. */
+ public void setRegion (TextureRegion region);
+
+ /** Updates any values the attachment calculates using the {@link #getRegion()}. Must be called after setting the
+ * {@link #getRegion()} or if the region's properties are changed. */
public void updateRegion ();
/** The color to tint the attachment. */
public Color getColor ();
+
+ public @Null Sequence getSequence ();
+
+ public void setSequence (@Null Sequence sequence);
}
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 a82a2a63d..b3172167a 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
@@ -36,6 +36,8 @@ import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.utils.Null;
+import com.esotericsoftware.spine.Slot;
+
/** An attachment that displays a textured mesh. A mesh has hull vertices and internal vertices within the hull. Holes are not
* supported. Each vertex has UVs (texture coordinates) and triangles are used to map an image on to the mesh.
*
@@ -48,6 +50,7 @@ public class MeshAttachment extends VertexAttachment implements HasTextureRegion
private final Color color = new Color(1, 1, 1, 1);
private int hullLength;
private @Null MeshAttachment parentMesh;
+ private @Null Sequence sequence;
// Nonessential.
private @Null short[] edges;
@@ -57,18 +60,47 @@ public class MeshAttachment extends VertexAttachment implements HasTextureRegion
super(name);
}
+ /** Copy constructor. Use {@link #newLinkedMesh()} if the other mesh is a linked mesh. */
+ protected MeshAttachment (MeshAttachment other) {
+ super(other);
+
+ if (parentMesh != null) throw new IllegalArgumentException("Use newLinkedMesh to copy a linked mesh.");
+
+ region = other.region;
+ path = other.path;
+ color.set(other.color);
+
+ regionUVs = new float[other.regionUVs.length];
+ arraycopy(other.regionUVs, 0, regionUVs, 0, regionUVs.length);
+
+ uvs = new float[other.uvs.length];
+ arraycopy(other.uvs, 0, uvs, 0, uvs.length);
+
+ triangles = new short[other.triangles.length];
+ arraycopy(other.triangles, 0, triangles, 0, triangles.length);
+
+ hullLength = other.hullLength;
+
+ // Nonessential.
+ if (other.edges != null) {
+ edges = new short[other.edges.length];
+ arraycopy(other.edges, 0, edges, 0, edges.length);
+ }
+ width = other.width;
+ height = other.height;
+ }
+
public void setRegion (TextureRegion region) {
if (region == null) throw new IllegalArgumentException("region cannot be null.");
this.region = region;
}
- public TextureRegion getRegion () {
- if (region == null) throw new IllegalStateException("Region has not been set: " + this);
+ public @Null TextureRegion getRegion () {
return region;
}
- /** Calculates {@link #uvs} using {@link #regionUVs} and the {@link #region}. Must be called after changing the region or the
- * region's properties. */
+ /** Calculates {@link #uvs} using the {@link #regionUVs} and {@link #region}. Must be called if the {@link #region},
+ * {@link #regionUVs}, or the region's properties are changed. */
public void updateRegion () {
float[] regionUVs = this.regionUVs;
if (this.uvs == null || this.uvs.length != regionUVs.length) this.uvs = new float[regionUVs.length];
@@ -131,6 +163,12 @@ public class MeshAttachment extends VertexAttachment implements HasTextureRegion
}
}
+ /** If the attachment has a {@link #sequence}, the {@link #region} may be changed. */
+ public void computeWorldVertices (Slot slot, int start, int count, float[] worldVertices, int offset, int stride) {
+ if (sequence != null) sequence.apply(slot, this);
+ super.computeWorldVertices(slot, start, count, worldVertices, offset, stride);
+ }
+
/** Triplets of vertex indices which describe the mesh's triangulation. */
public short[] getTriangles () {
return triangles;
@@ -210,6 +248,14 @@ public class MeshAttachment extends VertexAttachment implements HasTextureRegion
this.height = height;
}
+ public @Null Sequence getSequence () {
+ return sequence;
+ }
+
+ public void setSequence (@Null Sequence sequence) {
+ this.sequence = sequence;
+ }
+
/** The parent mesh if this is a linked mesh, else null. A linked mesh shares the {@link #bones}, {@link #vertices},
* {@link #regionUVs}, {@link #triangles}, {@link #hullLength}, {@link #edges}, {@link #width}, and {@link #height} with the
* parent mesh, but may have a different {@link #name} or {@link #path} (and therefore a different texture). */
@@ -232,42 +278,19 @@ public class MeshAttachment extends VertexAttachment implements HasTextureRegion
}
}
- public Attachment copy () {
- if (parentMesh != null) return newLinkedMesh();
-
- MeshAttachment copy = new MeshAttachment(name);
- copy.region = region;
- copy.path = path;
- copy.color.set(color);
-
- copyTo(copy);
- copy.regionUVs = new float[regionUVs.length];
- arraycopy(regionUVs, 0, copy.regionUVs, 0, regionUVs.length);
- copy.uvs = new float[uvs.length];
- arraycopy(uvs, 0, copy.uvs, 0, uvs.length);
- copy.triangles = new short[triangles.length];
- arraycopy(triangles, 0, copy.triangles, 0, triangles.length);
- copy.hullLength = hullLength;
-
- // Nonessential.
- if (edges != null) {
- copy.edges = new short[edges.length];
- arraycopy(edges, 0, copy.edges, 0, edges.length);
- }
- copy.width = width;
- copy.height = height;
- return copy;
- }
-
/** Returns a new mesh with the {@link #parentMesh} set to this mesh's parent mesh, if any, else to this mesh. */
public MeshAttachment newLinkedMesh () {
MeshAttachment mesh = new MeshAttachment(name);
+ mesh.timelineAttachment = timelineAttachment;
mesh.region = region;
mesh.path = path;
mesh.color.set(color);
- mesh.deformAttachment = deformAttachment;
mesh.setParentMesh(parentMesh != null ? parentMesh : this);
- mesh.updateRegion();
+ if (mesh.getRegion() != null) mesh.updateRegion();
return mesh;
}
+
+ public MeshAttachment copy () {
+ return parentMesh != null ? newLinkedMesh() : new MeshAttachment(this);
+ }
}
diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/PathAttachment.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/PathAttachment.java
index fdd622399..2f75df7ba 100644
--- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/PathAttachment.java
+++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/PathAttachment.java
@@ -49,6 +49,18 @@ public class PathAttachment extends VertexAttachment {
super(name);
}
+ /** Copy constructor. */
+ protected PathAttachment (PathAttachment other) {
+ super(other);
+
+ lengths = new float[other.lengths.length];
+ arraycopy(other.lengths, 0, lengths, 0, lengths.length);
+
+ closed = other.closed;
+ constantSpeed = other.constantSpeed;
+ color.set(other.color);
+ }
+
/** If true, the start and end knots are connected. */
public boolean getClosed () {
return closed;
@@ -83,14 +95,7 @@ public class PathAttachment extends VertexAttachment {
return color;
}
- public Attachment copy () {
- PathAttachment copy = new PathAttachment(name);
- copyTo(copy);
- copy.lengths = new float[lengths.length];
- arraycopy(lengths, 0, copy.lengths, 0, lengths.length);
- copy.closed = closed;
- copy.constantSpeed = constantSpeed;
- copy.color.set(color);
- return copy;
+ public PathAttachment copy () {
+ return new PathAttachment(this);
}
}
diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/PointAttachment.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/PointAttachment.java
index b9b26a107..4a3e5a14a 100644
--- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/PointAttachment.java
+++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/PointAttachment.java
@@ -51,6 +51,15 @@ public class PointAttachment extends Attachment {
super(name);
}
+ /** Copy constructor. */
+ protected PointAttachment (PointAttachment other) {
+ super(other);
+ x = other.x;
+ y = other.y;
+ rotation = other.rotation;
+ color.set(other.color);
+ }
+
public float getX () {
return x;
}
@@ -94,12 +103,7 @@ public class PointAttachment extends Attachment {
return (float)Math.atan2(y, x) * radDeg;
}
- public Attachment copy () {
- PointAttachment copy = new PointAttachment(name);
- copy.x = x;
- copy.y = y;
- copy.rotation = rotation;
- copy.color.set(color);
- return copy;
+ public PointAttachment copy () {
+ return new PointAttachment(this);
}
}
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 0f0a3e138..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
@@ -34,21 +34,19 @@ import static com.esotericsoftware.spine.utils.SpineUtils.*;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
+import com.badlogic.gdx.utils.Null;
import com.esotericsoftware.spine.Bone;
+import com.esotericsoftware.spine.Slot;
/** An attachment that displays a textured quadrilateral.
*
* See Region attachments in the Spine User Guide. */
public class RegionAttachment extends Attachment implements HasTextureRegion {
- static public final int BLX = 0;
- static public final int BLY = 1;
- static public final int ULX = 2;
- static public final int ULY = 3;
- static public final int URX = 4;
- static public final int URY = 5;
- static public final int BRX = 6;
- static public final int BRY = 7;
+ static public final int BLX = 0, BLY = 1;
+ static public final int ULX = 2, ULY = 3;
+ static public final int URX = 4, URY = 5;
+ static public final int BRX = 6, BRY = 7;
private TextureRegion region;
private String path;
@@ -56,13 +54,31 @@ public class RegionAttachment extends Attachment implements HasTextureRegion {
private final float[] uvs = new float[8];
private final float[] offset = new float[8];
private final Color color = new Color(1, 1, 1, 1);
+ private @Null Sequence sequence;
public RegionAttachment (String name) {
super(name);
}
- /** Calculates the {@link #offset} using the {@link #region}. Must be called after changing the region or the region's
- * properties. */
+ /** Copy constructor. */
+ protected RegionAttachment (RegionAttachment other) {
+ super(other);
+ region = other.region;
+ path = other.path;
+ x = other.x;
+ y = other.y;
+ scaleX = other.scaleX;
+ scaleY = other.scaleY;
+ rotation = other.rotation;
+ width = other.width;
+ height = other.height;
+ arraycopy(other.uvs, 0, uvs, 0, 8);
+ arraycopy(other.offset, 0, offset, 0, 8);
+ color.set(other.color);
+ }
+
+ /** Calculates the {@link #offset} and {@link #uvs} using the {@link #region} and the attachment's transform. Must be called if
+ * the {@link #region}, transform, or the region's properties are changed. */
public void updateRegion () {
float width = getWidth();
float height = getHeight();
@@ -70,11 +86,13 @@ public class RegionAttachment extends Attachment implements HasTextureRegion {
float localY2 = height / 2;
float localX = -localX2;
float localY = -localY2;
+ boolean rotated = false;
if (region instanceof AtlasRegion) {
AtlasRegion region = (AtlasRegion)this.region;
localX += region.offsetX / region.originalWidth * width;
localY += region.offsetY / region.originalHeight * height;
if (region.degrees == 90) {
+ rotated = true;
localX2 -= (region.originalWidth - region.offsetX - region.packedHeight) / region.originalWidth * width;
localY2 -= (region.originalHeight - region.offsetY - region.packedWidth) / region.originalHeight * height;
} else {
@@ -110,13 +128,9 @@ public class RegionAttachment extends Attachment implements HasTextureRegion {
offset[URY] = localY2Cos + localX2Sin;
offset[BRX] = localX2Cos - localYSin;
offset[BRY] = localYCos + localX2Sin;
- }
- public void setRegion (TextureRegion region) {
- if (region == null) throw new IllegalArgumentException("region cannot be null.");
- this.region = region;
float[] uvs = this.uvs;
- if (region instanceof AtlasRegion && ((AtlasRegion)region).degrees == 90) {
+ if (rotated) {
uvs[URX] = region.getU();
uvs[URY] = region.getV2();
uvs[BRX] = region.getU();
@@ -137,20 +151,28 @@ public class RegionAttachment extends Attachment implements HasTextureRegion {
}
}
- public TextureRegion getRegion () {
- if (region == null) throw new IllegalStateException("Region has not been set: " + name);
+ public void setRegion (TextureRegion region) {
+ if (region == null) throw new IllegalArgumentException("region cannot be null.");
+ this.region = region;
+ }
+
+ public @Null TextureRegion getRegion () {
return region;
}
- /** Transforms the attachment's four vertices to world coordinates.
+ /** Transforms the attachment's four vertices to world coordinates. If the attachment has a {@link #sequence}, the
+ * {@link #region} may be changed.
*
* See World transforms in the Spine
* Runtimes Guide.
* @param worldVertices The output world vertices. Must have a length >=
- * See Sequence attachments in the Spine User Guide. */
-public class SequenceAttachment
@@ -120,17 +140,6 @@ abstract public class VertexAttachment extends Attachment {
}
}
- /** Deform keys for the deform attachment are also applied to this attachment.
- * @return May be null if no deform keys should be applied. */
- public @Null VertexAttachment getDeformAttachment () {
- return deformAttachment;
- }
-
- /** @param deformAttachment May be null if no deform keys should be applied. */
- public void setDeformAttachment (@Null VertexAttachment deformAttachment) {
- this.deformAttachment = deformAttachment;
- }
-
/** The bones which affect the {@link #getVertices()}. The array entries are, for each vertex, the number of bones affecting
* the vertex followed by that many bone indices, which is the index of the bone in {@link Skeleton#getBones()}. Will be null
* if this attachment has no weights. */
@@ -164,29 +173,22 @@ abstract public class VertexAttachment extends Attachment {
this.worldVerticesLength = worldVerticesLength;
}
+ /** Timelines for the timeline attachment are also applied to this attachment.
+ * @return May be null if no attachment-specific timelines should be applied. */
+ public @Null Attachment getTimelineAttachment () {
+ return timelineAttachment;
+ }
+
+ /** @param timelineAttachment May be null if no attachment-specific timelines should be applied. */
+ public void setTimelineAttachment (Attachment timelineAttachment) {
+ this.timelineAttachment = timelineAttachment;
+ }
+
/** Returns a unique ID for this attachment. */
public int getId () {
return id;
}
- /** Does not copy id (generated) or name (set on construction). */
- void copyTo (VertexAttachment attachment) {
- if (bones != null) {
- attachment.bones = new int[bones.length];
- arraycopy(bones, 0, attachment.bones, 0, bones.length);
- } else
- attachment.bones = null;
-
- if (vertices != null) {
- attachment.vertices = new float[vertices.length];
- arraycopy(vertices, 0, attachment.vertices, 0, vertices.length);
- } else
- attachment.vertices = null;
-
- attachment.worldVerticesLength = worldVerticesLength;
- attachment.deformAttachment = deformAttachment;
- }
-
static private synchronized int nextID () {
return nextID++;
}
diff --git a/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java b/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java
index d8ab6f51d..f72a48852 100644
--- a/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java
+++ b/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java
@@ -258,7 +258,6 @@ public class SkeletonViewer extends ApplicationAdapter {
skeleton.setSlotsToSetupPose();
delta = Math.min(delta, 0.032f) * ui.speedSlider.getValue();
- skeleton.update(delta);
state.update(delta);
state.apply(skeleton);
skeleton.updateWorldTransform();
frameCount, inclusive.
+ * @param time Seconds between frames. */
+ public void setFrame (int frame, float time, SequenceMode mode, int index, float delay) {
+ frame *= ENTRIES;
+ frames[frame] = time;
+ frames[frame + MODE] = mode.ordinal() | (index << 4);
+ frames[frame + DELAY] = delay;
+ }
+
+ public void apply (Skeleton skeleton, float lastTime, float time, @Null ArrayanimationTime, which is between {@link #getAnimationStart()}
- * and {@link #getAnimationEnd()}. When the trackTime is 0, the animationTime is equal to the
- * animationStart time. */
+ /** Uses {@link #getTrackTime()} to compute the animationTime. When the trackTime is 0, the
+ * animationTime is equal to the animationStart time.
+ * animationTime is between {@link #getAnimationStart()} and {@link #getAnimationEnd()}, except if this
+ * track entry is non-looping and {@link #getAnimationEnd()} is >= to the animation {@link Animation#duration}, then
+ * animationTime continues to increase past {@link #getAnimationEnd()}. */
public float getAnimationTime () {
if (loop) {
float duration = animationEnd - animationStart;
if (duration == 0) return animationStart;
return (trackTime % duration) + animationStart;
}
- return Math.min(trackTime + animationStart, animationEnd);
+ float animationTime = trackTime + animationStart;
+ return animationEnd >= animation.duration ? animationTime : Math.min(animationTime, animationEnd);
}
/** Multiplier for the delta time when this track entry is updated, causing time for this animation to pass slower or
diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java
index b743170ee..8d2154229 100644
--- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java
+++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java
@@ -42,7 +42,6 @@ import com.esotericsoftware.spine.attachments.Attachment;
import com.esotericsoftware.spine.attachments.MeshAttachment;
import com.esotericsoftware.spine.attachments.PathAttachment;
import com.esotericsoftware.spine.attachments.RegionAttachment;
-import com.esotericsoftware.spine.attachments.SequenceAttachment;
/** Stores the current pose for a skeleton.
* offset + 8.
* @param offset The worldVertices index to begin writing values.
* @param stride The number of worldVertices entries between the value pairs written. */
- public void computeWorldVertices (Bone bone, float[] worldVertices, int offset, int stride) {
+ public void computeWorldVertices (Slot slot, float[] worldVertices, int offset, int stride) {
+ if (sequence != null) sequence.apply(slot, this);
+
float[] vertexOffset = this.offset;
+ Bone bone = slot.getBone();
float x = bone.getWorldX(), y = bone.getWorldY();
float a = bone.getA(), b = bone.getB(), c = bone.getC(), d = bone.getD();
float offsetX, offsetY;
@@ -265,20 +287,15 @@ public class RegionAttachment extends Attachment implements HasTextureRegion {
this.path = path;
}
- public Attachment copy () {
- RegionAttachment copy = new RegionAttachment(name);
- copy.region = region;
- copy.path = path;
- copy.x = x;
- copy.y = y;
- copy.scaleX = scaleX;
- copy.scaleY = scaleY;
- copy.rotation = rotation;
- copy.width = width;
- copy.height = height;
- arraycopy(uvs, 0, copy.uvs, 0, 8);
- arraycopy(offset, 0, copy.offset, 0, 8);
- copy.color.set(color);
- return copy;
+ public @Null Sequence getSequence () {
+ return sequence;
+ }
+
+ public void setSequence (@Null Sequence sequence) {
+ this.sequence = sequence;
+ }
+
+ public RegionAttachment copy () {
+ return new RegionAttachment(this);
}
}
diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/Sequence.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/Sequence.java
new file mode 100644
index 000000000..3b732adbb
--- /dev/null
+++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/Sequence.java
@@ -0,0 +1,122 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated September 24, 2021. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2021, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
+ * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+package com.esotericsoftware.spine.attachments;
+
+import static com.esotericsoftware.spine.utils.SpineUtils.*;
+
+import com.badlogic.gdx.graphics.g2d.TextureRegion;
+
+import com.esotericsoftware.spine.Slot;
+
+public class Sequence {
+ static private int nextID;
+
+ private final int id = nextID();
+ private final TextureRegion[] regions;
+ private int start, digits, setupIndex;
+
+ public Sequence (int count) {
+ regions = new TextureRegion[count];
+ }
+
+ /** Copy constructor. */
+ protected Sequence (Sequence other) {
+ regions = new TextureRegion[other.regions.length];
+ arraycopy(other.regions, 0, regions, 0, regions.length);
+
+ start = other.start;
+ digits = other.digits;
+ setupIndex = other.setupIndex;
+ }
+
+ public