[libgdx] Fixed mixing out an attachment timeline when a subsequent entry sets the same attachment.

closes #1292
This commit is contained in:
NathanSweet 2019-03-08 20:15:12 +01:00
parent 79123e747d
commit a321aa76d5

View File

@ -43,6 +43,7 @@ import com.badlogic.gdx.utils.Pool;
import com.badlogic.gdx.utils.Pool.Poolable; import com.badlogic.gdx.utils.Pool.Poolable;
import com.esotericsoftware.spine.Animation.AttachmentTimeline; import com.esotericsoftware.spine.Animation.AttachmentTimeline;
import com.esotericsoftware.spine.Animation.DrawOrderTimeline; import com.esotericsoftware.spine.Animation.DrawOrderTimeline;
import com.esotericsoftware.spine.Animation.EventTimeline;
import com.esotericsoftware.spine.Animation.MixBlend; import com.esotericsoftware.spine.Animation.MixBlend;
import com.esotericsoftware.spine.Animation.MixDirection; import com.esotericsoftware.spine.Animation.MixDirection;
import com.esotericsoftware.spine.Animation.RotateTimeline; import com.esotericsoftware.spine.Animation.RotateTimeline;
@ -79,6 +80,12 @@ public class AnimationState {
* (which affects B and C). Without using D to mix out, A would be applied fully until mixing completes, then snap into * (which affects B and C). Without using D to mix out, A would be applied fully until mixing completes, then snap into
* place. */ * place. */
static private final int HOLD_MIX = 3; static private final int HOLD_MIX = 3;
/** 1) An attachment timeline in a subsequent track entry sets the attachment for the same slot as this attachment
* timeline.<br>
* Result: This attachment timeline will not use MixDirection.out, which would otherwise show the setup mode attachment (or
* none if not visible in setup mode). This allows deform timelines to be applied for the subsequent entry to mix from, rather
* than mixing from the setup pose. */
static private final int NOT_LAST = 4;
private AnimationStateData data; private AnimationStateData data;
final Array<TrackEntry> tracks = new Array(); final Array<TrackEntry> tracks = new Array();
@ -229,7 +236,7 @@ public class AnimationState {
for (int ii = 0; ii < timelineCount; ii++) { for (int ii = 0; ii < timelineCount; ii++) {
Timeline timeline = (Timeline)timelines[ii]; Timeline timeline = (Timeline)timelines[ii];
MixBlend timelineBlend = timelineMode[ii] == SUBSEQUENT ? blend : MixBlend.setup; MixBlend timelineBlend = (timelineMode[ii] & NOT_LAST - 1) == SUBSEQUENT ? blend : MixBlend.setup;
if (timeline instanceof RotateTimeline) { if (timeline instanceof RotateTimeline) {
applyRotateTimeline(timeline, skeleton, animationTime, mix, timelineBlend, timelinesRotation, ii << 1, applyRotateTimeline(timeline, skeleton, animationTime, mix, timelineBlend, timelinesRotation, ii << 1,
firstFrame); firstFrame);
@ -285,7 +292,7 @@ public class AnimationState {
MixDirection direction = MixDirection.out; MixDirection direction = MixDirection.out;
MixBlend timelineBlend; MixBlend timelineBlend;
float alpha; float alpha;
switch (timelineMode[i]) { switch (timelineMode[i] & NOT_LAST - 1) {
case SUBSEQUENT: case SUBSEQUENT:
if (!attachments && timeline instanceof AttachmentTimeline) continue; if (!attachments && timeline instanceof AttachmentTimeline) continue;
if (!drawOrder && timeline instanceof DrawOrderTimeline) continue; if (!drawOrder && timeline instanceof DrawOrderTimeline) continue;
@ -313,7 +320,7 @@ public class AnimationState {
} else { } else {
if (timelineBlend == MixBlend.setup) { if (timelineBlend == MixBlend.setup) {
if (timeline instanceof AttachmentTimeline) { if (timeline instanceof AttachmentTimeline) {
if (attachments) direction = MixDirection.in; if (attachments || (timelineMode[i] & NOT_LAST) == NOT_LAST) direction = MixDirection.in;
} else if (timeline instanceof DrawOrderTimeline) { } else if (timeline instanceof DrawOrderTimeline) {
if (drawOrder) direction = MixDirection.in; if (drawOrder) direction = MixDirection.in;
} }
@ -688,22 +695,31 @@ public class AnimationState {
private void animationsChanged () { private void animationsChanged () {
animationsChanged = false; animationsChanged = false;
// Process in the order that animations are applied.
propertyIDs.clear(2048); propertyIDs.clear(2048);
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) continue; if (entry == null) continue;
// Move to last entry, then iterate in reverse (the order animations are applied). while (entry.mixingFrom != null) // Move to last entry, then iterate in reverse.
while (entry.mixingFrom != null)
entry = entry.mixingFrom; entry = entry.mixingFrom;
do { do {
if (entry.mixingTo == null || entry.mixBlend != MixBlend.add) setTimelineModes(entry); if (entry.mixingTo == null || entry.mixBlend != MixBlend.add) computeHold(entry);
entry = entry.mixingTo; entry = entry.mixingTo;
} while (entry != null); } while (entry != null);
} }
// Process in the reverse order that animations are applied.
propertyIDs.clear(2048);
for (int i = tracks.size - 1; i >= 0; i--) {
TrackEntry entry = tracks.get(i);
do {
computeNotLast(entry);
entry = entry.mixingFrom;
} while (entry != null);
}
} }
private void setTimelineModes (TrackEntry entry) { private void computeHold (TrackEntry entry) {
TrackEntry to = entry.mixingTo; TrackEntry to = entry.mixingTo;
Object[] timelines = entry.animation.timelines.items; Object[] timelines = entry.animation.timelines.items;
int timelinesCount = entry.animation.timelines.size; int timelinesCount = entry.animation.timelines.size;
@ -722,12 +738,14 @@ public class AnimationState {
outer: outer:
for (int i = 0; i < timelinesCount; i++) { for (int i = 0; i < timelinesCount; i++) {
int id = ((Timeline)timelines[i]).getPropertyId(); Timeline timeline = (Timeline)timelines[i];
int id = timeline.getPropertyId();
if (!propertyIDs.add(id)) if (!propertyIDs.add(id))
timelineMode[i] = SUBSEQUENT; timelineMode[i] = SUBSEQUENT;
else if (to == null || !hasTimeline(to, id)) else if (to == null || timeline instanceof AttachmentTimeline || timeline instanceof DrawOrderTimeline
|| timeline instanceof EventTimeline || !hasTimeline(to, id)) {
timelineMode[i] = FIRST; timelineMode[i] = FIRST;
else { } else {
for (TrackEntry next = to.mixingTo; next != null; next = next.mixingTo) { for (TrackEntry next = to.mixingTo; next != null; next = next.mixingTo) {
if (hasTimeline(next, id)) continue; if (hasTimeline(next, id)) continue;
if (next.mixDuration > 0) { if (next.mixDuration > 0) {
@ -742,6 +760,20 @@ public class AnimationState {
} }
} }
private void computeNotLast (TrackEntry entry) {
Object[] timelines = entry.animation.timelines.items;
int timelinesCount = entry.animation.timelines.size;
int[] timelineMode = entry.timelineMode.items;
IntSet propertyIDs = this.propertyIDs;
for (int i = 0; i < timelinesCount; i++) {
if (timelines[i] instanceof AttachmentTimeline) {
AttachmentTimeline timeline = (AttachmentTimeline)timelines[i];
if (!propertyIDs.add(timeline.slotIndex)) timelineMode[i] |= NOT_LAST;
}
}
}
private boolean hasTimeline (TrackEntry entry, int id) { private boolean hasTimeline (TrackEntry entry, int id) {
Object[] timelines = entry.animation.timelines.items; Object[] timelines = entry.animation.timelines.items;
for (int i = 0, n = entry.animation.timelines.size; i < n; i++) for (int i = 0, n = entry.animation.timelines.size; i < n; i++)