Browse Source

[csharp] Changed Timeline.Apply. Animation, AnimationState handle setup pose before first key.

pharan 8 years ago
parent
commit
44a44aa6c6
2 changed files with 214 additions and 106 deletions
  1. 191 81
      spine-csharp/src/Animation.cs
  2. 23 25
      spine-csharp/src/AnimationState.cs

+ 191 - 81
spine-csharp/src/Animation.cs

@@ -50,16 +50,8 @@ namespace Spine {
 		}
 		}
 
 
 		/// <summary>Applies all the animation's timelines to the specified skeleton.</summary>
 		/// <summary>Applies all the animation's timelines to the specified skeleton.</summary>
-		/// <param name="skeleton">The skeleton to be posed.</param>
-		/// <param name="lastTime">The last time the animation was applied.</param>
-		/// <param name="time">The point in time in the animation to apply to the skeleton.</param>
-		/// <param name="loop">If true, time wraps within the animation duration.</param>
-		/// <param name="events">Any triggered events are added. May be null.</param>
-		/// <param name="alpha">The percentage between this animation's pose and the current pose.</param>
-		/// <param name="setupPose">If true, the animation is mixed with the setup pose, else it is mixed with the current pose. Passing true when alpha is 1 is slightly more efficient.</param>
-		/// <param name="mixingOut">True when mixing over time toward the setup or current pose, false when mixing toward the keyed pose. Irrelevant when alpha is 1.</param>
-		/// <seealso cref="Timeline.Apply(Skeleton, float, float, ExposedList, float, bool, bool)"/>
-		public void Apply (Skeleton skeleton, float lastTime, float time, bool loop, ExposedList<Event> events, float alpha, bool setupPose, bool mixingOut) {
+		/// <seealso cref="Timeline.Apply(Skeleton, float, float, ExposedList, float, MixPose, MixDirection)"/>
+		public void Apply (Skeleton skeleton, float lastTime, float time, bool loop, ExposedList<Event> events, float alpha, MixPose pose, MixDirection direction) {
 			if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
 			if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
 
 
 			if (loop && duration != 0) {
 			if (loop && duration != 0) {
@@ -69,7 +61,7 @@ namespace Spine {
 
 
 			ExposedList<Timeline> timelines = this.timelines;
 			ExposedList<Timeline> timelines = this.timelines;
 			for (int i = 0, n = timelines.Count; i < n; i++)
 			for (int i = 0, n = timelines.Count; i < n; i++)
-				timelines.Items[i].Apply(skeleton, lastTime, time, events, alpha, setupPose, mixingOut);
+				timelines.Items[i].Apply(skeleton, lastTime, time, events, alpha, pose, direction);
 		}
 		}
 
 
 		/// <param name="target">After the first and before the last entry.</param>
 		/// <param name="target">After the first and before the last entry.</param>
@@ -113,14 +105,41 @@ namespace Spine {
 
 
 	public interface Timeline {
 	public interface Timeline {
 		/// <summary>Sets the value(s) for the specified time.</summary>
 		/// <summary>Sets the value(s) for the specified time.</summary>
-		/// <param name="events">Any triggered events are added. May be null.</param>
-		/// <param name="setupPose">True when the timeline is mixed with the setup pose, false when it is mixed with the current pose. Passing true when alpha is 1 is slightly more efficient.</param>
-		/// <param name="mixingOut">True when mixing over time toward the setup or current pose, false when mixing toward the keyed pose.
-		/// Used for timelines with instant transitions, eg draw order, attachment visibility, scale sign.</param>
-		void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> events, float alpha, bool setupPose, bool mixingOut);
+		/// <param name="skeleton">The skeleton the timeline is being applied to. This provides access to the bones, slots, and other skeleton components the timeline may change.</param>
+		/// <param name="lastTime">lastTime The time this timeline was last applied. Timelines such as EventTimeline trigger only at specific times rather than every frame. In that case, the timeline triggers everything between lastTime (exclusive) and <code>time</code> (inclusive).</param>
+		/// <param name="time">The time within the animation. Most timelines find the key before and the key after this time so they can interpolate between the keys.</param>
+		/// <param name="events">If any events are fired, they are added to this list. Can be null to ignore firing events or if the timeline does not fire events. May be null.</param>
+		/// <param name="alpha">alpha 0 applies the current or setup pose value (depending on pose parameter). 1 applies the timeline 
+		/// 	value. Between 0 and 1 applies a value between the current or setup pose and the timeline value. By adjusting
+		/// 	alpha over time, an animation can be mixed in or out. <code>alpha</code> can also be useful to
+		/// 	 apply animations on top of each other (layered).</param>
+		/// <param name="pose">Controls how mixing is applied when alpha is than 1.</param>
+		/// <param name="direction">Indicates whether the timeline is mixing in or out. Used by timelines which perform instant transitions such as DrawOrderTimeline and AttachmentTimeline.</param>
+		void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> events, float alpha, MixPose pose, MixDirection direction);
 		int PropertyId { get; }
 		int PropertyId { get; }
 	}
 	}
 
 
+	/// <summary>
+	/// Controls how a timeline is mixed with the setup or current pose.</summary>
+	/// <seealso cref="Timeline.Apply(Skeleton, float, float, ExposedList, float, MixPose, MixDirection)"/>
+	public enum MixPose {
+		/// <summary> The timeline value is mixed with the setup pose (the current pose is not used).</summary>
+		Setup,
+		/// <summary> The timeline value is mixed with the current pose. The setup pose is used as the timeline value before the first key,
+		/// except for timelines which perform instant transitions, such as DrawOrderTimeline or AttachmentTimeline.</summary>
+		Current,
+		/// <summary> The timeline value is mixed with the current pose. No change is made before the first key (the current pose is kept until the first key).</summary>
+		CurrentLayered
+	}
+
+	/// <summary>
+	/// Indicates whether a timeline's <code>alpha</code> is mixing out over time toward 0 (the setup or current pose) or mixing in toward 1 (the timeline's pose).</summary>
+	/// <seealso cref="Timeline.Apply(Skeleton, float, float, ExposedList, float, MixPose, MixDirection)"/>
+	public enum MixDirection {
+		In,
+		Out
+	}
+
 	internal enum TimelineType {
 	internal enum TimelineType {
 		Rotate = 0, Translate, Scale, Shear, //
 		Rotate = 0, Translate, Scale, Shear, //
 		Attachment, Color, Deform, //
 		Attachment, Color, Deform, //
@@ -143,7 +162,7 @@ namespace Spine {
 			curves = new float[(frameCount - 1) * BEZIER_SIZE];
 			curves = new float[(frameCount - 1) * BEZIER_SIZE];
 		}
 		}
 
 
-		abstract public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, bool setupPose, bool mixingOut);
+		abstract public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixPose pose, MixDirection direction);
 
 
 		abstract public int PropertyId { get; }
 		abstract public int PropertyId { get; }
 
 
@@ -239,17 +258,26 @@ namespace Spine {
 			frames[frameIndex + ROTATION] = degrees;
 			frames[frameIndex + ROTATION] = degrees;
 		}
 		}
 
 
