Use time for Bezier segments rather than percent.

Saves calculating the percent between frames for the Bezier path.
This commit is contained in:
NathanSweet 2019-10-29 10:56:22 +01:00
parent 927b9cf866
commit f165ffeb1c
3 changed files with 258 additions and 178 deletions

View File

@ -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 <code>frameCount</code> 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 <code>frameCount - 1</code>. */
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 <code>bezierCount</code> specified in the constructor was 0. */
/** Shrinks the storage for Bezier curves, for use when <code>bezierCount</code> 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.
* <p>
* <code>cx1</code> and <code>cx2</code> are from 0 to 1, representing the percent of time between the two key frames.
* <p>
* <code>cy1</code> and <code>cy2</code> are the percent of the difference between the key frame's values.
* @param frameIndex Between 0 and <code>frameCount - 1</code>. */
public void setBezier (int frameIndex, int bezierIndex, float cx1, float cy1, float cx2, float cy2) {
* @param frameIndex Between 0 and <code>frameCount - 1</code>.
* @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.
* <p>
* <code>y1</code> and <code>y2</code> are the key frame values for this and the next frame.
* <p>
* <code>cx1</code> and <code>cx2</code> are from 0 to 1, representing the percent of time between the two key frames.
* <p>
* <code>cy1</code> and <code>cy2</code> are in the key frames' value space.
* @param frameIndex Between 0 and <code>frameCount - 1</code>. */
public void setBezier (int frameIndex, int bezierIndex, float y1, float cx1, float cy1, float cx2, float cy2, float y2) {
* @param frameIndex Between 0 and <code>frameCount - 1</code>.
* @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 <code>frameCount - 1</code>. */
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);
}
}

View File

@ -593,24 +593,26 @@ public class SkeletonBinary {
}
case SLOT_COLOR: {
ColorTimeline timeline = new ColorTimeline(frameCount, input.readInt(true), slotIndex);
for (int frameIndex = 0, bezierIndex = 0; frameIndex < frameCount; frameIndex++) {
float time = input.readFloat();
for (int frameIndex = 0, bezierIndex = 0; frameIndex < frameCount; frameIndex++) {
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);
for (int frameIndex = 0, bezierIndex = 0; frameIndex < frameCount; frameIndex++) {
float time = input.readFloat();
for (int frameIndex = 0, bezierIndex = 0; frameIndex < frameCount; frameIndex++) {
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);
for (int frameIndex = 0, bezierIndex = 0; frameIndex < frameCount; frameIndex++) {
float time = input.readFloat();
for (int frameIndex = 0, bezierIndex = 0; frameIndex < frameCount; frameIndex++) {
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;

View File

@ -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));
}
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));
}
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));
}
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));
}
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));
}
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));
}
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));
}
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));
}
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));
}
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));
}
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;