mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-21 01:36:02 +08:00
parent
c59a58ae5b
commit
b4924b0a49
@ -530,6 +530,33 @@ public class AnimationStateTest {
|
||||
state.addAnimation(0, "events1", false, 5);
|
||||
run(0.1f, 10, null);
|
||||
|
||||
setup("setAnimation during AnimationStateListener"); // 23
|
||||
state.addListener(new AnimationStateListener() {
|
||||
public void start (TrackEntry entry) {
|
||||
if (entry.getAnimation().getName().equals("events1")) state.setAnimation(1, "events2", false);
|
||||
}
|
||||
|
||||
public void interrupt (TrackEntry entry) {
|
||||
state.addAnimation(3, "events2", false, 0);
|
||||
}
|
||||
|
||||
public void event (TrackEntry entry, Event event) {
|
||||
if (entry.getTrackIndex() != 2) state.setAnimation(2, "events2", false);
|
||||
}
|
||||
|
||||
public void end (TrackEntry entry) {
|
||||
if (entry.getAnimation().getName().equals("events1")) state.setAnimation(0, "events2", false);
|
||||
}
|
||||
|
||||
public void complete (TrackEntry entry) {
|
||||
if (entry.getAnimation().getName().equals("events1")) state.setAnimation(1, "events2", false);
|
||||
}
|
||||
});
|
||||
state.addAnimation(0, "events1", false, 0);
|
||||
state.addAnimation(0, "events2", false, 0);
|
||||
state.setAnimation(1, "events2", false);
|
||||
run(0.1f, 10, null);
|
||||
|
||||
System.out.println("AnimationState tests passed.");
|
||||
}
|
||||
|
||||
@ -538,11 +565,13 @@ public class AnimationStateTest {
|
||||
expected.addAll(expectedArray);
|
||||
stateData = new AnimationStateData(skeletonData);
|
||||
state = new AnimationState(stateData);
|
||||
state.addListener(stateListener);
|
||||
time = 0;
|
||||
fail = false;
|
||||
log(test + ": " + description);
|
||||
log(String.format("%-3s%-12s%-7s%-7s%-7s", "#", "EVENT", "TRACK", "TOTAL", "RESULT"));
|
||||
if (expectedArray.length > 0) {
|
||||
state.addListener(stateListener);
|
||||
log(String.format("%-3s%-12s%-7s%-7s%-7s", "#", "EVENT", "TRACK", "TOTAL", "RESULT"));
|
||||
}
|
||||
}
|
||||
|
||||
void run (float incr, float endTime, TestListener listener) {
|
||||
|
||||
@ -65,7 +65,7 @@ public class BonePlotting {
|
||||
for (Animation animation : skeletonData.getAnimations()) {
|
||||
float time = 0;
|
||||
while (time < animation.getDuration()) {
|
||||
animation.apply(skeleton, time, time, false, null);
|
||||
animation.apply(skeleton, time, time, false, null, 1, false);
|
||||
skeleton.updateWorldTransform();
|
||||
System.out
|
||||
.println(animation.getName() + "," + bone.getWorldX() + "," + bone.getWorldY() + "," + bone.getWorldRotationX());
|
||||
|
||||
@ -116,9 +116,8 @@ public class Box2DExample extends ApplicationAdapter {
|
||||
Box2dAttachment attachment = (Box2dAttachment)slot.getAttachment();
|
||||
|
||||
PolygonShape boxPoly = new PolygonShape();
|
||||
boxPoly.setAsBox(attachment.getWidth() / 2 * attachment.getScaleX(),
|
||||
attachment.getHeight() / 2 * attachment.getScaleY(), vector.set(attachment.getX(), attachment.getY()),
|
||||
attachment.getRotation() * MathUtils.degRad);
|
||||
boxPoly.setAsBox(attachment.getWidth() / 2 * attachment.getScaleX(), attachment.getHeight() / 2 * attachment.getScaleY(),
|
||||
vector.set(attachment.getX(), attachment.getY()), attachment.getRotation() * MathUtils.degRad);
|
||||
|
||||
BodyDef boxBodyDef = new BodyDef();
|
||||
boxBodyDef.type = BodyType.StaticBody;
|
||||
@ -146,7 +145,7 @@ public class Box2DExample extends ApplicationAdapter {
|
||||
batch.setTransformMatrix(camera.view);
|
||||
batch.begin();
|
||||
|
||||
animation.apply(skeleton, time, time, true, events);
|
||||
animation.apply(skeleton, time, time, true, events, 1, false);
|
||||
skeleton.x += 8 * delta;
|
||||
skeleton.updateWorldTransform();
|
||||
skeletonRenderer.draw(batch, skeleton);
|
||||
|
||||
@ -177,7 +177,7 @@ public class EventTimelineTests {
|
||||
|
||||
int beforeCount = firedEvents.size;
|
||||
Array<Event> original = new Array(firedEvents);
|
||||
timeline.apply(skeleton, lastTimeLooped, timeLooped, firedEvents, 1);
|
||||
timeline.apply(skeleton, lastTimeLooped, timeLooped, firedEvents, 1, false);
|
||||
|
||||
while (beforeCount < firedEvents.size) {
|
||||
char fired = firedEvents.get(beforeCount).getData().getName().charAt(0);
|
||||
@ -186,7 +186,7 @@ public class EventTimelineTests {
|
||||
} else {
|
||||
if (firedEvents.size > eventsCount) {
|
||||
if (print) System.out.println(lastTimeLooped + "->" + timeLooped + ": " + fired + " == ?");
|
||||
timeline.apply(skeleton, lastTimeLooped, timeLooped, original, 1);
|
||||
timeline.apply(skeleton, lastTimeLooped, timeLooped, original, 1, false);
|
||||
fail("Too many events fired.");
|
||||
}
|
||||
}
|
||||
@ -194,7 +194,7 @@ public class EventTimelineTests {
|
||||
System.out.println(lastTimeLooped + "->" + timeLooped + ": " + fired + " == " + events[eventIndex]);
|
||||
}
|
||||
if (fired != events[eventIndex]) {
|
||||
timeline.apply(skeleton, lastTimeLooped, timeLooped, original, 1);
|
||||
timeline.apply(skeleton, lastTimeLooped, timeLooped, original, 1, false);
|
||||
fail("Wrong event fired.");
|
||||
}
|
||||
eventIndex++;
|
||||
@ -206,7 +206,7 @@ public class EventTimelineTests {
|
||||
i++;
|
||||
}
|
||||
if (firedEvents.size < eventsCount) {
|
||||
timeline.apply(skeleton, lastTimeLooped, timeLooped, firedEvents, 1);
|
||||
timeline.apply(skeleton, lastTimeLooped, timeLooped, firedEvents, 1, false);
|
||||
if (print) System.out.println(firedEvents);
|
||||
fail("Event not fired: " + events[eventIndex] + ", " + frames[eventIndex]);
|
||||
}
|
||||
|
||||
@ -104,21 +104,22 @@ public class MixTest extends ApplicationAdapter {
|
||||
skeleton.setX(-50);
|
||||
} else if (time > beforeJump + jump) {
|
||||
// just walk after jump
|
||||
walkAnimation.apply(skeleton, time, time, true, events);
|
||||
walkAnimation.apply(skeleton, time, time, true, events, 1, false);
|
||||
} else if (time > blendOutStart) {
|
||||
// blend out jump
|
||||
walkAnimation.apply(skeleton, time, time, true, events);
|
||||
jumpAnimation.mix(skeleton, time - beforeJump, time - beforeJump, false, events, 1 - (time - blendOutStart) / blendOut);
|
||||
walkAnimation.apply(skeleton, time, time, true, events, 1, false);
|
||||
jumpAnimation.apply(skeleton, time - beforeJump, time - beforeJump, false, events, 1 - (time - blendOutStart) / blendOut,
|
||||
false);
|
||||
} else if (time > beforeJump + blendIn) {
|
||||
// just jump
|
||||
jumpAnimation.apply(skeleton, time - beforeJump, time - beforeJump, false, events);
|
||||
jumpAnimation.apply(skeleton, time - beforeJump, time - beforeJump, false, events, 1, false);
|
||||
} else if (time > beforeJump) {
|
||||
// blend in jump
|
||||
walkAnimation.apply(skeleton, time, time, true, events);
|
||||
jumpAnimation.mix(skeleton, time - beforeJump, time - beforeJump, false, events, (time - beforeJump) / blendIn);
|
||||
walkAnimation.apply(skeleton, time, time, true, events, 1, false);
|
||||
jumpAnimation.apply(skeleton, time - beforeJump, time - beforeJump, false, events, (time - beforeJump) / blendIn, false);
|
||||
} else {
|
||||
// just walk before jump
|
||||
walkAnimation.apply(skeleton, time, time, true, events);
|
||||
walkAnimation.apply(skeleton, time, time, true, events, 1, false);
|
||||
}
|
||||
|
||||
skeleton.updateWorldTransform();
|
||||
|
||||
@ -131,7 +131,7 @@ public class NormalMapTest extends ApplicationAdapter {
|
||||
public void render () {
|
||||
float lastTime = time;
|
||||
time += Gdx.graphics.getDeltaTime();
|
||||
if (animation != null) animation.apply(skeleton, lastTime, time, true, null);
|
||||
if (animation != null) animation.apply(skeleton, lastTime, time, true, null, 1, false);
|
||||
skeleton.updateWorldTransform();
|
||||
skeleton.update(Gdx.graphics.getDeltaTime());
|
||||
|
||||
|
||||
@ -64,27 +64,14 @@ public class Animation {
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
/** Poses the skeleton at the specified time for this animation.
|
||||
* @param lastTime The last time the animation was applied.
|
||||
* @param events Any triggered events are added. May be null. */
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, boolean loop, Array<Event> events) {
|
||||
if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null.");
|
||||
|
||||
if (loop && duration != 0) {
|
||||
time %= duration;
|
||||
if (lastTime > 0) lastTime %= duration;
|
||||
}
|
||||
|
||||
Array<Timeline> timelines = this.timelines;
|
||||
for (int i = 0, n = timelines.size; i < n; i++)
|
||||
timelines.get(i).apply(skeleton, lastTime, time, events, 1);
|
||||
}
|
||||
|
||||
/** Poses the skeleton at the specified time for this animation mixed with the current pose.
|
||||
/** Poses the skeleton at the specified time for this animation mixed with the current or setup pose.
|
||||
* @param lastTime The last time the animation was applied.
|
||||
* @param events Any triggered events are added. May be null.
|
||||
* @param alpha The amount of this animation that affects the current pose. */
|
||||
public void mix (Skeleton skeleton, float lastTime, float time, boolean loop, Array<Event> events, float alpha) {
|
||||
* @param alpha The amount of this animation that affects the current pose.
|
||||
* @param setupPose If true, the animation is mixed with the setup pose, else it is mixed with the current pose. Pass true when
|
||||
* alpha is 1. */
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, boolean loop, Array<Event> events, float alpha,
|
||||
boolean setupPose) {
|
||||
if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null.");
|
||||
|
||||
if (loop && duration != 0) {
|
||||
@ -94,7 +81,7 @@ public class Animation {
|
||||
|
||||
Array<Timeline> timelines = this.timelines;
|
||||
for (int i = 0, n = timelines.size; i < n; i++)
|
||||
timelines.get(i).apply(skeleton, lastTime, time, events, alpha);
|
||||
timelines.get(i).apply(skeleton, lastTime, time, events, alpha, setupPose);
|
||||
}
|
||||
|
||||
public String getName () {
|
||||
@ -147,8 +134,20 @@ public class Animation {
|
||||
|
||||
static public interface Timeline {
|
||||
/** Sets the value(s) for the specified time.
|
||||
* @param events May be null to not collect fired events. */
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha);
|
||||
* @param events May be null to not collect fired events.
|
||||
* @param setupPose If true, the timeline is mixed with the setup pose, else it is mixed with the current pose. Pass true
|
||||
* when alpha is 1. */
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha, boolean setupPose);
|
||||
|
||||
public int getId ();
|
||||
}
|
||||
|
||||
static private enum TimelineType {
|
||||
rotate, translate, scale, shear, //
|
||||
attachment, color, deform, //
|
||||
event, drawOrder, //
|
||||
ikConstraint, transformConstraint, //
|
||||
pathConstraintPosition, pathConstraintSpacing, pathConstraintMix
|
||||
}
|
||||
|
||||
/** Base class for frames that use an interpolation bezier curve. */
|
||||
@ -251,6 +250,10 @@ public class Animation {
|
||||
frames = new float[frameCount << 1];
|
||||
}
|
||||
|
||||
public int getId () {
|
||||
return (TimelineType.rotate.ordinal() << 24) + boneIndex;
|
||||
}
|
||||
|
||||
public void setBoneIndex (int index) {
|
||||
if (index < 0) throw new IllegalArgumentException("index must be >= 0.");
|
||||
this.boneIndex = index;
|
||||
@ -271,19 +274,19 @@ public class Animation {
|
||||
frames[frameIndex + ROTATION] = degrees;
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha) {
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha, boolean setupPose) {
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
Bone bone = skeleton.bones.get(boneIndex);
|
||||
|
||||
if (time >= frames[frames.length - ENTRIES]) { // Time is after last frame.
|
||||
float amount = bone.data.rotation + frames[frames.length + PREV_ROTATION] - bone.rotation;
|
||||
while (amount > 180)
|
||||
amount -= 360;
|
||||
while (amount < -180)
|
||||
amount += 360;
|
||||
bone.rotation += amount * alpha;
|
||||
if (setupPose)
|
||||
bone.rotation = bone.data.rotation + frames[frames.length + PREV_ROTATION] * alpha;
|
||||
else {
|
||||
float amount = bone.data.rotation + frames[frames.length + PREV_ROTATION] - bone.rotation;
|
||||
amount -= (16384 - (int)(16384.499999999996 - amount / 360)) * 360;
|
||||
bone.rotation += amount * alpha;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -294,16 +297,16 @@ public class Animation {
|
||||
float percent = getCurvePercent((frame >> 1) - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
|
||||
|
||||
float amount = frames[frame + ROTATION] - prevRotation;
|
||||
while (amount > 180)
|
||||
amount -= 360;
|
||||
while (amount < -180)
|
||||
amount += 360;
|
||||
amount = bone.data.rotation + (prevRotation + amount * percent) - bone.rotation;
|
||||
while (amount > 180)
|
||||
amount -= 360;
|
||||
while (amount < -180)
|
||||
amount += 360;
|
||||
bone.rotation += amount * alpha;
|
||||
amount = amount - (16384 - (int)(16384.499999999996 - amount / 360)) * 360;
|
||||
if (setupPose) {
|
||||
amount = prevRotation + amount * percent;
|
||||
amount = amount - (16384 - (int)(16384.499999999996 - amount / 360)) * 360;
|
||||
bone.rotation = bone.data.rotation + amount * alpha;
|
||||
} else {
|
||||
amount = bone.data.rotation + (prevRotation + amount * percent) - bone.rotation;
|
||||
amount -= (16384 - (int)(16384.499999999996 - amount / 360)) * 360;
|
||||
bone.rotation += amount * alpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -320,6 +323,10 @@ public class Animation {
|
||||
frames = new float[frameCount * ENTRIES];
|
||||
}
|
||||
|
||||
public int getId () {
|
||||
return (TimelineType.translate.ordinal() << 24) + boneIndex;
|
||||
}
|
||||
|
||||
public void setBoneIndex (int index) {
|
||||
if (index < 0) throw new IllegalArgumentException("index must be >= 0.");
|
||||
this.boneIndex = index;
|
||||
@ -341,15 +348,20 @@ public class Animation {
|
||||
frames[frameIndex + Y] = y;
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha) {
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha, boolean setupPose) {
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
Bone bone = skeleton.bones.get(boneIndex);
|
||||
|
||||
if (time >= frames[frames.length - ENTRIES]) { // Time is after last frame.
|
||||
bone.x += (bone.data.x + frames[frames.length + PREV_X] - bone.x) * alpha;
|
||||
bone.y += (bone.data.y + frames[frames.length + PREV_Y] - bone.y) * alpha;
|
||||
if (setupPose) {
|
||||
bone.x = bone.data.x + frames[frames.length + PREV_X] * alpha;
|
||||
bone.y = bone.data.y + frames[frames.length + PREV_Y] * alpha;
|
||||
} else {
|
||||
bone.x += (bone.data.x + frames[frames.length + PREV_X] - bone.x) * alpha;
|
||||
bone.y += (bone.data.y + frames[frames.length + PREV_Y] - bone.y) * alpha;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -360,8 +372,13 @@ public class Animation {
|
||||
float frameTime = frames[frame];
|
||||
float percent = getCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
|
||||
|
||||
bone.x += (bone.data.x + prevX + (frames[frame + X] - prevX) * percent - bone.x) * alpha;
|
||||
bone.y += (bone.data.y + prevY + (frames[frame + Y] - prevY) * percent - bone.y) * alpha;
|
||||
if (setupPose) {
|
||||
bone.x = bone.data.x + (prevX + (frames[frame + X] - prevX) * percent) * alpha;
|
||||
bone.y = bone.data.y + (prevY + (frames[frame + Y] - prevY) * percent) * alpha;
|
||||
} else {
|
||||
bone.x += (bone.data.x + prevX + (frames[frame + X] - prevX) * percent - bone.x) * alpha;
|
||||
bone.y += (bone.data.y + prevY + (frames[frame + Y] - prevY) * percent - bone.y) * alpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -370,7 +387,11 @@ public class Animation {
|
||||
super(frameCount);
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha) {
|
||||
public int getId () {
|
||||
return (TimelineType.scale.ordinal() << 24) + boneIndex;
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha, boolean setupPose) {
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
@ -409,7 +430,11 @@ public class Animation {
|
||||
super(frameCount);
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha) {
|
||||
public int getId () {
|
||||
return (TimelineType.shear.ordinal() << 24) + boneIndex;
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha, boolean setupPose) {
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
@ -445,6 +470,10 @@ public class Animation {
|
||||
frames = new float[frameCount * ENTRIES];
|
||||
}
|
||||
|
||||
public int getId () {
|
||||
return (TimelineType.color.ordinal() << 24) + slotIndex;
|
||||
}
|
||||
|
||||
public void setSlotIndex (int index) {
|
||||
if (index < 0) throw new IllegalArgumentException("index must be >= 0.");
|
||||
this.slotIndex = index;
|
||||
@ -468,7 +497,7 @@ public class Animation {
|
||||
frames[frameIndex + A] = a;
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha) {
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha, boolean setupPose) {
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
@ -513,6 +542,10 @@ public class Animation {
|
||||
attachmentNames = new String[frameCount];
|
||||
}
|
||||
|
||||
public int getId () {
|
||||
return (TimelineType.attachment.ordinal() << 24) + slotIndex;
|
||||
}
|
||||
|
||||
public int getFrameCount () {
|
||||
return frames.length;
|
||||
}
|
||||
@ -540,7 +573,7 @@ public class Animation {
|
||||
attachmentNames[frameIndex] = attachmentName;
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha) {
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha, boolean setupPose) {
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
@ -556,117 +589,11 @@ public class Animation {
|
||||
}
|
||||
}
|
||||
|
||||
static public class EventTimeline implements Timeline {
|
||||
private final float[] frames; // time, ...
|
||||
private final Event[] events;
|
||||
|
||||
public EventTimeline (int frameCount) {
|
||||
frames = new float[frameCount];
|
||||
events = new Event[frameCount];
|
||||
}
|
||||
|
||||
public int getFrameCount () {
|
||||
return frames.length;
|
||||
}
|
||||
|
||||
public float[] getFrames () {
|
||||
return frames;
|
||||
}
|
||||
|
||||
public Event[] getEvents () {
|
||||
return events;
|
||||
}
|
||||
|
||||
/** Sets the time of the specified keyframe. */
|
||||
public void setFrame (int frameIndex, Event event) {
|
||||
frames[frameIndex] = event.time;
|
||||
events[frameIndex] = event;
|
||||
}
|
||||
|
||||
/** Fires events for frames > lastTime and <= time. */
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> firedEvents, float alpha) {
|
||||
if (firedEvents == null) return;
|
||||
float[] frames = this.frames;
|
||||
int frameCount = frames.length;
|
||||
|
||||
if (lastTime > time) { // Fire events after last time for looped animations.
|
||||
apply(skeleton, lastTime, Integer.MAX_VALUE, firedEvents, alpha);
|
||||
lastTime = -1f;
|
||||
} else if (lastTime >= frames[frameCount - 1]) // Last time is after last frame.
|
||||
return;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
int frame;
|
||||
if (lastTime < frames[0])
|
||||
frame = 0;
|
||||
else {
|
||||
frame = binarySearch(frames, lastTime);
|
||||
float frameTime = frames[frame];
|
||||
while (frame > 0) { // Fire multiple events with the same frame.
|
||||
if (frames[frame - 1] != frameTime) break;
|
||||
frame--;
|
||||
}
|
||||
}
|
||||
for (; frame < frameCount && time >= frames[frame]; frame++)
|
||||
firedEvents.add(events[frame]);
|
||||
}
|
||||
}
|
||||
|
||||
static public class DrawOrderTimeline implements Timeline {
|
||||
private final float[] frames; // time, ...
|
||||
private final int[][] drawOrders;
|
||||
|
||||
public DrawOrderTimeline (int frameCount) {
|
||||
frames = new float[frameCount];
|
||||
drawOrders = new int[frameCount][];
|
||||
}
|
||||
|
||||
public int getFrameCount () {
|
||||
return frames.length;
|
||||
}
|
||||
|
||||
public float[] getFrames () {
|
||||
return frames;
|
||||
}
|
||||
|
||||
public int[][] getDrawOrders () {
|
||||
return drawOrders;
|
||||
}
|
||||
|
||||
/** Sets the time of the specified keyframe.
|
||||
* @param drawOrder May be null to use bind pose draw order. */
|
||||
public void setFrame (int frameIndex, float time, int[] drawOrder) {
|
||||
frames[frameIndex] = time;
|
||||
drawOrders[frameIndex] = drawOrder;
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> firedEvents, float alpha) {
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
int frame;
|
||||
if (time >= frames[frames.length - 1]) // Time is after last frame.
|
||||
frame = frames.length - 1;
|
||||
else
|
||||
frame = binarySearch(frames, time) - 1;
|
||||
|
||||
Array<Slot> drawOrder = skeleton.drawOrder;
|
||||
Array<Slot> slots = skeleton.slots;
|
||||
int[] drawOrderToSetupIndex = drawOrders[frame];
|
||||
if (drawOrderToSetupIndex == null)
|
||||
System.arraycopy(slots.items, 0, drawOrder.items, 0, slots.size);
|
||||
else {
|
||||
for (int i = 0, n = drawOrderToSetupIndex.length; i < n; i++)
|
||||
drawOrder.set(i, slots.get(drawOrderToSetupIndex[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static public class DeformTimeline extends CurveTimeline {
|
||||
private final float[] frames; // time, ...
|
||||
private final float[][] frameVertices;
|
||||
int slotIndex;
|
||||
VertexAttachment attachment;
|
||||
private final float[] frames; // time, ...
|
||||
private final float[][] frameVertices;
|
||||
|
||||
public DeformTimeline (int frameCount) {
|
||||
super(frameCount);
|
||||
@ -674,6 +601,10 @@ public class Animation {
|
||||
frameVertices = new float[frameCount][];
|
||||
}
|
||||
|
||||
public int getId () {
|
||||
return (TimelineType.deform.ordinal() << 24) + slotIndex;
|
||||
}
|
||||
|
||||
public void setSlotIndex (int index) {
|
||||
if (index < 0) throw new IllegalArgumentException("index must be >= 0.");
|
||||
this.slotIndex = index;
|
||||
@ -687,7 +618,7 @@ public class Animation {
|
||||
this.attachment = attachment;
|
||||
}
|
||||
|
||||
public Attachment getAttachment () {
|
||||
public VertexAttachment getAttachment () {
|
||||
return attachment;
|
||||
}
|
||||
|
||||
@ -705,7 +636,8 @@ public class Animation {
|
||||
frameVertices[frameIndex] = vertices;
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> firedEvents, float alpha) {
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> firedEvents, float alpha,
|
||||
boolean setupPose) {
|
||||
Slot slot = skeleton.slots.get(slotIndex);
|
||||
Attachment slotAttachment = slot.attachment;
|
||||
if (!(slotAttachment instanceof VertexAttachment) || !((VertexAttachment)slotAttachment).applyDeform(attachment)) return;
|
||||
@ -751,6 +683,122 @@ public class Animation {
|
||||
}
|
||||
}
|
||||
|
||||
static public class EventTimeline implements Timeline {
|
||||
private final float[] frames; // time, ...
|
||||
private final Event[] events;
|
||||
|
||||
public EventTimeline (int frameCount) {
|
||||
frames = new float[frameCount];
|
||||
events = new Event[frameCount];
|
||||
}
|
||||
|
||||
public int getId () {
|
||||
return TimelineType.event.ordinal() << 24;
|
||||
}
|
||||
|
||||
public int getFrameCount () {
|
||||
return frames.length;
|
||||
}
|
||||
|
||||
public float[] getFrames () {
|
||||
return frames;
|
||||
}
|
||||
|
||||
public Event[] getEvents () {
|
||||
return events;
|
||||
}
|
||||
|
||||
/** Sets the time of the specified keyframe. */
|
||||
public void setFrame (int frameIndex, Event event) {
|
||||
frames[frameIndex] = event.time;
|
||||
events[frameIndex] = event;
|
||||
}
|
||||
|
||||
/** Fires events for frames > lastTime and <= time. */
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> firedEvents, float alpha,
|
||||
boolean setupPose) {
|
||||
if (firedEvents == null) return;
|
||||
float[] frames = this.frames;
|
||||
int frameCount = frames.length;
|
||||
|
||||
if (lastTime > time) { // Fire events after last time for looped animations.
|
||||
apply(skeleton, lastTime, Integer.MAX_VALUE, firedEvents, alpha, setupPose);
|
||||
lastTime = -1f;
|
||||
} else if (lastTime >= frames[frameCount - 1]) // Last time is after last frame.
|
||||
return;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
int frame;
|
||||
if (lastTime < frames[0])
|
||||
frame = 0;
|
||||
else {
|
||||
frame = binarySearch(frames, lastTime);
|
||||
float frameTime = frames[frame];
|
||||
while (frame > 0) { // Fire multiple events with the same frame.
|
||||
if (frames[frame - 1] != frameTime) break;
|
||||
frame--;
|
||||
}
|
||||
}
|
||||
for (; frame < frameCount && time >= frames[frame]; frame++)
|
||||
firedEvents.add(events[frame]);
|
||||
}
|
||||
}
|
||||
|
||||
static public class DrawOrderTimeline implements Timeline {
|
||||
private final float[] frames; // time, ...
|
||||
private final int[][] drawOrders;
|
||||
|
||||
public DrawOrderTimeline (int frameCount) {
|
||||
frames = new float[frameCount];
|
||||
drawOrders = new int[frameCount][];
|
||||
}
|
||||
|
||||
public int getId () {
|
||||
return TimelineType.drawOrder.ordinal() << 24;
|
||||
}
|
||||
|
||||
public int getFrameCount () {
|
||||
return frames.length;
|
||||
}
|
||||
|
||||
public float[] getFrames () {
|
||||
return frames;
|
||||
}
|
||||
|
||||
public int[][] getDrawOrders () {
|
||||
return drawOrders;
|
||||
}
|
||||
|
||||
/** Sets the time of the specified keyframe.
|
||||
* @param drawOrder May be null to use bind pose draw order. */
|
||||
public void setFrame (int frameIndex, float time, int[] drawOrder) {
|
||||
frames[frameIndex] = time;
|
||||
drawOrders[frameIndex] = drawOrder;
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> firedEvents, float alpha,
|
||||
boolean setupPose) {
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
int frame;
|
||||
if (time >= frames[frames.length - 1]) // Time is after last frame.
|
||||
frame = frames.length - 1;
|
||||
else
|
||||
frame = binarySearch(frames, time) - 1;
|
||||
|
||||
Array<Slot> drawOrder = skeleton.drawOrder;
|
||||
Array<Slot> slots = skeleton.slots;
|
||||
int[] drawOrderToSetupIndex = drawOrders[frame];
|
||||
if (drawOrderToSetupIndex == null)
|
||||
System.arraycopy(slots.items, 0, drawOrder.items, 0, slots.size);
|
||||
else {
|
||||
for (int i = 0, n = drawOrderToSetupIndex.length; i < n; i++)
|
||||
drawOrder.set(i, slots.get(drawOrderToSetupIndex[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static public class IkConstraintTimeline extends CurveTimeline {
|
||||
static public final int ENTRIES = 3;
|
||||
static private final int PREV_TIME = -3, PREV_MIX = -2, PREV_BEND_DIRECTION = -1;
|
||||
@ -764,6 +812,10 @@ public class Animation {
|
||||
frames = new float[frameCount * ENTRIES];
|
||||
}
|
||||
|
||||
public int getId () {
|
||||
return (TimelineType.ikConstraint.ordinal() << 24) + ikConstraintIndex;
|
||||
}
|
||||
|
||||
public void setIkConstraintIndex (int index) {
|
||||
if (index < 0) throw new IllegalArgumentException("index must be >= 0.");
|
||||
this.ikConstraintIndex = index;
|
||||
@ -785,7 +837,7 @@ public class Animation {
|
||||
frames[frameIndex + BEND_DIRECTION] = bendDirection;
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha) {
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha, boolean setupPose) {
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
@ -821,6 +873,10 @@ public class Animation {
|
||||
frames = new float[frameCount * ENTRIES];
|
||||
}
|
||||
|
||||
public int getId () {
|
||||
return (TimelineType.transformConstraint.ordinal() << 24) + transformConstraintIndex;
|
||||
}
|
||||
|
||||
public void setTransformConstraintIndex (int index) {
|
||||
if (index < 0) throw new IllegalArgumentException("index must be >= 0.");
|
||||
this.transformConstraintIndex = index;
|
||||
@ -844,7 +900,7 @@ public class Animation {
|
||||
frames[frameIndex + SHEAR] = shearMix;
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha) {
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha, boolean setupPose) {
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
@ -890,6 +946,10 @@ public class Animation {
|
||||
frames = new float[frameCount * ENTRIES];
|
||||
}
|
||||
|
||||
public int getId () {
|
||||
return (TimelineType.pathConstraintPosition.ordinal() << 24) + pathConstraintIndex;
|
||||
}
|
||||
|
||||
public void setPathConstraintIndex (int index) {
|
||||
if (index < 0) throw new IllegalArgumentException("index must be >= 0.");
|
||||
this.pathConstraintIndex = index;
|
||||
@ -910,7 +970,7 @@ public class Animation {
|
||||
frames[frameIndex + VALUE] = value;
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha) {
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha, boolean setupPose) {
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
@ -937,7 +997,11 @@ public class Animation {
|
||||
super(frameCount);
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha) {
|
||||
public int getId () {
|
||||
return (TimelineType.pathConstraintSpacing.ordinal() << 24) + pathConstraintIndex;
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha, boolean setupPose) {
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
@ -973,6 +1037,10 @@ public class Animation {
|
||||
frames = new float[frameCount * ENTRIES];
|
||||
}
|
||||
|
||||
public int getId () {
|
||||
return (TimelineType.pathConstraintMix.ordinal() << 24) + pathConstraintIndex;
|
||||
}
|
||||
|
||||
public void setPathConstraintIndex (int index) {
|
||||
if (index < 0) throw new IllegalArgumentException("index must be >= 0.");
|
||||
this.pathConstraintIndex = index;
|
||||
@ -994,7 +1062,7 @@ public class Animation {
|
||||
frames[frameIndex + TRANSLATE] = translateMix;
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha) {
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha, boolean setupPose) {
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
|
||||
@ -32,7 +32,9 @@
|
||||
package com.esotericsoftware.spine;
|
||||
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.BooleanArray;
|
||||
import com.badlogic.gdx.utils.IntArray;
|
||||
import com.badlogic.gdx.utils.IntSet;
|
||||
import com.badlogic.gdx.utils.Pool;
|
||||
import com.badlogic.gdx.utils.Pool.Poolable;
|
||||
import com.esotericsoftware.spine.Animation.AttachmentTimeline;
|
||||
@ -51,6 +53,7 @@ public class AnimationState {
|
||||
}
|
||||
};
|
||||
private final EventQueue queue = new EventQueue(listeners, trackEntryPool);
|
||||
private final IntSet usage = new IntSet();
|
||||
private float timeScale = 1;
|
||||
|
||||
/** Creates an uninitialized AnimationState. The animation state data must be set before use. */
|
||||
@ -116,12 +119,12 @@ public class AnimationState {
|
||||
if (current == null) continue;
|
||||
if (current.delay > 0) continue;
|
||||
|
||||
float alpha = current.alpha;
|
||||
float mix = current.alpha;
|
||||
if (current.mixingFrom != null) {
|
||||
alpha *= current.mixTime / current.mixDuration;
|
||||
applyMixingFrom(current.mixingFrom, skeleton, alpha);
|
||||
if (alpha >= 1) {
|
||||
alpha = 1;
|
||||
mix *= current.mixTime / current.mixDuration;
|
||||
applyMixingFrom(current.mixingFrom, skeleton, mix);
|
||||
if (mix >= 1) {
|
||||
mix = 1;
|
||||
queue.end(current.mixingFrom);
|
||||
current.mixingFrom = null;
|
||||
}
|
||||
@ -129,8 +132,9 @@ public class AnimationState {
|
||||
|
||||
float animationLast = current.animationLast, animationTime = current.getAnimationTime();
|
||||
Array<Timeline> timelines = current.animation.timelines;
|
||||
BooleanArray setupPose = current.setupPose;
|
||||
for (int ii = 0, n = timelines.size; ii < n; ii++)
|
||||
timelines.get(ii).apply(skeleton, animationLast, animationTime, events, alpha);
|
||||
timelines.get(ii).apply(skeleton, animationLast, animationTime, events, mix, setupPose.get(ii));
|
||||
queueEvents(current, animationTime);
|
||||
current.animationLast = animationTime;
|
||||
current.trackLast = current.trackTime;
|
||||
@ -145,16 +149,25 @@ public class AnimationState {
|
||||
|
||||
float animationLast = entry.animationLast, animationTime = entry.getAnimationTime();
|
||||
Array<Timeline> timelines = entry.animation.timelines;
|
||||
float alpha = entry.alpha;
|
||||
BooleanArray setupPose = entry.setupPose;
|
||||
float alphaFull = entry.alpha, alphaMix = entry.alpha * (1 - mix);
|
||||
if (attachments && drawOrder) {
|
||||
for (int i = 0, n = timelines.size; i < n; i++)
|
||||
timelines.get(i).apply(skeleton, animationLast, animationTime, events, alpha);
|
||||
for (int i = 0, n = timelines.size; i < n; i++) {
|
||||
Timeline timeline = timelines.get(i);
|
||||
if (setupPose.get(i))
|
||||
timeline.apply(skeleton, animationLast, animationTime, events, alphaMix, true);
|
||||
else
|
||||
timeline.apply(skeleton, animationLast, animationTime, events, alphaFull, false);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0, n = timelines.size; i < n; i++) {
|
||||
Timeline timeline = timelines.get(i);
|
||||
if (!attachments && timeline instanceof AttachmentTimeline) continue;
|
||||
if (!drawOrder && timeline instanceof DrawOrderTimeline) continue;
|
||||
timeline.apply(skeleton, animationLast, animationTime, events, alpha);
|
||||
if (setupPose.get(i))
|
||||
timeline.apply(skeleton, animationLast, animationTime, events, alphaMix, true);
|
||||
else
|
||||
timeline.apply(skeleton, animationLast, animationTime, events, alphaFull, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -254,6 +267,58 @@ public class AnimationState {
|
||||
}
|
||||
|
||||
queue.drain();
|
||||
|
||||
updateSetupPose();
|
||||
}
|
||||
|
||||
private void updateSetupPose () {
|
||||
usage.clear();
|
||||
int i = 0, n = tracks.size;
|
||||
for (; i < n; i++) {
|
||||
TrackEntry entry = tracks.get(i);
|
||||
if (entry == null) continue;
|
||||
if (entry.mixingFrom != null) {
|
||||
updateFirstSetupPose(entry.mixingFrom);
|
||||
updateSetupPose(entry);
|
||||
} else
|
||||
updateFirstSetupPose(entry);
|
||||
break;
|
||||
}
|
||||
for (i++; i < n; i++) {
|
||||
TrackEntry entry = tracks.get(i);
|
||||
if (entry == null) continue;
|
||||
if (entry.mixingFrom != null) updateSetupPose(entry.mixingFrom);
|
||||
updateSetupPose(entry);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateFirstSetupPose (TrackEntry entry) {
|
||||
IntSet usage = this.usage;
|
||||
BooleanArray setupPose = entry.setupPose;
|
||||
setupPose.clear();
|
||||
Array<Timeline> timelines = entry.animation.timelines;
|
||||
for (int ii = 0, nn = timelines.size; ii < nn; ii++) {
|
||||
Timeline timeline = timelines.get(ii);
|
||||
usage.add(timeline.getId());
|
||||
setupPose.add(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSetupPose (TrackEntry entry) {
|
||||
IntSet usage = this.usage;
|
||||
BooleanArray setupPose = entry.setupPose;
|
||||
setupPose.clear();
|
||||
Array<Timeline> timelines = entry.animation.timelines;
|
||||
for (int ii = 0, nn = timelines.size; ii < nn; ii++) {
|
||||
Timeline timeline = timelines.get(ii);
|
||||
int id = timeline.getId();
|
||||
if (usage.contains(id))
|
||||
setupPose.add(false);
|
||||
else {
|
||||
usage.add(id);
|
||||
setupPose.add(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @see #setAnimation(int, Animation, boolean) */
|
||||
@ -426,12 +491,14 @@ public class AnimationState {
|
||||
float delay, trackTime, trackLast, trackEnd, animationStart, animationEnd, animationLast, timeScale;
|
||||
float alpha;
|
||||
float mixTime, mixDuration;
|
||||
final BooleanArray setupPose = new BooleanArray();
|
||||
|
||||
public void reset () {
|
||||
next = null;
|
||||
mixingFrom = null;
|
||||
animation = null;
|
||||
listener = null;
|
||||
setupPose.clear();
|
||||
}
|
||||
|
||||
public int getTrackIndex () {
|
||||
|
||||
@ -264,8 +264,7 @@ public class SkeletonViewer extends ApplicationAdapter {
|
||||
ShapeRenderer shapes = debugRenderer.getShapeRenderer();
|
||||
TrackEntry entry = state.getCurrent(0);
|
||||
if (entry != null) {
|
||||
float percent = entry.getTrackTime() / entry.getTrackEnd();
|
||||
if (entry.getLoop()) percent %= 1;
|
||||
float percent = entry.getAnimationTime() / entry.getAnimationEnd();
|
||||
float x = ui.window.getRight() + (Gdx.graphics.getWidth() - ui.window.getRight()) * percent;
|
||||
shapes.setColor(Color.CYAN);
|
||||
shapes.begin(ShapeType.Line);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user