mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-04 22:34:53 +08:00
Separate path timelines.
This commit is contained in:
parent
b41dead263
commit
b3f84390c5
@ -866,17 +866,16 @@ public class Animation {
|
||||
}
|
||||
}
|
||||
|
||||
// BOZO! - Separate into multiple timelines.
|
||||
static public class PathConstraintTimeline extends CurveTimeline {
|
||||
static public final int ENTRIES = 5;
|
||||
static private final int PREV_TIME = -5, PREV_POSITION = -4, PREV_ROTATE = -3, PREV_TRANSLATE = -2;
|
||||
static private final int POSITION = 1, ROTATE = 2, TRANSLATE = 3;
|
||||
static public class PathConstraintPositionTimeline extends CurveTimeline {
|
||||
static public final int ENTRIES = 2;
|
||||
static final int PREV_TIME = -2, PREV_VALUE = -1;
|
||||
static final int VALUE = 1;
|
||||
|
||||
int pathConstraintIndex;
|
||||
|
||||
private final float[] frames; // time, rotate mix, translate mix, scale mix, shear mix, ...
|
||||
final float[] frames; // time, position, ...
|
||||
|
||||
public PathConstraintTimeline (int frameCount) {
|
||||
public PathConstraintPositionTimeline (int frameCount) {
|
||||
super(frameCount);
|
||||
frames = new float[frameCount * ENTRIES];
|
||||
}
|
||||
@ -894,11 +893,93 @@ public class Animation {
|
||||
return frames;
|
||||
}
|
||||
|
||||
/** Sets the time, position, and mixes of the specified keyframe. */
|
||||
public void setFrame (int frameIndex, float time, float position, float rotateMix, float translateMix) {
|
||||
/** Sets the time and value of the specified keyframe. */
|
||||
public void setFrame (int frameIndex, float time, float value) {
|
||||
frameIndex *= ENTRIES;
|
||||
frames[frameIndex] = time;
|
||||
frames[frameIndex + VALUE] = value;
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha) {
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
PathConstraint constraint = skeleton.pathConstraints.get(pathConstraintIndex);
|
||||
|
||||
if (time >= frames[frames.length - ENTRIES]) { // Time is after last frame.
|
||||
int i = frames.length;
|
||||
constraint.position += (frames[i + PREV_VALUE] - constraint.position) * alpha;
|
||||
return;
|
||||
}
|
||||
|
||||
// Interpolate between the previous frame and the current frame.
|
||||
int frame = binarySearch(frames, time, ENTRIES);
|
||||
float position = frames[frame + PREV_VALUE];
|
||||
float frameTime = frames[frame];
|
||||
float percent = getCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
|
||||
|
||||
constraint.position += (position + (frames[frame + VALUE] - position) * percent - constraint.position) * alpha;
|
||||
}
|
||||
}
|
||||
|
||||
static public class PathConstraintSpacingTimeline extends PathConstraintPositionTimeline {
|
||||
public PathConstraintSpacingTimeline (int frameCount) {
|
||||
super(frameCount);
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha) {
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
PathConstraint constraint = skeleton.pathConstraints.get(pathConstraintIndex);
|
||||
|
||||
if (time >= frames[frames.length - ENTRIES]) { // Time is after last frame.
|
||||
int i = frames.length;
|
||||
constraint.spacing += (frames[i + PREV_VALUE] - constraint.spacing) * alpha;
|
||||
return;
|
||||
}
|
||||
|
||||
// Interpolate between the previous frame and the current frame.
|
||||
int frame = binarySearch(frames, time, ENTRIES);
|
||||
float spacing = frames[frame + PREV_VALUE];
|
||||
float frameTime = frames[frame];
|
||||
float percent = getCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
|
||||
|
||||
constraint.spacing += (spacing + (frames[frame + VALUE] - spacing) * percent - constraint.spacing) * alpha;
|
||||
}
|
||||
}
|
||||
|
||||
static public class PathConstraintMixTimeline extends CurveTimeline {
|
||||
static public final int ENTRIES = 3;
|
||||
static private final int PREV_TIME = -3, PREV_ROTATE = -2, PREV_TRANSLATE = -1;
|
||||
static private final int ROTATE = 1, TRANSLATE = 2;
|
||||
|
||||
int pathConstraintIndex;
|
||||
|
||||
private final float[] frames; // time, rotate mix, translate mix, ...
|
||||
|
||||
public PathConstraintMixTimeline (int frameCount) {
|
||||
super(frameCount);
|
||||
frames = new float[frameCount * ENTRIES];
|
||||
}
|
||||
|
||||
public void setPathConstraintIndex (int index) {
|
||||
if (index < 0) throw new IllegalArgumentException("index must be >= 0.");
|
||||
this.pathConstraintIndex = index;
|
||||
}
|
||||
|
||||
public int getPathConstraintIndex () {
|
||||
return pathConstraintIndex;
|
||||
}
|
||||
|
||||
public float[] getFrames () {
|
||||
return frames;
|
||||
}
|
||||
|
||||
/** Sets the time and mixes of the specified keyframe. */
|
||||
public void setFrame (int frameIndex, float time, float rotateMix, float translateMix) {
|
||||
frameIndex *= ENTRIES;
|
||||
frames[frameIndex] = time;
|
||||
frames[frameIndex + POSITION] = position;
|
||||
frames[frameIndex + ROTATE] = rotateMix;
|
||||
frames[frameIndex + TRANSLATE] = translateMix;
|
||||
}
|
||||
@ -911,7 +992,6 @@ public class Animation {
|
||||
|
||||
if (time >= frames[frames.length - ENTRIES]) { // Time is after last frame.
|
||||
int i = frames.length;
|
||||
constraint.position += (frames[i + PREV_POSITION] - constraint.position) * alpha;
|
||||
constraint.rotateMix += (frames[i + PREV_ROTATE] - constraint.rotateMix) * alpha;
|
||||
constraint.translateMix += (frames[i + PREV_TRANSLATE] - constraint.translateMix) * alpha;
|
||||
return;
|
||||
@ -919,13 +999,11 @@ public class Animation {
|
||||
|
||||
// Interpolate between the previous frame and the current frame.
|
||||
int frame = binarySearch(frames, time, ENTRIES);
|
||||
float position = frames[frame + PREV_POSITION];
|
||||
float rotate = frames[frame + PREV_ROTATE];
|
||||
float translate = frames[frame + PREV_TRANSLATE];
|
||||
float frameTime = frames[frame];
|
||||
float percent = getCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
|
||||
|
||||
constraint.position += (position + (frames[frame + POSITION] - position) * percent - constraint.position) * alpha;
|
||||
constraint.rotateMix += (rotate + (frames[frame + ROTATE] - rotate) * percent - constraint.rotateMix) * alpha;
|
||||
constraint.translateMix += (translate + (frames[frame + TRANSLATE] - translate) * percent - constraint.translateMix)
|
||||
* alpha;
|
||||
|
||||
@ -144,7 +144,7 @@ public class PathConstraint implements Updatable {
|
||||
Slot target = this.target;
|
||||
float position = this.position;
|
||||
int verticesLength = path.getWorldVerticesLength(), curveCount = verticesLength / 6, lastCurve = NONE;
|
||||
float[] spaces = this.spaces.items, out = this.positions.setSize(spacesCount * 3), world;
|
||||
float[] spaces = this.spaces.items, out = this.positions.setSize(spacesCount * 3 + 2), world;
|
||||
boolean closed = path.getClosed();
|
||||
|
||||
if (!path.getConstantSpeed()) {
|
||||
|
||||
@ -48,7 +48,9 @@ import com.esotericsoftware.spine.Animation.DeformTimeline;
|
||||
import com.esotericsoftware.spine.Animation.DrawOrderTimeline;
|
||||
import com.esotericsoftware.spine.Animation.EventTimeline;
|
||||
import com.esotericsoftware.spine.Animation.IkConstraintTimeline;
|
||||
import com.esotericsoftware.spine.Animation.PathConstraintTimeline;
|
||||
import com.esotericsoftware.spine.Animation.PathConstraintMixTimeline;
|
||||
import com.esotericsoftware.spine.Animation.PathConstraintPositionTimeline;
|
||||
import com.esotericsoftware.spine.Animation.PathConstraintSpacingTimeline;
|
||||
import com.esotericsoftware.spine.Animation.RotateTimeline;
|
||||
import com.esotericsoftware.spine.Animation.ScaleTimeline;
|
||||
import com.esotericsoftware.spine.Animation.ShearTimeline;
|
||||
@ -69,12 +71,17 @@ import com.esotericsoftware.spine.attachments.RegionAttachment;
|
||||
import com.esotericsoftware.spine.attachments.VertexAttachment;
|
||||
|
||||
public class SkeletonBinary {
|
||||
static public final int TIMELINE_ROTATE = 0;
|
||||
static public final int TIMELINE_TRANSLATE = 1;
|
||||
static public final int TIMELINE_SCALE = 2;
|
||||
static public final int TIMELINE_SHEAR = 3;
|
||||
static public final int TIMELINE_ATTACHMENT = 4;
|
||||
static public final int TIMELINE_COLOR = 5;
|
||||
static public final int BONE_ROTATE = 0;
|
||||
static public final int BONE_TRANSLATE = 1;
|
||||
static public final int BONE_SCALE = 2;
|
||||
static public final int BONE_SHEAR = 3;
|
||||
|
||||
static public final int SLOT_ATTACHMENT = 0;
|
||||
static public final int SLOT_COLOR = 1;
|
||||
|
||||
static public final int PATH_POSITION = 0;
|
||||
static public final int PATH_SPACING = 1;
|
||||
static public final int PATH_MIX = 2;
|
||||
|
||||
static public final int CURVE_LINEAR = 0;
|
||||
static public final int CURVE_STEPPED = 1;
|
||||
@ -488,7 +495,7 @@ public class SkeletonBinary {
|
||||
int timelineType = input.readByte();
|
||||
int frameCount = input.readInt(true);
|
||||
switch (timelineType) {
|
||||
case TIMELINE_COLOR: {
|
||||
case SLOT_COLOR: {
|
||||
ColorTimeline timeline = new ColorTimeline(frameCount);
|
||||
timeline.slotIndex = slotIndex;
|
||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
||||
@ -501,7 +508,7 @@ public class SkeletonBinary {
|
||||
duration = Math.max(duration, timeline.getFrames()[(frameCount - 1) * ColorTimeline.ENTRIES]);
|
||||
break;
|
||||
}
|
||||
case TIMELINE_ATTACHMENT:
|
||||
case SLOT_ATTACHMENT:
|
||||
AttachmentTimeline timeline = new AttachmentTimeline(frameCount);
|
||||
timeline.slotIndex = slotIndex;
|
||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++)
|
||||
@ -520,7 +527,7 @@ public class SkeletonBinary {
|
||||
int timelineType = input.readByte();
|
||||
int frameCount = input.readInt(true);
|
||||
switch (timelineType) {
|
||||
case TIMELINE_ROTATE: {
|
||||
case BONE_ROTATE: {
|
||||
RotateTimeline timeline = new RotateTimeline(frameCount);
|
||||
timeline.boneIndex = boneIndex;
|
||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
||||
@ -531,14 +538,14 @@ public class SkeletonBinary {
|
||||
duration = Math.max(duration, timeline.getFrames()[(frameCount - 1) * RotateTimeline.ENTRIES]);
|
||||
break;
|
||||
}
|
||||
case TIMELINE_TRANSLATE:
|
||||
case TIMELINE_SCALE:
|
||||
case TIMELINE_SHEAR: {
|
||||
case BONE_TRANSLATE:
|
||||
case BONE_SCALE:
|
||||
case BONE_SHEAR: {
|
||||
TranslateTimeline timeline;
|
||||
float timelineScale = 1;
|
||||
if (timelineType == TIMELINE_SCALE)
|
||||
if (timelineType == BONE_SCALE)
|
||||
timeline = new ScaleTimeline(frameCount);
|
||||
else if (timelineType == TIMELINE_SHEAR)
|
||||
else if (timelineType == BONE_SHEAR)
|
||||
timeline = new ShearTimeline(frameCount);
|
||||
else {
|
||||
timeline = new TranslateTimeline(frameCount);
|
||||
@ -590,15 +597,37 @@ public class SkeletonBinary {
|
||||
// Path constraint timelines.
|
||||
for (int i = 0, n = input.readInt(true); i < n; i++) {
|
||||
int index = input.readInt(true);
|
||||
int frameCount = input.readInt(true);
|
||||
PathConstraintTimeline timeline = new PathConstraintTimeline(frameCount);
|
||||
timeline.pathConstraintIndex = index;
|
||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
||||
timeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readFloat(), input.readFloat());
|
||||
if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
|
||||
for (int ii = 0, nn = input.readInt(true); ii < nn; ii++) {
|
||||
int timelineType = input.readByte();
|
||||
int frameCount = input.readInt(true);
|
||||
switch (timelineType) {
|
||||
case PATH_POSITION:
|
||||
case PATH_SPACING: {
|
||||
PathConstraintPositionTimeline timeline;
|
||||
if (timelineType == PATH_SPACING)
|
||||
timeline = new PathConstraintSpacingTimeline(frameCount);
|
||||
else
|
||||
timeline = new PathConstraintPositionTimeline(frameCount);
|
||||
timeline.pathConstraintIndex = index;
|
||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
||||
timeline.setFrame(frameIndex, input.readFloat(), input.readFloat());
|
||||
if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
|
||||
}
|
||||
timelines.add(timeline);
|
||||
duration = Math.max(duration, timeline.getFrames()[(frameCount - 1) * PathConstraintPositionTimeline.ENTRIES]);
|
||||
}
|
||||
case PATH_MIX: {
|
||||
PathConstraintMixTimeline timeline = new PathConstraintMixTimeline(frameCount);
|
||||
timeline.pathConstraintIndex = index;
|
||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
||||
timeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readFloat());
|
||||
if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
|
||||
}
|
||||
timelines.add(timeline);
|
||||
duration = Math.max(duration, timeline.getFrames()[(frameCount - 1) * PathConstraintMixTimeline.ENTRIES]);
|
||||
}
|
||||
}
|
||||
}
|
||||
timelines.add(timeline);
|
||||
duration = Math.max(duration, timeline.getFrames()[(frameCount - 1) * PathConstraintTimeline.ENTRIES]);
|
||||
}
|
||||
|
||||
// Deform timelines.
|
||||
|
||||
@ -90,7 +90,7 @@ public class SkeletonData {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @return -1 if the bone was not found. */
|
||||
/** @return -1 if the slot was not found. */
|
||||
public int findSlotIndex (String slotName) {
|
||||
if (slotName == null) throw new IllegalArgumentException("slotName cannot be null.");
|
||||
Array<SlotData> slots = this.slots;
|
||||
@ -206,6 +206,15 @@ public class SkeletonData {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @return -1 if the path constraint was not found. */
|
||||
public int findPathConstraintIndex (String pathConstraintName) {
|
||||
if (pathConstraintName == null) throw new IllegalArgumentException("pathConstraintName cannot be null.");
|
||||
Array<PathConstraintData> pathConstraints = this.pathConstraints;
|
||||
for (int i = 0, n = pathConstraints.size; i < n; i++)
|
||||
if (pathConstraints.get(i).name.equals(pathConstraintName)) return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
/** @return May be null. */
|
||||
|
||||
@ -47,7 +47,9 @@ import com.esotericsoftware.spine.Animation.DeformTimeline;
|
||||
import com.esotericsoftware.spine.Animation.DrawOrderTimeline;
|
||||
import com.esotericsoftware.spine.Animation.EventTimeline;
|
||||
import com.esotericsoftware.spine.Animation.IkConstraintTimeline;
|
||||
import com.esotericsoftware.spine.Animation.PathConstraintTimeline;
|
||||
import com.esotericsoftware.spine.Animation.PathConstraintMixTimeline;
|
||||
import com.esotericsoftware.spine.Animation.PathConstraintPositionTimeline;
|
||||
import com.esotericsoftware.spine.Animation.PathConstraintSpacingTimeline;
|
||||
import com.esotericsoftware.spine.Animation.RotateTimeline;
|
||||
import com.esotericsoftware.spine.Animation.ScaleTimeline;
|
||||
import com.esotericsoftware.spine.Animation.ShearTimeline;
|
||||
@ -280,7 +282,7 @@ public class SkeletonJson {
|
||||
|
||||
String type = map.getString("type", AttachmentType.region.name());
|
||||
|
||||
// Warning: These types are deprecated and will be removed in the near future.
|
||||
// BOZO - Warning: These types are deprecated and will be removed in the near future.
|
||||
if (type.equals("skinnedmesh")) type = "weightedmesh";
|
||||
if (type.equals("weightedmesh")) type = "mesh";
|
||||
if (type.equals("weightedlinkedmesh")) type = "linkedmesh";
|
||||
@ -393,7 +395,6 @@ public class SkeletonJson {
|
||||
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")) {
|
||||
@ -428,7 +429,6 @@ public class SkeletonJson {
|
||||
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("rotate")) {
|
||||
@ -506,19 +506,42 @@ public class SkeletonJson {
|
||||
}
|
||||
|
||||
// Path constraint timelines.
|
||||
for (JsonValue constraintMap = map.getChild("path"); constraintMap != null; constraintMap = constraintMap.next) {
|
||||
PathConstraintData constraint = skeletonData.findPathConstraint(constraintMap.name);
|
||||
PathConstraintTimeline timeline = new PathConstraintTimeline(constraintMap.size);
|
||||
timeline.pathConstraintIndex = skeletonData.getPathConstraints().indexOf(constraint, true);
|
||||
int frameIndex = 0;
|
||||
for (JsonValue valueMap = constraintMap.child; valueMap != null; valueMap = valueMap.next) {
|
||||
timeline.setFrame(frameIndex, valueMap.getFloat("time"), valueMap.getFloat("scaleMix", 1),
|
||||
valueMap.getFloat("rotateMix", 1), valueMap.getFloat("translateMix", 1));
|
||||
readCurve(valueMap, timeline, frameIndex);
|
||||
frameIndex++;
|
||||
for (JsonValue constraintMap = map.getChild("paths"); constraintMap != null; constraintMap = constraintMap.next) {
|
||||
int index = skeletonData.findPathConstraintIndex(constraintMap.name);
|
||||
if (index == -1) throw new SerializationException("Path constraint not found: " + constraintMap.name);
|
||||
for (JsonValue timelineMap = constraintMap.child; timelineMap != null; timelineMap = timelineMap.next) {
|
||||
String timelineName = timelineMap.name;
|
||||
if (timelineName.equals("position") || timelineName.equals("spacing")) {
|
||||
PathConstraintPositionTimeline timeline;
|
||||
if (timelineName.equals("spacing"))
|
||||
timeline = new PathConstraintSpacingTimeline(timelineMap.size);
|
||||
else
|
||||
timeline = new PathConstraintPositionTimeline(timelineMap.size);
|
||||
timeline.pathConstraintIndex = index;
|
||||
int frameIndex = 0;
|
||||
for (JsonValue valueMap = constraintMap.child; valueMap != null; valueMap = valueMap.next) {
|
||||
timeline.setFrame(frameIndex, valueMap.getFloat("time"), valueMap.getFloat(timelineName, 1));
|
||||
readCurve(valueMap, timeline, frameIndex);
|
||||
frameIndex++;
|
||||
}
|
||||
timelines.add(timeline);
|
||||
duration = Math.max(duration,
|
||||
timeline.getFrames()[(timeline.getFrameCount() - 1) * PathConstraintPositionTimeline.ENTRIES]);
|
||||
} else if (timelineName.equals("mix")) {
|
||||
PathConstraintMixTimeline timeline = new PathConstraintMixTimeline(timelineMap.size);
|
||||
timeline.pathConstraintIndex = index;
|
||||
int frameIndex = 0;
|
||||
for (JsonValue valueMap = constraintMap.child; valueMap != null; valueMap = valueMap.next) {
|
||||
timeline.setFrame(frameIndex, valueMap.getFloat("time"), valueMap.getFloat("rotateMix", 1),
|
||||
valueMap.getFloat("translateMix", 1));
|
||||
readCurve(valueMap, timeline, frameIndex);
|
||||
frameIndex++;
|
||||
}
|
||||
timelines.add(timeline);
|
||||
duration = Math.max(duration,
|
||||
timeline.getFrames()[(timeline.getFrameCount() - 1) * PathConstraintMixTimeline.ENTRIES]);
|
||||
}
|
||||
}
|
||||
timelines.add(timeline);
|
||||
duration = Math.max(duration, timeline.getFrames()[(timeline.getFrameCount() - 1) * PathConstraintTimeline.ENTRIES]);
|
||||
}
|
||||
|
||||
// Deform timelines.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user