mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-21 01:36:02 +08:00
Fixed dipping when mixing more than 2 animations that key the same property.
Fixes `a->b,a` where a and b key the same property.
This commit is contained in:
parent
b882fb8c51
commit
02660ddd0a
@ -57,6 +57,7 @@ public class AnimationState {
|
|||||||
final Array<AnimationStateListener> listeners = new Array();
|
final Array<AnimationStateListener> listeners = new Array();
|
||||||
private final EventQueue queue = new EventQueue();
|
private final EventQueue queue = new EventQueue();
|
||||||
private final IntSet propertyIDs = new IntSet();
|
private final IntSet propertyIDs = new IntSet();
|
||||||
|
private final Array<TrackEntry> mixingTo = new Array();
|
||||||
boolean animationsChanged;
|
boolean animationsChanged;
|
||||||
private float timeScale = 1;
|
private float timeScale = 1;
|
||||||
|
|
||||||
@ -161,7 +162,7 @@ public class AnimationState {
|
|||||||
// Apply mixing from entries first.
|
// Apply mixing from entries first.
|
||||||
float mix = current.alpha;
|
float mix = current.alpha;
|
||||||
if (current.mixingFrom != null)
|
if (current.mixingFrom != null)
|
||||||
mix *= applyMixingFrom(current, skeleton, 0);
|
mix *= applyMixingFrom(current, skeleton);
|
||||||
else if (current.trackTime >= current.trackEnd && current.next == null) //
|
else if (current.trackTime >= current.trackEnd && current.next == null) //
|
||||||
mix = 0; // Set to setup pose the last time the entry will be applied.
|
mix = 0; // Set to setup pose the last time the entry will be applied.
|
||||||
|
|
||||||
@ -173,11 +174,12 @@ public class AnimationState {
|
|||||||
for (int ii = 0; ii < timelineCount; ii++)
|
for (int ii = 0; ii < timelineCount; ii++)
|
||||||
((Timeline)timelines[ii]).apply(skeleton, animationLast, animationTime, events, 1, true, false);
|
((Timeline)timelines[ii]).apply(skeleton, animationLast, animationTime, events, 1, true, false);
|
||||||
} else {
|
} else {
|
||||||
|
int[] timelineData = current.timelineData.items;
|
||||||
|
|
||||||
boolean firstFrame = current.timelinesRotation.size == 0;
|
boolean firstFrame = current.timelinesRotation.size == 0;
|
||||||
if (firstFrame) current.timelinesRotation.setSize(timelineCount << 1);
|
if (firstFrame) current.timelinesRotation.setSize(timelineCount << 1);
|
||||||
float[] timelinesRotation = current.timelinesRotation.items;
|
float[] timelinesRotation = current.timelinesRotation.items;
|
||||||
|
|
||||||
int[] timelineData = current.timelineData.items;
|
|
||||||
for (int ii = 0; ii < timelineCount; ii++) {
|
for (int ii = 0; ii < timelineCount; ii++) {
|
||||||
Timeline timeline = (Timeline)timelines[ii];
|
Timeline timeline = (Timeline)timelines[ii];
|
||||||
if (timeline instanceof RotateTimeline) {
|
if (timeline instanceof RotateTimeline) {
|
||||||
@ -196,7 +198,10 @@ public class AnimationState {
|
|||||||
queue.drain();
|
queue.drain();
|
||||||
}
|
}
|
||||||
|
|
||||||
private float applyMixingFrom (TrackEntry to, Skeleton skeleton, float parentMix) {
|
private float applyMixingFrom (TrackEntry to, Skeleton skeleton) {
|
||||||
|
TrackEntry from = to.mixingFrom;
|
||||||
|
if (from.mixingFrom != null) applyMixingFrom(from, skeleton);
|
||||||
|
|
||||||
float mix;
|
float mix;
|
||||||
if (to.mixDuration == 0) // Single frame mix to undo mixingFrom changes.
|
if (to.mixDuration == 0) // Single frame mix to undo mixingFrom changes.
|
||||||
mix = 1;
|
mix = 1;
|
||||||
@ -205,16 +210,14 @@ public class AnimationState {
|
|||||||
if (mix > 1) mix = 1;
|
if (mix > 1) mix = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
TrackEntry from = to.mixingFrom;
|
|
||||||
if (from.mixingFrom != null) applyMixingFrom(from, skeleton, mix);
|
|
||||||
|
|
||||||
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();
|
||||||
int timelineCount = from.animation.timelines.size;
|
int timelineCount = from.animation.timelines.size;
|
||||||
Object[] timelines = from.animation.timelines.items;
|
Object[] timelines = from.animation.timelines.items;
|
||||||
int[] timelineData = from.timelineData.items;
|
int[] timelineData = from.timelineData.items;
|
||||||
float alphaMix = from.alpha * to.mixAlpha * (1 - mix), alphaDip = from.alpha * to.mixAlpha * (1 - parentMix);
|
Object[] timelineDipMix = from.timelineDipMix.items;
|
||||||
|
float alphaDip = from.alpha * to.mixAlpha, alphaMix = alphaDip * (1 - mix);
|
||||||
|
|
||||||
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);
|
||||||
@ -223,8 +226,24 @@ public class AnimationState {
|
|||||||
for (int i = 0; i < timelineCount; i++) {
|
for (int i = 0; i < timelineCount; i++) {
|
||||||
Timeline timeline = (Timeline)timelines[i];
|
Timeline timeline = (Timeline)timelines[i];
|
||||||
int data = timelineData[i];
|
int data = timelineData[i];
|
||||||
boolean first = data > 0;
|
boolean first;
|
||||||
float alpha = data == DIP ? alphaDip : alphaMix;
|
float alpha;
|
||||||
|
switch (data) {
|
||||||
|
case SUBSEQUENT:
|
||||||
|
first = false;
|
||||||
|
alpha = alphaMix;
|
||||||
|
break;
|
||||||
|
case FIRST:
|
||||||
|
first = true;
|
||||||
|
alpha = alphaMix;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
first = true;
|
||||||
|
alpha = alphaDip;
|
||||||
|
TrackEntry dipMix = (TrackEntry)timelineDipMix[i];
|
||||||
|
if (dipMix != null) alpha *= Math.max(0, 1 - dipMix.mixTime / dipMix.mixDuration);
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (timeline instanceof RotateTimeline)
|
if (timeline instanceof RotateTimeline)
|
||||||
applyRotateTimeline(timeline, skeleton, animationTime, alpha, first, timelinesRotation, i << 1, firstFrame);
|
applyRotateTimeline(timeline, skeleton, animationTime, alpha, first, timelinesRotation, i << 1, firstFrame);
|
||||||
else {
|
else {
|
||||||
@ -388,8 +407,7 @@ public class AnimationState {
|
|||||||
if (interrupt) queue.interrupt(from);
|
if (interrupt) queue.interrupt(from);
|
||||||
current.mixingFrom = from;
|
current.mixingFrom = from;
|
||||||
current.mixTime = 0;
|
current.mixTime = 0;
|
||||||
current.mixAlpha *= Math.min(from.mixTime / from.mixDuration, 1); // Store interrupted mix percentage.
|
current.mixAlpha *= Math.min(1, from.mixTime / from.mixDuration); // Store interrupted mix percentage.
|
||||||
|
|
||||||
from.timelinesRotation.clear(); // Reset rotation for mixing out, in case entry was mixed in.
|
from.timelinesRotation.clear(); // Reset rotation for mixing out, in case entry was mixed in.
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -567,14 +585,15 @@ public class AnimationState {
|
|||||||
private void animationsChanged () {
|
private void animationsChanged () {
|
||||||
animationsChanged = false;
|
animationsChanged = false;
|
||||||
|
|
||||||
// Set timelinesData for all entries, from lowest track to highest.
|
|
||||||
IntSet propertyIDs = this.propertyIDs;
|
IntSet propertyIDs = this.propertyIDs;
|
||||||
propertyIDs.clear();
|
propertyIDs.clear();
|
||||||
|
Array<TrackEntry> mixingTo = this.mixingTo;
|
||||||
|
|
||||||
TrackEntry lastEntry = null;
|
TrackEntry lastEntry = null;
|
||||||
for (int i = 0, n = tracks.size; i < n; i++) {
|
for (int i = 0, n = tracks.size; i < n; i++) {
|
||||||
TrackEntry entry = tracks.get(i);
|
TrackEntry entry = tracks.get(i);
|
||||||
if (entry != null) {
|
if (entry != null) {
|
||||||
entry.setTimelineData(lastEntry, propertyIDs);
|
entry.setTimelineData(lastEntry, mixingTo, propertyIDs);
|
||||||
lastEntry = entry;
|
lastEntry = entry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -662,6 +681,7 @@ public class AnimationState {
|
|||||||
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 IntArray timelineData = new IntArray();
|
final IntArray timelineData = new IntArray();
|
||||||
|
final Array<TrackEntry> timelineDipMix = new Array();
|
||||||
final FloatArray timelinesRotation = new FloatArray();
|
final FloatArray timelinesRotation = new FloatArray();
|
||||||
|
|
||||||
public void reset () {
|
public void reset () {
|
||||||
@ -670,21 +690,41 @@ public class AnimationState {
|
|||||||
animation = null;
|
animation = null;
|
||||||
listener = null;
|
listener = null;
|
||||||
timelineData.clear();
|
timelineData.clear();
|
||||||
|
timelineDipMix.clear();
|
||||||
timelinesRotation.clear();
|
timelinesRotation.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
TrackEntry setTimelineData (TrackEntry parent, IntSet propertyIDs) {
|
/** @param to May be null. */
|
||||||
TrackEntry lastEntry = mixingFrom != null ? mixingFrom.setTimelineData(this, propertyIDs) : this;
|
TrackEntry setTimelineData (TrackEntry to, Array<TrackEntry> mixingToArray, IntSet propertyIDs) {
|
||||||
int n = animation.timelines.size;
|
if (to != null) mixingToArray.add(to);
|
||||||
|
TrackEntry lastEntry = mixingFrom != null ? mixingFrom.setTimelineData(this, mixingToArray, propertyIDs) : this;
|
||||||
|
if (to != null) mixingToArray.pop();
|
||||||
|
|
||||||
|
Object[] mixingTo = mixingToArray.items;
|
||||||
|
int mixingToLast = mixingToArray.size - 1;
|
||||||
Object[] timelines = animation.timelines.items;
|
Object[] timelines = animation.timelines.items;
|
||||||
int[] timelineData = this.timelineData.setSize(n << 1);
|
int timelinesCount = animation.timelines.size;
|
||||||
for (int i = 0; i < n; i++) {
|
int[] timelineData = this.timelineData.setSize(timelinesCount);
|
||||||
|
Object[] timelineDipMix = this.timelineDipMix.setSize(timelinesCount);
|
||||||
|
|
||||||
|
outer:
|
||||||
|
for (int i = 0; i < timelinesCount; i++) {
|
||||||
int id = ((Timeline)timelines[i]).getPropertyId();
|
int id = ((Timeline)timelines[i]).getPropertyId();
|
||||||
boolean first = propertyIDs.add(id);
|
if (!propertyIDs.add(id))
|
||||||
if (first && parent != null && parent.hasTimeline(id))
|
timelineData[i] = SUBSEQUENT;
|
||||||
|
else if (to == null || !to.hasTimeline(id))
|
||||||
|
timelineData[i] = FIRST;
|
||||||
|
else {
|
||||||
timelineData[i] = DIP;
|
timelineData[i] = DIP;
|
||||||
else
|
for (int ii = mixingToLast; ii >= 0; ii--) {
|
||||||
timelineData[i] = first ? FIRST : SUBSEQUENT;
|
TrackEntry entry = (TrackEntry)mixingTo[ii];
|
||||||
|
if (!entry.hasTimeline(id)) {
|
||||||
|
timelineDipMix[i] = entry;
|
||||||
|
continue outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timelineDipMix[i] = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return lastEntry;
|
return lastEntry;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user