diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java index 67c7602e5..69d218e70 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java @@ -270,8 +270,7 @@ public class Animation { float[] curves; /** @param frameEntries The number of entries stored per frame. - * @param bezierCount The number of frames that will use Bezier curves, used to reduce allocations. Pass 0 if unknown, which - * will allocate for frameCount Bezier curves (see {@link #shrink(int)}). + * @param bezierCount The maximum number of frames that will use Bezier curves. See {@link #shrink(int)}. * @param propertyIds Unique identifiers for each property the timeline modifies. */ public CurveTimeline (int frameCount, int frameEntries, int bezierCount, String... propertyIds) { super(frameCount, frameEntries, propertyIds); @@ -290,9 +289,7 @@ public class Animation { curves[frameIndex] = STEPPED; } - /** Sets the specified key frame to Bezier interpolation. - * @param frameIndex Between 0 and frameCount - 1. */ - protected int setBezier (int frameIndex, int bezierIndex) { + int setBezier (int frameIndex, int bezierIndex) { int index = getFrameCount() - 1 + bezierIndex * BEZIER_SIZE; curves[frameIndex] = BEZIER + index; return index; @@ -311,7 +308,8 @@ public class Animation { return BEZIER; } - /** Shrinks the storage for Bezier curves, for use when bezierCount specified in the constructor was 0. */ + /** Shrinks the storage for Bezier curves, for use when bezierCount specified in the constructor was larger + * than the actual number of Bezier curves. */ public void shrink (int bezierCount) { int size = getFrameCount() - 1 + bezierCount * BEZIER_SIZE; if (curves.length > size) { @@ -325,24 +323,27 @@ public class Animation { /** The base class for timelines that use interpolation between key frames for one or more properties using a percentage of the * difference between values. */ static public abstract class PercentCurveTimeline extends CurveTimeline { - public PercentCurveTimeline (int frameCount, int frameEntries, int bezierIndex, String... propertyIds) { - super(frameCount, frameEntries, bezierIndex, propertyIds); + /** @param bezierCount The maximum number of frames that will use Bezier curves. See {@link #shrink(int)}. + * @param propertyIds Unique identifiers for each property the timeline modifies. */ + public PercentCurveTimeline (int frameCount, int frameEntries, int bezierCount, String... propertyIds) { + super(frameCount, frameEntries, bezierCount, propertyIds); } /** Sets the specified key frame to Bezier interpolation. - *

- * cx1 and cx2 are from 0 to 1, representing the percent of time between the two key frames. - *

- * cy1 and cy2 are the percent of the difference between the key frame's values. - * @param frameIndex Between 0 and frameCount - 1. */ - public void setBezier (int frameIndex, int bezierIndex, float cx1, float cy1, float cx2, float cy2) { + * @param frameIndex Between 0 and frameCount - 1. + * @param cx1 The time for the first Bezier handle. + * @param cy1 The percentage of the difference between the key frame's values for the first Bezier handle. + * @param cx2 The time of the second Bezier handle. + * @param cy2 The percentage of the difference between the key frame's values for the second Bezier handle. */ + public void setBezier (int frameIndex, int bezierIndex, float time1, float cx1, float cy1, float cx2, float cy2, + float time2) { int i = setBezier(frameIndex, bezierIndex); float[] curves = this.curves; - float tmpx = cx2 * 0.03f - cx1 * 0.06f, tmpy = cy2 * 0.03f - cy1 * 0.06f; - float dddx = (cx1 - cx2 + 0.33333333f) * 0.018f, dddy = (cy1 - cy2 + 0.33333333f) * 0.018f; + float tmpx = (time1 - cx1 * 2 + cx2) * 0.03f, tmpy = cy2 * 0.03f - cy1 * 0.06f; + float dddx = ((cx1 - cx2) * 3 - time1 + time2) * 0.006f, dddy = (cy1 - cy2 + 0.33333333f) * 0.018f; float ddx = tmpx * 2 + dddx, ddy = tmpy * 2 + dddy; - float dx = cx1 * 0.3f + tmpx + dddx * 0.16666667f, dy = cy1 * 0.3f + tmpy + dddy * 0.16666667f; - float x = dx, y = dy; + float dx = (cx1 - time1) * 0.3f + tmpx + dddx * 0.16666667f, dy = cy1 * 0.3f + tmpy + dddy * 0.16666667f; + float x = time1 + dx, y = dy; for (int n = i + BEZIER_SIZE; i < n; i += 2) { curves[i] = x; curves[i + 1] = y; @@ -360,21 +361,19 @@ public class Animation { public float getCurvePercent (int frameIndex, float time, float time1, float time2) { float[] curves = this.curves; int i = (int)curves[frameIndex]; + if (i == LINEAR) return MathUtils.clamp((time - time1) / (time2 - time1), 0, 1); if (i == STEPPED) return 0; - float percent = MathUtils.clamp((time - time1) / (time2 - time1), 0, 1); - if (i == LINEAR) return percent; i -= BEZIER; - float x = 0; - for (int start = i, n = i + BEZIER_SIZE; i < n; i += 2) { - x = curves[i]; - if (x >= percent) { - if (i == start) return curves[i + 1] * percent / x; // First point is 0,0. - float prevX = curves[i - 2], prevY = curves[i - 1]; - return prevY + (curves[i + 1] - prevY) * (percent - prevX) / (x - prevX); + if (curves[i] > time) return curves[i + 1] * (time - time1) / (curves[i] - time1); + i += 2; + for (int n = i + BEZIER_SIZE - 2; i < n; i += 2) { + if (curves[i] >= time) { + float x = curves[i - 2], y = curves[i - 1]; + return y + (curves[i + 1] - y) * (time - x) / (curves[i] - x); } } - float y = curves[i - 1]; - return y + (1 - y) * (percent - x) / (1 - x); // Last point is 1,1. + float x = curves[i - 2], y = curves[i - 1]; + return y + (1 - y) * (time - x) / (time2 - x); } } @@ -385,8 +384,10 @@ public class Animation { static final int PREV_TIME = -2, PREV_VALUE = -1; static final int VALUE = 1; - public ValueCurveTimeline (int frameCount, int bezierIndex, String... propertyIds) { - super(frameCount, 2, bezierIndex, propertyIds); + /** @param bezierCount The maximum number of frames that will use Bezier curves. See {@link #shrink(int)}. + * @param propertyIds Unique identifiers for each property the timeline modifies. */ + public ValueCurveTimeline (int frameCount, int bezierCount, String... propertyIds) { + super(frameCount, 2, bezierCount, propertyIds); } public int getFrameCount () { @@ -401,53 +402,50 @@ public class Animation { } /** Sets the specified key frame to Bezier interpolation. - *

- * y1 and y2 are the key frame values for this and the next frame. - *

- * cx1 and cx2 are from 0 to 1, representing the percent of time between the two key frames. - *

- * cy1 and cy2 are in the key frames' value space. - * @param frameIndex Between 0 and frameCount - 1. */ - public void setBezier (int frameIndex, int bezierIndex, float y1, float cx1, float cy1, float cx2, float cy2, float y2) { + * @param frameIndex Between 0 and frameCount - 1. + * @param cx1 The time for the first Bezier handle. + * @param cy1 The value for the first Bezier handle. + * @param cx2 The time of the second Bezier handle. + * @param cy2 The value for the second Bezier handle. */ + public void setBezier (int frameIndex, int bezierIndex, float time1, float value1, float cx1, float cy1, float cx2, + float cy2, float time2, float value2) { int i = setBezier(frameIndex, bezierIndex); float[] curves = this.curves; - float tmpx = cx2 * 0.03f - cx1 * 0.06f, tmpy = (y1 - cy1 * 2 + cy2) * 0.03f; - float dddfx = (cx1 - cx2 + 0.33333333f) * 0.018f, dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.006f; - float ddx = tmpx * 2 + dddfx, ddy = tmpy * 2 + dddfy; - float dx = cx1 * 0.3f + tmpx + dddfx * 0.16666667f, dy = (cy1 - y1) * 0.3f + tmpy + dddfy * 0.16666667f; - float x = dx, y = y1 + dy; + float tmpx = (time1 - cx1 * 2 + cx2) * 0.03f, tmpy = (value1 - cy1 * 2 + cy2) * 0.03f; + float dddx = ((cx1 - cx2) * 3 - time1 + time2) * 0.006f, dddy = ((cy1 - cy2) * 3 - value1 + value2) * 0.006f; + float ddx = tmpx * 2 + dddx, ddy = tmpy * 2 + dddy; + float dx = (cx1 - time1) * 0.3f + tmpx + dddx * 0.16666667f, dy = (cy1 - value1) * 0.3f + tmpy + dddy * 0.16666667f; + float x = time1 + dx, y = value1 + dy; for (int n = i + BEZIER_SIZE; i < n; i += 2) { curves[i] = x; curves[i + 1] = y; dx += ddx; dy += ddy; - ddx += dddfx; - ddy += dddfy; + ddx += dddx; + ddy += dddy; x += dx; y += dy; } } - /** Returns the interpolated value for the specified key frame and times. + /** Returns the interpolated value for the specified key frame, times, and values. * @param frameIndex Between 0 and frameCount - 1. */ public float getCurveValue (int frameIndex, float time, float time1, float value1, float time2, float value2) { float[] curves = this.curves; int i = (int)curves[frameIndex]; + if (i == LINEAR) return value1 + (value2 - value1) * MathUtils.clamp((time - time1) / (time2 - time1), 0, 1); if (i == STEPPED) return value1; - float percent = MathUtils.clamp((time - time1) / (time2 - time1), 0, 1); - if (i == LINEAR) return value1 + (value2 - value1) * percent; i -= BEZIER; - float x = 0; - for (int start = i, n = i + BEZIER_SIZE; i < n; i += 2) { - x = curves[i]; - if (x >= percent) { - if (i == start) return value1 + (curves[i + 1] - value1) * percent / x; // First point is 0,value1. - float prevY = curves[i - 1], prevX = curves[i - 2]; - return prevY + (curves[i + 1] - prevY) * (percent - prevX) / (x - prevX); + if (curves[i] > time) return value1 + (curves[i + 1] - value1) * (time - value1) / (curves[i] - value1); + i += 2; + for (int n = i + BEZIER_SIZE - 2; i < n; i += 2) { + if (curves[i] >= time) { + float x = curves[i - 2], y = curves[i - 1]; + return y + (curves[i + 1] - y) * (time - x) / (curves[i] - x); } } - float y = curves[i - 1]; - return y + (value2 - y) * (percent - x) / (1 - x); // Last point is 1,value2. + float x = curves[i - 2], y = curves[i - 1]; + return y + (value2 - y) * (time - x) / (time2 - x); } } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java index e599bfcfc..06129a9e7 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java @@ -593,24 +593,26 @@ public class SkeletonBinary { } case SLOT_COLOR: { ColorTimeline timeline = new ColorTimeline(frameCount, input.readInt(true), slotIndex); + float time = input.readFloat(); for (int frameIndex = 0, bezierIndex = 0; frameIndex < frameCount; frameIndex++) { - float time = input.readFloat(); Color.rgba8888ToColor(tempColor1, input.readInt()); timeline.setFrame(frameIndex, time, tempColor1.r, tempColor1.g, tempColor1.b, tempColor1.a); - if (frameIndex < frameLast) bezierIndex = readCurve(input, timeline, frameIndex, bezierIndex); + if (frameIndex < frameLast) + bezierIndex = readCurve(input, timeline, frameIndex, bezierIndex, time, time = input.readFloat()); } timelines.add(timeline); break; } case SLOT_TWO_COLOR: { TwoColorTimeline timeline = new TwoColorTimeline(frameCount, input.readInt(true), slotIndex); + float time = input.readFloat(); for (int frameIndex = 0, bezierIndex = 0; frameIndex < frameCount; frameIndex++) { - float time = input.readFloat(); Color.rgba8888ToColor(tempColor1, input.readInt()); Color.rgb888ToColor(tempColor2, input.readInt()); timeline.setFrame(frameIndex, time, tempColor1.r, tempColor1.g, tempColor1.b, tempColor1.a, tempColor2.r, tempColor2.g, tempColor2.b); - if (frameIndex < frameLast) bezierIndex = readCurve(input, timeline, frameIndex, bezierIndex); + if (frameIndex < frameLast) + bezierIndex = readCurve(input, timeline, frameIndex, bezierIndex, time, time = input.readFloat()); } timelines.add(timeline); break; @@ -628,38 +630,44 @@ public class SkeletonBinary { switch (timelineType) { case BONE_ROTATE: { RotateTimeline timeline = new RotateTimeline(frameCount, input.readInt(true), boneIndex); - float value = input.readFloat(); + float time = input.readFloat(), value = input.readFloat(); for (int frameIndex = 0, bezierIndex = 0; frameIndex < frameCount; frameIndex++) { - timeline.setFrame(frameIndex, input.readFloat(), value); - if (frameIndex < frameLast) - readCurve(input, timeline, frameIndex, bezierIndex, value, value = input.readFloat()); + timeline.setFrame(frameIndex, time, value); + if (frameIndex < frameLast) readCurve(input, timeline, frameIndex, bezierIndex, time, value, + time = input.readFloat(), value = input.readFloat()); } timelines.add(timeline); break; } case BONE_TRANSLATE: { TranslateTimeline timeline = new TranslateTimeline(frameCount, input.readInt(true), boneIndex); + float time = input.readFloat(); for (int frameIndex = 0, bezierIndex = 0; frameIndex < frameCount; frameIndex++) { - timeline.setFrame(frameIndex, input.readFloat(), input.readFloat() * scale, input.readFloat() * scale); - if (frameIndex < frameLast) bezierIndex = readCurve(input, timeline, frameIndex, bezierIndex); + timeline.setFrame(frameIndex, time, input.readFloat() * scale, input.readFloat() * scale); + if (frameIndex < frameLast) + bezierIndex = readCurve(input, timeline, frameIndex, bezierIndex, time, time = input.readFloat()); } timelines.add(timeline); break; } case BONE_SCALE: { ScaleTimeline timeline = new ScaleTimeline(frameCount, input.readInt(true), boneIndex); + float time = input.readFloat(); for (int frameIndex = 0, bezierIndex = 0; frameIndex < frameCount; frameIndex++) { - timeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readFloat()); - if (frameIndex < frameLast) bezierIndex = readCurve(input, timeline, frameIndex, bezierIndex); + timeline.setFrame(frameIndex, time, input.readFloat(), input.readFloat()); + if (frameIndex < frameLast) + bezierIndex = readCurve(input, timeline, frameIndex, bezierIndex, time, time = input.readFloat()); } timelines.add(timeline); break; } case BONE_SHEAR: { ShearTimeline timeline = new ShearTimeline(frameCount, input.readInt(true), boneIndex); + float time = input.readFloat(); for (int frameIndex = 0, bezierIndex = 0; frameIndex < frameCount; frameIndex++) { - timeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readFloat()); - if (frameIndex < frameLast) bezierIndex = readCurve(input, timeline, frameIndex, bezierIndex); + timeline.setFrame(frameIndex, time, input.readFloat(), input.readFloat()); + if (frameIndex < frameLast) + bezierIndex = readCurve(input, timeline, frameIndex, bezierIndex, time, time = input.readFloat()); } timelines.add(timeline); break; @@ -673,10 +681,12 @@ public class SkeletonBinary { int index = input.readInt(true); int frameCount = input.readInt(true), frameLast = frameCount - 1; IkConstraintTimeline timeline = new IkConstraintTimeline(frameCount, input.readInt(true), index); + float time = input.readFloat(); for (int frameIndex = 0, bezierIndex = 0; frameIndex < frameCount; frameIndex++) { - timeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readFloat() * scale, input.readByte(), + timeline.setFrame(frameIndex, time, input.readFloat(), input.readFloat() * scale, input.readByte(), input.readBoolean(), input.readBoolean()); - if (frameIndex < frameLast) bezierIndex = readCurve(input, timeline, frameIndex, bezierIndex); + if (frameIndex < frameLast) + bezierIndex = readCurve(input, timeline, frameIndex, bezierIndex, time, time = input.readFloat()); } timelines.add(timeline); } @@ -686,10 +696,11 @@ public class SkeletonBinary { int index = input.readInt(true); int frameCount = input.readInt(true), frameLast = frameCount - 1; TransformConstraintTimeline timeline = new TransformConstraintTimeline(frameCount, input.readInt(true), index); + float time = input.readFloat(); for (int frameIndex = 0, bezierIndex = 0; frameIndex < frameCount; frameIndex++) { - timeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readFloat(), input.readFloat(), - input.readFloat()); - if (frameIndex < frameLast) bezierIndex = readCurve(input, timeline, frameIndex, bezierIndex); + timeline.setFrame(frameIndex, time, input.readFloat(), input.readFloat(), input.readFloat(), input.readFloat()); + if (frameIndex < frameLast) + bezierIndex = readCurve(input, timeline, frameIndex, bezierIndex, time, time = input.readFloat()); } timelines.add(timeline); } @@ -707,9 +718,11 @@ public class SkeletonBinary { index); float timelineScale = data.spacingMode == SpacingMode.length || data.spacingMode == SpacingMode.fixed ? scale : 1; + float time = input.readFloat(); for (int frameIndex = 0, bezierIndex = 0; frameIndex < frameCount; frameIndex++) { - timeline.setFrame(frameIndex, input.readFloat(), input.readFloat() * timelineScale); - if (frameIndex < frameLast) bezierIndex = readCurve(input, timeline, frameIndex, bezierIndex); + timeline.setFrame(frameIndex, time, input.readFloat() * timelineScale); + if (frameIndex < frameLast) + bezierIndex = readCurve(input, timeline, frameIndex, bezierIndex, time, time = input.readFloat()); } timelines.add(timeline); break; @@ -718,18 +731,22 @@ public class SkeletonBinary { PathConstraintPositionTimeline timeline = new PathConstraintPositionTimeline(frameCount, input.readInt(true), index); float timelineScale = data.positionMode == PositionMode.fixed ? scale : 1; + float time = input.readFloat(); for (int frameIndex = 0, bezierIndex = 0; frameIndex < frameCount; frameIndex++) { - timeline.setFrame(frameIndex, input.readFloat(), input.readFloat() * timelineScale); - if (frameIndex < frameLast) bezierIndex = readCurve(input, timeline, frameIndex, bezierIndex); + timeline.setFrame(frameIndex, time, input.readFloat() * timelineScale); + if (frameIndex < frameLast) + bezierIndex = readCurve(input, timeline, frameIndex, bezierIndex, time, time = input.readFloat()); } timelines.add(timeline); break; } case PATH_MIX: { PathConstraintMixTimeline timeline = new PathConstraintMixTimeline(frameCount, input.readInt(true), index); + float time = input.readFloat(); for (int frameIndex = 0, bezierIndex = 0; frameIndex < frameCount; frameIndex++) { - timeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readFloat()); - if (frameIndex < frameLast) bezierIndex = readCurve(input, timeline, frameIndex, bezierIndex); + timeline.setFrame(frameIndex, time, input.readFloat(), input.readFloat()); + if (frameIndex < frameLast) + bezierIndex = readCurve(input, timeline, frameIndex, bezierIndex, time, time = input.readFloat()); } timelines.add(timeline); break; @@ -752,8 +769,8 @@ public class SkeletonBinary { int frameCount = input.readInt(true), frameLast = frameCount - 1; DeformTimeline timeline = new DeformTimeline(frameCount, input.readInt(true), slotIndex, attachment); + float time = input.readFloat(); for (int frameIndex = 0, bezierIndex = 0; frameIndex < frameCount; frameIndex++) { - float time = input.readFloat(); float[] deform; int end = input.readInt(true); if (end == 0) @@ -776,7 +793,8 @@ public class SkeletonBinary { } timeline.setFrame(frameIndex, time, deform); - if (frameIndex < frameLast) bezierIndex = readCurve(input, timeline, frameIndex, bezierIndex); + if (frameIndex < frameLast) + bezierIndex = readCurve(input, timeline, frameIndex, bezierIndex, time, time = input.readFloat()); } timelines.add(timeline); } @@ -845,28 +863,29 @@ public class SkeletonBinary { return new Animation(name, timelines, duration); } - int readCurve (SkeletonInput input, PercentCurveTimeline timeline, int frameIndex, int bezierIndex) throws IOException { - switch (input.readByte()) { - case CURVE_STEPPED: - timeline.setStepped(frameIndex); - break; - case CURVE_BEZIER: - timeline.setBezier(frameIndex, bezierIndex++, input.readFloat(), input.readFloat(), input.readFloat(), - input.readFloat()); - break; - } - return bezierIndex; - } - - int readCurve (SkeletonInput input, ValueCurveTimeline timeline, int frameIndex, int bezierIndex, float value1, float value2) + int readCurve (SkeletonInput input, PercentCurveTimeline timeline, int frameIndex, int bezierIndex, float time1, float time2) throws IOException { switch (input.readByte()) { case CURVE_STEPPED: timeline.setStepped(frameIndex); break; case CURVE_BEZIER: - timeline.setBezier(frameIndex, bezierIndex++, value1, input.readFloat(), input.readFloat(), input.readFloat(), - input.readFloat(), value2); + timeline.setBezier(frameIndex, bezierIndex++, time1, input.readFloat(), input.readFloat(), input.readFloat(), + input.readFloat(), time2); + break; + } + return bezierIndex; + } + + int readCurve (SkeletonInput input, ValueCurveTimeline timeline, int frameIndex, int bezierIndex, float time1, float value1, + float time2, float value2) throws IOException { + switch (input.readByte()) { + case CURVE_STEPPED: + timeline.setStepped(frameIndex); + break; + case CURVE_BEZIER: + timeline.setBezier(frameIndex, bezierIndex++, time1, value1, input.readFloat(), input.readFloat(), input.readFloat(), + input.readFloat(), time2, value2); break; } return bezierIndex; diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java index 4c6a25da6..fa176e55d 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java @@ -507,34 +507,45 @@ public class SkeletonJson { SlotData slot = skeletonData.findSlot(slotMap.name); if (slot == null) throw new SerializationException("Slot not found: " + slotMap.name); for (JsonValue timelineMap = slotMap.child; timelineMap != null; timelineMap = timelineMap.next) { + JsonValue valueMap = timelineMap.child; + if (valueMap == null) continue; String timelineName = timelineMap.name; - int frameIndex = 0, bezierIndex = 0; if (timelineName.equals("attachment")) { AttachmentTimeline timeline = new AttachmentTimeline(timelineMap.size, slot.index); - for (JsonValue valueMap = timelineMap.child; valueMap != null; valueMap = valueMap.next, frameIndex++) + for (int frameIndex = 0; valueMap != null; valueMap = valueMap.next, frameIndex++) timeline.setFrame(frameIndex, valueMap.getFloat("time", 0), valueMap.getString("name")); timelines.add(timeline); } else if (timelineName.equals("color")) { ColorTimeline timeline = new ColorTimeline(timelineMap.size, 0, slot.index); - for (JsonValue valueMap = timelineMap.child; valueMap != null; valueMap = valueMap.next, frameIndex++) { + float time = timelineMap.child.getFloat("time", 0); + for (int frameIndex = 0, bezierIndex = 0;; frameIndex++) { Color color = Color.valueOf(valueMap.getString("color")); - timeline.setFrame(frameIndex, valueMap.getFloat("time", 0), color.r, color.g, color.b, color.a); - bezierIndex = readCurve(valueMap, timeline, frameIndex, bezierIndex); + timeline.setFrame(frameIndex, time, color.r, color.g, color.b, color.a); + valueMap = valueMap.next; + if (valueMap == null) { + timeline.shrink(bezierIndex); + break; + } + bezierIndex = readCurve(valueMap, timeline, frameIndex, bezierIndex, time, time = valueMap.getFloat("time", 0)); } - timeline.shrink(bezierIndex); timelines.add(timeline); } else if (timelineName.equals("twoColor")) { TwoColorTimeline timeline = new TwoColorTimeline(timelineMap.size, 0, slot.index); - for (JsonValue valueMap = timelineMap.child; valueMap != null; valueMap = valueMap.next, frameIndex++) { + float time = timelineMap.child.getFloat("time", 0); + for (int frameIndex = 0, bezierIndex = 0;; frameIndex++) { Color light = Color.valueOf(valueMap.getString("light")), dark = Color.valueOf(valueMap.getString("dark")); - timeline.setFrame(frameIndex, valueMap.getFloat("time", 0), light.r, light.g, light.b, light.a, // + timeline.setFrame(frameIndex, time, light.r, light.g, light.b, light.a, // dark.r, dark.g, dark.b); - bezierIndex = readCurve(valueMap, timeline, frameIndex, bezierIndex); + valueMap = valueMap.next; + if (valueMap == null) { + timeline.shrink(bezierIndex); + break; + } + bezierIndex = readCurve(valueMap, timeline, frameIndex, bezierIndex, time, time = valueMap.getFloat("time", 0)); } - timeline.shrink(bezierIndex); timelines.add(timeline); } else @@ -550,49 +561,53 @@ public class SkeletonJson { JsonValue valueMap = timelineMap.child; if (valueMap == null) continue; String timelineName = timelineMap.name; - int frameIndex = 0, bezierIndex = 0; if (timelineName.equals("rotate")) { - RotateTimeline timeline = new RotateTimeline(timelineMap.size, 0, bone.index); - float rotation = valueMap.getFloat("angle", 0); - for (;; frameIndex++) { - timeline.setFrame(frameIndex, valueMap.getFloat("time", 0), rotation); - valueMap = valueMap.next; - if (valueMap == null) break; - bezierIndex = readCurve(valueMap, timeline, frameIndex, bezierIndex, rotation, - rotation = valueMap.getFloat("angle", 0)); - } - timeline.shrink(bezierIndex); - timelines.add(timeline); + timelines.add(readTimeline(valueMap, new RotateTimeline(timelineMap.size, 0, bone.index))); } else if (timelineName.equals("translate")) { TranslateTimeline timeline = new TranslateTimeline(timelineMap.size, 0, bone.index); - for (; valueMap != null; valueMap = valueMap.next, frameIndex++) { + float time = valueMap.getFloat("time", 0); + for (int frameIndex = 0, bezierIndex = 0;; frameIndex++) { float x = valueMap.getFloat("x", 0), y = valueMap.getFloat("y", 0); - timeline.setFrame(frameIndex, valueMap.getFloat("time", 0), x * scale, y * scale); - bezierIndex = readCurve(valueMap, timeline, frameIndex, bezierIndex); + timeline.setFrame(frameIndex, time, x * scale, y * scale); + valueMap = valueMap.next; + if (valueMap == null) { + timeline.shrink(bezierIndex); + break; + } + bezierIndex = readCurve(valueMap, timeline, frameIndex, bezierIndex, time, time = valueMap.getFloat("time", 0)); } - timeline.shrink(bezierIndex); timelines.add(timeline); } else if (timelineName.equals("scale")) { ScaleTimeline timeline = new ScaleTimeline(timelineMap.size, 0, bone.index); - for (; valueMap != null; valueMap = valueMap.next, frameIndex++) { + float time = valueMap.getFloat("time", 0); + for (int frameIndex = 0, bezierIndex = 0;; frameIndex++) { float x = valueMap.getFloat("x", 1), y = valueMap.getFloat("y", 1); - timeline.setFrame(frameIndex, valueMap.getFloat("time", 0), x, y); - bezierIndex = readCurve(valueMap, timeline, frameIndex, bezierIndex); + timeline.setFrame(frameIndex, time, x, y); + valueMap = valueMap.next; + if (valueMap == null) { + timeline.shrink(bezierIndex); + break; + } + bezierIndex = readCurve(valueMap, timeline, frameIndex, bezierIndex, time, time = valueMap.getFloat("time", 0)); } - timeline.shrink(bezierIndex); timelines.add(timeline); } else if (timelineName.equals("shear")) { ShearTimeline timeline = new ShearTimeline(timelineMap.size, 0, bone.index); - for (; valueMap != null; valueMap = valueMap.next, frameIndex++) { + float time = valueMap.getFloat("time", 0); + for (int frameIndex = 0, bezierIndex = 0;; frameIndex++) { float x = valueMap.getFloat("x", 0), y = valueMap.getFloat("y", 0); - timeline.setFrame(frameIndex, valueMap.getFloat("time", 0), x, y); - bezierIndex = readCurve(valueMap, timeline, frameIndex, bezierIndex); + timeline.setFrame(frameIndex, time, x, y); + valueMap = valueMap.next; + if (valueMap == null) { + timeline.shrink(bezierIndex); + break; + } + bezierIndex = readCurve(valueMap, timeline, frameIndex, bezierIndex, time, time = valueMap.getFloat("time", 0)); } - timeline.shrink(bezierIndex); timelines.add(timeline); } else @@ -601,33 +616,45 @@ public class SkeletonJson { } // IK constraint timelines. - for (JsonValue constraintMap = map.getChild("ik"); constraintMap != null; constraintMap = constraintMap.next) { - IkConstraintData constraint = skeletonData.findIkConstraint(constraintMap.name); - IkConstraintTimeline timeline = new IkConstraintTimeline(constraintMap.size, 0, + for (JsonValue timelineMap = map.getChild("ik"); timelineMap != null; timelineMap = timelineMap.next) { + JsonValue valueMap = timelineMap.child; + if (valueMap == null) continue; + IkConstraintData constraint = skeletonData.findIkConstraint(timelineMap.name); + IkConstraintTimeline timeline = new IkConstraintTimeline(timelineMap.size, 0, skeletonData.getIkConstraints().indexOf(constraint, true)); - int frameIndex = 0, bezierIndex = 0; - for (JsonValue valueMap = constraintMap.child; valueMap != null; valueMap = valueMap.next, frameIndex++) { - timeline.setFrame(frameIndex, valueMap.getFloat("time", 0), valueMap.getFloat("mix", 1), - valueMap.getFloat("softness", 0) * scale, valueMap.getBoolean("bendPositive", true) ? 1 : -1, - valueMap.getBoolean("compress", false), valueMap.getBoolean("stretch", false)); - bezierIndex = readCurve(valueMap, timeline, frameIndex, bezierIndex); + float time = valueMap.getFloat("time", 0); + for (int frameIndex = 0, bezierIndex = 0;; frameIndex++) { + timeline.setFrame(frameIndex, time, valueMap.getFloat("mix", 1), valueMap.getFloat("softness", 0) * scale, + valueMap.getBoolean("bendPositive", true) ? 1 : -1, valueMap.getBoolean("compress", false), + valueMap.getBoolean("stretch", false)); + valueMap = valueMap.next; + if (valueMap == null) { + timeline.shrink(bezierIndex); + break; + } + bezierIndex = readCurve(valueMap, timeline, frameIndex, bezierIndex, time, time = valueMap.getFloat("time", 0)); } - timeline.shrink(bezierIndex); timelines.add(timeline); } // Transform constraint timelines. - for (JsonValue constraintMap = map.getChild("transform"); constraintMap != null; constraintMap = constraintMap.next) { - TransformConstraintData constraint = skeletonData.findTransformConstraint(constraintMap.name); - TransformConstraintTimeline timeline = new TransformConstraintTimeline(constraintMap.size, 0, + for (JsonValue timelineMap = map.getChild("transform"); timelineMap != null; timelineMap = timelineMap.next) { + JsonValue valueMap = timelineMap.child; + if (valueMap == null) continue; + TransformConstraintData constraint = skeletonData.findTransformConstraint(timelineMap.name); + TransformConstraintTimeline timeline = new TransformConstraintTimeline(timelineMap.size, 0, skeletonData.getTransformConstraints().indexOf(constraint, true)); - int frameIndex = 0, bezierIndex = 0; - for (JsonValue valueMap = constraintMap.child; valueMap != null; valueMap = valueMap.next, frameIndex++) { - timeline.setFrame(frameIndex, valueMap.getFloat("time", 0), valueMap.getFloat("rotateMix", 1), - valueMap.getFloat("translateMix", 1), valueMap.getFloat("scaleMix", 1), valueMap.getFloat("shearMix", 1)); - bezierIndex = readCurve(valueMap, timeline, frameIndex, bezierIndex); + float time = valueMap.getFloat("time", 0); + for (int frameIndex = 0, bezierIndex = 0;; frameIndex++) { + timeline.setFrame(frameIndex, time, valueMap.getFloat("rotateMix", 1), valueMap.getFloat("translateMix", 1), + valueMap.getFloat("scaleMix", 1), valueMap.getFloat("shearMix", 1)); + valueMap = valueMap.next; + if (valueMap == null) { + timeline.shrink(bezierIndex); + break; + } + bezierIndex = readCurve(valueMap, timeline, frameIndex, bezierIndex, time, time = valueMap.getFloat("time", 0)); } - timeline.shrink(bezierIndex); timelines.add(timeline); } @@ -637,37 +664,52 @@ public class SkeletonJson { if (data == null) throw new SerializationException("Path constraint not found: " + constraintMap.name); int index = skeletonData.pathConstraints.indexOf(data, true); for (JsonValue timelineMap = constraintMap.child; timelineMap != null; timelineMap = timelineMap.next) { + JsonValue valueMap = timelineMap.child; + if (valueMap == null) continue; String timelineName = timelineMap.name; - int frameIndex = 0, bezierIndex = 0; if (timelineName.equals("position")) { PathConstraintPositionTimeline timeline = new PathConstraintPositionTimeline(timelineMap.size, 0, index); float timelineScale = data.positionMode == PositionMode.fixed ? scale : 1; - for (JsonValue valueMap = timelineMap.child; valueMap != null; valueMap = valueMap.next, frameIndex++) { - timeline.setFrame(frameIndex, valueMap.getFloat("time", 0), valueMap.getFloat(timelineName, 0) * timelineScale); - bezierIndex = readCurve(valueMap, timeline, frameIndex, bezierIndex); + float time = valueMap.getFloat("time", 0); + for (int frameIndex = 0, bezierIndex = 0;; frameIndex++) { + timeline.setFrame(frameIndex, time, valueMap.getFloat(timelineName, 0) * timelineScale); + valueMap = valueMap.next; + if (valueMap == null) { + timeline.shrink(bezierIndex); + break; + } + bezierIndex = readCurve(valueMap, timeline, frameIndex, bezierIndex, time, time = valueMap.getFloat("time", 0)); } - timeline.shrink(bezierIndex); timelines.add(timeline); } else if (timelineName.equals("spacing")) { PathConstraintSpacingTimeline timeline = new PathConstraintSpacingTimeline(timelineMap.size, 0, index); float timelineScale = data.spacingMode == SpacingMode.length || data.spacingMode == SpacingMode.fixed ? scale : 1; - for (JsonValue valueMap = timelineMap.child; valueMap != null; valueMap = valueMap.next, frameIndex++) { - timeline.setFrame(frameIndex, valueMap.getFloat("time", 0), valueMap.getFloat(timelineName, 0) * timelineScale); - bezierIndex = readCurve(valueMap, timeline, frameIndex, bezierIndex); + float time = valueMap.getFloat("time", 0); + for (int frameIndex = 0, bezierIndex = 0;; frameIndex++) { + timeline.setFrame(frameIndex, time, valueMap.getFloat(timelineName, 0) * timelineScale); + valueMap = valueMap.next; + if (valueMap == null) { + timeline.shrink(bezierIndex); + break; + } + bezierIndex = readCurve(valueMap, timeline, frameIndex, bezierIndex, time, time = valueMap.getFloat("time", 0)); } - timeline.shrink(bezierIndex); timelines.add(timeline); } else if (timelineName.equals("mix")) { PathConstraintMixTimeline timeline = new PathConstraintMixTimeline(timelineMap.size, 0, index); - for (JsonValue valueMap = timelineMap.child; valueMap != null; valueMap = valueMap.next, frameIndex++) { - timeline.setFrame(frameIndex, valueMap.getFloat("time", 0), valueMap.getFloat("rotateMix", 1), - valueMap.getFloat("translateMix", 1)); - bezierIndex = readCurve(valueMap, timeline, frameIndex, bezierIndex); + float time = valueMap.getFloat("time", 0); + for (int frameIndex = 0, bezierIndex = 0;; frameIndex++) { + timeline.setFrame(frameIndex, time, valueMap.getFloat("rotateMix", 1), valueMap.getFloat("translateMix", 1)); + valueMap = valueMap.next; + if (valueMap == null) { + timeline.shrink(bezierIndex); + break; + } + bezierIndex = readCurve(valueMap, timeline, frameIndex, bezierIndex, time, time = valueMap.getFloat("time", 0)); } - timeline.shrink(bezierIndex); timelines.add(timeline); } } @@ -681,6 +723,9 @@ public class SkeletonJson { SlotData slot = skeletonData.findSlot(slotMap.name); if (slot == null) throw new SerializationException("Slot not found: " + slotMap.name); for (JsonValue timelineMap = slotMap.child; timelineMap != null; timelineMap = timelineMap.next) { + JsonValue valueMap = timelineMap.child; + if (valueMap == null) continue; + VertexAttachment attachment = (VertexAttachment)skin.getAttachment(slot.index, timelineMap.name); if (attachment == null) throw new SerializationException("Deform attachment not found: " + timelineMap.name); boolean weighted = attachment.getBones() != null; @@ -688,8 +733,9 @@ public class SkeletonJson { int deformLength = weighted ? vertices.length / 3 * 2 : vertices.length; DeformTimeline timeline = new DeformTimeline(timelineMap.size, 0, slot.index, attachment); - int frameIndex = 0, bezierIndex = 0; - for (JsonValue valueMap = timelineMap.child; valueMap != null; valueMap = valueMap.next, frameIndex++) { + int bezierIndex = 0; + float time = valueMap.getFloat("time", 0); + for (int frameIndex = 0;; frameIndex++) { float[] deform; JsonValue verticesValue = valueMap.get("vertices"); if (verticesValue == null) @@ -708,8 +754,10 @@ public class SkeletonJson { } } - timeline.setFrame(frameIndex, valueMap.getFloat("time", 0), deform); - bezierIndex = readCurve(valueMap, timeline, frameIndex, bezierIndex); + timeline.setFrame(frameIndex, time, deform); + valueMap = valueMap.next; + if (valueMap == null) break; + bezierIndex = readCurve(valueMap, timeline, frameIndex, bezierIndex, time, time = valueMap.getFloat("time", 0)); } timeline.shrink(bezierIndex); timelines.add(timeline); @@ -782,27 +830,42 @@ public class SkeletonJson { skeletonData.animations.add(new Animation(name, timelines, duration)); } - int readCurve (JsonValue map, PercentCurveTimeline timeline, int frameIndex, int bezierIndex) { + private ValueCurveTimeline readTimeline (JsonValue valueMap, ValueCurveTimeline timeline) { + float time = valueMap.getFloat("time", 0), value = valueMap.getFloat("value", 0); + int bezierIndex = 0; + for (int frameIndex = 0;; frameIndex++) { + timeline.setFrame(frameIndex, time, value); + valueMap = valueMap.next; + if (valueMap == null) break; + bezierIndex = readCurve(valueMap, timeline, frameIndex, bezierIndex, // + time, value, time = valueMap.getFloat("time", 0), value = valueMap.getFloat("value", 0)); + } + timeline.shrink(bezierIndex); + return timeline; + } + + int readCurve (JsonValue map, PercentCurveTimeline timeline, int frameIndex, int bezierIndex, float time1, float time2) { JsonValue curve = map.get("curve"); if (curve != null) { if (curve.isString()) timeline.setStepped(frameIndex); else { - timeline.setBezier(frameIndex, bezierIndex++, curve.asFloat(), map.getFloat("c2", 0), map.getFloat("c3", 1), - map.getFloat("c4", 1)); + timeline.setBezier(frameIndex, bezierIndex++, time1, curve.asFloat(), map.getFloat("c2", 0), map.getFloat("c3", 1), + map.getFloat("c4", 1), time2); } } return bezierIndex; } - int readCurve (JsonValue map, ValueCurveTimeline timeline, int frameIndex, int bezierIndex, float value1, float value2) { + int readCurve (JsonValue map, ValueCurveTimeline timeline, int frameIndex, int bezierIndex, float time1, float value1, + float time2, float value2) { JsonValue curve = map.get("curve"); if (curve != null) { if (curve.isString()) timeline.setStepped(frameIndex); else { - timeline.setBezier(frameIndex, bezierIndex++, value1, curve.asFloat(), map.getFloat("c2", 0), map.getFloat("c3", 1), - map.getFloat("c4", 1), value2); + timeline.setBezier(frameIndex, bezierIndex++, time1, value1, curve.asFloat(), map.getFloat("c2", 0), + map.getFloat("c3", 1), map.getFloat("c4", 1), time2, value2); } } return bezierIndex;