From a321aa76d556a618a09abb3adeb7473a6c099304 Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Fri, 8 Mar 2019 20:15:12 +0100 Subject: [PATCH] [libgdx] Fixed mixing out an attachment timeline when a subsequent entry sets the same attachment. closes #1292 --- .../spine/AnimationState.java | 54 +++++++++++++++---- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java index 2bb23536c..1fcac711b 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java @@ -43,6 +43,7 @@ import com.badlogic.gdx.utils.Pool; import com.badlogic.gdx.utils.Pool.Poolable; import com.esotericsoftware.spine.Animation.AttachmentTimeline; import com.esotericsoftware.spine.Animation.DrawOrderTimeline; +import com.esotericsoftware.spine.Animation.EventTimeline; import com.esotericsoftware.spine.Animation.MixBlend; import com.esotericsoftware.spine.Animation.MixDirection; 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 * place. */ 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.
+ * 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; final Array tracks = new Array(); @@ -229,7 +236,7 @@ public class AnimationState { for (int ii = 0; ii < timelineCount; 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) { applyRotateTimeline(timeline, skeleton, animationTime, mix, timelineBlend, timelinesRotation, ii << 1, firstFrame); @@ -285,7 +292,7 @@ public class AnimationState { MixDirection direction = MixDirection.out; MixBlend timelineBlend; float alpha; - switch (timelineMode[i]) { + switch (timelineMode[i] & NOT_LAST - 1) { case SUBSEQUENT: if (!attachments && timeline instanceof AttachmentTimeline) continue; if (!drawOrder && timeline instanceof DrawOrderTimeline) continue; @@ -313,7 +320,7 @@ public class AnimationState { } else { if (timelineBlend == MixBlend.setup) { 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) { if (drawOrder) direction = MixDirection.in; } @@ -688,22 +695,31 @@ public class AnimationState { private void animationsChanged () { animationsChanged = false; + // Process in the order that animations are applied. propertyIDs.clear(2048); - for (int i = 0, n = tracks.size; i < n; i++) { TrackEntry entry = tracks.get(i); if (entry == null) continue; - // Move to last entry, then iterate in reverse (the order animations are applied). - while (entry.mixingFrom != null) + while (entry.mixingFrom != null) // Move to last entry, then iterate in reverse. entry = entry.mixingFrom; do { - if (entry.mixingTo == null || entry.mixBlend != MixBlend.add) setTimelineModes(entry); + if (entry.mixingTo == null || entry.mixBlend != MixBlend.add) computeHold(entry); entry = entry.mixingTo; } 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; Object[] timelines = entry.animation.timelines.items; int timelinesCount = entry.animation.timelines.size; @@ -722,12 +738,14 @@ public class AnimationState { outer: 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)) 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; - else { + } else { for (TrackEntry next = to.mixingTo; next != null; next = next.mixingTo) { if (hasTimeline(next, id)) continue; 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) { Object[] timelines = entry.animation.timelines.items; for (int i = 0, n = entry.animation.timelines.size; i < n; i++)