Browse Source

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

closes #1292
NathanSweet 6 years ago
parent
commit
a321aa76d5

+ 43 - 11
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.<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;
 	final Array<TrackEntry> 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++)