From fae60d88997f4486f7774dcea985fd2be3f84ba9 Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Sun, 30 Oct 2016 22:48:33 +0100 Subject: [PATCH] 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. --- .../spine/AnimationState.java | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 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 fa34641ac..9429bf305 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java +++ b/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 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 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 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. + *

+ * 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 ? "" : animation.name; }