mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-26 22:49:01 +08:00
Merge branch 'spine-3.5' into spine-as3-3.5
This commit is contained in:
commit
552db5823f
@ -136,8 +136,8 @@ public class Animation {
|
|||||||
static public interface Timeline {
|
static public interface Timeline {
|
||||||
/** Sets the value(s) for the specified time.
|
/** Sets the value(s) for the specified time.
|
||||||
* @param events May be null to not collect fired events.
|
* @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. Passing true
|
* @param setupPose True when the timeline is mixed with the setup pose, false when it is mixed with the current pose.
|
||||||
* when alpha is 1 is slightly more efficient.
|
* Passing true when alpha is 1 is slightly more efficient.
|
||||||
* @param mixingOut True when mixing over time toward the setup or current pose, false when mixing toward the keyed pose.
|
* @param mixingOut True when mixing over time toward the setup or current pose, false when mixing toward the keyed pose.
|
||||||
* Irrelevant when alpha is 1. */
|
* Irrelevant when alpha is 1. */
|
||||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha, boolean setupPose,
|
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha, boolean setupPose,
|
||||||
|
|||||||
@ -52,7 +52,7 @@ public class AnimationState {
|
|||||||
static private final Animation emptyAnimation = new Animation("<empty>", new Array(0), 0);
|
static private final Animation emptyAnimation = new Animation("<empty>", new Array(0), 0);
|
||||||
|
|
||||||
private AnimationStateData data;
|
private AnimationStateData data;
|
||||||
private final Array<TrackEntry> tracks = new Array();
|
final Array<TrackEntry> tracks = new Array();
|
||||||
private final Array<Event> events = new Array();
|
private final Array<Event> events = new Array();
|
||||||
final Array<AnimationStateListener> listeners = new Array();
|
final Array<AnimationStateListener> listeners = new Array();
|
||||||
private final EventQueue queue = new EventQueue();
|
private final EventQueue queue = new EventQueue();
|
||||||
@ -60,9 +60,6 @@ public class AnimationState {
|
|||||||
boolean animationsChanged;
|
boolean animationsChanged;
|
||||||
private float timeScale = 1;
|
private float timeScale = 1;
|
||||||
|
|
||||||
StringBuilder last = new StringBuilder();
|
|
||||||
StringBuilder log = new StringBuilder();
|
|
||||||
|
|
||||||
final Pool<TrackEntry> trackEntryPool = new Pool() {
|
final Pool<TrackEntry> trackEntryPool = new Pool() {
|
||||||
protected Object newObject () {
|
protected Object newObject () {
|
||||||
return new TrackEntry();
|
return new TrackEntry();
|
||||||
@ -112,9 +109,9 @@ public class AnimationState {
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
updateMixingFrom(current, delta);
|
updateMixingFrom(current, delta, true);
|
||||||
} else {
|
} else {
|
||||||
updateMixingFrom(current, delta);
|
updateMixingFrom(current, delta, true);
|
||||||
// Clear the track when there is no next entry, the track end time is reached, and there is no mixingFrom.
|
// Clear the track when there is no next entry, the track end time is reached, and there is no mixingFrom.
|
||||||
if (current.trackLast >= current.trackEnd && current.mixingFrom == null) {
|
if (current.trackLast >= current.trackEnd && current.mixingFrom == null) {
|
||||||
tracks.set(i, null);
|
tracks.set(i, null);
|
||||||
@ -130,11 +127,11 @@ public class AnimationState {
|
|||||||
queue.drain();
|
queue.drain();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateMixingFrom (TrackEntry entry, float delta) {
|
private void updateMixingFrom (TrackEntry entry, float delta, boolean canEnd) {
|
||||||
TrackEntry from = entry.mixingFrom;
|
TrackEntry from = entry.mixingFrom;
|
||||||
if (from == null) return;
|
if (from == null) return;
|
||||||
|
|
||||||
if (entry.mixTime >= entry.mixDuration && entry.mixTime > 0) {
|
if (canEnd && entry.mixTime >= entry.mixDuration && entry.mixTime > 0) {
|
||||||
queue.end(from);
|
queue.end(from);
|
||||||
TrackEntry newFrom = from.mixingFrom;
|
TrackEntry newFrom = from.mixingFrom;
|
||||||
entry.mixingFrom = newFrom;
|
entry.mixingFrom = newFrom;
|
||||||
@ -150,7 +147,7 @@ public class AnimationState {
|
|||||||
from.trackTime += mixingFromDelta;
|
from.trackTime += mixingFromDelta;
|
||||||
entry.mixTime += mixingFromDelta;
|
entry.mixTime += mixingFromDelta;
|
||||||
|
|
||||||
updateMixingFrom(from, delta);
|
updateMixingFrom(from, delta, canEnd && from.alpha == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Poses the skeleton using the track entry animations. There are no side effects other than invoking listeners, so the
|
/** Poses the skeleton using the track entry animations. There are no side effects other than invoking listeners, so the
|
||||||
@ -161,7 +158,7 @@ public class AnimationState {
|
|||||||
|
|
||||||
Array<Event> events = this.events;
|
Array<Event> events = this.events;
|
||||||
|
|
||||||
for (int i = 0; i < tracks.size; i++) {
|
for (int i = 0, n = tracks.size; i < n; i++) {
|
||||||
TrackEntry current = tracks.get(i);
|
TrackEntry current = tracks.get(i);
|
||||||
if (current == null || current.delay > 0) continue;
|
if (current == null || current.delay > 0) continue;
|
||||||
|
|
||||||
@ -171,24 +168,24 @@ public class AnimationState {
|
|||||||
|
|
||||||
// Apply current entry.
|
// Apply current entry.
|
||||||
float animationLast = current.animationLast, animationTime = current.getAnimationTime();
|
float animationLast = current.animationLast, animationTime = current.getAnimationTime();
|
||||||
Array<Timeline> timelines = current.animation.timelines;
|
int timelineCount = current.animation.timelines.size;
|
||||||
log("apply current: " + current + ", mix: " + mix + " * " + current.alpha);
|
Object[] timelines = current.animation.timelines.items;
|
||||||
if (mix == 1) {
|
if (mix == 1) {
|
||||||
for (int ii = 0, n = timelines.size; ii < n; ii++)
|
for (int ii = 0; ii < timelineCount; ii++)
|
||||||
timelines.get(ii).apply(skeleton, animationLast, animationTime, events, 1, false, false);
|
((Timeline)timelines[ii]).apply(skeleton, animationLast, animationTime, events, 1, true, false);
|
||||||
} else {
|
} else {
|
||||||
boolean firstFrame = current.timelinesRotation.size == 0;
|
boolean firstFrame = current.timelinesRotation.size == 0;
|
||||||
if (firstFrame) current.timelinesRotation.setSize(timelines.size << 1);
|
if (firstFrame) current.timelinesRotation.setSize(timelineCount << 1);
|
||||||
float[] timelinesRotation = current.timelinesRotation.items;
|
float[] timelinesRotation = current.timelinesRotation.items;
|
||||||
|
|
||||||
boolean[] timelinesFirst = current.timelinesFirst.items;
|
boolean[] timelinesFirst = current.timelinesFirst.items;
|
||||||
for (int ii = 0, n = timelines.size; ii < n; ii++) {
|
for (int ii = 0; ii < timelineCount; ii++) {
|
||||||
Timeline timeline = timelines.get(ii);
|
Timeline timeline = (Timeline)timelines[ii];
|
||||||
if (timeline instanceof RotateTimeline) {
|
if (timeline instanceof RotateTimeline) {
|
||||||
applyRotateTimeline((RotateTimeline)timeline, skeleton, animationLast, animationTime, events, mix,
|
applyRotateTimeline(timeline, skeleton, animationTime, mix, timelinesFirst[ii], timelinesRotation, ii << 1,
|
||||||
timelinesFirst[ii], false, timelinesRotation, ii << 1, firstFrame);
|
firstFrame);
|
||||||
} else {
|
} else
|
||||||
timeline.apply(skeleton, animationLast, animationTime, events, mix, timelinesFirst[ii], false);
|
timeline.apply(skeleton, animationLast, animationTime, events, mix, timelinesFirst[ii], false);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
queueEvents(current, animationTime);
|
queueEvents(current, animationTime);
|
||||||
@ -197,62 +194,44 @@ public class AnimationState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
queue.drain();
|
queue.drain();
|
||||||
|
|
||||||
if (!log.toString().equals(last.toString())) {
|
|
||||||
System.out.println(log);
|
|
||||||
last.setLength(0);
|
|
||||||
last.append(log);
|
|
||||||
}
|
|
||||||
log.setLength(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void log (String m) {
|
|
||||||
log.append(m);
|
|
||||||
log.append('\n');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private float applyMixingFrom (TrackEntry entry, Skeleton skeleton, float alpha) {
|
private float applyMixingFrom (TrackEntry entry, Skeleton skeleton, float alpha) {
|
||||||
|
TrackEntry from = entry.mixingFrom;
|
||||||
|
if (from.mixingFrom != null) applyMixingFrom(from, skeleton, alpha);
|
||||||
|
|
||||||
float mix;
|
float mix;
|
||||||
if (entry.mixDuration == 0) // Single frame mix to undo mixingFrom changes.
|
if (entry.mixDuration == 0) // Single frame mix to undo mixingFrom changes.
|
||||||
mix = 1;
|
mix = 1;
|
||||||
else {
|
else {
|
||||||
mix = alpha * entry.mixTime / entry.mixDuration;
|
mix = entry.mixTime / entry.mixDuration;
|
||||||
if (mix > 1) mix = 1;
|
if (mix > 1) mix = 1;
|
||||||
|
mix *= alpha;
|
||||||
}
|
}
|
||||||
|
|
||||||
TrackEntry from = entry.mixingFrom;
|
|
||||||
if (from.mixingFrom != null) applyMixingFrom(from, skeleton, alpha);
|
|
||||||
|
|
||||||
Array<Event> events = mix < from.eventThreshold ? this.events : null;
|
Array<Event> events = mix < from.eventThreshold ? this.events : null;
|
||||||
boolean attachments = mix < from.attachmentThreshold, drawOrder = mix < from.drawOrderThreshold;
|
boolean attachments = mix < from.attachmentThreshold, drawOrder = mix < from.drawOrderThreshold;
|
||||||
|
|
||||||
float animationLast = from.animationLast, animationTime = from.getAnimationTime();
|
float animationLast = from.animationLast, animationTime = from.getAnimationTime();
|
||||||
Array<Timeline> timelines = from.animation.timelines;
|
alpha = from.alpha * (1 - mix);
|
||||||
int timelineCount = timelines.size;
|
int timelineCount = from.animation.timelines.size;
|
||||||
boolean[] timelinesFirst = from.timelinesFirst.items, timelinesLast = from.timelinesLast.items;
|
Object[] timelines = from.animation.timelines.items;
|
||||||
float alphaFull = from.alpha, alphaMix = alphaFull * (1 - mix);
|
boolean[] timelinesFirst = from.timelinesFirst.items;
|
||||||
|
|
||||||
boolean firstFrame = from.timelinesRotation.size == 0;
|
boolean firstFrame = from.timelinesRotation.size == 0;
|
||||||
if (firstFrame) from.timelinesRotation.setSize(timelineCount << 1);
|
if (firstFrame) from.timelinesRotation.setSize(timelineCount << 1);
|
||||||
float[] timelinesRotation = from.timelinesRotation.items;
|
float[] timelinesRotation = from.timelinesRotation.items;
|
||||||
|
|
||||||
log("applyMixingFrom: " + entry.mixingFrom + " -> " + entry + ", mix: " + entry.mixTime / entry.mixDuration);
|
|
||||||
if (timelineCount == 0) log("apply from: " + from + " " + alphaFull + " * " + entry.alpha);
|
|
||||||
|
|
||||||
for (int i = 0; i < timelineCount; i++) {
|
for (int i = 0; i < timelineCount; i++) {
|
||||||
Timeline timeline = timelines.get(i);
|
Timeline timeline = (Timeline)timelines[i];
|
||||||
boolean setupPose = timelinesFirst[i];
|
boolean setupPose = timelinesFirst[i];
|
||||||
float a = timelinesLast[i] ? alphaMix : alphaFull;
|
if (timeline instanceof RotateTimeline)
|
||||||
log("apply from: " + from + " " + a + " * " + entry.alpha);
|
applyRotateTimeline(timeline, skeleton, animationTime, alpha, setupPose, timelinesRotation, i << 1, firstFrame);
|
||||||
if (timeline instanceof RotateTimeline) {
|
else {
|
||||||
applyRotateTimeline((RotateTimeline)timeline, skeleton, animationLast, animationTime, events, a, setupPose, setupPose,
|
|
||||||
timelinesRotation, i << 1, firstFrame);
|
|
||||||
} else {
|
|
||||||
if (!setupPose) {
|
if (!setupPose) {
|
||||||
if (!attachments && timeline instanceof AttachmentTimeline) continue;
|
if (!attachments && timeline instanceof AttachmentTimeline) continue;
|
||||||
if (!drawOrder && timeline instanceof DrawOrderTimeline) continue;
|
if (!drawOrder && timeline instanceof DrawOrderTimeline) continue;
|
||||||
}
|
}
|
||||||
timeline.apply(skeleton, animationLast, animationTime, events, a, setupPose, setupPose);
|
timeline.apply(skeleton, animationLast, animationTime, events, alpha, setupPose, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,18 +242,18 @@ public class AnimationState {
|
|||||||
return mix;
|
return mix;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @param events May be null. */
|
private void applyRotateTimeline (Timeline timeline, Skeleton skeleton, float time, float alpha, boolean setupPose,
|
||||||
private void applyRotateTimeline (RotateTimeline timeline, Skeleton skeleton, float lastTime, float time, Array<Event> events,
|
float[] timelinesRotation, int i, boolean firstFrame) {
|
||||||
float alpha, boolean setupPose, boolean mixingOut, float[] timelinesRotation, int i, boolean firstFrame) {
|
|
||||||
if (alpha == 1) {
|
if (alpha == 1) {
|
||||||
timeline.apply(skeleton, lastTime, time, events, 1, setupPose, setupPose);
|
timeline.apply(skeleton, 0, time, null, 1, setupPose, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
float[] frames = timeline.frames;
|
RotateTimeline rotateTimeline = (RotateTimeline)timeline;
|
||||||
|
float[] frames = rotateTimeline.frames;
|
||||||
if (time < frames[0]) return; // Time is before first frame.
|
if (time < frames[0]) return; // Time is before first frame.
|
||||||
|
|
||||||
Bone bone = skeleton.bones.get(timeline.boneIndex);
|
Bone bone = skeleton.bones.get(rotateTimeline.boneIndex);
|
||||||
|
|
||||||
float r2;
|
float r2;
|
||||||
if (time >= frames[frames.length - ENTRIES]) // Time is after last frame.
|
if (time >= frames[frames.length - ENTRIES]) // Time is after last frame.
|
||||||
@ -284,7 +263,7 @@ public class AnimationState {
|
|||||||
int frame = Animation.binarySearch(frames, time, ENTRIES);
|
int frame = Animation.binarySearch(frames, time, ENTRIES);
|
||||||
float prevRotation = frames[frame + PREV_ROTATION];
|
float prevRotation = frames[frame + PREV_ROTATION];
|
||||||
float frameTime = frames[frame];
|
float frameTime = frames[frame];
|
||||||
float percent = timeline.getCurvePercent((frame >> 1) - 1,
|
float percent = rotateTimeline.getCurvePercent((frame >> 1) - 1,
|
||||||
1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
|
1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
|
||||||
|
|
||||||
r2 = frames[frame + ROTATION] - prevRotation;
|
r2 = frames[frame + ROTATION] - prevRotation;
|
||||||
@ -293,7 +272,7 @@ public class AnimationState {
|
|||||||
r2 -= (16384 - (int)(16384.499999999996 - r2 / 360)) * 360;
|
r2 -= (16384 - (int)(16384.499999999996 - r2 / 360)) * 360;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mix between two rotations using the direction of the shortest route on the first frame while detecting crosses.
|
// Mix between rotations using the direction of the shortest route on the first frame while detecting crosses.
|
||||||
float r1 = setupPose ? bone.data.rotation : bone.rotation;
|
float r1 = setupPose ? bone.data.rotation : bone.rotation;
|
||||||
float total, diff = r2 - r1;
|
float total, diff = r2 - r1;
|
||||||
if (diff == 0) {
|
if (diff == 0) {
|
||||||
@ -319,7 +298,7 @@ public class AnimationState {
|
|||||||
if (Math.abs(lastTotal) > 180) lastTotal += 360 * Math.signum(lastTotal);
|
if (Math.abs(lastTotal) > 180) lastTotal += 360 * Math.signum(lastTotal);
|
||||||
dir = current;
|
dir = current;
|
||||||
}
|
}
|
||||||
total = diff + lastTotal - lastTotal % 360; // Keep loops part of lastTotal.
|
total = diff + lastTotal - lastTotal % 360; // Store loops as part of lastTotal.
|
||||||
if (dir != current) total += 360 * Math.signum(lastTotal);
|
if (dir != current) total += 360 * Math.signum(lastTotal);
|
||||||
timelinesRotation[i] = total;
|
timelinesRotation[i] = total;
|
||||||
}
|
}
|
||||||
@ -405,19 +384,12 @@ public class AnimationState {
|
|||||||
if (from != null) {
|
if (from != null) {
|
||||||
queue.interrupt(from);
|
queue.interrupt(from);
|
||||||
current.mixingFrom = from;
|
current.mixingFrom = from;
|
||||||
// entry.mixTime = Math.max(0, entry.mixDuration - current.trackTime);
|
|
||||||
// log("setCurrent mixTime: " + entry.mixDuration + " - " + current.trackTime + " = " + entry.mixTime);
|
|
||||||
current.mixTime = 0;
|
current.mixTime = 0;
|
||||||
|
|
||||||
from.timelinesRotation.clear(); // BOZO - Needed? Recursive?
|
from.timelinesRotation.clear();
|
||||||
|
|
||||||
// float alpha = 1;
|
// If not completely mixed in, set alpha so mixing out happens from current mix to zero.
|
||||||
float duration = from.animationEnd - from.animationStart;
|
if (from.mixingFrom != null) from.alpha *= Math.min(from.mixTime / from.mixDuration, 1);
|
||||||
if (duration > 0) from.alpha *= (from.getAnimationTime() - from.animationStart) / duration;
|
|
||||||
// do {
|
|
||||||
// from.alpha *= alpha;
|
|
||||||
// from = from.mixingFrom;
|
|
||||||
// } while (from != null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
queue.start(current);
|
queue.start(current);
|
||||||
@ -592,32 +564,6 @@ public class AnimationState {
|
|||||||
TrackEntry entry = tracks.get(i);
|
TrackEntry entry = tracks.get(i);
|
||||||
if (entry != null) checkTimelinesFirst(entry);
|
if (entry != null) checkTimelinesFirst(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute timelinesLast from highest to lowest track entries that have mixingFrom.
|
|
||||||
propertyIDs.clear();
|
|
||||||
int lowestMixingFrom = n;
|
|
||||||
for (i = 0; i < n; i++) { // Find lowest with a mixingFrom entry.
|
|
||||||
TrackEntry entry = tracks.get(i);
|
|
||||||
if (entry == null) continue;
|
|
||||||
if (entry.mixingFrom != null) {
|
|
||||||
lowestMixingFrom = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (i = n - 1; i >= lowestMixingFrom; i--) {
|
|
||||||
TrackEntry entry = tracks.get(i);
|
|
||||||
if (entry == null) continue;
|
|
||||||
|
|
||||||
Array<Timeline> timelines = entry.animation.timelines;
|
|
||||||
for (int ii = 0, nn = timelines.size; ii < nn; ii++)
|
|
||||||
propertyIDs.add(timelines.get(ii).getPropertyId());
|
|
||||||
|
|
||||||
entry = entry.mixingFrom;
|
|
||||||
while (entry != null) {
|
|
||||||
checkTimelinesUsage(entry, entry.timelinesLast);
|
|
||||||
entry = entry.mixingFrom;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** From last to first mixingFrom entries, sets timelinesFirst to true on last, calls checkTimelineUsage on rest. */
|
/** From last to first mixingFrom entries, sets timelinesFirst to true on last, calls checkTimelineUsage on rest. */
|
||||||
@ -727,7 +673,7 @@ public class AnimationState {
|
|||||||
float animationStart, animationEnd, animationLast, nextAnimationLast;
|
float animationStart, animationEnd, animationLast, nextAnimationLast;
|
||||||
float delay, trackTime, trackLast, nextTrackLast, trackEnd, timeScale;
|
float delay, trackTime, trackLast, nextTrackLast, trackEnd, timeScale;
|
||||||
float alpha, mixTime, mixDuration, mixAlpha;
|
float alpha, mixTime, mixDuration, mixAlpha;
|
||||||
final BooleanArray timelinesFirst = new BooleanArray(), timelinesLast = new BooleanArray();
|
final BooleanArray timelinesFirst = new BooleanArray();
|
||||||
final FloatArray timelinesRotation = new FloatArray();
|
final FloatArray timelinesRotation = new FloatArray();
|
||||||
|
|
||||||
public void reset () {
|
public void reset () {
|
||||||
@ -736,7 +682,6 @@ public class AnimationState {
|
|||||||
animation = null;
|
animation = null;
|
||||||
listener = null;
|
listener = null;
|
||||||
timelinesFirst.clear();
|
timelinesFirst.clear();
|
||||||
timelinesLast.clear();
|
|
||||||
timelinesRotation.clear();
|
timelinesRotation.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user