From 077afcea093bf3640229ae882a54e505ee562347 Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Tue, 21 Apr 2020 17:34:11 +0200 Subject: [PATCH 1/2] [libgdx] Fixed curve values not being scaled when loading data. Added SkeletonLoader. Javadocs. --- .../esotericsoftware/spine/SimpleTest1.java | 8 +- .../esotericsoftware/spine/SimpleTest2.java | 6 +- .../esotericsoftware/spine/SimpleTest3.java | 8 +- .../esotericsoftware/spine/SimpleTest4.java | 4 +- .../spine/SkeletonBinary.java | 77 ++++++--------- .../esotericsoftware/spine/SkeletonJson.java | 93 ++++++++----------- .../spine/SkeletonLoader.java | 46 +++++++++ 7 files changed, 120 insertions(+), 122 deletions(-) create mode 100644 spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonLoader.java diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest1.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest1.java index b8d5ecd4d..7889f248f 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest1.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest1.java @@ -58,15 +58,11 @@ public class SimpleTest1 extends ApplicationAdapter { atlas = new TextureAtlas(Gdx.files.internal("spineboy/spineboy-pma.atlas")); - SkeletonJson loader = new SkeletonJson(atlas); // This loads skeleton JSON data, which is stateless. + SkeletonLoader loader = new SkeletonJson(atlas); // This loads skeleton JSON data, which is stateless. + // SkeletonLoader loader = new SkeletonBinary(atlas); // Or use SkeletonBinary to load binary data. loader.setScale(0.6f); // Load the skeleton at 60% the size it was in Spine. SkeletonData skeletonData = loader.readSkeletonData(Gdx.files.internal("spineboy/spineboy-pro.json")); - // Binary would be loaded similarly: - // SkeletonBinary loader = new SkeletonBinary(atlas); - // loader.setScale(0.6f); - // SkeletonData skeletonData = loader.readSkeletonData(Gdx.files.internal("spineboy/spineboy-pro.skel")); - skeleton = new Skeleton(skeletonData); // Skeleton holds skeleton state (bone positions, slot attachments, etc). skeleton.setPosition(250, 20); diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest2.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest2.java index 3e385a7b8..f7dc47060 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest2.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest2.java @@ -65,14 +65,10 @@ public class SimpleTest2 extends ApplicationAdapter { atlas = new TextureAtlas(Gdx.files.internal("spineboy/spineboy-pma.atlas")); SkeletonJson loader = new SkeletonJson(atlas); // This loads skeleton JSON data, which is stateless. + // SkeletonLoader loader = new SkeletonBinary(atlas); // Or use SkeletonBinary to load binary data. loader.setScale(0.6f); // Load the skeleton at 60% the size it was in Spine. SkeletonData skeletonData = loader.readSkeletonData(Gdx.files.internal("spineboy/spineboy-ess.json")); - // Binary would be loaded similarly: - // SkeletonBinary loader = new SkeletonBinary(atlas); - // loader.setScale(0.6f); - // SkeletonData skeletonData = loader.readSkeletonData(Gdx.files.internal("spineboy/spineboy-ess.skel")); - skeleton = new Skeleton(skeletonData); // Skeleton holds skeleton state (bone positions, slot attachments, etc). skeleton.setPosition(250, 20); skeleton.setAttachment("head-bb", "head"); // Attach "head" bounding box to "head-bb" slot. diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest3.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest3.java index 14480f986..fed6f8627 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest3.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest3.java @@ -60,14 +60,10 @@ public class SimpleTest3 extends ApplicationAdapter { atlas = new TextureAtlas(Gdx.files.internal("raptor/raptor-pma.atlas")); SkeletonJson loader = new SkeletonJson(atlas); // This loads skeleton JSON data, which is stateless. - loader.setScale(0.5f); // Load the skeleton at 50% the size it was in Spine. + // SkeletonLoader loader = new SkeletonBinary(atlas); // Or use SkeletonBinary to load binary data. + loader.setScale(0.1f); // Load the skeleton at 50% the size it was in Spine. SkeletonData skeletonData = loader.readSkeletonData(Gdx.files.internal("raptor/raptor-pro.json")); - // Binary would be loaded similarly: - // SkeletonBinary loader = new SkeletonBinary(atlas); - // loader.setScale(0.5f); - // SkeletonData skeletonData = loader.readSkeletonData(Gdx.files.internal("raptor/raptor-pro.skel")); - skeleton = new Skeleton(skeletonData); // Skeleton holds skeleton state (bone positions, slot attachments, etc). skeleton.setPosition(250, 20); diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest4.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest4.java index 689dbe150..b7a22cddb 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest4.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest4.java @@ -35,6 +35,7 @@ import com.badlogic.gdx.backends.lwjgl.LwjglApplication; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.OrthographicCamera; import com.badlogic.gdx.graphics.g2d.TextureAtlas; + import com.esotericsoftware.spine.utils.TwoColorPolygonBatch; public class SimpleTest4 extends ApplicationAdapter { @@ -59,7 +60,8 @@ public class SimpleTest4 extends ApplicationAdapter { atlas = new TextureAtlas(Gdx.files.internal("goblins/goblins-pma.atlas")); SkeletonJson loader = new SkeletonJson(atlas); // This loads skeleton JSON data, which is stateless. - loader.setScale(0.6f); // Load the skeleton at 60% the size it was in Spine. + // SkeletonLoader loader = new SkeletonBinary(atlas); // Or use SkeletonBinary to load binary data. + loader.setScale(1.3f); // Load the skeleton at 130% the size it was in Spine. SkeletonData skeletonData = loader.readSkeletonData(Gdx.files.internal("goblins/goblins-pro.json")); skeleton = new Skeleton(skeletonData); // Skeleton holds skeleton state (bone positions, slot attachments, etc). 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 691a9611f..bb67841bc 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java @@ -66,7 +66,6 @@ import com.esotericsoftware.spine.PathConstraintData.PositionMode; import com.esotericsoftware.spine.PathConstraintData.RotateMode; import com.esotericsoftware.spine.PathConstraintData.SpacingMode; import com.esotericsoftware.spine.SkeletonJson.LinkedMesh; -import com.esotericsoftware.spine.attachments.AtlasAttachmentLoader; import com.esotericsoftware.spine.attachments.Attachment; import com.esotericsoftware.spine.attachments.AttachmentLoader; import com.esotericsoftware.spine.attachments.AttachmentType; @@ -83,7 +82,7 @@ import com.esotericsoftware.spine.attachments.VertexAttachment; * See Spine binary format and * JSON and binary data in the Spine * Runtimes Guide. */ -public class SkeletonBinary { +public class SkeletonBinary extends SkeletonLoader { static public final int BONE_ROTATE = 0; static public final int BONE_TRANSLATE = 1; static public final int BONE_SCALE = 2; @@ -101,30 +100,12 @@ public class SkeletonBinary { static public final int CURVE_STEPPED = 1; static public final int CURVE_BEZIER = 2; - private final AttachmentLoader attachmentLoader; - private float scale = 1; - private final Array linkedMeshes = new Array(); + public SkeletonBinary (AttachmentLoader attachmentLoader) { + super(attachmentLoader); + } public SkeletonBinary (TextureAtlas atlas) { - attachmentLoader = new AtlasAttachmentLoader(atlas); - } - - public SkeletonBinary (AttachmentLoader attachmentLoader) { - if (attachmentLoader == null) throw new IllegalArgumentException("attachmentLoader cannot be null."); - this.attachmentLoader = attachmentLoader; - } - - /** Scales bone positions, image sizes, and translations as they are loaded. This allows different size images to be used at - * runtime than were used in Spine. - *

- * See Scaling in the Spine Runtimes Guide. */ - public float getScale () { - return scale; - } - - public void setScale (float scale) { - if (scale == 0) throw new IllegalArgumentException("scale cannot be 0."); - this.scale = scale; + super(atlas); } public SkeletonData readSkeletonData (FileHandle file) { @@ -610,10 +591,10 @@ public class SkeletonBinary { timeline.setStepped(frame); break; case CURVE_BEZIER: - setBezier(input, timeline, bezier++, frame, 0, time, time2, r, r2); - setBezier(input, timeline, bezier++, frame, 1, time, time2, g, g2); - setBezier(input, timeline, bezier++, frame, 2, time, time2, b, b2); - setBezier(input, timeline, bezier++, frame, 3, time, time2, a, a2); + setBezier(input, timeline, bezier++, frame, 0, time, time2, r, r2, 1); + setBezier(input, timeline, bezier++, frame, 1, time, time2, g, g2, 1); + setBezier(input, timeline, bezier++, frame, 2, time, time2, b, b2, 1); + setBezier(input, timeline, bezier++, frame, 3, time, time2, a, a2, 1); } time = time2; r = r2; @@ -644,13 +625,13 @@ public class SkeletonBinary { timeline.setStepped(frame); break; case CURVE_BEZIER: - setBezier(input, timeline, bezier++, frame, 0, time, time2, r, nr); - setBezier(input, timeline, bezier++, frame, 1, time, time2, g, ng); - setBezier(input, timeline, bezier++, frame, 2, time, time2, b, nb); - setBezier(input, timeline, bezier++, frame, 3, time, time2, a, na); - setBezier(input, timeline, bezier++, frame, 4, time, time2, r2, nr2); - setBezier(input, timeline, bezier++, frame, 5, time, time2, g2, ng2); - setBezier(input, timeline, bezier++, frame, 6, time, time2, b2, nb2); + setBezier(input, timeline, bezier++, frame, 0, time, time2, r, nr, 1); + setBezier(input, timeline, bezier++, frame, 1, time, time2, g, ng, 1); + setBezier(input, timeline, bezier++, frame, 2, time, time2, b, nb, 1); + setBezier(input, timeline, bezier++, frame, 3, time, time2, a, na, 1); + setBezier(input, timeline, bezier++, frame, 4, time, time2, r2, nr2, 1); + setBezier(input, timeline, bezier++, frame, 5, time, time2, g2, ng2, 1); + setBezier(input, timeline, bezier++, frame, 6, time, time2, b2, nb2, 1); } time = time2; r = nr; @@ -703,8 +684,8 @@ public class SkeletonBinary { timeline.setStepped(frame); break; case CURVE_BEZIER: - setBezier(input, timeline, bezier++, frame, 0, time, time2, mix, mix2); - setBezier(input, timeline, bezier++, frame, 1, time, time2, softness, softness2); + setBezier(input, timeline, bezier++, frame, 0, time, time2, mix, mix2, 1); + setBezier(input, timeline, bezier++, frame, 1, time, time2, softness, softness2, 1); } time = time2; mix = mix2; @@ -729,10 +710,10 @@ public class SkeletonBinary { timeline.setStepped(frame); break; case CURVE_BEZIER: - setBezier(input, timeline, bezier++, frame, 0, time, time2, rotateMix, rotateMix2); - setBezier(input, timeline, bezier++, frame, 1, time, time2, translateMix, translateMix2); - setBezier(input, timeline, bezier++, frame, 2, time, time2, scaleMix, scaleMix2); - setBezier(input, timeline, bezier++, frame, 3, time, time2, shearMix, shearMix2); + setBezier(input, timeline, bezier++, frame, 0, time, time2, rotateMix, rotateMix2, 1); + setBezier(input, timeline, bezier++, frame, 1, time, time2, translateMix, translateMix2, 1); + setBezier(input, timeline, bezier++, frame, 2, time, time2, scaleMix, scaleMix2, 1); + setBezier(input, timeline, bezier++, frame, 3, time, time2, shearMix, shearMix2, 1); } time = time2; rotateMix = rotateMix2; @@ -812,7 +793,7 @@ public class SkeletonBinary { timeline.setStepped(frame); break; case CURVE_BEZIER: - setBezier(input, timeline, bezier++, frame, 0, time, time2, 0, 1); + setBezier(input, timeline, bezier++, frame, 0, time, time2, 0, 1, 1); } time = time2; } @@ -891,7 +872,7 @@ public class SkeletonBinary { timeline.setStepped(frame); break; case CURVE_BEZIER: - setBezier(input, timeline, bezier++, frame, 0, time, time2, value, value2); + setBezier(input, timeline, bezier++, frame, 0, time, time2, value, value2, 1); } time = time2; value = value2; @@ -910,8 +891,8 @@ public class SkeletonBinary { timeline.setStepped(frame); break; case CURVE_BEZIER: - setBezier(input, timeline, bezier++, frame, 0, time, time2, value1, nvalue1); - setBezier(input, timeline, bezier++, frame, 1, time, time2, value2, nvalue2); + setBezier(input, timeline, bezier++, frame, 0, time, time2, value1, nvalue1, scale); + setBezier(input, timeline, bezier++, frame, 1, time, time2, value2, nvalue2, scale); } time = time2; value1 = nvalue1; @@ -921,9 +902,9 @@ public class SkeletonBinary { } void setBezier (SkeletonInput input, CurveTimeline timeline, int bezier, int frame, int value, float time1, float time2, - float value1, float value2) throws IOException { - timeline.setBezier(bezier, frame, value, time1, value1, input.readFloat(), input.readFloat(), input.readFloat(), - input.readFloat(), time2, value2); + float value1, float value2, float scale) throws IOException { + timeline.setBezier(bezier, frame, value, time1, value1, input.readFloat(), input.readFloat() * scale, input.readFloat(), + input.readFloat() * scale, time2, value2); } static class Vertices { 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 2ad62d83e..8f6e34111 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java @@ -64,7 +64,6 @@ import com.esotericsoftware.spine.BoneData.TransformMode; import com.esotericsoftware.spine.PathConstraintData.PositionMode; import com.esotericsoftware.spine.PathConstraintData.RotateMode; import com.esotericsoftware.spine.PathConstraintData.SpacingMode; -import com.esotericsoftware.spine.attachments.AtlasAttachmentLoader; import com.esotericsoftware.spine.attachments.Attachment; import com.esotericsoftware.spine.attachments.AttachmentLoader; import com.esotericsoftware.spine.attachments.AttachmentType; @@ -77,35 +76,19 @@ import com.esotericsoftware.spine.attachments.RegionAttachment; import com.esotericsoftware.spine.attachments.VertexAttachment; /** Loads skeleton data in the Spine JSON format. + *

+ * JSON is human readable but the binary format is much smaller on disk and faster to load. See {@link SkeletonBinary}. *

* See Spine JSON format and * JSON and binary data in the Spine * Runtimes Guide. */ -public class SkeletonJson { - private final AttachmentLoader attachmentLoader; - private float scale = 1; - private final Array linkedMeshes = new Array(); +public class SkeletonJson extends SkeletonLoader { + public SkeletonJson (AttachmentLoader attachmentLoader) { + super(attachmentLoader); + } public SkeletonJson (TextureAtlas atlas) { - attachmentLoader = new AtlasAttachmentLoader(atlas); - } - - public SkeletonJson (AttachmentLoader attachmentLoader) { - if (attachmentLoader == null) throw new IllegalArgumentException("attachmentLoader cannot be null."); - this.attachmentLoader = attachmentLoader; - } - - /** Scales bone positions, image sizes, and translations as they are loaded. This allows different size images to be used at - * runtime than were used in Spine. - *

- * See Scaling in the Spine Runtimes Guide. */ - public float getScale () { - return scale; - } - - public void setScale (float scale) { - if (scale == 0) throw new IllegalArgumentException("scale cannot be 0."); - this.scale = scale; + super(atlas); } protected JsonValue parse (FileHandle file) { @@ -128,8 +111,6 @@ public class SkeletonJson { if (skeletonMap != null) { skeletonData.hash = skeletonMap.getString("hash", null); skeletonData.version = skeletonMap.getString("spine", null); - if ("3.8.75".equals(skeletonData.version)) - throw new RuntimeException("Unsupported skeleton data, please export with a newer version of Spine."); skeletonData.x = skeletonMap.getFloat("x", 0); skeletonData.y = skeletonMap.getFloat("y", 0); skeletonData.width = skeletonMap.getFloat("width", 0); @@ -491,7 +472,7 @@ public class SkeletonJson { for (int i = 0, n = vertices.length; i < n;) { int boneCount = (int)vertices[i++]; bones.add(boneCount); - for (int nn = i + boneCount * 4; i < nn; i += 4) { + for (int nn = i + (boneCount << 2); i < nn; i += 4) { bones.add((int)vertices[i]); weights.add(vertices[i + 1] * scale); weights.add(vertices[i + 2] * scale); @@ -544,10 +525,10 @@ public class SkeletonJson { float na = Integer.parseInt(color.substring(6, 8), 16) / 255f; JsonValue curve = keyMap.get("curve"); if (curve != null) { - bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, r, nr); - bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, g, ng); - bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, b, nb); - bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, a, na); + bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, r, nr, 1); + bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, g, ng, 1); + bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, b, nb, 1); + bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, a, na, 1); } time = time2; r = nr; @@ -589,13 +570,13 @@ public class SkeletonJson { float nb2 = Integer.parseInt(color.substring(12, 14), 16) / 255f; JsonValue curve = keyMap.get("curve"); if (curve != null) { - bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, r, nr); - bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, g, ng); - bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, b, nb); - bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, a, na); - bezier = readCurve(curve, timeline, bezier, frame, 4, time, time2, r2, nr2); - bezier = readCurve(curve, timeline, bezier, frame, 5, time, time2, g2, ng2); - bezier = readCurve(curve, timeline, bezier, frame, 6, time, time2, b2, nb2); + bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, r, nr, 1); + bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, g, ng, 1); + bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, b, nb, 1); + bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, a, na, 1); + bezier = readCurve(curve, timeline, bezier, frame, 4, time, time2, r2, nr2, 1); + bezier = readCurve(curve, timeline, bezier, frame, 5, time, time2, g2, ng2, 1); + bezier = readCurve(curve, timeline, bezier, frame, 6, time, time2, b2, nb2, 1); } time = time2; r = nr; @@ -660,8 +641,8 @@ public class SkeletonJson { float mix2 = nextMap.getFloat("mix", 1), softness2 = nextMap.getFloat("softness", 0) * scale; JsonValue curve = keyMap.get("curve"); if (curve != null) { - bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, mix, mix2); - bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, softness, softness2); + bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, mix, mix2, 1); + bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, softness, softness2, 1); } time = time2; mix = mix2; @@ -693,10 +674,10 @@ public class SkeletonJson { float scaleMix2 = nextMap.getFloat("scaleMix", 1), shearMix2 = nextMap.getFloat("shearMix", 1); JsonValue curve = keyMap.get("curve"); if (curve != null) { - bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, rotateMix, rotateMix2); - bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, translateMix, translateMix2); - bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, scaleMix, scaleMix2); - bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, shearMix, shearMix2); + bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, rotateMix, rotateMix2, 1); + bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, translateMix, translateMix2, 1); + bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, scaleMix, scaleMix2, 1); + bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, shearMix, shearMix2, 1); } time = time2; rotateMix = rotateMix2; @@ -777,7 +758,7 @@ public class SkeletonJson { } 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); + if (curve != null) bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, 0, 1, 1); time = time2; keyMap = nextMap; } @@ -853,16 +834,16 @@ public class SkeletonJson { } private Timeline readTimeline (JsonValue keyMap, CurveTimeline1 timeline, float defaultValue, float scale) { - float time = keyMap.getFloat("time", 0), value = keyMap.getFloat("value", defaultValue); + float time = keyMap.getFloat("time", 0), value = keyMap.getFloat("value", defaultValue) * scale; int bezier = 0; for (int frame = 0;; frame++) { timeline.setFrame(frame, time, value); JsonValue nextMap = keyMap.next; if (nextMap == null) break; float time2 = nextMap.getFloat("time", 0); - float value2 = nextMap.getFloat("value", defaultValue); + float value2 = nextMap.getFloat("value", defaultValue) * scale; JsonValue curve = keyMap.get("curve"); - if (curve != null) bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, value, value2); + if (curve != null) bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, value, value2, scale); time = time2; value = value2; keyMap = nextMap; @@ -874,18 +855,18 @@ public class SkeletonJson { private Timeline readTimeline (JsonValue keyMap, CurveTimeline2 timeline, String name1, String name2, float defaultValue, float scale) { float time = keyMap.getFloat("time", 0); - float value1 = keyMap.getFloat(name1, defaultValue), value2 = keyMap.getFloat(name2, defaultValue); + float value1 = keyMap.getFloat(name1, defaultValue) * scale, value2 = keyMap.getFloat(name2, defaultValue) * scale; int bezier = 0; for (int frame = 0;; frame++) { timeline.setFrame(frame, time, value1, value2); JsonValue nextMap = keyMap.next; if (nextMap == null) break; float time2 = nextMap.getFloat("time", 0); - float nvalue1 = nextMap.getFloat(name1, defaultValue), nvalue2 = nextMap.getFloat(name2, defaultValue); + float nvalue1 = nextMap.getFloat(name1, defaultValue) * scale, nvalue2 = nextMap.getFloat(name2, defaultValue) * scale; JsonValue curve = keyMap.get("curve"); if (curve != null) { - bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, value1, nvalue1); - bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, value2, nvalue2); + bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, value1, nvalue1, scale); + bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, value2, nvalue2, scale); } time = time2; value1 = nvalue1; @@ -897,18 +878,18 @@ public class SkeletonJson { } int readCurve (JsonValue curve, CurveTimeline timeline, int bezier, int frame, int value, float time1, float time2, - float value1, float value2) { + float value1, float value2, float scale) { if (curve.isString()) { if (value != 0) timeline.setStepped(frame); } else { - curve = curve.get(value * 4); + curve = curve.get(value << 2); float cx1 = curve.asFloat(); curve = curve.next; - float cy1 = curve.asFloat(); + float cy1 = curve.asFloat() * scale; curve = curve.next; float cx2 = curve.asFloat(); curve = curve.next; - float cy2 = curve.asFloat(); + float cy2 = curve.asFloat() * scale; setBezier(timeline, frame, value, bezier++, time1, value1, cx1, cy1, cx2, cy2, time2, value2); } return bezier; diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonLoader.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonLoader.java new file mode 100644 index 000000000..d8538cdf0 --- /dev/null +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonLoader.java @@ -0,0 +1,46 @@ + +package com.esotericsoftware.spine; + +import com.badlogic.gdx.files.FileHandle; +import com.badlogic.gdx.graphics.g2d.TextureAtlas; +import com.badlogic.gdx.utils.Array; + +import com.esotericsoftware.spine.SkeletonJson.LinkedMesh; +import com.esotericsoftware.spine.attachments.AtlasAttachmentLoader; +import com.esotericsoftware.spine.attachments.AttachmentLoader; + +/** Base class for loading skeleton data from a file. */ +abstract public class SkeletonLoader { + final AttachmentLoader attachmentLoader; + float scale = 1; + final Array linkedMeshes = new Array(); + + /** Creates a skeleton loader that loads attachments using an {@link AtlasAttachmentLoader} with the specified atlas. */ + public SkeletonLoader (TextureAtlas atlas) { + attachmentLoader = new AtlasAttachmentLoader(atlas); + } + + /** Creates a skeleton loader that loads attachments using the specified attachment loader. + *

+ * See Loading skeleton data in the + * Spine Runtimes Guide. */ + public SkeletonLoader (AttachmentLoader attachmentLoader) { + if (attachmentLoader == null) throw new IllegalArgumentException("attachmentLoader cannot be null."); + this.attachmentLoader = attachmentLoader; + } + + /** Scales bone positions, image sizes, and translations as they are loaded. This allows different size images to be used at + * runtime than were used in Spine. + *

+ * See Scaling in the Spine Runtimes Guide. */ + public float getScale () { + return scale; + } + + public void setScale (float scale) { + if (scale == 0) throw new IllegalArgumentException("scale cannot be 0."); + this.scale = scale; + } + + abstract public SkeletonData readSkeletonData (FileHandle file); +} From 3f3098902b90e434bfc6cac6e5ce1d80f716194b Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Tue, 21 Apr 2020 17:34:50 +0200 Subject: [PATCH 2/2] [libgdx] SkeletonViewer, even more sophisticated atlas file search. --- .../spine/SkeletonViewer.java | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) 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 8099b0c12..5d70cd4de 100644 --- a/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java +++ b/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java @@ -94,9 +94,10 @@ public class SkeletonViewer extends ApplicationAdapter { static final float checkModifiedInterval = 0.250f; static final float reloadDelay = 1; static float uiScale = 1; + static String[] startSuffixes = {"", "-pro", "-ess"}; static String[] dataSuffixes = {".json", ".skel"}; - static String[] atlasSuffixes = {".atlas", "-pro.atlas", "-ess.atlas"}; - static String[] extraSuffixes = {"", ".txt", ".bytes"}; + static String[] endSuffixes = {"", ".txt", ".bytes"}; + static String[] atlasSuffixes = {".atlas", "-pma.atlas"}; static String[] args; static final String version = ""; // Replaced by build. @@ -146,12 +147,14 @@ public class SkeletonViewer extends ApplicationAdapter { FileHandle atlasFile (FileHandle skeletonFile) { String baseName = skeletonFile.name(); - for (String extraSuffix : extraSuffixes) { - for (String dataSuffix : dataSuffixes) { - String suffix = dataSuffix + extraSuffix; - if (baseName.endsWith(suffix)) { - FileHandle file = atlasFile(skeletonFile, baseName.substring(0, baseName.length() - suffix.length())); - if (file != null) return file; + for (String startSuffix : startSuffixes) { + for (String endSuffix : endSuffixes) { + for (String dataSuffix : dataSuffixes) { + String suffix = startSuffix + dataSuffix + endSuffix; + if (baseName.endsWith(suffix)) { + FileHandle file = atlasFile(skeletonFile, baseName.substring(0, baseName.length() - suffix.length())); + if (file != null) return file; + } } } } @@ -159,10 +162,12 @@ public class SkeletonViewer extends ApplicationAdapter { } private FileHandle atlasFile (FileHandle skeletonFile, String baseName) { - for (String extraSuffix : extraSuffixes) { - for (String suffix : atlasSuffixes) { - FileHandle file = skeletonFile.sibling(baseName + suffix + extraSuffix); - if (file.exists()) return file; + for (String startSuffix : startSuffixes) { + for (String endSuffix : endSuffixes) { + for (String suffix : atlasSuffixes) { + FileHandle file = skeletonFile.sibling(baseName + startSuffix + suffix + endSuffix); + if (file.exists()) return file; + } } } return null; @@ -238,6 +243,7 @@ public class SkeletonViewer extends ApplicationAdapter { state = new AnimationState(new AnimationStateData(skeletonData)); state.addListener(new AnimationStateAdapter() { + public void event (TrackEntry entry, Event event) { ui.toast(event.getData().getName()); } @@ -254,6 +260,7 @@ public class SkeletonViewer extends ApplicationAdapter { ui.window.getTitleLabel().setText(skeletonFile.name()); { + Array items = new Array(); for (Skin skin : skeletonData.getSkins()) items.add(skin.getName());