diff --git a/spine-libgdx/src/com/esotericsoftware/spine/Animation.java b/spine-libgdx/src/com/esotericsoftware/spine/Animation.java index 177093606..207d791ae 100644 --- a/spine-libgdx/src/com/esotericsoftware/spine/Animation.java +++ b/spine-libgdx/src/com/esotericsoftware/spine/Animation.java @@ -28,9 +28,12 @@ package com.esotericsoftware.spine; +import com.esotericsoftware.spine.attachments.MeshAttachment; + import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.FloatArray; public class Animation { final String name; @@ -201,9 +204,9 @@ public class Animation { int i = BEZIER_SEGMENTS - 2; while (true) { if (x >= percent) { - float lastX = x - dfx; - float lastY = y - dfy; - return lastY + (y - lastY) * (percent - lastX) / (x - lastX); + float prevX = x - dfx; + float prevY = y - dfy; + return prevY + (y - prevY) * (percent - prevX) / (x - prevX); } if (i == 0) break; i--; @@ -219,7 +222,7 @@ public class Animation { } static public class RotateTimeline extends CurveTimeline { - static private final int LAST_FRAME_TIME = -2; + static private final int PREV_FRAME_TIME = -2; static private final int FRAME_VALUE = 1; int boneIndex; @@ -265,19 +268,19 @@ public class Animation { return; } - // Interpolate between the last frame and the current frame. + // Interpolate between the previous frame and the current frame. int frameIndex = binarySearch(frames, time, 2); - float lastFrameValue = frames[frameIndex - 1]; + float prevFrameValue = frames[frameIndex - 1]; float frameTime = frames[frameIndex]; - float percent = MathUtils.clamp(1 - (time - frameTime) / (frames[frameIndex + LAST_FRAME_TIME] - frameTime), 0, 1); + float percent = MathUtils.clamp(1 - (time - frameTime) / (frames[frameIndex + PREV_FRAME_TIME] - frameTime), 0, 1); percent = getCurvePercent(frameIndex / 2 - 1, percent); - float amount = frames[frameIndex + FRAME_VALUE] - lastFrameValue; + float amount = frames[frameIndex + FRAME_VALUE] - prevFrameValue; while (amount > 180) amount -= 360; while (amount < -180) amount += 360; - amount = bone.data.rotation + (lastFrameValue + amount * percent) - bone.rotation; + amount = bone.data.rotation + (prevFrameValue + amount * percent) - bone.rotation; while (amount > 180) amount -= 360; while (amount < -180) @@ -287,7 +290,7 @@ public class Animation { } static public class TranslateTimeline extends CurveTimeline { - static final int LAST_FRAME_TIME = -3; + static final int PREV_FRAME_TIME = -3; static final int FRAME_X = 1; static final int FRAME_Y = 2; @@ -331,16 +334,16 @@ public class Animation { return; } - // Interpolate between the last frame and the current frame. + // Interpolate between the previous frame and the current frame. int frameIndex = binarySearch(frames, time, 3); - float lastFrameX = frames[frameIndex - 2]; - float lastFrameY = frames[frameIndex - 1]; + float prevFrameX = frames[frameIndex - 2]; + float prevFrameY = frames[frameIndex - 1]; float frameTime = frames[frameIndex]; - float percent = MathUtils.clamp(1 - (time - frameTime) / (frames[frameIndex + LAST_FRAME_TIME] - frameTime), 0, 1); + float percent = MathUtils.clamp(1 - (time - frameTime) / (frames[frameIndex + PREV_FRAME_TIME] - frameTime), 0, 1); percent = getCurvePercent(frameIndex / 3 - 1, percent); - bone.x += (bone.data.x + lastFrameX + (frames[frameIndex + FRAME_X] - lastFrameX) * percent - bone.x) * alpha; - bone.y += (bone.data.y + lastFrameY + (frames[frameIndex + FRAME_Y] - lastFrameY) * percent - bone.y) * alpha; + bone.x += (bone.data.x + prevFrameX + (frames[frameIndex + FRAME_X] - prevFrameX) * percent - bone.x) * alpha; + bone.y += (bone.data.y + prevFrameY + (frames[frameIndex + FRAME_Y] - prevFrameY) * percent - bone.y) * alpha; } } @@ -360,23 +363,23 @@ public class Animation { return; } - // Interpolate between the last frame and the current frame. + // Interpolate between the previous frame and the current frame. int frameIndex = binarySearch(frames, time, 3); - float lastFrameX = frames[frameIndex - 2]; - float lastFrameY = frames[frameIndex - 1]; + float prevFrameX = frames[frameIndex - 2]; + float prevFrameY = frames[frameIndex - 1]; float frameTime = frames[frameIndex]; - float percent = MathUtils.clamp(1 - (time - frameTime) / (frames[frameIndex + LAST_FRAME_TIME] - frameTime), 0, 1); + float percent = MathUtils.clamp(1 - (time - frameTime) / (frames[frameIndex + PREV_FRAME_TIME] - frameTime), 0, 1); percent = getCurvePercent(frameIndex / 3 - 1, percent); - bone.scaleX += (bone.data.scaleX - 1 + lastFrameX + (frames[frameIndex + FRAME_X] - lastFrameX) * percent - bone.scaleX) + bone.scaleX += (bone.data.scaleX - 1 + prevFrameX + (frames[frameIndex + FRAME_X] - prevFrameX) * percent - bone.scaleX) * alpha; - bone.scaleY += (bone.data.scaleY - 1 + lastFrameY + (frames[frameIndex + FRAME_Y] - lastFrameY) * percent - bone.scaleY) + bone.scaleY += (bone.data.scaleY - 1 + prevFrameY + (frames[frameIndex + FRAME_Y] - prevFrameY) * percent - bone.scaleY) * alpha; } } static public class ColorTimeline extends CurveTimeline { - static private final int LAST_FRAME_TIME = -5; + static private final int PREV_FRAME_TIME = -5; static private final int FRAME_R = 1; static private final int FRAME_G = 2; static private final int FRAME_B = 3; @@ -428,20 +431,20 @@ public class Animation { return; } - // Interpolate between the last frame and the current frame. + // Interpolate between the previous frame and the current frame. int frameIndex = binarySearch(frames, time, 5); - float lastFrameR = frames[frameIndex - 4]; - float lastFrameG = frames[frameIndex - 3]; - float lastFrameB = frames[frameIndex - 2]; - float lastFrameA = frames[frameIndex - 1]; + float prevFrameR = frames[frameIndex - 4]; + float prevFrameG = frames[frameIndex - 3]; + float prevFrameB = frames[frameIndex - 2]; + float prevFrameA = frames[frameIndex - 1]; float frameTime = frames[frameIndex]; - float percent = MathUtils.clamp(1 - (time - frameTime) / (frames[frameIndex + LAST_FRAME_TIME] - frameTime), 0, 1); + float percent = MathUtils.clamp(1 - (time - frameTime) / (frames[frameIndex + PREV_FRAME_TIME] - frameTime), 0, 1); percent = getCurvePercent(frameIndex / 5 - 1, percent); - float r = lastFrameR + (frames[frameIndex + FRAME_R] - lastFrameR) * percent; - float g = lastFrameG + (frames[frameIndex + FRAME_G] - lastFrameG) * percent; - float b = lastFrameB + (frames[frameIndex + FRAME_B] - lastFrameB) * percent; - float a = lastFrameA + (frames[frameIndex + FRAME_A] - lastFrameA) * percent; + float r = prevFrameR + (frames[frameIndex + FRAME_R] - prevFrameR) * percent; + float g = prevFrameG + (frames[frameIndex + FRAME_G] - prevFrameG) * percent; + float b = prevFrameB + (frames[frameIndex + FRAME_B] - prevFrameB) * percent; + float a = prevFrameA + (frames[frameIndex + FRAME_A] - prevFrameA) * percent; if (alpha < 1) color.add((r - color.r) * alpha, (g - color.g) * alpha, (b - color.b) * alpha, (a - color.a) * alpha); else @@ -606,4 +609,84 @@ public class Animation { } } } + + static public class FfdTimeline extends CurveTimeline { + private final float[] frames; // time, ... + private final float[][] frameVertices; + int slotIndex; + MeshAttachment meshAttachment; + + public FfdTimeline (int frameCount) { + super(frameCount); + frames = new float[frameCount]; + frameVertices = new float[frameCount][]; + } + + public void setSlotIndex (int slotIndex) { + this.slotIndex = slotIndex; + } + + public int getSlotIndex () { + return slotIndex; + } + + public void setMeshAttachment (MeshAttachment attachment) { + this.meshAttachment = attachment; + } + + public MeshAttachment getMeshAttachment () { + return meshAttachment; + } + + public float[] getFrames () { + return frames; + } + + public float[][] getVertices () { + return frameVertices; + } + + /** Sets the time of the specified keyframe. */ + public void setFrame (int frameIndex, float time, float[] vertices) { + frames[frameIndex] = time; + frameVertices[frameIndex] = vertices; + } + + public void apply (Skeleton skeleton, float lastTime, float time, Array firedEvents, float alpha) { + Slot slot = skeleton.slots.get(slotIndex); + if (slot.getAttachment() != meshAttachment) return; + + FloatArray verticesArray = slot.getAttachmentVertices(); + verticesArray.size = 0; + + float[] frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + float[][] frameVertices = this.frameVertices; + int vertexCount = frameVertices[0].length; + verticesArray.ensureCapacity(vertexCount); + verticesArray.size = vertexCount; + float[] vertices = verticesArray.items; + + if (time >= frames[frames.length - 1]) { // Time is after last frame. + System.arraycopy(frameVertices[frames.length - 1], 0, vertices, 0, vertexCount); + return; + } + + // Interpolate between the previous frame and the current frame. + int frameIndex = binarySearch(frames, time, 1); + float frameTime = frames[frameIndex]; + float percent = MathUtils.clamp(1 - (time - frameTime) / (frames[frameIndex - 1] - frameTime), 0, 1); + percent = getCurvePercent(frameIndex - 1, percent); + + float[] prevVertices = frameVertices[frameIndex - 1]; + float[] nextVertices = frameVertices[frameIndex]; + + // BOZO - FFD, use alpha for mixing? + for (int i = 0; i < vertexCount; i++) { + float prev = prevVertices[i]; + vertices[i] = prev + (nextVertices[i] - prev) * percent; + } + } + } } diff --git a/spine-libgdx/src/com/esotericsoftware/spine/BoneData.java b/spine-libgdx/src/com/esotericsoftware/spine/BoneData.java index fdef45c2e..9f5ca13c0 100644 --- a/spine-libgdx/src/com/esotericsoftware/spine/BoneData.java +++ b/spine-libgdx/src/com/esotericsoftware/spine/BoneData.java @@ -28,6 +28,8 @@ package com.esotericsoftware.spine; +import com.badlogic.gdx.graphics.Color; + public class BoneData { final BoneData parent; final String name; @@ -37,6 +39,9 @@ public class BoneData { float scaleX = 1, scaleY = 1; boolean inheritScale = true, inheritRotation = true; + // Nonessential. + final Color color = new Color(1, 1, 1, 1); + /** @param parent May be null. */ public BoneData (String name, BoneData parent) { if (name == null) throw new IllegalArgumentException("name cannot be null."); @@ -131,6 +136,10 @@ public class BoneData { this.inheritRotation = inheritRotation; } + public Color getColor () { + return color; + } + public String toString () { return name; } diff --git a/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java b/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java index 4e317ce44..a43c2fcfb 100644 --- a/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java +++ b/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java @@ -33,6 +33,7 @@ import com.esotericsoftware.spine.Animation.ColorTimeline; import com.esotericsoftware.spine.Animation.CurveTimeline; import com.esotericsoftware.spine.Animation.DrawOrderTimeline; import com.esotericsoftware.spine.Animation.EventTimeline; +import com.esotericsoftware.spine.Animation.FfdTimeline; import com.esotericsoftware.spine.Animation.RotateTimeline; import com.esotericsoftware.spine.Animation.ScaleTimeline; import com.esotericsoftware.spine.Animation.Timeline; @@ -62,6 +63,7 @@ public class SkeletonBinary { static public final int TIMELINE_COLOR = 4; static public final int TIMELINE_EVENT = 5; static public final int TIMELINE_DRAWORDER = 6; + static public final int TIMELINE_FFD = 7; static public final int CURVE_LINEAR = 0; static public final int CURVE_STEPPED = 1; @@ -92,11 +94,14 @@ public class SkeletonBinary { public SkeletonData readSkeletonData (FileHandle file) { if (file == null) throw new IllegalArgumentException("file cannot be null."); + float scale = this.scale; + SkeletonData skeletonData = new SkeletonData(); skeletonData.name = file.nameWithoutExtension(); DataInput input = new DataInput(file.read(512)); try { + boolean nonessential = input.readBoolean(); // Bones. for (int i = 0, n = input.readInt(true); i < n; i++) { String name = input.readString(); @@ -110,8 +115,9 @@ public class SkeletonBinary { boneData.scaleY = input.readFloat(); boneData.rotation = input.readFloat(); boneData.length = input.readFloat() * scale; - boneData.inheritScale = input.readByte() == 1; - boneData.inheritRotation = input.readByte() == 1; + boneData.inheritScale = input.readBoolean(); + boneData.inheritRotation = input.readBoolean(); + if (nonessential) Color.rgba8888ToColor(boneData.getColor(), input.readInt()); skeletonData.addBone(boneData); } @@ -122,12 +128,12 @@ public class SkeletonBinary { SlotData slotData = new SlotData(slotName, boneData); Color.rgba8888ToColor(slotData.getColor(), input.readInt()); slotData.attachmentName = input.readString(); - slotData.additiveBlending = input.readByte() == 1; + slotData.additiveBlending = input.readBoolean(); skeletonData.addSlot(slotData); } // Default skin. - Skin defaultSkin = readSkin(input, "default"); + Skin defaultSkin = readSkin(input, "default", nonessential); if (defaultSkin != null) { skeletonData.defaultSkin = defaultSkin; skeletonData.addSkin(defaultSkin); @@ -135,7 +141,7 @@ public class SkeletonBinary { // Skins. for (int i = 0, n = input.readInt(true); i < n; i++) - skeletonData.addSkin(readSkin(input, input.readString())); + skeletonData.addSkin(readSkin(input, input.readString(), nonessential)); // Events. for (int i = 0, n = input.readInt(true); i < n; i++) { @@ -165,7 +171,7 @@ public class SkeletonBinary { return skeletonData; } - private Skin readSkin (DataInput input, String skinName) throws IOException { + private Skin readSkin (DataInput input, String skinName, boolean nonessential) throws IOException { int slotCount = input.readInt(true); if (slotCount == 0) return null; Skin skin = new Skin(skinName); @@ -173,13 +179,15 @@ public class SkeletonBinary { int slotIndex = input.readInt(true); for (int ii = 0, nn = input.readInt(true); ii < nn; ii++) { String name = input.readString(); - skin.addAttachment(slotIndex, name, readAttachment(input, skin, name)); + skin.addAttachment(slotIndex, name, readAttachment(input, skin, name, nonessential)); } } return skin; } - private Attachment readAttachment (DataInput input, Skin skin, String attachmentName) throws IOException { + private Attachment readAttachment (DataInput input, Skin skin, String attachmentName, boolean nonessential) throws IOException { + float scale = this.scale; + String name = input.readString(); if (name == null) name = attachmentName; @@ -214,8 +222,8 @@ public class SkeletonBinary { short[] triangles = readShortArray(input); float[] uvs = readFloatArray(input, 1); Color.rgba8888ToColor(mesh.getColor(), input.readInt()); - mesh.setEdges(readIntArray(input)); - if (mesh.getEdges().length > 0) { + if (nonessential) { + mesh.setEdges(readIntArray(input)); mesh.setHullLength(input.readInt(true)); mesh.setWidth(input.readFloat() * scale); mesh.setHeight(input.readFloat() * scale); @@ -253,25 +261,26 @@ public class SkeletonBinary { private void readAnimation (String name, DataInput input, SkeletonData skeletonData) { Array timelines = new Array(); + float scale = this.scale; float duration = 0; try { + // SRT. for (int i = 0, n = input.readInt(true); i < n; i++) { int boneIndex = input.readInt(true); - int itemCount = input.readInt(true); - for (int ii = 0; ii < itemCount; ii++) { + for (int ii = 0, nn = input.readInt(true); ii < nn; ii++) { int timelineType = input.readByte(); - int keyCount = input.readInt(true); + int frameCount = input.readInt(true); switch (timelineType) { case TIMELINE_ROTATE: { - RotateTimeline timeline = new RotateTimeline(keyCount); + RotateTimeline timeline = new RotateTimeline(frameCount); timeline.boneIndex = boneIndex; - for (int frameIndex = 0; frameIndex < keyCount; frameIndex++) { + for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) { timeline.setFrame(frameIndex, input.readFloat(), input.readFloat()); - if (frameIndex < keyCount - 1) readCurve(input, frameIndex, timeline); + if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline); } timelines.add(timeline); - duration = Math.max(duration, timeline.getFrames()[keyCount * 2 - 2]); + duration = Math.max(duration, timeline.getFrames()[frameCount * 2 - 2]); break; } case TIMELINE_TRANSLATE: @@ -279,56 +288,88 @@ public class SkeletonBinary { TranslateTimeline timeline; float timelineScale = 1; if (timelineType == TIMELINE_SCALE) - timeline = new ScaleTimeline(keyCount); + timeline = new ScaleTimeline(frameCount); else { - timeline = new TranslateTimeline(keyCount); + timeline = new TranslateTimeline(frameCount); timelineScale = scale; } timeline.boneIndex = boneIndex; - for (int frameIndex = 0; frameIndex < keyCount; frameIndex++) { + for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) { timeline.setFrame(frameIndex, input.readFloat(), input.readFloat() * timelineScale, input.readFloat() * timelineScale); - if (frameIndex < keyCount - 1) readCurve(input, frameIndex, timeline); + if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline); } timelines.add(timeline); - duration = Math.max(duration, timeline.getFrames()[keyCount * 3 - 3]); + duration = Math.max(duration, timeline.getFrames()[frameCount * 3 - 3]); break; } } } + // FFD. + for (int i = 0, n = input.readInt(true); i < n; i++) { + Skin skin = skeletonData.getSkins().get(input.readInt(true) + 1); + 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++) { + MeshAttachment mesh = (MeshAttachment)skin.getAttachment(slotIndex, input.readString()); + int frameCount = input.readInt(true); + FfdTimeline timeline = new FfdTimeline(frameCount); + timeline.slotIndex = slotIndex; + timeline.meshAttachment = mesh; + for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) { + float time = input.readFloat(); + float[] vertices; + int vertexCount = input.readInt(true); + if (vertexCount == 0) + vertices = mesh.getVertices(); + else { + vertices = new float[vertexCount]; + for (int vertex = 0; vertex < vertexCount; vertex++) + vertices[vertex] = input.readFloat() * scale; + } + timeline.setFrame(frameIndex, time, vertices); + if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline); + } + timelines.add(timeline); + duration = Math.max(duration, timeline.getFrames()[frameCount - 1]); + } + } + } + + // Color, attachment. for (int i = 0, n = input.readInt(true); i < n; i++) { int slotIndex = input.readInt(true); - int itemCount = input.readInt(true); - for (int ii = 0; ii < itemCount; ii++) { + for (int ii = 0, nn = input.readInt(true); ii < nn; ii++) { int timelineType = input.readByte(); - int keyCount = input.readInt(true); + int frameCount = input.readInt(true); switch (timelineType) { case TIMELINE_COLOR: { - ColorTimeline timeline = new ColorTimeline(keyCount); + ColorTimeline timeline = new ColorTimeline(frameCount); timeline.slotIndex = slotIndex; - for (int frameIndex = 0; frameIndex < keyCount; frameIndex++) { + for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) { float time = input.readFloat(); Color.rgba8888ToColor(tempColor, input.readInt()); timeline.setFrame(frameIndex, time, tempColor.r, tempColor.g, tempColor.b, tempColor.a); - if (frameIndex < keyCount - 1) readCurve(input, frameIndex, timeline); + if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline); } timelines.add(timeline); - duration = Math.max(duration, timeline.getFrames()[keyCount * 5 - 5]); + duration = Math.max(duration, timeline.getFrames()[frameCount * 5 - 5]); break; } case TIMELINE_ATTACHMENT: - AttachmentTimeline timeline = new AttachmentTimeline(keyCount); + AttachmentTimeline timeline = new AttachmentTimeline(frameCount); timeline.slotIndex = slotIndex; - for (int frameIndex = 0; frameIndex < keyCount; frameIndex++) + for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) timeline.setFrame(frameIndex, input.readFloat(), input.readString()); timelines.add(timeline); - duration = Math.max(duration, timeline.getFrames()[keyCount - 1]); + duration = Math.max(duration, timeline.getFrames()[frameCount - 1]); break; } } } + // Events. int eventCount = input.readInt(true); if (eventCount > 0) { EventTimeline timeline = new EventTimeline(eventCount); @@ -345,6 +386,7 @@ public class SkeletonBinary { duration = Math.max(duration, timeline.getFrames()[eventCount - 1]); } + // Draw order. int drawOrderCount = input.readInt(true); if (drawOrderCount > 0) { DrawOrderTimeline timeline = new DrawOrderTimeline(drawOrderCount); diff --git a/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java b/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java index 5ea57414f..b5b64abb3 100644 --- a/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java +++ b/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java @@ -33,6 +33,7 @@ import com.esotericsoftware.spine.Animation.ColorTimeline; import com.esotericsoftware.spine.Animation.CurveTimeline; import com.esotericsoftware.spine.Animation.DrawOrderTimeline; import com.esotericsoftware.spine.Animation.EventTimeline; +import com.esotericsoftware.spine.Animation.FfdTimeline; import com.esotericsoftware.spine.Animation.RotateTimeline; import com.esotericsoftware.spine.Animation.ScaleTimeline; import com.esotericsoftware.spine.Animation.Timeline; @@ -54,12 +55,6 @@ import com.badlogic.gdx.utils.JsonValue; import com.badlogic.gdx.utils.SerializationException; public class SkeletonJson { - static public final String TIMELINE_SCALE = "scale"; - static public final String TIMELINE_ROTATE = "rotate"; - static public final String TIMELINE_TRANSLATE = "translate"; - static public final String TIMELINE_ATTACHMENT = "attachment"; - static public final String TIMELINE_COLOR = "color"; - private final AttachmentLoader attachmentLoader; private float scale = 1; @@ -83,13 +78,15 @@ public class SkeletonJson { public SkeletonData readSkeletonData (FileHandle file) { if (file == null) throw new IllegalArgumentException("file cannot be null."); + float scale = this.scale; + SkeletonData skeletonData = new SkeletonData(); skeletonData.name = file.nameWithoutExtension(); JsonValue root = new JsonReader().parse(file); // Bones. - for (JsonValue boneMap = root.getChild("bones"); boneMap != null; boneMap = boneMap.next()) { + for (JsonValue boneMap = root.getChild("bones"); boneMap != null; boneMap = boneMap.next) { BoneData parent = null; String parentName = boneMap.getString("parent", null); if (parentName != null) { @@ -105,11 +102,15 @@ public class SkeletonJson { boneData.scaleY = boneMap.getFloat("scaleY", 1); boneData.inheritScale = boneMap.getBoolean("inheritScale", true); boneData.inheritRotation = boneMap.getBoolean("inheritRotation", true); + + String color = boneMap.getString("color", null); + if (color != null) boneData.getColor().set(Color.valueOf(color)); + skeletonData.addBone(boneData); } // Slots. - for (JsonValue slotMap = root.getChild("slots"); slotMap != null; slotMap = slotMap.next()) { + for (JsonValue slotMap = root.getChild("slots"); slotMap != null; slotMap = slotMap.next) { String slotName = slotMap.getString("name"); String boneName = slotMap.getString("bone"); BoneData boneData = skeletonData.findBone(boneName); @@ -127,14 +128,14 @@ public class SkeletonJson { } // Skins. - for (JsonValue skinMap = root.getChild("skins"); skinMap != null; skinMap = skinMap.next()) { - Skin skin = new Skin(skinMap.name()); - for (JsonValue slotEntry = skinMap.child(); slotEntry != null; slotEntry = slotEntry.next()) { - int slotIndex = skeletonData.findSlotIndex(slotEntry.name()); - if (slotIndex == -1) throw new SerializationException("Slot not found: " + slotEntry.name()); - for (JsonValue entry = slotEntry.child(); entry != null; entry = entry.next()) { - Attachment attachment = readAttachment(skin, entry.name(), entry); - if (attachment != null) skin.addAttachment(slotIndex, entry.name(), attachment); + for (JsonValue skinMap = root.getChild("skins"); skinMap != null; skinMap = skinMap.next) { + Skin skin = new Skin(skinMap.name); + for (JsonValue slotEntry = skinMap.child; slotEntry != null; slotEntry = slotEntry.next) { + int slotIndex = skeletonData.findSlotIndex(slotEntry.name); + if (slotIndex == -1) throw new SerializationException("Slot not found: " + slotEntry.name); + for (JsonValue entry = slotEntry.child; entry != null; entry = entry.next) { + Attachment attachment = readAttachment(skin, entry.name, entry); + if (attachment != null) skin.addAttachment(slotIndex, entry.name, attachment); } } skeletonData.addSkin(skin); @@ -142,8 +143,8 @@ public class SkeletonJson { } // Events. - for (JsonValue eventMap = root.getChild("events"); eventMap != null; eventMap = eventMap.next()) { - EventData eventData = new EventData(eventMap.name()); + for (JsonValue eventMap = root.getChild("events"); eventMap != null; eventMap = eventMap.next) { + EventData eventData = new EventData(eventMap.name); eventData.intValue = eventMap.getInt("int", 0); eventData.floatValue = eventMap.getFloat("float", 0f); eventData.stringValue = eventMap.getString("string", null); @@ -151,8 +152,8 @@ public class SkeletonJson { } // Animations. - for (JsonValue animationMap = root.getChild("animations"); animationMap != null; animationMap = animationMap.next()) - readAnimation(animationMap.name(), animationMap, skeletonData); + for (JsonValue animationMap = root.getChild("animations"); animationMap != null; animationMap = animationMap.next) + readAnimation(animationMap.name, animationMap, skeletonData); skeletonData.bones.shrink(); skeletonData.slots.shrink(); @@ -162,6 +163,7 @@ public class SkeletonJson { } private Attachment readAttachment (Skin skin, String name, JsonValue map) { + float scale = this.scale; name = map.getString("name", name); switch (AttachmentType.valueOf(map.getString("type", AttachmentType.region.name()))) { @@ -182,17 +184,26 @@ public class SkeletonJson { return region; case boundingbox: { BoundingBoxAttachment box = attachmentLoader.newBoundingBoxAttachment(skin, name); - box.setVertices(readFloatArray(map.require("vertices"), scale)); + float[] vertices = map.require("vertices").asFloatArray(); + if (scale != 1) { + for (int i = 0, n = vertices.length; i < n; i++) + vertices[i] *= scale; + } + box.setVertices(vertices); return box; } case mesh: { MeshAttachment mesh = attachmentLoader.newMeshAttachment(skin, name, map.getString("path", name)); - float[] vertices = readFloatArray(map.require("vertices"), scale); - short[] triangles = readShortArray(map.require("triangles")); - float[] uvs = readFloatArray(map.require("uvs"), 1); + float[] vertices = map.require("vertices").asFloatArray(); + if (scale != 1) { + for (int i = 0, n = vertices.length; i < n; i++) + vertices[i] *= scale; + } + short[] triangles = map.require("triangles").asShortArray(); + float[] uvs = map.require("uvs").asFloatArray(); mesh.setMesh(vertices, triangles, uvs); if (map.has("hull")) mesh.setHullLength(map.require("hull").asInt()); - if (map.has("edges")) mesh.setEdges(readIntArray(map.require("edges"))); + if (map.has("edges")) mesh.setEdges(map.require("edges").asIntArray()); mesh.setWidth(map.getFloat("width", 0) * scale); mesh.setHeight(map.getFloat("height", 0) * scale); return mesh; @@ -210,58 +221,34 @@ public class SkeletonJson { return null; } - private float[] readFloatArray (JsonValue jsonArray, float scale) { - float[] array = new float[jsonArray.size]; - int i = 0; - for (JsonValue point = jsonArray.child; point != null; point = point.next()) - array[i++] = point.asFloat() * scale; - return array; - } - - private short[] readShortArray (JsonValue jsonArray) { - short[] array = new short[jsonArray.size]; - int i = 0; - for (JsonValue point = jsonArray.child; point != null; point = point.next()) - array[i++] = (short)point.asInt(); - return array; - } - - private int[] readIntArray (JsonValue jsonArray) { - int[] array = new int[jsonArray.size]; - int i = 0; - for (JsonValue point = jsonArray.child; point != null; point = point.next()) - array[i++] = point.asInt(); - return array; - } - private void readAnimation (String name, JsonValue map, SkeletonData skeletonData) { Array timelines = new Array(); float duration = 0; - for (JsonValue boneMap = map.getChild("bones"); boneMap != null; boneMap = boneMap.next()) { - int boneIndex = skeletonData.findBoneIndex(boneMap.name()); - if (boneIndex == -1) throw new SerializationException("Bone not found: " + boneMap.name()); + // SRT. + for (JsonValue boneMap = map.getChild("bones"); boneMap != null; boneMap = boneMap.next) { + int boneIndex = skeletonData.findBoneIndex(boneMap.name); + if (boneIndex == -1) throw new SerializationException("Bone not found: " + boneMap.name); - for (JsonValue timelineMap = boneMap.child(); timelineMap != null; timelineMap = timelineMap.next()) { - String timelineName = timelineMap.name(); - if (timelineName.equals(TIMELINE_ROTATE)) { + for (JsonValue timelineMap = boneMap.child; timelineMap != null; timelineMap = timelineMap.next) { + String timelineName = timelineMap.name; + if (timelineName.equals("rotate")) { RotateTimeline timeline = new RotateTimeline(timelineMap.size); timeline.boneIndex = boneIndex; int frameIndex = 0; - for (JsonValue valueMap = timelineMap.child(); valueMap != null; valueMap = valueMap.next()) { - float time = valueMap.getFloat("time"); - timeline.setFrame(frameIndex, time, valueMap.getFloat("angle")); + for (JsonValue valueMap = timelineMap.child; valueMap != null; valueMap = valueMap.next) { + timeline.setFrame(frameIndex, valueMap.getFloat("time"), valueMap.getFloat("angle")); readCurve(timeline, frameIndex, valueMap); frameIndex++; } timelines.add(timeline); duration = Math.max(duration, timeline.getFrames()[timeline.getFrameCount() * 2 - 2]); - } else if (timelineName.equals(TIMELINE_TRANSLATE) || timelineName.equals(TIMELINE_SCALE)) { + } else if (timelineName.equals("translate") || timelineName.equals("scale")) { TranslateTimeline timeline; float timelineScale = 1; - if (timelineName.equals(TIMELINE_SCALE)) + if (timelineName.equals("scale")) timeline = new ScaleTimeline(timelineMap.size); else { timeline = new TranslateTimeline(timelineMap.size); @@ -270,10 +257,9 @@ public class SkeletonJson { timeline.boneIndex = boneIndex; int frameIndex = 0; - for (JsonValue valueMap = timelineMap.child(); valueMap != null; valueMap = valueMap.next()) { - float time = valueMap.getFloat("time"); + for (JsonValue valueMap = timelineMap.child; valueMap != null; valueMap = valueMap.next) { float x = valueMap.getFloat("x", 0), y = valueMap.getFloat("y", 0); - timeline.setFrame(frameIndex, time, x * timelineScale, y * timelineScale); + timeline.setFrame(frameIndex, valueMap.getFloat("time"), x * timelineScale, y * timelineScale); readCurve(timeline, frameIndex, valueMap); frameIndex++; } @@ -281,53 +267,88 @@ public class SkeletonJson { duration = Math.max(duration, timeline.getFrames()[timeline.getFrameCount() * 3 - 3]); } else - throw new RuntimeException("Invalid timeline type for a bone: " + timelineName + " (" + boneMap.name() + ")"); + throw new RuntimeException("Invalid timeline type for a bone: " + timelineName + " (" + boneMap.name + ")"); } } - for (JsonValue slotMap = map.getChild("slots"); slotMap != null; slotMap = slotMap.next()) { - int slotIndex = skeletonData.findSlotIndex(slotMap.name()); - if (slotIndex == -1) throw new SerializationException("Slot not found: " + slotMap.name()); + // FFD. + for (JsonValue ffdMap = map.getChild("ffd"); ffdMap != null; ffdMap = ffdMap.next) { + Skin skin = skeletonData.findSkin(ffdMap.name); + if (skin == null) throw new SerializationException("Skin not found: " + ffdMap.name); + for (JsonValue slotMap = ffdMap.child; slotMap != null; slotMap = slotMap.next) { + int slotIndex = skeletonData.findSlotIndex(slotMap.name); + if (slotIndex == -1) throw new SerializationException("Slot not found: " + slotMap.name); + for (JsonValue meshMap = slotMap.child; meshMap != null; meshMap = meshMap.next) { + FfdTimeline timeline = new FfdTimeline(meshMap.size); + MeshAttachment mesh = (MeshAttachment)skin.getAttachment(slotIndex, meshMap.name); + if (mesh == null) throw new SerializationException("Mesh attachment not found: " + meshMap.name); + timeline.slotIndex = slotIndex; + timeline.meshAttachment = mesh; - for (JsonValue timelineMap = slotMap.child(); timelineMap != null; timelineMap = timelineMap.next()) { - String timelineName = timelineMap.name(); - if (timelineName.equals(TIMELINE_COLOR)) { + int frameIndex = 0; + for (JsonValue valueMap = meshMap.child; valueMap != null; valueMap = valueMap.next) { + float[] vertices; + JsonValue verticesValue = valueMap.get("vertices"); + if (verticesValue == null) + vertices = mesh.getVertices(); + else { + vertices = verticesValue.asFloatArray(); + if (scale != 1) { + for (int i = 0, n = vertices.length; i < n; i++) + vertices[i] *= scale; + } + } + timeline.setFrame(frameIndex, valueMap.getFloat("time"), vertices); + readCurve(timeline, frameIndex, valueMap); + frameIndex++; + } + timelines.add(timeline); + duration = Math.max(duration, timeline.getFrames()[timeline.getFrameCount() - 1]); + } + } + } + + // Color, attachment. + for (JsonValue slotMap = map.getChild("slots"); slotMap != null; slotMap = slotMap.next) { + int slotIndex = skeletonData.findSlotIndex(slotMap.name); + if (slotIndex == -1) throw new SerializationException("Slot not found: " + slotMap.name); + + for (JsonValue timelineMap = slotMap.child; timelineMap != null; timelineMap = timelineMap.next) { + String timelineName = timelineMap.name; + if (timelineName.equals("color")) { ColorTimeline timeline = new ColorTimeline(timelineMap.size); timeline.slotIndex = slotIndex; int frameIndex = 0; - for (JsonValue valueMap = timelineMap.child(); valueMap != null; valueMap = valueMap.next()) { - float time = valueMap.getFloat("time"); + for (JsonValue valueMap = timelineMap.child; valueMap != null; valueMap = valueMap.next) { Color color = Color.valueOf(valueMap.getString("color")); - timeline.setFrame(frameIndex, time, color.r, color.g, color.b, color.a); + timeline.setFrame(frameIndex, valueMap.getFloat("time"), color.r, color.g, color.b, color.a); readCurve(timeline, frameIndex, valueMap); frameIndex++; } timelines.add(timeline); duration = Math.max(duration, timeline.getFrames()[timeline.getFrameCount() * 5 - 5]); - } else if (timelineName.equals(TIMELINE_ATTACHMENT)) { + } else if (timelineName.equals("attachment")) { AttachmentTimeline timeline = new AttachmentTimeline(timelineMap.size); timeline.slotIndex = slotIndex; int frameIndex = 0; - for (JsonValue valueMap = timelineMap.child(); valueMap != null; valueMap = valueMap.next()) { - float time = valueMap.getFloat("time"); - timeline.setFrame(frameIndex++, time, valueMap.getString("name")); - } + for (JsonValue valueMap = timelineMap.child; valueMap != null; valueMap = valueMap.next) + timeline.setFrame(frameIndex++, valueMap.getFloat("time"), valueMap.getString("name")); timelines.add(timeline); duration = Math.max(duration, timeline.getFrames()[timeline.getFrameCount() - 1]); - } else - throw new RuntimeException("Invalid timeline type for a slot: " + timelineName + " (" + slotMap.name() + ")"); + throw new RuntimeException("Invalid timeline type for a slot: " + timelineName + " (" + slotMap.name + ")"); } } + // Events. JsonValue eventsMap = map.get("events"); if (eventsMap != null) { EventTimeline timeline = new EventTimeline(eventsMap.size); int frameIndex = 0; - for (JsonValue eventMap = eventsMap.child; eventMap != null; eventMap = eventMap.next()) { + for (JsonValue eventMap = eventsMap.child; eventMap != null; eventMap = eventMap.next) { EventData eventData = skeletonData.findEvent(eventMap.getString("name")); if (eventData == null) throw new SerializationException("Event not found: " + eventMap.getString("name")); Event event = new Event(eventData); @@ -340,12 +361,13 @@ public class SkeletonJson { duration = Math.max(duration, timeline.getFrames()[timeline.getFrameCount() - 1]); } + // Draw order. JsonValue drawOrdersMap = map.get("draworder"); if (drawOrdersMap != null) { DrawOrderTimeline timeline = new DrawOrderTimeline(drawOrdersMap.size); int slotCount = skeletonData.slots.size; int frameIndex = 0; - for (JsonValue drawOrderMap = drawOrdersMap.child; drawOrderMap != null; drawOrderMap = drawOrderMap.next()) { + for (JsonValue drawOrderMap = drawOrdersMap.child; drawOrderMap != null; drawOrderMap = drawOrderMap.next) { int[] drawOrder = null; JsonValue offsets = drawOrderMap.get("offsets"); if (offsets != null) { @@ -354,7 +376,7 @@ public class SkeletonJson { drawOrder[i] = -1; int[] unchanged = new int[slotCount - offsets.size]; int originalIndex = 0, unchangedIndex = 0; - for (JsonValue offsetMap = offsets.child; offsetMap != null; offsetMap = offsetMap.next()) { + for (JsonValue offsetMap = offsets.child; offsetMap != null; offsetMap = offsetMap.next) { int slotIndex = skeletonData.findSlotIndex(offsetMap.getString("slot")); if (slotIndex == -1) throw new SerializationException("Slot not found: " + offsetMap.getString("slot")); // Collect unchanged items. diff --git a/spine-libgdx/src/com/esotericsoftware/spine/Slot.java b/spine-libgdx/src/com/esotericsoftware/spine/Slot.java index a22b9f330..5d6229a34 100644 --- a/spine-libgdx/src/com/esotericsoftware/spine/Slot.java +++ b/spine-libgdx/src/com/esotericsoftware/spine/Slot.java @@ -31,6 +31,7 @@ package com.esotericsoftware.spine; import com.esotericsoftware.spine.attachments.Attachment; import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.utils.FloatArray; public class Slot { final SlotData data; @@ -39,6 +40,7 @@ public class Slot { final Color color; Attachment attachment; private float attachmentTime; + private final FloatArray attachmentVertices = new FloatArray(); Slot () { data = null; @@ -92,11 +94,13 @@ public class Slot { return attachment; } - /** Sets the attachment and resets {@link #getAttachmentTime()}. + /** Sets the attachment, resets {@link #getAttachmentTime()}, and clears {@link #getAttachmentVertices()}. * @param attachment May be null. */ public void setAttachment (Attachment attachment) { + if (this.attachment == attachment) return; this.attachment = attachment; attachmentTime = skeleton.time; + attachmentVertices.clear(); } public void setAttachmentTime (float time) { @@ -108,6 +112,10 @@ public class Slot { return skeleton.time - attachmentTime; } + public FloatArray getAttachmentVertices () { + return attachmentVertices; + } + void setToSetupPose (int slotIndex) { color.set(data.color); setAttachment(data.attachmentName == null ? null : skeleton.getAttachment(slotIndex, data.attachmentName)); diff --git a/spine-libgdx/src/com/esotericsoftware/spine/attachments/MeshAttachment.java b/spine-libgdx/src/com/esotericsoftware/spine/attachments/MeshAttachment.java index 6c8cd228a..d5da4b653 100644 --- a/spine-libgdx/src/com/esotericsoftware/spine/attachments/MeshAttachment.java +++ b/spine-libgdx/src/com/esotericsoftware/spine/attachments/MeshAttachment.java @@ -34,19 +34,22 @@ import com.esotericsoftware.spine.Slot; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.g2d.TextureRegion; +import com.badlogic.gdx.utils.FloatArray; import com.badlogic.gdx.utils.NumberUtils; /** Attachment that displays a texture region. */ public class MeshAttachment extends Attachment { private TextureRegion region; private String path; - private int hullLength; private float[] vertices; private short[] triangles; - private int[] edges; private float[] worldVertices; private final Color color = new Color(1, 1, 1, 1); + + // Nonessential. + private int[] edges; private float width, height; + private int hullLength; public MeshAttachment (String name) { super(name); @@ -71,41 +74,24 @@ public class MeshAttachment extends Attachment { float g = skeletonColor.g * slotColor.g * regionColor.g; float b = skeletonColor.b * slotColor.b * regionColor.b; float a = skeletonColor.a * slotColor.a * regionColor.a * 255; - float color; - if (premultipliedAlpha) { - r *= a; - g *= a; - b *= a; - } else { - r *= 255; - g *= 255; - b *= 255; - } - color = NumberUtils.intToFloatColor( // + float multiplier = premultipliedAlpha ? a : 255; + float color = NumberUtils.intToFloatColor( // ((int)(a) << 24) // - | ((int)(b) << 16) // - | ((int)(g) << 8) // - | ((int)(r))); + | ((int)(b * multiplier) << 16) // + | ((int)(g * multiplier) << 8) // + | ((int)(r * multiplier))); float[] worldVertices = this.worldVertices; - float[] vertices = this.vertices; - Bone bone1 = slot.getBone(); - float x = skeleton.getX(); - float y = skeleton.getY(); - float m00 = bone1.getM00(); - float m01 = bone1.getM01(); - float m10 = bone1.getM10(); - float m11 = bone1.getM11(); - - float vx, vy; - for (int v = 0, w = 0, n = vertices.length; v < n; v += 2, w += 5) { - vx = vertices[v]; - vy = vertices[v + 1]; - float wx1 = vx * m00 + vy * m01 + x + bone1.getWorldX(); - float wy1 = vx * m10 + vy * m11 + y + bone1.getWorldY(); - worldVertices[w] = wx1; - worldVertices[w + 1] = wy1; - worldVertices[w + 2] = Color.WHITE.toFloatBits(); + FloatArray verticesArray = slot.getAttachmentVertices(); + float[] vertices = verticesArray.size > 0 ? verticesArray.items : this.vertices; + Bone bone = slot.getBone(); + float x = skeleton.getX() + bone.getWorldX(), y = skeleton.getY() + bone.getWorldY(); + float m00 = bone.getM00(), m01 = bone.getM01(), m10 = bone.getM10(), m11 = bone.getM11(); + for (int v = 0, w = 0, n = worldVertices.length; w < n; v += 2, w += 5) { + float vx = vertices[v]; + float vy = vertices[v + 1]; + worldVertices[w] = vx * m00 + vy * m01 + x; + worldVertices[w + 1] = vx * m10 + vy * m11 + y; worldVertices[w + 2] = color; } } @@ -173,9 +159,19 @@ public class MeshAttachment extends Attachment { int worldVerticesLength = vertices.length / 2 * 5; if (worldVertices == null || worldVertices.length != worldVerticesLength) worldVertices = new float[worldVerticesLength]; - for (int i = 0, w = 3, n = vertices.length; i < n; i += 2, w += 5) { - worldVertices[w] = uvs[i]; - worldVertices[w + 1] = uvs[i + 1]; + float u, v, w, h; + if (region == null) { + u = v = 0; + w = h = 1; + } else { + u = region.getU(); + v = region.getV(); + w = region.getU2() - u; + h = region.getV2() - v; + } + for (int i = 0, ii = 3, n = vertices.length; i < n; i += 2, ii += 5) { + worldVertices[ii] = u + uvs[i] * w; + worldVertices[ii + 1] = v + uvs[i + 1] * h; } } } diff --git a/spine-libgdx/src/com/esotericsoftware/spine/attachments/RegionAttachment.java b/spine-libgdx/src/com/esotericsoftware/spine/attachments/RegionAttachment.java index 14b189a14..47e83a9a3 100644 --- a/spine-libgdx/src/com/esotericsoftware/spine/attachments/RegionAttachment.java +++ b/spine-libgdx/src/com/esotericsoftware/spine/attachments/RegionAttachment.java @@ -153,32 +153,20 @@ public class RegionAttachment extends Attachment { float g = skeletonColor.g * slotColor.g * regionColor.g; float b = skeletonColor.b * slotColor.b * regionColor.b; float a = skeletonColor.a * slotColor.a * regionColor.a * 255; - float color; - if (premultipliedAlpha) { - r *= a; - g *= a; - b *= a; - } else { - r *= 255; - g *= 255; - b *= 255; - } - color = NumberUtils.intToFloatColor( // + float multiplier = premultipliedAlpha ? a : 255; + float color = NumberUtils.intToFloatColor( // ((int)(a) << 24) // - | ((int)(b) << 16) // - | ((int)(g) << 8) // - | ((int)(r))); + | ((int)(b * multiplier) << 16) // + | ((int)(g * multiplier) << 8) // + | ((int)(r * multiplier))); float[] vertices = this.vertices; float[] offset = this.offset; Bone bone = slot.getBone(); - float x = bone.getWorldX() + skeleton.getX(); - float y = bone.getWorldY() + skeleton.getY(); - float m00 = bone.getM00(); - float m01 = bone.getM01(); - float m10 = bone.getM10(); - float m11 = bone.getM11(); + float x = skeleton.getX() + bone.getWorldX(), y = skeleton.getY() + bone.getWorldY(); + float m00 = bone.getM00(), m01 = bone.getM01(), m10 = bone.getM10(), m11 = bone.getM11(); float offsetX, offsetY; + offsetX = offset[BRX]; offsetY = offset[BRY]; vertices[X1] = offsetX * m00 + offsetY * m01 + x; // br diff --git a/spine-libgdx/test/com/esotericsoftware/spine/SkeletonTest.java b/spine-libgdx/test/com/esotericsoftware/spine/SkeletonTest.java index 545c6d622..23a5784ca 100644 --- a/spine-libgdx/test/com/esotericsoftware/spine/SkeletonTest.java +++ b/spine-libgdx/test/com/esotericsoftware/spine/SkeletonTest.java @@ -38,14 +38,14 @@ import com.badlogic.gdx.graphics.GL10; import com.badlogic.gdx.graphics.Pixmap; import com.badlogic.gdx.graphics.Pixmap.Format; import com.badlogic.gdx.graphics.Texture; -import com.badlogic.gdx.graphics.g2d.SpriteBatch; +import com.badlogic.gdx.graphics.g2d.PolygonSpriteBatch; import com.badlogic.gdx.graphics.g2d.TextureAtlas; import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion; import com.badlogic.gdx.graphics.g2d.TextureAtlas.TextureAtlasData; import com.badlogic.gdx.utils.Array; public class SkeletonTest extends ApplicationAdapter { - SpriteBatch batch; + PolygonSpriteBatch batch; float time; SkeletonRenderer renderer; SkeletonRendererDebug debugRenderer; @@ -56,7 +56,7 @@ public class SkeletonTest extends ApplicationAdapter { Array events = new Array(); public void create () { - batch = new SpriteBatch(); + batch = new PolygonSpriteBatch(); renderer = new SkeletonRenderer(); debugRenderer = new SkeletonRendererDebug(); @@ -120,6 +120,7 @@ public class SkeletonTest extends ApplicationAdapter { if (x > Gdx.graphics.getWidth()) skeleton.setFlipX(true); if (x < 0) skeleton.setFlipX(false); skeleton.setX(x); + skeleton.setX(300); Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT); @@ -134,7 +135,7 @@ public class SkeletonTest extends ApplicationAdapter { renderer.draw(batch, skeleton); batch.end(); - debugRenderer.draw(skeleton); + //debugRenderer.draw(skeleton); } public void resize (int width, int height) { diff --git a/spine-libgdx/test/goblins.json b/spine-libgdx/test/goblins.json index a95064dcd..bdce9477c 100644 --- a/spine-libgdx/test/goblins.json +++ b/spine-libgdx/test/goblins.json @@ -8,10 +8,10 @@ { "name": "torso", "parent": "hip", "length": 85.82, "x": -6.42, "y": 1.97, "rotation": 93.92 }, { "name": "left lower leg", "parent": "left upper leg", "length": 49.89, "x": 56.34, "y": 0.98, "rotation": -16.65 }, { "name": "left shoulder", "parent": "torso", "length": 35.43, "x": 74.04, "y": -20.38, "rotation": -156.96 }, - { "name": "neck", "parent": "torso", "length": 18.38, "x": 81.67, "y": -6.34, "rotation": -13.92 }, + { "name": "neck", "parent": "torso", "length": 18.38, "x": 81.67, "y": -6.34, "rotation": -1.51 }, { "name": "right lower leg", "parent": "right upper leg", "length": 58.52, "x": 42.99, "y": -0.61, "rotation": -14.34 }, { "name": "right shoulder", "parent": "torso", "length": 37.24, "x": 76.02, "y": 18.14, "rotation": 133.88 }, - { "name": "head", "parent": "neck", "length": 68.28, "x": 20.93, "y": 11.59 }, + { "name": "head", "parent": "neck", "length": 68.28, "x": 20.93, "y": 11.59, "rotation": -13.92, "color": "d48387ff" }, { "name": "left arm", "parent": "left shoulder", "length": 35.62, "x": 37.85, "y": -2.34, "rotation": 28.16 }, { "name": "left foot", "parent": "left lower leg", "length": 46.5, "x": 58.94, "y": -7.61, "rotation": 102.43 }, { "name": "right arm", "parent": "right shoulder", "length": 36.74, "x": 37.6, "y": 0.31, "rotation": 36.32 }, @@ -26,9 +26,9 @@ { "name": "left hand", "bone": "left hand", "attachment": "left hand" }, { "name": "left foot", "bone": "left foot", "attachment": "left foot" }, { "name": "left lower leg", "bone": "left lower leg", "attachment": "left lower leg" }, - { "name": "left upper leg", "bone": "left upper leg", "attachment": "boundingbox" }, + { "name": "left upper leg", "bone": "left upper leg", "attachment": "left upper leg" }, { "name": "neck", "bone": "neck", "attachment": "neck" }, - { "name": "torso", "bone": "torso", "attachment": "torso" }, + { "name": "torso", "bone": "torso", "attachment": "goblin/head" }, { "name": "pelvis", "bone": "pelvis", "attachment": "pelvis" }, { "name": "right foot", "bone": "right foot", "attachment": "right foot" }, { "name": "right lower leg", "bone": "right lower leg", "attachment": "right lower leg" }, @@ -40,81 +40,34 @@ { "name": "right shoulder", "bone": "right shoulder", "attachment": "right shoulder" }, { "name": "right arm", "bone": "right arm", "attachment": "right arm" }, { "name": "right hand item", "bone": "right hand", "attachment": "dagger" }, - { "name": "right hand", "bone": "right hand", "attachment": "right hand" }, - { "name": "bounding box", "bone": "head", "attachment": "bbox" } + { "name": "right hand", "bone": "right hand", "attachment": "right hand" } ], "skins": { "default": { - "bounding box": { - "bbox": { - "type": "boundingbox", - "vertices": [ - -7.2252045, - -34.808647, - -1.9847412, - -40.70198, - 12.63089, - -37.503178, - 13.559738, - -44.273937, - 21.15651, - -45.197586, - 25.916733, - -36.59557, - 30.31987, - -37.271088, - 31.82283, - -47.966175, - 36.74109, - -47.863335, - 37.828888, - -37.763573, - 52.86676, - -18.987732, - 54.687653, - 4.058235, - 42.064606, - 24.16484, - 37.09868, - 25.39936, - 36.084442, - 44.725235, - 28.417389, - 53.716904, - 23.382614, - 49.773712, - 28.766006, - 39.868294, - 25.873352, - 27.605164, - 8.960373, - 14.733994, - -0.746933, - 1.7576027 - ] - } - }, "left hand item": { "dagger": { "x": 7.88, "y": -23.45, "rotation": 10.47, "width": 26, "height": 108 }, "spear": { "x": -4.55, "y": 39.2, "rotation": 13.04, "width": 22, "height": 368 } }, - "left upper leg": { - "boundingbox": { - "type": "boundingbox", - "vertices": [ -73.94766, 8.514406, -49.917465, 25.294191, -79.28125, 46.664314, -95.755325, 34.604897, -74.9664, 27.453112 ] - } - }, "right hand item": { "dagger": { "x": 6.51, "y": -24.15, "rotation": -8.06, "width": 26, "height": 108 } + }, + "torso": { + "goblin/head": { + "type": "mesh", + "vertices": [ 87.34, -39.83, 109.98, 60.57, 174.32, 46.07, 151.68, -54.33 ], + "triangles": [ 1, 2, 3, 1, 3, 0 ], + "uvs": [ 1, 1, 0, 1, 0, 0, 1, 0 ], + "edges": [ 0, 2, 2, 4, 4, 6, 0, 6 ], + "hull": 8, + "width": 103, + "height": 66 + } } }, "goblin": { "eyes": { "eyes closed": { "name": "goblin/eyes-closed", "x": 32.21, "y": -21.27, "rotation": -88.92, "width": 34, "height": 12 } }, - "head": { - "head": { "name": "goblin/head", "x": 25.73, "y": 2.33, "rotation": -92.29, "width": 103, "height": 66 } - }, "left arm": { "left arm": { "name": "goblin/left-arm", @@ -546,11 +499,25 @@ ] } }, + "ffd": { + "default": { + "torso": { + "goblin/head": [ + { "time": 0 }, + { + "time": 0.5, + "vertices": [ 78.53, -53.55, 96.74, 63.46, 197.1, 24.55, 144.41, -37.48 ] + }, + { "time": 1 } + ] + } + } + }, "slots": { - "eyes": { - "attachment": [ - { "time": 0.7, "name": "eyes closed" }, - { "time": 0.8, "name": null } + "head": { + "color": [ + { "time": 0.1, "color": "ffffffff" }, + { "time": 0.8, "color": "fcff00ff" } ] } }, diff --git a/spine-libgdx/test/goblins.skel b/spine-libgdx/test/goblins.skel index 529c4836e..531a96ab5 100644 Binary files a/spine-libgdx/test/goblins.skel and b/spine-libgdx/test/goblins.skel differ