Browse Source

Minor AnimationState fixes.

- Added mixAlpha to keep it separate from alpha.
- Stored mixAlpha on the "mixing to" entry (where mixTime, etc is stored), not the mixingFrom entry.
- Fixed weirdness using TrackEntry alpha.
- Removed clearing timelinesRotation to avoid rotations flipping to other side when a new animation is set.
- Added TrackEntry#resetRotationDirections to give control over rotations when using alpha over long periods of time.
NathanSweet 9 years ago
parent
commit
fae60d8899

+ 25 - 16
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java

@@ -163,7 +163,7 @@ public class AnimationState {
 
 			// Apply mixing from entries first.
 			float mix = current.alpha;
-			if (current.mixingFrom != null) mix = applyMixingFrom(current, skeleton, mix);
+			if (current.mixingFrom != null) mix *= applyMixingFrom(current, skeleton);
 
 			// Apply current entry.
 			float animationLast = current.animationLast, animationTime = current.getAnimationTime();
@@ -195,9 +195,9 @@ public class AnimationState {
 		queue.drain();
 	}
 
-	private float applyMixingFrom (TrackEntry entry, Skeleton skeleton, float alpha) {
+	private float applyMixingFrom (TrackEntry entry, Skeleton skeleton) {
 		TrackEntry from = entry.mixingFrom;
-		if (from.mixingFrom != null) applyMixingFrom(from, skeleton, alpha);
+		if (from.mixingFrom != null) applyMixingFrom(from, skeleton);
 
 		float mix;
 		if (entry.mixDuration == 0) // Single frame mix to undo mixingFrom changes.
@@ -205,16 +205,15 @@ public class AnimationState {
 		else {
 			mix = entry.mixTime / entry.mixDuration;
 			if (mix > 1) mix = 1;
-			mix *= alpha;
 		}
 
 		Array<Event> events = mix < from.eventThreshold ? this.events : null;
 		boolean attachments = mix < from.attachmentThreshold, drawOrder = mix < from.drawOrderThreshold;
 		float animationLast = from.animationLast, animationTime = from.getAnimationTime();
-		alpha = from.alpha * (1 - mix);
 		int timelineCount = from.animation.timelines.size;
 		Object[] timelines = from.animation.timelines.items;
 		boolean[] timelinesFirst = from.timelinesFirst.items;
+		float alpha = from.alpha * entry.mixAlpha * (1 - mix);
 
 		boolean firstFrame = from.timelinesRotation.size == 0;
 		if (firstFrame) from.timelinesRotation.setSize(timelineCount << 1);
@@ -385,10 +384,8 @@ public class AnimationState {
 			current.mixingFrom = from;
 			current.mixTime = 0;
 
-			from.timelinesRotation.clear();
-
-			// If not completely mixed in, set alpha so mixing out happens from current mix to zero.
-			if (from.mixingFrom != null) from.alpha *= Math.min(from.mixTime / from.mixDuration, 1);
+			// If not completely mixed in, set mixAlpha so mixing out happens from current mix to zero.
+			if (from.mixingFrom != null) current.mixAlpha *= Math.min(from.mixTime / from.mixDuration, 1);
 		}
 
 		queue.start(current);
@@ -544,6 +541,7 @@ public class AnimationState {
 		entry.timeScale = 1;
 
 		entry.alpha = 1;
+		entry.mixAlpha = 1;
 		entry.mixTime = 0;
 		entry.mixDuration = last == null ? 0 : data.getMix(last.animation, animation);
 		return entry;
@@ -563,7 +561,7 @@ public class AnimationState {
 
 		IntSet propertyIDs = this.propertyIDs;
 
-		// Compute timelinesFirst from lowest to highest track entries.
+		// Set timelinesFirst for all entries, from lowest track to highest.
 		int i = 0, n = tracks.size;
 		propertyIDs.clear();
 		for (; i < n; i++) { // Find first non-null entry.
@@ -587,11 +585,11 @@ public class AnimationState {
 			return;
 		}
 		IntSet propertyIDs = this.propertyIDs;
-		Array<Timeline> timelines = entry.animation.timelines;
-		int n = timelines.size;
+		int n = entry.animation.timelines.size;
+		Object[] timelines = entry.animation.timelines.items;
 		boolean[] usage = entry.timelinesFirst.setSize(n);
 		for (int i = 0; i < n; i++) {
-			propertyIDs.add(timelines.get(i).getPropertyId());
+			propertyIDs.add(((Timeline)timelines[i]).getPropertyId());
 			usage[i] = true;
 		}
 	}
@@ -604,11 +602,11 @@ public class AnimationState {
 
 	private void checkTimelinesUsage (TrackEntry entry, BooleanArray usageArray) {
 		IntSet propertyIDs = this.propertyIDs;
-		Array<Timeline> timelines = entry.animation.timelines;
-		int n = timelines.size;
+		int n = entry.animation.timelines.size;
+		Object[] timelines = entry.animation.timelines.items;
 		boolean[] usage = usageArray.setSize(n);
 		for (int i = 0; i < n; i++)
-			usage[i] = propertyIDs.add(timelines.get(i).getPropertyId());
+			usage[i] = propertyIDs.add(((Timeline)timelines[i]).getPropertyId());
 	}
 
 	/** Returns the track entry for the animation currently playing on the track, or null if no animation is currently playing. */
@@ -924,6 +922,17 @@ public class AnimationState {
 			return mixingFrom;
 		}
 
+		/** Resets the rotation directions for mixing this entry's rotate timelines. This can be useful to avoid bones rotating the
+		 * long way around when using {@link #alpha} and starting animations on other tracks.
+		 * <p>
+		 * Mixing involves finding a rotation between two others, which has two possible solutions: the short way or the long way
+		 * around. The two rotations likely change over time, so which direction is the short or long way also changes. If the short
+		 * way was always chosen, bones would flip to the other side when that direction became the long way. TrackEntry chooses the
+		 * short way the first time it is applied and remembers that direction. */
+		public void resetRotationDirections () {
+			timelinesRotation.clear();
+		}
+
 		public String toString () {
 			return animation == null ? "<none>" : animation.name;
 		}