mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-05 23:05:01 +08:00
FFD
This commit is contained in:
parent
525ba52a6c
commit
9f19d9af39
@ -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<Event> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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<Timeline> 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);
|
||||
|
||||
@ -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<Timeline> 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.
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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<Event> 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) {
|
||||
|
||||
@ -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" }
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user