Przeglądaj źródła

Merge branch '3.7-beta' of https://github.com/esotericsoftware/spine-runtimes into 3.7-beta

badlogic 7 lat temu
rodzic
commit
ab3dcb0823

+ 91 - 65
spine-csharp/src/AnimationState.cs

@@ -34,17 +34,16 @@ using System.Collections.Generic;
 namespace Spine {
 namespace Spine {
 	public class AnimationState {
 	public class AnimationState {
 		static readonly Animation EmptyAnimation = new Animation("<empty>", new ExposedList<Timeline>(), 0);
 		static readonly Animation EmptyAnimation = new Animation("<empty>", new ExposedList<Timeline>(), 0);
-		internal const int Subsequent = 0, First = 1, Dip = 2, DipMix = 3;
+		internal const int Subsequent = 0, First = 1, Hold = 2, HoldMix = 3;
 
 
 		private AnimationStateData data;
 		private AnimationStateData data;
 
 
-		Pool<TrackEntry> trackEntryPool = new Pool<TrackEntry>();
+		private readonly Pool<TrackEntry> trackEntryPool = new Pool<TrackEntry>();
 		private readonly ExposedList<TrackEntry> tracks = new ExposedList<TrackEntry>();
 		private readonly ExposedList<TrackEntry> tracks = new ExposedList<TrackEntry>();
 		private readonly ExposedList<Event> events = new ExposedList<Event>();
 		private readonly ExposedList<Event> events = new ExposedList<Event>();
 		private readonly EventQueue queue; // Initialized by constructor.
 		private readonly EventQueue queue; // Initialized by constructor.
 
 
 		private readonly HashSet<int> propertyIDs = new HashSet<int>();
 		private readonly HashSet<int> propertyIDs = new HashSet<int>();
-		private readonly ExposedList<TrackEntry> mixingTo = new ExposedList<TrackEntry>();
 		private bool animationsChanged;
 		private bool animationsChanged;
 
 
 		private float timeScale = 1;
 		private float timeScale = 1;
@@ -119,6 +118,7 @@ namespace Spine {
 					// End mixing from entries once all have completed.
 					// End mixing from entries once all have completed.
 					var from = current.mixingFrom;
 					var from = current.mixingFrom;
 					current.mixingFrom = null;
 					current.mixingFrom = null;
+					if (from != null) from.mixingTo = null;
 					while (from != null) {
 					while (from != null) {
 						queue.End(from);
 						queue.End(from);
 						from = from.mixingFrom;
 						from = from.mixingFrom;
@@ -146,6 +146,7 @@ namespace Spine {
 				// Require totalAlpha == 0 to ensure mixing is complete, unless mixDuration == 0 (the transition is a single frame).
 				// Require totalAlpha == 0 to ensure mixing is complete, unless mixDuration == 0 (the transition is a single frame).
 				if (from.totalAlpha == 0 || to.mixDuration == 0) {
 				if (from.totalAlpha == 0 || to.mixDuration == 0) {
 					to.mixingFrom = from.mixingFrom;
 					to.mixingFrom = from.mixingFrom;
+					if (from.mixingFrom != null) from.mixingFrom.mixingTo = to;
 					to.interruptAlpha = from.interruptAlpha;
 					to.interruptAlpha = from.interruptAlpha;
 					queue.End(from);
 					queue.End(from);
 				}
 				}
@@ -187,11 +188,11 @@ namespace Spine {
 				int timelineCount = current.animation.timelines.Count;
 				int timelineCount = current.animation.timelines.Count;
 				var timelines = current.animation.timelines;
 				var timelines = current.animation.timelines;
 				var timelinesItems = timelines.Items;
 				var timelinesItems = timelines.Items;
-				if (mix == 1 || blend == MixBlend.Add) {
+				if (i == 0 && (mix == 1 || blend == MixBlend.Add)) {
 					for (int ii = 0; ii < timelineCount; ii++)
 					for (int ii = 0; ii < timelineCount; ii++)
 						timelinesItems[ii].Apply(skeleton, animationLast, animationTime, events, mix, blend, MixDirection.In);
 						timelinesItems[ii].Apply(skeleton, animationLast, animationTime, events, mix, blend, MixDirection.In);
 				} else {
 				} else {
-					var timelineData = current.timelineData.Items;
+					var timelineMode = current.timelineMode.Items;
 
 
 					bool firstFrame = current.timelinesRotation.Count == 0;
 					bool firstFrame = current.timelinesRotation.Count == 0;
 					if (firstFrame) current.timelinesRotation.EnsureCapacity(timelines.Count << 1);
 					if (firstFrame) current.timelinesRotation.EnsureCapacity(timelines.Count << 1);
@@ -199,7 +200,7 @@ namespace Spine {
 
 
 					for (int ii = 0; ii < timelineCount; ii++) {
 					for (int ii = 0; ii < timelineCount; ii++) {
 						Timeline timeline = timelinesItems[ii];
 						Timeline timeline = timelinesItems[ii];
-						MixBlend timelineBlend = timelineData[ii] >= AnimationState.Subsequent ? blend : MixBlend.Setup;
+						MixBlend timelineBlend = timelineMode[ii] >= AnimationState.Subsequent ? blend : MixBlend.Setup;
 						var rotateTimeline = timeline as RotateTimeline;
 						var rotateTimeline = timeline as RotateTimeline;
 						if (rotateTimeline != null)
 						if (rotateTimeline != null)
 							ApplyRotateTimeline(rotateTimeline, skeleton, animationTime, mix, timelineBlend, timelinesRotation, ii << 1, firstFrame);
 							ApplyRotateTimeline(rotateTimeline, skeleton, animationTime, mix, timelineBlend, timelinesRotation, ii << 1, firstFrame);
@@ -237,14 +238,14 @@ namespace Spine {
 			var timelines = from.animation.timelines;
 			var timelines = from.animation.timelines;
 			int timelineCount = timelines.Count;
 			int timelineCount = timelines.Count;
 			var timelinesItems = timelines.Items;
 			var timelinesItems = timelines.Items;
-			float alphaDip = from.alpha * to.interruptAlpha, alphaMix = alphaDip * (1 - mix);
+			float alphaHold = from.alpha * to.interruptAlpha, alphaMix = alphaHold * (1 - mix);
 
 
 			if (blend == MixBlend.Add) {
 			if (blend == MixBlend.Add) {
 				for (int i = 0; i < timelineCount; i++)
 				for (int i = 0; i < timelineCount; i++)
 					(timelinesItems[i]).Apply(skeleton, animationLast, animationTime, eventBuffer, alphaMix, blend, MixDirection.Out);
 					(timelinesItems[i]).Apply(skeleton, animationLast, animationTime, eventBuffer, alphaMix, blend, MixDirection.Out);
 			} else {
 			} else {
-				var timelineData = from.timelineData.Items;
-				var timelineDipMix = from.timelineDipMix.Items;
+				var timelineMode = from.timelineMode.Items;
+				var timelineHoldMix = from.timelineHoldMix.Items;
 
 
 				bool firstFrame = from.timelinesRotation.Count == 0;
 				bool firstFrame = from.timelinesRotation.Count == 0;
 				if (firstFrame)	from.timelinesRotation.Resize(timelines.Count << 1); // from.timelinesRotation.setSize
 				if (firstFrame)	from.timelinesRotation.Resize(timelines.Count << 1); // from.timelinesRotation.setSize
@@ -255,7 +256,7 @@ namespace Spine {
 					Timeline timeline = timelinesItems[i];
 					Timeline timeline = timelinesItems[i];
 					MixBlend timelineBlend;
 					MixBlend timelineBlend;
 					float alpha;
 					float alpha;
-					switch (timelineData[i]) {
+					switch (timelineMode[i]) {
 						case AnimationState.Subsequent:
 						case AnimationState.Subsequent:
 							if (!attachments && timeline is AttachmentTimeline)
 							if (!attachments && timeline is AttachmentTimeline)
 								continue;
 								continue;
@@ -268,14 +269,14 @@ namespace Spine {
 							timelineBlend = MixBlend.Setup;
 							timelineBlend = MixBlend.Setup;
 							alpha = alphaMix;
 							alpha = alphaMix;
 							break;
 							break;
-						case AnimationState.Dip:
+						case AnimationState.Hold:
 							timelineBlend = MixBlend.Setup;
 							timelineBlend = MixBlend.Setup;
-							alpha = alphaDip;
+							alpha = alphaHold;
 							break;
 							break;
 						default:
 						default:
 							timelineBlend = MixBlend.Setup;
 							timelineBlend = MixBlend.Setup;
-							TrackEntry dipMix = timelineDipMix[i];
-							alpha = alphaDip * Math.Max(0, 1 - dipMix.mixTime / dipMix.mixDuration);
+							TrackEntry holdMix = timelineHoldMix[i];
+							alpha = alphaHold * Math.Max(0, 1 - holdMix.mixTime / holdMix.mixDuration);
 							break;
 							break;
 					}
 					}
 					from.totalAlpha += alpha;
 					from.totalAlpha += alpha;
@@ -428,6 +429,7 @@ namespace Spine {
 				if (from == null) break;
 				if (from == null) break;
 				queue.End(from);
 				queue.End(from);
 				entry.mixingFrom = null;
 				entry.mixingFrom = null;
+				entry.mixingTo = null;
 				entry = from;
 				entry = from;
 			}
 			}
 
 
@@ -444,6 +446,7 @@ namespace Spine {
 			if (from != null) {
 			if (from != null) {
 				if (interrupt) queue.Interrupt(from);
 				if (interrupt) queue.Interrupt(from);
 				current.mixingFrom = from;
 				current.mixingFrom = from;
+				current.mixingTo = current;
 				current.mixTime = 0;
 				current.mixTime = 0;
 
 
 				// Store interrupted mix percentage.
 				// Store interrupted mix percentage.
@@ -599,6 +602,7 @@ namespace Spine {
 			entry.trackIndex = trackIndex;
 			entry.trackIndex = trackIndex;
 			entry.animation = animation;
 			entry.animation = animation;
 			entry.loop = loop;
 			entry.loop = loop;
+			entry.holdPrevious = false;
 
 
 			entry.eventThreshold = 0;
 			entry.eventThreshold = 0;
 			entry.attachmentThreshold = 0;
 			entry.attachmentThreshold = 0;
@@ -636,17 +640,71 @@ namespace Spine {
 		private void AnimationsChanged () {
 		private void AnimationsChanged () {
 			animationsChanged = false;
 			animationsChanged = false;
 
 
-			var propertyIDs = this.propertyIDs;
-			propertyIDs.Clear();
-			var mixingTo = this.mixingTo;
+			this.propertyIDs.Clear();
 
 
 			var tracksItems = tracks.Items;
 			var tracksItems = tracks.Items;
 			for (int i = 0, n = tracks.Count; i < n; i++) {
 			for (int i = 0, n = tracks.Count; i < n; i++) {
 				var entry = tracksItems[i];
 				var entry = tracksItems[i];
-				if (entry != null && (i == 0 || entry.mixBlend != MixBlend.Add)) entry.SetTimelineData(null, mixingTo, propertyIDs);
+				if (entry == null) continue;
+				// Move to last entry, then iterate in reverse (the order animations are applied).
+				while (entry.mixingFrom != null)
+					entry = entry.mixingFrom;
+
+				do {
+					if (entry.mixingTo == null || entry.mixBlend != MixBlend.Add) SetTimelineModes(entry);
+					entry = entry.mixingTo;
+				} while (entry != null);
+
+			}
+		}
+
+		private void SetTimelineModes (TrackEntry entry) {
+			var to = entry.mixingTo;
+			var timelines = entry.animation.timelines.Items;
+			int timelinesCount = entry.animation.timelines.Count;
+			var timelineMode = entry.timelineMode.Resize(timelinesCount).Items; //timelineMode.setSize(timelinesCount);
+			entry.timelineHoldMix.Clear();
+			var timelineHoldMix = entry.timelineHoldMix.Resize(timelinesCount).Items; //timelineHoldMix.setSize(timelinesCount);
+			var propertyIDs = this.propertyIDs;
+
+			if (to != null && to.holdPrevious) {
+				for (int i = 0; i < timelinesCount; i++) {
+					propertyIDs.Add(timelines[i].PropertyId);
+					timelineMode[i] = AnimationState.Hold;
+				}
+				return;
+			}
+
+			// outer:
+			for (int i = 0; i < timelinesCount; i++) {
+				int id = timelines[i].PropertyId;
+				if (!propertyIDs.Add(id))
+					timelineMode[i] = AnimationState.Subsequent;
+				else if (to == null || !HasTimeline(to, id))
+					timelineMode[i] = AnimationState.First;
+				else {
+					for (TrackEntry next = to.mixingTo; next != null; next = next.mixingTo) {
+						if (HasTimeline(next, id)) continue;
+						if (next.mixDuration > 0) {
+							timelineMode[i] = AnimationState.HoldMix;
+							timelineHoldMix[i] = next;
+							goto continue_outer; // continue outer;
+						}
+						break;
+					}
+					timelineMode[i] = AnimationState.Hold;
+				}
+				continue_outer:	{}
 			}
 			}
 		}
 		}
 
 
+		static bool HasTimeline (TrackEntry entry, int id) {
+			var timelines = entry.animation.timelines.Items;
+			for (int i = 0, n = entry.animation.timelines.Count; i < n; i++)
+				if (timelines[i].PropertyId == id) return true;
+			return false;
+		}
+
 		/// <returns>The track entry for the animation currently playing on the track, or null if no animation is currently playing.</returns>
 		/// <returns>The track entry for the animation currently playing on the track, or null if no animation is currently playing.</returns>
 		public TrackEntry GetCurrent (int trackIndex) {
 		public TrackEntry GetCurrent (int trackIndex) {
 			return (trackIndex >= tracks.Count) ? null : tracks.Items[trackIndex];
 			return (trackIndex >= tracks.Count) ? null : tracks.Items[trackIndex];
@@ -675,17 +733,17 @@ namespace Spine {
 	public class TrackEntry : Pool<TrackEntry>.IPoolable {
 	public class TrackEntry : Pool<TrackEntry>.IPoolable {
 		internal Animation animation;
 		internal Animation animation;
 
 
-		internal TrackEntry next, mixingFrom;
+		internal TrackEntry next, mixingFrom, mixingTo;
 		internal int trackIndex;
 		internal int trackIndex;
 
 
-		internal bool loop;
+		internal bool loop, holdPrevious;
 		internal float eventThreshold, attachmentThreshold, drawOrderThreshold;
 		internal float eventThreshold, attachmentThreshold, drawOrderThreshold;
 		internal float animationStart, animationEnd, animationLast, nextAnimationLast;
 		internal float animationStart, animationEnd, animationLast, nextAnimationLast;
 		internal float delay, trackTime, trackLast, nextTrackLast, trackEnd, timeScale = 1f;
 		internal float delay, trackTime, trackLast, nextTrackLast, trackEnd, timeScale = 1f;
 		internal float alpha, mixTime, mixDuration, interruptAlpha, totalAlpha;
 		internal float alpha, mixTime, mixDuration, interruptAlpha, totalAlpha;
 		internal MixBlend mixBlend = MixBlend.Replace;
 		internal MixBlend mixBlend = MixBlend.Replace;
-		internal readonly ExposedList<int> timelineData = new ExposedList<int>();
-		internal readonly ExposedList<TrackEntry> timelineDipMix = new ExposedList<TrackEntry>();
+		internal readonly ExposedList<int> timelineMode = new ExposedList<int>();
+		internal readonly ExposedList<TrackEntry> timelineHoldMix = new ExposedList<TrackEntry>();
 		internal readonly ExposedList<float> timelinesRotation = new ExposedList<float>();
 		internal readonly ExposedList<float> timelinesRotation = new ExposedList<float>();
 
 
 		// IPoolable.Reset()
 		// IPoolable.Reset()
@@ -693,8 +751,8 @@ namespace Spine {
 			next = null;
 			next = null;
 			mixingFrom = null;
 			mixingFrom = null;
 			animation = null;
 			animation = null;
-			timelineData.Clear();
-			timelineDipMix.Clear();
+			timelineMode.Clear();
+			timelineHoldMix.Clear();
 			timelinesRotation.Clear();
 			timelinesRotation.Clear();
 
 
 			Start = null;
 			Start = null;
@@ -705,47 +763,6 @@ namespace Spine {
 			Event = null;
 			Event = null;
 		}
 		}
 
 
-		/// <summary>Sets the timeline data.</summary>
-		/// <param name="to">May be null.</param>
-		internal TrackEntry SetTimelineData (TrackEntry to, ExposedList<TrackEntry> mixingToArray, HashSet<int> propertyIDs) {
-			if (to != null) mixingToArray.Add(to);
-			var lastEntry = mixingFrom != null ? mixingFrom.SetTimelineData(this, mixingToArray, propertyIDs) : this;
-			if (to != null) mixingToArray.Pop();
-
-			var mixingTo = mixingToArray.Items;
-			int mixingToLast = mixingToArray.Count - 1;
-			var timelines = animation.timelines.Items;
-			int timelinesCount = animation.timelines.Count;
-			var timelineDataItems = timelineData.Resize(timelinesCount).Items; // timelineData.setSize(timelinesCount);
-			timelineDipMix.Clear();
-			var timelineDipMixItems = timelineDipMix.Resize(timelinesCount).Items; //timelineDipMix.setSize(timelinesCount);
-
-			// outer:
-			for (int i = 0; i < timelinesCount; i++) {
-				int id = timelines[i].PropertyId;
-				if (!propertyIDs.Add(id)) {
-					timelineDataItems[i] = AnimationState.Subsequent;
-				} else if (to == null || !to.HasTimeline(id)) {
-					timelineDataItems[i] = AnimationState.First;
-				} else {
-					for (int ii = mixingToLast; ii >= 0; ii--) {
-						var entry = mixingTo[ii];
-						if (!entry.HasTimeline(id)) {
-							if (entry.mixDuration > 0) {
-								timelineDataItems[i] = AnimationState.DipMix;
-								timelineDipMixItems[i] = entry;
-								goto continue_outer; // continue outer;
-							}
-							break;
-						}
-					}
-					timelineDataItems[i] = AnimationState.Dip;
-				}
-				continue_outer: {}
-			}
-			return lastEntry;
-		}
-
 		bool HasTimeline (int id) {
 		bool HasTimeline (int id) {
 			var timelines = animation.timelines.Items;
 			var timelines = animation.timelines.Items;
 			for (int i = 0, n = animation.timelines.Count; i < n; i++)
 			for (int i = 0, n = animation.timelines.Count; i < n; i++)
@@ -903,6 +920,15 @@ namespace Spine {
 		/// mixing is currently occuring. When mixing from multiple animations, MixingFrom makes up a linked list.</summary>
 		/// mixing is currently occuring. When mixing from multiple animations, MixingFrom makes up a linked list.</summary>
 		public TrackEntry MixingFrom { get { return mixingFrom; } }
 		public TrackEntry MixingFrom { get { return mixingFrom; } }
 
 
+		/// <summary>
+		/// If true, when mixing from the previous animation to this animation, the previous animation is applied as normal instead of being mixed out.
+		/// 
+		/// When mixing between animations that key the same property, if a lower track also keys that property then the value will briefly dip toward the lower track value during the mix. This happens because the first animation mixes from 100% to 0% while the second animation mixes from 0% to 100%. Setting HoldPrevious to true applies the first animation at 100% during the mix so the lower track value is overwritten. Such dipping does not occur on the lowest track which keys the property, only when a higher track also keys the property.
+		/// 
+		/// Snapping will occur if HoldPrevious is true and this animation does not key all the same properties as the previous animation.
+		/// </summary>
+		public bool HoldPrevious { get { return holdPrevious; } set { holdPrevious = value; } }
+
 		public event AnimationState.TrackEntryDelegate Start, Interrupt, End, Dispose, Complete;
 		public event AnimationState.TrackEntryDelegate Start, Interrupt, End, Dispose, Complete;
 		public event AnimationState.TrackEntryEventDelegate Event;
 		public event AnimationState.TrackEntryEventDelegate Event;
 		internal void OnStart () { if (Start != null) Start(this); }
 		internal void OnStart () { if (Start != null) Start(this); }

+ 0 - 1
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java

@@ -666,7 +666,6 @@ public class AnimationState {
 	private void animationsChanged () {
 	private void animationsChanged () {
 		animationsChanged = false;
 		animationsChanged = false;
 
 
-		IntSet propertyIDs = this.propertyIDs;
 		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++) {