-		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, bool setupPose, bool mixingOut) {
+		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixPose pose, MixDirection direction) {
 			Bone bone = skeleton.bones.Items[boneIndex];
 			Bone bone = skeleton.bones.Items[boneIndex];
 
 
 			float[] frames = this.frames;
 			float[] frames = this.frames;
 			if (time < frames[0]) {
 			if (time < frames[0]) {
-				if (setupPose) bone.rotation = bone.data.rotation;
+				switch (pose) {
+				case MixPose.Setup:
+					bone.rotation = bone.data.rotation;
+					return;
+				case MixPose.Current:
+					float rr = bone.data.rotation - bone.rotation;
+					rr -= (16384 - (int)(16384.499999999996 - rr / 360)) * 360;
+					bone.rotation += rr * alpha;
+					return;
+				}
 				return;
 				return;
 			}
 			}
 
 
 			if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
 			if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
-				if (setupPose) {
+				if (pose == MixPose.Setup) {
 					bone.rotation = bone.data.rotation + frames[frames.Length + PREV_ROTATION] * alpha;
 					bone.rotation = bone.data.rotation + frames[frames.Length + PREV_ROTATION] * alpha;
 				} else {
 				} else {
 					float rr = bone.data.rotation + frames[frames.Length + PREV_ROTATION] - bone.rotation;
 					float rr = bone.data.rotation + frames[frames.Length + PREV_ROTATION] - bone.rotation;
@@ -268,7 +296,7 @@ namespace Spine {
 			float r = frames[frame + ROTATION] - prevRotation;
 			float r = frames[frame + ROTATION] - prevRotation;
 			r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360;
 			r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360;
 			r = prevRotation + r * percent;
 			r = prevRotation + r * percent;
-			if (setupPose) {
+			if (pose == MixPose.Setup) {
 				r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360;
 				r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360;
 				bone.rotation = bone.data.rotation + r * alpha;
 				bone.rotation = bone.data.rotation + r * alpha;
 			} else {
 			} else {
@@ -307,14 +335,20 @@ namespace Spine {
 			frames[frameIndex + Y] = y;
 			frames[frameIndex + Y] = y;
 		}
 		}
 
 
-		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, bool setupPose, bool mixingOut) {
+		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixPose pose, MixDirection direction) {
 			Bone bone = skeleton.bones.Items[boneIndex];
 			Bone bone = skeleton.bones.Items[boneIndex];
 
 
 			float[] frames = this.frames;
 			float[] frames = this.frames;
 			if (time < frames[0]) {
 			if (time < frames[0]) {
-				if (setupPose) {
+				switch (pose) {
+				case MixPose.Setup:
 					bone.x = bone.data.x;
 					bone.x = bone.data.x;
 					bone.y = bone.data.y;
 					bone.y = bone.data.y;
+					return;
+				case MixPose.Current:
+					bone.x += (bone.data.x - bone.x) * alpha;
+					bone.y += (bone.data.y - bone.y) * alpha;
+					return;
 				}
 				}
 				return;
 				return;
 			}
 			}
@@ -335,7 +369,7 @@ namespace Spine {
 				x += (frames[frame + X] - x) * percent;
 				x += (frames[frame + X] - x) * percent;
 				y += (frames[frame + Y] - y) * percent;
 				y += (frames[frame + Y] - y) * percent;
 			}
 			}
-			if (setupPose) {
+			if (pose == MixPose.Setup) {
 				bone.x = bone.data.x + x * alpha;
 				bone.x = bone.data.x + x * alpha;
 				bone.y = bone.data.y + y * alpha;
 				bone.y = bone.data.y + y * alpha;
 			} else {
 			} else {
@@ -354,14 +388,20 @@ namespace Spine {
 			: base(frameCount) {
 			: base(frameCount) {
 		}
 		}
 
 
-		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, bool setupPose, bool mixingOut) {
+		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixPose pose, MixDirection direction) {
 			Bone bone = skeleton.bones.Items[boneIndex];
 			Bone bone = skeleton.bones.Items[boneIndex];
 
 
 			float[] frames = this.frames;
 			float[] frames = this.frames;
 			if (time < frames[0]) {
 			if (time < frames[0]) {
-				if (setupPose) {
+				switch (pose) {
+				case MixPose.Setup:
 					bone.scaleX = bone.data.scaleX;
 					bone.scaleX = bone.data.scaleX;
 					bone.scaleY = bone.data.scaleY;
 					bone.scaleY = bone.data.scaleY;
+					return;
+				case MixPose.Current:
+					bone.scaleX += (bone.data.scaleX - bone.scaleX) * alpha;
+					bone.scaleY += (bone.data.scaleY - bone.scaleY) * alpha;
+					return;
 				}
 				}
 				return;
 				return;
 			}
 			}
@@ -387,7 +427,7 @@ namespace Spine {
 				bone.scaleY = y;
 				bone.scaleY = y;
 			} else {
 			} else {
 				float bx, by;
 				float bx, by;
-				if (setupPose) {
+				if (pose == MixPose.Setup) {
 					bx = bone.data.scaleX;
 					bx = bone.data.scaleX;
 					by = bone.data.scaleY;
 					by = bone.data.scaleY;
 				} else {
 				} else {
@@ -395,7 +435,7 @@ namespace Spine {
 					by = bone.scaleY;
 					by = bone.scaleY;
 				}
 				}
 				// Mixing out uses sign of setup or current pose, else use sign of key.
 				// Mixing out uses sign of setup or current pose, else use sign of key.
-				if (mixingOut) {
+				if (direction == MixDirection.Out) {
 					x = Math.Abs(x) * Math.Sign(bx);
 					x = Math.Abs(x) * Math.Sign(bx);
 					y = Math.Abs(y) * Math.Sign(by);
 					y = Math.Abs(y) * Math.Sign(by);
 				} else {
 				} else {
@@ -417,13 +457,19 @@ namespace Spine {
 			: base(frameCount) {
 			: base(frameCount) {
 		}
 		}
 
 
-		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, bool setupPose, bool mixingOut) {
+		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixPose pose, MixDirection direction) {
 			Bone bone = skeleton.bones.Items[boneIndex];
 			Bone bone = skeleton.bones.Items[boneIndex];
 			float[] frames = this.frames;
 			float[] frames = this.frames;
 			if (time < frames[0]) {
 			if (time < frames[0]) {
-				if (setupPose) {
+				switch (pose) {
+				case MixPose.Setup:
 					bone.shearX = bone.data.shearX;
 					bone.shearX = bone.data.shearX;
 					bone.shearY = bone.data.shearY;
 					bone.shearY = bone.data.shearY;
+					return;
+				case MixPose.Current:
+					bone.shearX += (bone.data.shearX - bone.shearX) * alpha;
+					bone.shearY += (bone.data.shearY - bone.shearY) * alpha;
+					return;
 				}
 				}
 				return;
 				return;
 			}
 			}
@@ -444,7 +490,7 @@ namespace Spine {
 				x = x + (frames[frame + X] - x) * percent;
 				x = x + (frames[frame + X] - x) * percent;
 				y = y + (frames[frame + Y] - y) * percent;
 				y = y + (frames[frame + Y] - y) * percent;
 			}
 			}
-			if (setupPose) {
+			if (pose == MixPose.Setup) {
 				bone.shearX = bone.data.shearX + x * alpha;
 				bone.shearX = bone.data.shearX + x * alpha;
 				bone.shearY = bone.data.shearY + y * alpha;
 				bone.shearY = bone.data.shearY + y * alpha;
 			} else {
 			} else {
@@ -484,16 +530,24 @@ namespace Spine {
 			frames[frameIndex + A] = a;
 			frames[frameIndex + A] = a;
 		}
 		}
 
 
-		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, bool setupPose, bool mixingOut) {
+		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixPose pose, MixDirection direction) {
 			Slot slot = skeleton.slots.Items[slotIndex];
 			Slot slot = skeleton.slots.Items[slotIndex];
 			float[] frames = this.frames;
 			float[] frames = this.frames;
 			if (time < frames[0]) {
 			if (time < frames[0]) {
-				if (setupPose) {
-					var slotData = slot.data;
+				var slotData = slot.data;
+				switch (pose) {
+				case MixPose.Setup:
 					slot.r = slotData.r;
 					slot.r = slotData.r;
 					slot.g = slotData.g;
 					slot.g = slotData.g;
 					slot.b = slotData.b;
 					slot.b = slotData.b;
 					slot.a = slotData.a;
 					slot.a = slotData.a;
+					return;
+				case MixPose.Current:
+					slot.r += (slot.r - slotData.r) * alpha;
+					slot.g += (slot.g - slotData.g) * alpha;
+					slot.b += (slot.b - slotData.b) * alpha;
+					slot.a += (slot.a - slotData.a) * alpha;
+					return;
 				}
 				}
 				return;
 				return;
 			}
 			}
@@ -528,7 +582,7 @@ namespace Spine {
 				slot.a = a;
 				slot.a = a;
 			} else {
 			} else {
 				float br, bg, bb, ba;
 				float br, bg, bb, ba;
-				if (setupPose) {
+				if (pose == MixPose.Setup) {
 					br = slot.data.r;
 					br = slot.data.r;
 					bg = slot.data.g;
 					bg = slot.data.g;
 					bb = slot.data.b;
 					bb = slot.data.b;
@@ -587,14 +641,15 @@ namespace Spine {
 			frames[frameIndex + B2] = b2;
 			frames[frameIndex + B2] = b2;
 		}
 		}
 
 
-		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, bool setupPose, bool mixingOut) {
+		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixPose pose, MixDirection direction) {
 			Slot slot = skeleton.slots.Items[slotIndex];
 			Slot slot = skeleton.slots.Items[slotIndex];
 			float[] frames = this.frames;
 			float[] frames = this.frames;
 			if (time < frames[0]) { // Time is before first frame.
 			if (time < frames[0]) { // Time is before first frame.
-				if (setupPose) {
-//					slot.color.set(slot.data.color);
-//					slot.darkColor.set(slot.data.darkColor);
-					var slotData = slot.data;
+				var slotData = slot.data;
+				switch (pose) {
+				case MixPose.Setup:
+					//	slot.color.set(slot.data.color);
+					//	slot.darkColor.set(slot.data.darkColor);
 					slot.r = slotData.r;
 					slot.r = slotData.r;
 					slot.g = slotData.g;
 					slot.g = slotData.g;
 					slot.b = slotData.b;
 					slot.b = slotData.b;
@@ -602,6 +657,16 @@ namespace Spine {
 					slot.r2 = slotData.r2;
 					slot.r2 = slotData.r2;
 					slot.g2 = slotData.g2;
 					slot.g2 = slotData.g2;
 					slot.b2 = slotData.b2;
 					slot.b2 = slotData.b2;
+					return;
+				case MixPose.Current:
+					slot.r += (slot.r - slotData.r) * alpha;
+					slot.g += (slot.g - slotData.g) * alpha;
+					slot.b += (slot.b - slotData.b) * alpha;
+					slot.a += (slot.a - slotData.a) * alpha;
+					slot.r2 += (slot.r2 - slotData.r2) * alpha;
+					slot.g2 += (slot.g2 - slotData.g2) * alpha;
+					slot.b2 += (slot.b2 - slotData.b2) * alpha;
+					return;
 				}
 				}
 				return;
 				return;
 			}
 			}
@@ -648,7 +713,7 @@ namespace Spine {
 				slot.b2 = b2;
 				slot.b2 = b2;
 			} else {
 			} else {
 				float br, bg, bb, ba, br2, bg2, bb2;
 				float br, bg, bb, ba, br2, bg2, bb2;
-				if (setupPose) {
+				if (pose == MixPose.Setup) {
 					br = slot.data.r;
 					br = slot.data.r;
 					bg = slot.data.g;
 					bg = slot.data.g;
 					bb = slot.data.b;
 					bb = slot.data.b;
@@ -702,10 +767,10 @@ namespace Spine {
 			attachmentNames[frameIndex] = attachmentName;
 			attachmentNames[frameIndex] = attachmentName;
 		}
 		}
 
 
-		public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, bool setupPose, bool mixingOut) {
+		public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixPose pose, MixDirection direction) {
 			string attachmentName;
 			string attachmentName;
 			Slot slot = skeleton.slots.Items[slotIndex];
 			Slot slot = skeleton.slots.Items[slotIndex];
-			if (mixingOut && setupPose) {
+			if (direction == MixDirection.Out && pose == MixPose.Setup) {
 				attachmentName = slot.data.attachmentName;
 				attachmentName = slot.data.attachmentName;
 				slot.Attachment = attachmentName == null ? null : skeleton.GetAttachment(slotIndex, attachmentName);
 				slot.Attachment = attachmentName == null ? null : skeleton.GetAttachment(slotIndex, attachmentName);
 				return;
 				return;
@@ -713,7 +778,7 @@ namespace Spine {
 
 
 			float[] frames = this.frames;
 			float[] frames = this.frames;
 			if (time < frames[0]) { // Time is before first frame.
 			if (time < frames[0]) { // Time is before first frame.
-				if (setupPose) {
+				if (pose == MixPose.Setup) {
 					attachmentName = slot.data.attachmentName;
 					attachmentName = slot.data.attachmentName;
 					slot.Attachment = attachmentName == null ? null : skeleton.GetAttachment(slotIndex, attachmentName);
 					slot.Attachment = attachmentName == null ? null : skeleton.GetAttachment(slotIndex, attachmentName);
 				}
 				}
@@ -758,33 +823,44 @@ namespace Spine {
 			frameVertices[frameIndex] = vertices;
 			frameVertices[frameIndex] = vertices;
 		}
 		}
 
 
-		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, bool setupPose, bool mixingOut) {
+		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixPose pose, MixDirection direction) {
 			Slot slot = skeleton.slots.Items[slotIndex];
 			Slot slot = skeleton.slots.Items[slotIndex];
 			VertexAttachment slotAttachment = slot.attachment as VertexAttachment;
 			VertexAttachment slotAttachment = slot.attachment as VertexAttachment;
 			if (slotAttachment == null || !slotAttachment.ApplyDeform(attachment)) return;
 			if (slotAttachment == null || !slotAttachment.ApplyDeform(attachment)) return;
 
 
 			var verticesArray = slot.attachmentVertices;
 			var verticesArray = slot.attachmentVertices;
-			float[] frames = this.frames;
-			if (time < frames[0]) {
-				if (setupPose) verticesArray.Clear();
-				return;
-			}
-
 			float[][] frameVertices = this.frameVertices;
 			float[][] frameVertices = this.frameVertices;
 			int vertexCount = frameVertices[0].Length;
 			int vertexCount = frameVertices[0].Length;
-
-			if (verticesArray.Count != vertexCount && !setupPose) alpha = 1; // Don't mix from uninitialized slot vertices.
+			if (verticesArray.Count != vertexCount && pose != MixPose.Setup) alpha = 1; // Don't mix from uninitialized slot vertices.
 			// verticesArray.SetSize(vertexCount) // Ensure size and preemptively set count.
 			// verticesArray.SetSize(vertexCount) // Ensure size and preemptively set count.
 			if (verticesArray.Capacity < vertexCount) verticesArray.Capacity = vertexCount;
 			if (verticesArray.Capacity < vertexCount) verticesArray.Capacity = vertexCount;
 			verticesArray.Count = vertexCount;
 			verticesArray.Count = vertexCount;
 			float[] vertices = verticesArray.Items;
 			float[] vertices = verticesArray.Items;
 
 
+			float[] frames = this.frames;
+			if (time < frames[0]) {
+				switch (pose) {
+				case MixPose.Setup:
+					verticesArray.Clear();
+					return;
+				case MixPose.Current:
+					alpha = 1 - alpha;
+					for (int i = 0; i < vertexCount; i++)
+						vertices[i] *= alpha;
+
+					return;
+				}
+				return;
+			}
+
+
+
 			if (time >= frames[frames.Length - 1]) { // Time is after last frame.
 			if (time >= frames[frames.Length - 1]) { // Time is after last frame.
 				float[] lastVertices = frameVertices[frames.Length - 1];
 				float[] lastVertices = frameVertices[frames.Length - 1];
 				if (alpha == 1) {
 				if (alpha == 1) {
 					// Vertex positions or deform offsets, no alpha.
 					// Vertex positions or deform offsets, no alpha.
 					Array.Copy(lastVertices, 0, vertices, 0, vertexCount);
 					Array.Copy(lastVertices, 0, vertices, 0, vertexCount);
-				} else if (setupPose) {
+				} else if (pose == MixPose.Setup) {
 					VertexAttachment vertexAttachment = slotAttachment;
 					VertexAttachment vertexAttachment = slotAttachment;
 					if (vertexAttachment.bones == null) {
 					if (vertexAttachment.bones == null) {
 						// Unweighted vertex positions, with alpha.
 						// Unweighted vertex positions, with alpha.
@@ -819,7 +895,7 @@ namespace Spine {
 					float prev = prevVertices[i];
 					float prev = prevVertices[i];
 					vertices[i] = prev + (nextVertices[i] - prev) * percent;
 					vertices[i] = prev + (nextVertices[i] - prev) * percent;
 				}
 				}
-			} else if (setupPose) {
+			} else if (pose == MixPose.Setup) {
 				VertexAttachment vertexAttachment = (VertexAttachment)slotAttachment;
 				VertexAttachment vertexAttachment = (VertexAttachment)slotAttachment;
 				if (vertexAttachment.bones == null) {
 				if (vertexAttachment.bones == null) {
 					// Unweighted vertex positions, with alpha.
 					// Unweighted vertex positions, with alpha.
@@ -869,13 +945,13 @@ namespace Spine {
 		}
 		}
 
 
 		/// <summary>Fires events for frames &gt; lastTime and &lt;= time.</summary>
 		/// <summary>Fires events for frames &gt; lastTime and &lt;= time.</summary>
-		public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, bool setupPose, bool mixingOut) {
+		public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixPose pose, MixDirection direction) {
 			if (firedEvents == null) return;
 			if (firedEvents == null) return;
 			float[] frames = this.frames;
 			float[] frames = this.frames;
 			int frameCount = frames.Length;
 			int frameCount = frames.Length;
 
 
 			if (lastTime > time) { // Fire events after last time for looped animations.
 			if (lastTime > time) { // Fire events after last time for looped animations.
-				Apply(skeleton, lastTime, int.MaxValue, firedEvents, alpha, setupPose, mixingOut);
+				Apply(skeleton, lastTime, int.MaxValue, firedEvents, alpha, pose, direction);
 				lastTime = -1f;
 				lastTime = -1f;
 			} else if (lastTime >= frames[frameCount - 1]) // Last time is after last frame.
 			} else if (lastTime >= frames[frameCount - 1]) // Last time is after last frame.
 				return;
 				return;
@@ -921,17 +997,17 @@ namespace Spine {
 			drawOrders[frameIndex] = drawOrder;
 			drawOrders[frameIndex] = drawOrder;
 		}
 		}
 
 
-		public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, bool setupPose, bool mixingOut) {
+		public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixPose pose, MixDirection direction) {
 			ExposedList<Slot> drawOrder = skeleton.drawOrder;
 			ExposedList<Slot> drawOrder = skeleton.drawOrder;
 			ExposedList<Slot> slots = skeleton.slots;
 			ExposedList<Slot> slots = skeleton.slots;
-			if (mixingOut && setupPose) {
+			if (direction == MixDirection.Out && pose == MixPose.Setup) {
 				Array.Copy(slots.Items, 0, drawOrder.Items, 0, slots.Count);
 				Array.Copy(slots.Items, 0, drawOrder.Items, 0, slots.Count);
 				return;
 				return;
 			}
 			}
 
 
 			float[] frames = this.frames;
 			float[] frames = this.frames;
 			if (time < frames[0]) {
 			if (time < frames[0]) {
-				if (setupPose) Array.Copy(slots.Items, 0, drawOrder.Items, 0, slots.Count);
+				if (pose == MixPose.Setup) Array.Copy(slots.Items, 0, drawOrder.Items, 0, slots.Count);
 				return;
 				return;
 			}
 			}
 
 
@@ -983,25 +1059,31 @@ namespace Spine {
 			frames[frameIndex + BEND_DIRECTION] = bendDirection;
 			frames[frameIndex + BEND_DIRECTION] = bendDirection;
 		}
 		}
 
 
-		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, bool setupPose, bool mixingOut) {
+		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixPose pose, MixDirection direction) {
 			IkConstraint constraint = skeleton.ikConstraints.Items[ikConstraintIndex];
 			IkConstraint constraint = skeleton.ikConstraints.Items[ikConstraintIndex];
 			float[] frames = this.frames;
 			float[] frames = this.frames;
 			if (time < frames[0]) {
 			if (time < frames[0]) {
-				if (setupPose) {
+				switch (pose) {
+				case MixPose.Setup:
 					constraint.mix = constraint.data.mix;
 					constraint.mix = constraint.data.mix;
 					constraint.bendDirection = constraint.data.bendDirection;
 					constraint.bendDirection = constraint.data.bendDirection;
+					return;
+				case MixPose.Current:
+					constraint.mix += (constraint.data.mix - constraint.mix) * alpha;
+					constraint.bendDirection = constraint.data.bendDirection;
+					return;
 				}
 				}
 				return;
 				return;
 			}
 			}
 
 
 			if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
 			if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
-				if (setupPose) {
+				if (pose == MixPose.Setup) {
 					constraint.mix = constraint.data.mix + (frames[frames.Length + PREV_MIX] - constraint.data.mix) * alpha;
 					constraint.mix = constraint.data.mix + (frames[frames.Length + PREV_MIX] - constraint.data.mix) * alpha;
-					constraint.bendDirection = mixingOut ? constraint.data.bendDirection
+					constraint.bendDirection = direction == MixDirection.Out ? constraint.data.bendDirection
 						: (int)frames[frames.Length + PREV_BEND_DIRECTION];
 						: (int)frames[frames.Length + PREV_BEND_DIRECTION];
 				} else {
 				} else {
 					constraint.mix += (frames[frames.Length + PREV_MIX] - constraint.mix) * alpha;
 					constraint.mix += (frames[frames.Length + PREV_MIX] - constraint.mix) * alpha;
-					if (!mixingOut) constraint.bendDirection = (int)frames[frames.Length + PREV_BEND_DIRECTION];
+					if (direction == MixDirection.In) constraint.bendDirection = (int)frames[frames.Length + PREV_BEND_DIRECTION];
 				}
 				}
 				return;
 				return;
 			}
 			}
@@ -1012,12 +1094,12 @@ namespace Spine {
 			float frameTime = frames[frame];
 			float frameTime = frames[frame];
 			float percent = GetCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
 			float percent = GetCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
 
 
-			if (setupPose) {
+			if (pose == MixPose.Setup) {
 				constraint.mix = constraint.data.mix + (mix + (frames[frame + MIX] - mix) * percent - constraint.data.mix) * alpha;
 				constraint.mix = constraint.data.mix + (mix + (frames[frame + MIX] - mix) * percent - constraint.data.mix) * alpha;
-				constraint.bendDirection = mixingOut ? constraint.data.bendDirection : (int)frames[frame + PREV_BEND_DIRECTION];
+				constraint.bendDirection = direction == MixDirection.Out ? constraint.data.bendDirection : (int)frames[frame + PREV_BEND_DIRECTION];
 			} else {
 			} else {
 				constraint.mix += (mix + (frames[frame + MIX] - mix) * percent - constraint.mix) * alpha;
 				constraint.mix += (mix + (frames[frame + MIX] - mix) * percent - constraint.mix) * alpha;
-				if (!mixingOut) constraint.bendDirection = (int)frames[frame + PREV_BEND_DIRECTION];
+				if (direction == MixDirection.In) constraint.bendDirection = (int)frames[frame + PREV_BEND_DIRECTION];
 			}
 			}
 		}
 		}
 	}
 	}
@@ -1051,16 +1133,24 @@ namespace Spine {
 			frames[frameIndex + SHEAR] = shearMix;
 			frames[frameIndex + SHEAR] = shearMix;
 		}
 		}
 
 
-		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, bool setupPose, bool mixingOut) {
+		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixPose pose, MixDirection direction) {
 			TransformConstraint constraint = skeleton.transformConstraints.Items[transformConstraintIndex];
 			TransformConstraint constraint = skeleton.transformConstraints.Items[transformConstraintIndex];
 			float[] frames = this.frames;
 			float[] frames = this.frames;
 			if (time < frames[0]) {
 			if (time < frames[0]) {
-				if (setupPose) {
-					var data = constraint.data;
+				var data = constraint.data;
+				switch (pose) {
+				case MixPose.Setup:
 					constraint.rotateMix = data.rotateMix;
 					constraint.rotateMix = data.rotateMix;
 					constraint.translateMix = data.translateMix;
 					constraint.translateMix = data.translateMix;
 					constraint.scaleMix = data.scaleMix;
 					constraint.scaleMix = data.scaleMix;
 					constraint.shearMix = data.shearMix;
 					constraint.shearMix = data.shearMix;
+					return;
+				case MixPose.Current:
+					constraint.rotateMix += (data.rotateMix - constraint.rotateMix) * alpha;
+					constraint.translateMix += (data.translateMix - constraint.translateMix) * alpha;
+					constraint.scaleMix += (data.scaleMix - constraint.scaleMix) * alpha;
+					constraint.shearMix += (data.shearMix - constraint.shearMix) * alpha;
+					return;
 				}
 				}
 				return;
 				return;
 			}
 			}
@@ -1088,7 +1178,7 @@ namespace Spine {
 				scale += (frames[frame + SCALE] - scale) * percent;
 				scale += (frames[frame + SCALE] - scale) * percent;
 				shear += (frames[frame + SHEAR] - shear) * percent;
 				shear += (frames[frame + SHEAR] - shear) * percent;
 			}
 			}
-			if (setupPose) {
+			if (pose == MixPose.Setup) {
 				TransformConstraintData data = constraint.data;
 				TransformConstraintData data = constraint.data;
 				constraint.rotateMix = data.rotateMix + (rotate - data.rotateMix) * alpha;
 				constraint.rotateMix = data.rotateMix + (rotate - data.rotateMix) * alpha;
 				constraint.translateMix = data.translateMix + (translate - data.translateMix) * alpha;
 				constraint.translateMix = data.translateMix + (translate - data.translateMix) * alpha;
@@ -1130,11 +1220,18 @@ namespace Spine {
 			frames[frameIndex + VALUE] = value;
 			frames[frameIndex + VALUE] = value;
 		}
 		}
 
 
-		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, bool setupPose, bool mixingOut) {
+		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixPose pose, MixDirection direction) {
 			PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex];
 			PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex];
 			float[] frames = this.frames;
 			float[] frames = this.frames;
 			if (time < frames[0]) {
 			if (time < frames[0]) {
-				if (setupPose) constraint.position = constraint.data.position;
+				switch (pose) {
+				case MixPose.Setup:
+					constraint.position = constraint.data.position;
+					return;
+				case MixPose.Current:
+					constraint.position += (constraint.data.position - constraint.position) * alpha;
+					return;
+				}
 				return;
 				return;
 			}
 			}
 
 
@@ -1151,7 +1248,7 @@ namespace Spine {
 
 
 				position += (frames[frame + VALUE] - position) * percent;
 				position += (frames[frame + VALUE] - position) * percent;
 			}
 			}
-			if (setupPose)
+			if (pose == MixPose.Setup)
 				constraint.position = constraint.data.position + (position - constraint.data.position) * alpha;
 				constraint.position = constraint.data.position + (position - constraint.data.position) * alpha;
 			else
 			else
 				constraint.position += (position - constraint.position) * alpha;
 				constraint.position += (position - constraint.position) * alpha;
@@ -1167,11 +1264,18 @@ namespace Spine {
 			: base(frameCount) {
 			: base(frameCount) {
 		}
 		}
 
 
-		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, bool setupPose, bool mixingOut) {
+		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixPose pose, MixDirection direction) {
 			PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex];
 			PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex];
 			float[] frames = this.frames;
 			float[] frames = this.frames;
 			if (time < frames[0]) {
 			if (time < frames[0]) {
-				if (setupPose) constraint.spacing = constraint.data.spacing;
+				switch (pose) {
+				case MixPose.Setup:
+					constraint.spacing = constraint.data.spacing;
+					return;
+				case MixPose.Current:
+					constraint.spacing += (constraint.data.spacing - constraint.spacing) * alpha;
+					return;
+				}
 				return;
 				return;
 			}
 			}
 
 
@@ -1189,7 +1293,7 @@ namespace Spine {
 				spacing += (frames[frame + VALUE] - spacing) * percent;
 				spacing += (frames[frame + VALUE] - spacing) * percent;
 			}
 			}
 
 
-			if (setupPose)
+			if (pose == MixPose.Setup)
 				constraint.spacing = constraint.data.spacing + (spacing - constraint.data.spacing) * alpha;
 				constraint.spacing = constraint.data.spacing + (spacing - constraint.data.spacing) * alpha;
 			else
 			else
 				constraint.spacing += (spacing - constraint.spacing) * alpha;
 				constraint.spacing += (spacing - constraint.spacing) * alpha;
@@ -1224,13 +1328,19 @@ namespace Spine {
 			frames[frameIndex + TRANSLATE] = translateMix;
 			frames[frameIndex + TRANSLATE] = translateMix;
 		}
 		}
 
 
-		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, bool setupPose, bool mixingOut) {
+		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixPose pose, MixDirection direction) {
 			PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex];
 			PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex];
 			float[] frames = this.frames;
 			float[] frames = this.frames;
 			if (time < frames[0]) {
 			if (time < frames[0]) {
-				if (setupPose) {
+				switch (pose) {
+				case MixPose.Setup:
 					constraint.rotateMix = constraint.data.rotateMix;
 					constraint.rotateMix = constraint.data.rotateMix;
 					constraint.translateMix = constraint.data.translateMix;
 					constraint.translateMix = constraint.data.translateMix;
+					return;
+				case MixPose.Current:
+					constraint.rotateMix += (constraint.data.rotateMix - constraint.rotateMix) * alpha;
+					constraint.translateMix += (constraint.data.translateMix - constraint.translateMix) * alpha;
+					return;
 				}
 				}
 				return;
 				return;
 			}
 			}
@@ -1252,7 +1362,7 @@ namespace Spine {
 				translate += (frames[frame + TRANSLATE] - translate) * percent;
 				translate += (frames[frame + TRANSLATE] - translate) * percent;
 			}
 			}
 
 
-			if (setupPose) {
+			if (pose == MixPose.Setup) {
 				constraint.rotateMix = constraint.data.rotateMix + (rotate - constraint.data.rotateMix) * alpha;
 				constraint.rotateMix = constraint.data.rotateMix + (rotate - constraint.data.rotateMix) * alpha;
 				constraint.translateMix = constraint.data.translateMix + (translate - constraint.data.translateMix) * alpha;
 				constraint.translateMix = constraint.data.translateMix + (translate - constraint.data.translateMix) * alpha;
 			} else {
 			} else {

+ 23 - 25
spine-csharp/src/AnimationState.cs

@@ -171,11 +171,12 @@ namespace Spine {
 				TrackEntry current = tracksItems[i];
 				TrackEntry current = tracksItems[i];
 				if (current == null || current.delay > 0) continue;
 				if (current == null || current.delay > 0) continue;
 				applied = true;
 				applied = true;
+				MixPose currentPose = i == 0 ? MixPose.Current : MixPose.CurrentLayered;
 
 
 				// Apply mixing from entries first.
 				// Apply mixing from entries first.
 				float mix = current.alpha;
 				float mix = current.alpha;
 				if (current.mixingFrom != null)
 				if (current.mixingFrom != null)
-					mix *= ApplyMixingFrom(current, skeleton);
+					mix *= ApplyMixingFrom(current, skeleton, currentPose);
 				else if (current.trackTime >= current.trackEnd && current.next == null) //
 				else if (current.trackTime >= current.trackEnd && current.next == null) //
 					mix = 0; // Set to setup pose the last time the entry will be applied.
 					mix = 0; // Set to setup pose the last time the entry will be applied.
 
 
@@ -186,7 +187,7 @@ namespace Spine {
 				var timelinesItems = timelines.Items;
 				var timelinesItems = timelines.Items;
 				if (mix == 1) {
 				if (mix == 1) {
 					for (int ii = 0; ii < timelineCount; ii++)
 					for (int ii = 0; ii < timelineCount; ii++)
-						timelinesItems[ii].Apply(skeleton, animationLast, animationTime, events, 1, true, false);
+						timelinesItems[ii].Apply(skeleton, animationLast, animationTime, events, 1, MixPose.Setup, MixDirection.In);
 				} else {
 				} else {
 					var timelineData = current.timelineData.Items;
 					var timelineData = current.timelineData.Items;
 
 
@@ -196,13 +197,12 @@ namespace Spine {
 
 
 					for (int ii = 0; ii < timelineCount; ii++) {
 					for (int ii = 0; ii < timelineCount; ii++) {
 						Timeline timeline = timelinesItems[ii];
 						Timeline timeline = timelinesItems[ii];
+						MixPose pose = timelineData[ii] >= FIRST ? MixPose.Setup : currentPose;
 						var rotateTimeline = timeline as RotateTimeline;
 						var rotateTimeline = timeline as RotateTimeline;
-						if (rotateTimeline != null) {
-							ApplyRotateTimeline(rotateTimeline, skeleton, animationTime, mix, timelineData[ii] >= FIRST, timelinesRotation,
-								ii << 1, firstFrame);
-						} else {
-							timeline.Apply(skeleton, animationLast, animationTime, events, mix, timelineData[ii] > FIRST, false);
-						}
+						if (rotateTimeline != null)
+							ApplyRotateTimeline(rotateTimeline, skeleton, animationTime, mix, pose, timelinesRotation, ii << 1, firstFrame);
+						else
+							timeline.Apply(skeleton, animationLast, animationTime, events, mix, pose, MixDirection.In);
 					}
 					}
 				}
 				}
 				QueueEvents(current, animationTime);
 				QueueEvents(current, animationTime);
@@ -215,9 +215,9 @@ namespace Spine {
 			return applied;
 			return applied;
 		}
 		}
 
 
-		private float ApplyMixingFrom (TrackEntry to, Skeleton skeleton) {
+		private float ApplyMixingFrom (TrackEntry to, Skeleton skeleton, MixPose currentPose) {
 			TrackEntry from = to.mixingFrom;
 			TrackEntry from = to.mixingFrom;
-			if (from.mixingFrom != null) ApplyMixingFrom(from, skeleton);
+			if (from.mixingFrom != null) ApplyMixingFrom(from, skeleton, currentPose);
 
 
 			float mix;
 			float mix;
 			if (to.mixDuration == 0) // Single frame mix to undo mixingFrom changes.
 			if (to.mixDuration == 0) // Single frame mix to undo mixingFrom changes.
@@ -240,26 +240,28 @@ namespace Spine {
 			if (firstFrame) from.timelinesRotation.Resize(timelines.Count << 1); // from.timelinesRotation.setSize
 			if (firstFrame) from.timelinesRotation.Resize(timelines.Count << 1); // from.timelinesRotation.setSize
 			var timelinesRotation = from.timelinesRotation.Items;
 			var timelinesRotation = from.timelinesRotation.Items;
 
 
-			bool first;
+			MixPose pose;
 			float alphaDip = from.alpha * to.interruptAlpha, alphaMix = alphaDip * (1 - mix), alpha;
 			float alphaDip = from.alpha * to.interruptAlpha, alphaMix = alphaDip * (1 - mix), alpha;
 			from.totalAlpha = 0;
 			from.totalAlpha = 0;
 			for (int i = 0; i < timelineCount; i++) {
 			for (int i = 0; i < timelineCount; i++) {
 				Timeline timeline = timelinesItems[i];
 				Timeline timeline = timelinesItems[i];
 				switch (timelineData[i]) {
 				switch (timelineData[i]) {
 				case SUBSEQUENT:
 				case SUBSEQUENT:
-					first = false;
+					if (!attachments && timeline is AttachmentTimeline) continue;
+					if (!drawOrder && timeline is DrawOrderTimeline) continue;
+					pose = currentPose;
 					alpha = alphaMix;
 					alpha = alphaMix;
 					break;
 					break;
 				case FIRST:
 				case FIRST:
-					first = true;
+					pose = MixPose.Setup;
 					alpha = alphaMix;
 					alpha = alphaMix;
 					break;
 					break;
 				case DIP:
 				case DIP:
-					first = true;
+					pose = MixPose.Setup;
 					alpha = alphaDip;
 					alpha = alphaDip;
 					break;
 					break;
 				default:
 				default:
-					first = true;
+					pose = MixPose.Setup;
 					alpha = alphaDip;
 					alpha = alphaDip;
 					var dipMix = timelineDipMix[i];
 					var dipMix = timelineDipMix[i];
 					alpha *= Math.Max(0, 1 - dipMix.mixTime / dipMix.mixDuration);
 					alpha *= Math.Max(0, 1 - dipMix.mixTime / dipMix.mixDuration);
@@ -268,13 +270,9 @@ namespace Spine {
 				from.totalAlpha += alpha;
 				from.totalAlpha += alpha;
 				var rotateTimeline = timeline as RotateTimeline;
 				var rotateTimeline = timeline as RotateTimeline;
 				if (rotateTimeline != null) {
 				if (rotateTimeline != null) {
-					ApplyRotateTimeline(rotateTimeline, skeleton, animationTime, alpha, first, timelinesRotation, i << 1, firstFrame);
+					ApplyRotateTimeline(rotateTimeline, skeleton, animationTime, alpha, pose, timelinesRotation, i << 1, firstFrame);
 				} else {
 				} else {
-					if (!first) {
-						if (!attachments && timeline is AttachmentTimeline) continue;
-						if (!drawOrder && timeline is DrawOrderTimeline) continue;
-					}
-					timeline.Apply(skeleton, animationLast, animationTime, eventBuffer, alpha, first, true);
+					timeline.Apply(skeleton, animationLast, animationTime, eventBuffer, alpha, pose, MixDirection.Out);
 				}
 				}
 			}
 			}
 
 
@@ -286,20 +284,20 @@ namespace Spine {
 			return mix;
 			return mix;
 		}
 		}
 
 
-		static private void ApplyRotateTimeline (RotateTimeline rotateTimeline, Skeleton skeleton, float time, float alpha, bool setupPose,
+		static private void ApplyRotateTimeline (RotateTimeline rotateTimeline, Skeleton skeleton, float time, float alpha, MixPose pose,
 			float[] timelinesRotation, int i, bool firstFrame) {
 			float[] timelinesRotation, int i, bool firstFrame) {
 
 
 			if (firstFrame) timelinesRotation[i] = 0;
 			if (firstFrame) timelinesRotation[i] = 0;
 
 
 			if (alpha == 1) {
 			if (alpha == 1) {
-				rotateTimeline.Apply(skeleton, 0, time, null, 1, setupPose, false);
+				rotateTimeline.Apply(skeleton, 0, time, null, 1, pose, MixDirection.In);
 				return;
 				return;
 			}
 			}
 
 
 			Bone bone = skeleton.bones.Items[rotateTimeline.boneIndex];
 			Bone bone = skeleton.bones.Items[rotateTimeline.boneIndex];
 			float[] frames = rotateTimeline.frames;
 			float[] frames = rotateTimeline.frames;
 			if (time < frames[0]) {
 			if (time < frames[0]) {
-				if (setupPose) bone.rotation = bone.data.rotation;
+				if (pose == MixPose.Setup) bone.rotation = bone.data.rotation;
 				return;
 				return;
 			}
 			}
 
 
@@ -321,7 +319,7 @@ namespace Spine {
 			}
 			}
 
 
 			// Mix between rotations using the direction of the shortest route on the first frame while detecting crosses.
 			// Mix between rotations using the direction of the shortest route on the first frame while detecting crosses.
-			float r1 = setupPose ? bone.data.rotation : bone.rotation;
+			float r1 = pose == MixPose.Setup ? bone.data.rotation : bone.rotation;
 			float total, diff = r2 - r1;
 			float total, diff = r2 - r1;
 			if (diff == 0) {
 			if (diff == 0) {
 				total = timelinesRotation[i];
 				total = timelinesRotation[i];