Bladeren bron

[lua] Ported Animation/Timeline changes

badlogic 8 jaren geleden
bovenliggende
commit
2a973da308
4 gewijzigde bestanden met toevoegingen van 169 en 86 verwijderingen
  1. 4 0
      CHANGELOG.md
  2. 123 61
      spine-lua/Animation.lua
  3. 34 25
      spine-lua/AnimationState.lua
  4. 8 0
      spine-lua/attachments/VertexAttachment.lua

+ 4 - 0
CHANGELOG.md

@@ -45,6 +45,7 @@
   * Added `spClippingAttachment` and respective enum.
   * Added `spSkeletonClipper` and `spTriangulator`, used to implement software clipping of attachments.
   * `AnimationState#apply` returns boolean indicating if any timeline was applied or not.
+  * `Animation#apply` and `Timeline#apply`` now take enums `MixPose` and `MixDirection` instead of booleans
 
 ### Cocos2d-X
  * Fixed renderer to work with 3.6 changes
@@ -76,6 +77,7 @@
    *  `MeshAttachment.parentMesh` is now a private field to enforce using the `.ParentMesh` setter property in external code. The `MeshAttachment.ParentMesh` property is an appropriate replacement wherever `.parentMesh` was used.
  * **Additions**
    * `AnimationState#apply` returns boolean indicating if any timeline was applied or not.
+   * `Animation#apply` and `Timeline#apply`` now take enums `MixPose` and `MixDirection` instead of booleans
 
 ### Unity
  * Refactored renderer to work with new 3.6 features.
@@ -133,6 +135,7 @@
   * Added `ClippingAttachment`, additional method `newClippingAttachment` in `AttachmentLoader` interface.
   * Added `SkeletonClipper` and `Triangulator`, used to implement software clipping of attachments.
   * `AnimationState#apply` returns boolean indicating if any timeline was applied or not.
+  * `Animation#apply` and `Timeline#apply`` now take enums `MixPose` and `MixDirection` instead of booleans
 
 ### libGDX
  * Fixed renderer to work with 3.6 changes
@@ -154,6 +157,7 @@
   * Added `ClippingAttachment`, additional method `newClippingAttachment` in `AttachmentLoader` interface.
   * Added `SkeletonClipper` and `Triangulator`, used to implement software clipping of attachments.
   * `AnimationState#apply` returns boolean indicating if any timeline was applied or not.
+  * `Animation#apply` and `Timeline#apply`` now take enums `MixPose` and `MixDirection` instead of booleans
 
 ### Love2D
   * Fixed renderer to work with 3.6 changes

+ 123 - 61
spine-lua/Animation.lua

@@ -54,7 +54,7 @@ function Animation.new (name, timelines, duration)
 		duration = duration
 	}
 
-	function self:apply (skeleton, lastTime, time, loop, events, alpha, setupPose, mixingOut)
+	function self:apply (skeleton, lastTime, time, loop, events, alpha, pose, direction)
 		if not skeleton then error("skeleton cannot be nil.", 2) end
 
 		if loop and duration > 0 then
@@ -63,7 +63,7 @@ function Animation.new (name, timelines, duration)
 		end
 
 		for i,timeline in ipairs(self.timelines) do
-			timeline:apply(skeleton, lastTime, time, events, alpha, setupPose, mixingOut)
+			timeline:apply(skeleton, lastTime, time, events, alpha, pose, direction)
 		end
 	end
 
@@ -113,6 +113,18 @@ local function linearSearch (values, target, step)
 	return -1
 end
 
+Animation.MixPose = {
+	setup = 0,
+	current = 1,
+	currentLayered = 2
+}
+local MixPose = Animation.MixPose
+
+Animation.MixDirection = {
+	_in = 0, out = 1
+}
+local MixDirection = Animation.MixDirection
+
 Animation.TimelineType = {
 	rotate = 0, translate = 1, scale = 2, shear = 3,
 	attachment = 4, color = 5, deform = 6,
@@ -123,6 +135,7 @@ Animation.TimelineType = {
 }
 local TimelineType = Animation.TimelineType
 local SHL_24 = 16777216
+local SHL_27 = 134217728
 
 Animation.CurveTimeline = {}
 function Animation.CurveTimeline.new (frameCount)
@@ -242,19 +255,23 @@ function Animation.RotateTimeline.new (frameCount)
 		self.frames[frameIndex + ROTATION] = degrees
 	end
 
-	function self:apply (skeleton, lastTime, time, firedEvents, alpha, setupPose, mixingOut)
+	function self:apply (skeleton, lastTime, time, firedEvents, alpha, pose, direction)
 		local frames = self.frames
 
 		local bone = skeleton.bones[self.boneIndex]
 		if time < frames[0] then
-			if setupPose then
+			if pose == MixPose.setup then
 				bone.rotation = bone.data.rotation
+			elseif pose == MixPose.current then
+				local r = bone.data.rotation - bone.rotation
+				r = r - (16384 - math_floor(16384.499999999996 - r / 360)) * 360 -- Wrap within -180 and 180.
+				bone.rotation = bone.rotation + r * alpha
 			end
 			return
 		end
 
 		if time >= frames[zlen(frames) - ENTRIES] then -- Time is after last frame.
-			if setupPose then
+			if pose == MixPose.setup then
 				bone.rotation = bone.data.rotation + frames[zlen(frames) + PREV_ROTATION] * alpha
 			else
 				local r = bone.data.rotation + frames[zlen(frames) + PREV_ROTATION] - bone.rotation
@@ -273,7 +290,7 @@ function Animation.RotateTimeline.new (frameCount)
 		local r = frames[frame + ROTATION] - prevRotation
 		r = r - (16384 - math_floor(16384.499999999996 - r / 360)) * 360
 		r = prevRotation + r * percent
-		if setupPose then
+		if pose == MixPose.setup then
 			r = r - (16384 - math_floor(16384.499999999996 - r / 360)) * 360
 			bone.rotation = bone.data.rotation + r * alpha
 		else
@@ -312,14 +329,17 @@ function Animation.TranslateTimeline.new (frameCount)
 		self.frames[frameIndex + Y] = y
 	end
 
-	function self:apply (skeleton, lastTime, time, firedEvents, alpha, setupPose, mixingOut)
+	function self:apply (skeleton, lastTime, time, firedEvents, alpha, pose, direction)
 		local frames = self.frames
 
 		local bone = skeleton.bones[self.boneIndex]
 		if time < frames[0] then 
-			if (setupPose) then
+			if pose == MixPose.setup then
 				bone.x = bone.data.x
 				bone.y = bone.data.y
+			elseif pose == MixPose.current then
+				bone.x = bone.x + (bone.data.x - bone.x) * alpha
+				bone.y = bone.y + (bone.data.y - bone.y) * alpha
 			end
 			return
 		end
@@ -341,7 +361,7 @@ function Animation.TranslateTimeline.new (frameCount)
 			x = x + (frames[frame + X] - x) * percent
 			y = y + (frames[frame + Y] - y) * percent
 		end
-		if setupPose then
+		if pose == MixPose.setup then
 			bone.x = bone.data.x + x * alpha
 			bone.y = bone.data.y + y * alpha
 		else
@@ -370,14 +390,17 @@ function Animation.ScaleTimeline.new (frameCount)
 		return TimelineType.scale * SHL_24 + self.boneIndex
 	end
 
-	function self:apply (skeleton, lastTime, time, firedEvents, alpha, setupPose, mixingOut)
+	function self:apply (skeleton, lastTime, time, firedEvents, alpha, pose, direction)
 		local frames = self.frames
 
 		local bone = skeleton.bones[self.boneIndex]
 		if time < frames[0] then
-			if setupPose then
+			if pose == MixPose.setup then
 				bone.scaleX = bone.data.scaleX
 				bone.scaleY = bone.data.scaleY
+			elseif pose == MixPose.current then
+				bone.scaleX = bone.scaleX + (bone.data.scaleX - bone.scaleX) * alpha
+				bone.scaleY = bone.scaleY + (bone.data.scaleY - bone.scaleY) * alpha
 			end
 			return
 		end
@@ -405,7 +428,7 @@ function Animation.ScaleTimeline.new (frameCount)
 		else
 			local bx = 0
 			local by = 0
-			if setupPose then
+			if pose == MixPose.setup then
 				bx = bone.data.scaleX
 				by = bone.data.scaleY
 			else
@@ -413,7 +436,7 @@ function Animation.ScaleTimeline.new (frameCount)
 				by = bone.scaleY
 			end
 			-- Mixing out uses sign of setup or current pose, else use sign of key.
-			if mixingOut then
+			if direction == MixDirection.out then
 				x = math_abs(x) * math_signum(bx)
 				y = math_abs(y) * math_signum(by)
 			else
@@ -445,14 +468,17 @@ function Animation.ShearTimeline.new (frameCount)
 		return TimelineType.shear * SHL_24 + self.boneIndex
 	end
 
-	function self:apply (skeleton, lastTime, time, firedEvents, alpha, setupPose, mixingOut)
+	function self:apply (skeleton, lastTime, time, firedEvents, alpha, pose, direction)
 		local frames = self.frames
 
 		local bone = skeleton.bones[self.boneIndex]
 		if time < frames[0] then
-			if setupPose then
+			if pose == MixPose.setup then
 				bone.shearX = bone.data.shearX
 				bone.shearY = bone.data.shearY
+			elseif pose == MixPose.current then
+				bone.shearX = bone.shearX + (bone.data.shearX - bone.shearX) * alpha
+				bone.shearY = bone.shearX + (bone.data.shearY - bone.shearY) * alpha
 			end
 			return
 		end
@@ -474,7 +500,7 @@ function Animation.ShearTimeline.new (frameCount)
 			x = x + (frames[frame + X] - x) * percent
 			y = y + (frames[frame + Y] - y) * percent
 		end
-		if setupPose then
+		if pose == MixPose.setup then
 			bone.shearX = bone.data.shearX + x * alpha
 			bone.shearY = bone.data.shearY + y * alpha
 		else
@@ -518,12 +544,17 @@ function Animation.ColorTimeline.new (frameCount)
 		self.frames[frameIndex + A] = a
 	end
 
-	function self:apply (skeleton, lastTime, time, firedEvents, alpha, setupPose, mixingOut)
+	function self:apply (skeleton, lastTime, time, firedEvents, alpha, pose, direction)
 		local frames = self.frames
 		local slot = skeleton.slots[self.slotIndex]
 		if time < frames[0] then 
-			if setupPose then
+			if pose == MixPose.setup then
 				slot.color:setFrom(slot.data.color)
+			elseif pose == MixPose.current then
+				local color = slot.color
+				local setup = slot.data.color
+				color:add((setup.r - color.r) * alpha, (setup.g - color.g) * alpha, (setup.b - color.b) * alpha,
+						(setup.a - color.a) * alpha)
 			end
 			return
 		end
@@ -555,7 +586,7 @@ function Animation.ColorTimeline.new (frameCount)
 			slot.color:set(r, g, b, a)
 		else
 			local color = slot.color
-			if setupPose then color:setFrom(slot.data.color) end
+			if pose == MixPose.setup then color:setFrom(slot.data.color) end
 			color:add((r - color.r) * alpha, (g - color.g) * alpha, (b - color.b) * alpha, (a - color.a) * alpha)
 		end
 	end
@@ -604,13 +635,21 @@ function Animation.TwoColorTimeline.new (frameCount)
 		self.frames[frameIndex + B2] = b2
 	end
 
-	function self:apply (skeleton, lastTime, time, firedEvents, alpha, setupPose, mixingOut)
+	function self:apply (skeleton, lastTime, time, firedEvents, alpha, pose, direction)
 		local frames = self.frames
 		local slot = skeleton.slots[self.slotIndex]
 		if time < frames[0] then 
-			if setupPose then
+			if pose == MixPose.setup then
 				slot.color:setFrom(slot.data.color)
 				slot.darkColor:setFrom(slot.data.darkColor)
+			elseif pose == MixPose.current then
+				local light = slot.color
+				local dark = slot.darkColor
+				local setupLight = slot.data.color
+				local setupDark = slot.data.darkColor
+				light:add((setupLight.r - light.r) * alpha, (setupLight.g - light.g) * alpha, (setupLight.b - light.b) * alpha,
+					(setupLight.a - light.a) * alpha)
+				dark:add((setupDark.r - dark.r) * alpha, (setupDark.g - dark.g) * alpha, (setupDark.b - dark.b) * alpha, 0)
 			end
 			return
 		end
@@ -653,7 +692,7 @@ function Animation.TwoColorTimeline.new (frameCount)
 		else
 			local light = slot.color
 			local dark = slot.darkColor
-			if setupPose then 
+			if pose == MixPose.setup then 
 				light:setFrom(slot.data.color)
 				dark:setFrom(slot.data.darkColor)
 			end
@@ -687,10 +726,10 @@ function Animation.AttachmentTimeline.new (frameCount)
 		return TimelineType.attachment * SHL_24 + self.slotIndex
 	end
 
-	function self:apply (skeleton, lastTime, time, firedEvents, alpha, setupPose, mixingOut)
+	function self:apply (skeleton, lastTime, time, firedEvents, alpha, pose, direction)
 		local slot = skeleton.slots[self.slotIndex]
 		local attachmentName
-		if mixingOut and setupPose then
+		if direction == MixDirection.out and pose == MixPose.setup then
 			attachmentName = slot.data.attachmentName
 			if not attachmentName then
 				slot:setAttachment(nil)
@@ -702,7 +741,7 @@ function Animation.AttachmentTimeline.new (frameCount)
 		
 		local frames = self.frames
 		if time < frames[0] then 
-			if setupPose then
+			if pose == MixPose.setup then
 				attachmentName = slot.data.attachmentName
 				if not attachmentName then
 					slot:setAttachment(nil)
@@ -741,7 +780,7 @@ function Animation.DeformTimeline.new (frameCount)
 	self.type = TimelineType.deform
 
 	function self:getPropertyId ()
-		return TimelineType.deform * SHL_24 + self.slotIndex
+		return TimelineType.deform * SHL_27 + self.attachment.id + self.slotIndex
 	end
 
 	function self:setFrame (frameIndex, time, vertices)
@@ -749,7 +788,7 @@ function Animation.DeformTimeline.new (frameCount)
 		self.frameVertices[frameIndex] = vertices
 	end
 
-	function self:apply (skeleton, lastTime, time, firedEvents, alpha, setupPose, mixingOut)
+	function self:apply (skeleton, lastTime, time, firedEvents, alpha, pose, direction)
 		local slot = skeleton.slots[self.slotIndex]
 		local slotAttachment = slot.attachment
 		if not slotAttachment then return end
@@ -758,19 +797,27 @@ function Animation.DeformTimeline.new (frameCount)
 
 		local frames = self.frames
 		local verticesArray = slot.attachmentVertices
-		if time < frames[0] then
-			if setupPose then
-				verticesArray = {}
-				slot.attachmentVertices = verticesArray
-			end
-			return
-		end
 
 		local frameVertices = self.frameVertices
 		local vertexCount = #(frameVertices[0])
 
 		if (#verticesArray ~= vertexCount and not setupPose) then alpha = 1 end -- Don't mix from uninitialized slot vertices.
 		local vertices = utils.setArraySize(verticesArray, vertexCount)
+		
+		if time < frames[0] then
+			if pose == MixPose.setup then
+				verticesArray = {}
+				slot.attachmentVertices = verticesArray
+			elseif pose == MixPose.current then
+				alpha = 1 - alpha
+				local i = 1
+				while i <= vertexCount do
+					vertices[i] = vertices[i] * alpha
+					i = i + 1
+				end
+			end
+			return
+		end
 
 		if time >= frames[zlen(frames) - 1] then -- Time is after last frame.
 			local lastVertices = frameVertices[zlen(frames) - 1]
@@ -781,7 +828,7 @@ function Animation.DeformTimeline.new (frameCount)
 					vertices[i] = lastVertices[i]
 					i = i + 1
 				end
-			elseif setupPose then
+			elseif pose == MixPose.setup then
 				local vertexAttachment = slotAttachment
 				if vertexAttachment.bones == nil then
 					-- Unweighted vertex positions, with alpha.
@@ -826,7 +873,7 @@ function Animation.DeformTimeline.new (frameCount)
 				vertices[i] = prev + (nextVertices[i] - prev) * percent
 				i = i + 1
 			end
-		elseif setupPose then
+		elseif pose == MixPose.setup then
 			local vertexAttachment = slotAttachment
 			if vertexAttachment.bones == nil then
 				-- Unweighted vertex positions, with alpha.
@@ -883,14 +930,14 @@ function Animation.EventTimeline.new (frameCount)
 	end
 
 	-- Fires events for frames > lastTime and <= time.
-	function self:apply (skeleton, lastTime, time, firedEvents, alpha, setupPose, mixingOut)
+	function self:apply (skeleton, lastTime, time, firedEvents, alpha, pose, direction)
 		if not firedEvents then return end
 
 		local frames = self.frames
 		local frameCount = zlen(frames)
 
 		if lastTime > time then -- Fire events after last time for looped animations.
-			self:apply(skeleton, lastTime, 999999, firedEvents, alpha, setupPose, mixingOut)
+			self:apply(skeleton, lastTime, 999999, firedEvents, alpha, pose, direction)
 			lastTime = -1
 		elseif lastTime >= frames[frameCount - 1] then -- Last time is after last frame.
 			return
@@ -939,7 +986,7 @@ function Animation.DrawOrderTimeline.new (frameCount)
 		self.drawOrders[frameIndex] = drawOrder
 	end
 
-	function self:apply (skeleton, lastTime, time, firedEvents, alpha, setupPose, mixingOut)
+	function self:apply (skeleton, lastTime, time, firedEvents, alpha, pose, direction)
 		local drawOrder = skeleton.drawOrder
 		local slots = skeleton.slots
 		if mixingOut and setupPose then
@@ -950,7 +997,7 @@ function Animation.DrawOrderTimeline.new (frameCount)
 		end
 		local frames = self.frames
 		if time < frames[0] then 
-			if setupPose then
+			if pose == MixPose.setup then
 				for i,slot in ipairs(slots) do
 					drawOrder[i] = slots[i]
 				end
@@ -1006,29 +1053,32 @@ function Animation.IkConstraintTimeline.new (frameCount)
 		self.frames[frameIndex + BEND_DIRECTION] = bendDirection
 	end
 
-	function self:apply (skeleton, lastTime, time, firedEvents, alpha, setupPose, mixingOut)
+	function self:apply (skeleton, lastTime, time, firedEvents, alpha, pose, direction)
 		local frames = self.frames
 
 		local constraint = skeleton.ikConstraints[self.ikConstraintIndex]
 		if time < frames[0] then
-			if setupPose then
+			if pose == MixPose.setup then
 				constraint.mix = constraint.data.mix
 				constraint.bendDirection = constraint.data.bendDirection
+			elseif pose == MixPose.current then
+				constraint.mix = constraint.mix + (constraint.data.mix - constraint.mix) * alpha
+				constraint.bendDirection = constraint.data.bendDirection
 			end
 			return
 		end
 
 		if time >= frames[zlen(frames) - ENTRIES] then -- Time is after last frame.
-			if setupPose then
+			if pose == MixPose.setup then
 				constraint.mix = constraint.data.mix + (frames[zlen(frames) + PREV_MIX] - constraint.data.mix) * alpha
-				if mixingOut then 
+				if direction == MixDirection.out then 
 					constraint.bendDirection = constraint.data.bendDirection
 				else
 					constraint.bendDirection = math_floor(frames[zlen(frames) + PREV_BEND_DIRECTION]);
 				end
 			else
 				constraint.mix = constraint.mix + (frames[frames.length + PREV_MIX] - constraint.mix) * alpha;
-				if not mixingOut then constraint.bendDirection = math_floor(frames[zlen(frames) + PREV_BEND_DIRECTION]) end
+				if direction == MixDirection._in then constraint.bendDirection = math_floor(frames[zlen(frames) + PREV_BEND_DIRECTION]) end
 			end
 			return
 		end
@@ -1040,16 +1090,16 @@ function Animation.IkConstraintTimeline.new (frameCount)
 		local percent = self:getCurvePercent(math.floor(frame / ENTRIES) - 1,
 				1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime))
 
-		if setupPose then
+		if pose == MixPose.setup then
 			constraint.mix = constraint.data.mix + (mix + (frames[frame + MIX] - mix) * percent - constraint.data.mix) * alpha
-			if mixingOut then
+			if direction == MixDirection.out then
 				constraint.bendDirection = constraint.data.bendDirection
 			else
 				constraint.bendDirection = math_floor(frames[frame + PREV_BEND_DIRECTION])
 			end
 		else
 			constraint.mix = constraint.mix + (mix + (frames[frame + MIX] - mix) * percent - constraint.mix) * alpha;
-			if not mixingOut then constraint.bendDirection = math_floor(frames[frame + PREV_BEND_DIRECTION]) end
+			if direction == MixDirection._in then constraint.bendDirection = math_floor(frames[frame + PREV_BEND_DIRECTION]) end
 		end
 	end
 
@@ -1088,17 +1138,22 @@ function Animation.TransformConstraintTimeline.new (frameCount)
 		self.frames[frameIndex + SHEAR] = shearMix
 	end
 
-	function self:apply (skeleton, lastTime, time, firedEvents, alpha, setupPose, mixingOut)
+	function self:apply (skeleton, lastTime, time, firedEvents, alpha, pose, direction)
 		local frames = self.frames
 
 		local constraint = skeleton.transformConstraints[self.transformConstraintIndex]
 		if time < frames[0] then
-			if setupPose then
-				local data = constraint.data
+			local data = constraint.data
+			if pose == MixPose.setup then
 				constraint.rotateMix = data.rotateMix
 				constraint.translateMix = data.translateMix
 				constraint.scaleMix = data.scaleMix
 				constraint.shearMix = data.shearMix
+			elseif pose == MixPose.current then
+				constraint.rotateMix = constraint.rotateMix + (data.rotateMix - constraint.rotateMix) * alpha
+				constraint.translateMix = constraint.translateMix + (data.translateMix - constraint.translateMix) * alpha
+				constraint.scaleMix = constraint.scaleMix + (data.scaleMix - constraint.scaleMix) * alpha
+				constraint.shearMix = constraint.shearMix + (data.shearMix - constraint.shearMix) * alpha
 			end
 			return
 		end
@@ -1129,7 +1184,7 @@ function Animation.TransformConstraintTimeline.new (frameCount)
 			scale = scale + (frames[frame + SCALE] - scale) * percent
 			shear = shear + (frames[frame + SHEAR] - shear) * percent
 		end
-		if setupPose then
+		if pose == MixPose.setup then
 			local data = constraint.data
 			constraint.rotateMix = data.rotateMix + (rotate - data.rotateMix) * alpha
 			constraint.translateMix = data.translateMix + (translate - data.translateMix) * alpha
@@ -1169,13 +1224,15 @@ function Animation.PathConstraintPositionTimeline.new (frameCount)
 		self.frames[frameIndex + VALUE] = value
 	end
 
-	function self:apply (skeleton, lastTime, time, firedEvents, alpha, setupPose, mixingOut)
+	function self:apply (skeleton, lastTime, time, firedEvents, alpha, pose, direction)
 		local frames = self.frames
 
 		local constraint = skeleton.pathConstraints[self.pathConstraintIndex]
 		if (time < frames[0]) then
-			if setupPose then
+			if pose == MixPose.setup then
 				constraint.position = constraint.data.position	
+			elseif pose == MixPose.current then
+				constraint.position = constraint.position + (constraint.data.position - constraint.position) * alpha
 			end
 			return
 		end
@@ -1193,7 +1250,7 @@ function Animation.PathConstraintPositionTimeline.new (frameCount)
 
 			position = position + (frames[frame + VALUE] - position) * percent
 		end
-		if setupPose then
+		if pose == MixPose.setup then
 			constraint.position = constraint.data.position + (position - constraint.data.position) * alpha
 		else
 			constraint.position = constraint.position + (position - constraint.position) * alpha
@@ -1226,13 +1283,15 @@ function Animation.PathConstraintSpacingTimeline.new (frameCount)
 		self.frames[frameIndex + VALUE] = value
 	end
 
-	function self:apply (skeleton, lastTime, time, firedEvents, alpha, setupPose, mixingOut)
+	function self:apply (skeleton, lastTime, time, firedEvents, alpha, pose, direction)
 		local frames = self.frames
 
 		local constraint = skeleton.pathConstraints[self.pathConstraintIndex]
 		if (time < frames[0]) then
-			if setupPose then
+			if pose == MixPose.setup then
 				constraint.spacing = constraint.data.spacing
+			elseif pose == MixPose.current then
+				constraint.spacing = constraint.spacing + (constraint.data.spacing - constraint.spacing) * alpha
 			end
 			return
 		end
@@ -1251,7 +1310,7 @@ function Animation.PathConstraintSpacingTimeline.new (frameCount)
 			spacing = spacing + (frames[frame + VALUE] - spacing) * percent
 		end
 
-		if setupPose then
+		if pose == MixPose.setup then
 			constraint.spacing = constraint.data.spacing + (spacing - constraint.data.spacing) * alpha
 		else
 			constraint.spacing = constraint.spacing + (spacing - constraint.spacing) * alpha
@@ -1287,14 +1346,17 @@ function Animation.PathConstraintMixTimeline.new (frameCount)
 		self.frames[frameIndex + TRANSLATE] = translateMix
 	end
 
-	function self:apply (skeleton, lastTime, time, firedEvents, alpha, setupPose, mixingOut)
+	function self:apply (skeleton, lastTime, time, firedEvents, alpha, pose, direction)
 		local frames = self.frames
 
 		local constraint = skeleton.pathConstraints[self.pathConstraintIndex]
 		if (time < frames[0]) then
-			if setupPose then
+			if pose == MixPose.setup then
 				constraint.rotateMix = constraint.data.rotateMix
 				constraint.translateMix = constraint.data.translateMix
+			elseif pose == MixPose.current then
+				constraint.rotateMix = constraint.rotateMix + (constraint.data.rotateMix - constraint.rotateMix) * alpha
+				constraint.translateMix = constraint.translateMix + (constraint.data.translateMix - constraint.translateMix) * alpha
 			end
 			return
 		end
@@ -1317,7 +1379,7 @@ function Animation.PathConstraintMixTimeline.new (frameCount)
 			translate = translate + (frames[frame + TRANSLATE] - translate) * percent
 		end
 
-		if setupPose then
+		if pose == MixPose.setup then
 			constraint.rotateMix = constraint.data.rotateMix + (rotate - constraint.data.rotateMix) * alpha
 			constraint.translateMix = constraint.data.translateMix + (translate - constraint.data.translateMix) * alpha
 		else

+ 34 - 25
spine-lua/AnimationState.lua

@@ -32,6 +32,8 @@ local setmetatable = setmetatable
 local table_insert = table.insert
 local utils = require "spine-lua.utils"
 local Animation = require "spine-lua.Animation"
+local MixPose = Animation.MixPose
+local MixDirection = Animation.MixDirection
 local AnimationStateData = require "spine-lua.AnimationStateData"
 local math_min = math.min
 local math_max = math.max
@@ -371,10 +373,13 @@ function AnimationState:apply (skeleton)
 	for i,current in pairs(tracks) do
 		if not (current == nil or current.delay > 0) then
       applied = true
+			local currrentPose = MixPose.currentLayered
+			if i == 0 then currentPose = MixPose.current end
+			
 			-- Apply mixing from entries first.
 			local mix = current.alpha
 			if current.mixingFrom then 
-				mix = mix * self:applyMixingFrom(current, skeleton)
+				mix = mix * self:applyMixingFrom(current, skeleton, currentPose)
 			elseif current.trackTime >= current.trackEnd and current.next == nil then
 				mix = 0
 			end
@@ -385,7 +390,7 @@ function AnimationState:apply (skeleton)
 			local timelines = current.animation.timelines
 			if mix == 1 then
 				for i,timeline in ipairs(timelines) do
-					timeline:apply(skeleton, animationLast, animationTime, events, 1, true, false)
+					timeline:apply(skeleton, animationLast, animationTime, events, 1, MixPose.setup, MixDirection._in)
 				end
 			else
 				local timelineData = current.timelineData
@@ -393,11 +398,14 @@ function AnimationState:apply (skeleton)
 				local timelinesRotation = current.timelinesRotation
 
 				for i,timeline in ipairs(timelines) do
+					local pose = MixPose.currentPose
+					if timelineData[i] >= FIRST then pose = MixPose.setup end
+					
 					if timeline.type == Animation.TimelineType.rotate then
-						self:applyRotateTimeline(timeline, skeleton, animationTime, mix, timelineData[i] >= FIRST, timelinesRotation, i * 2,
+						self:applyRotateTimeline(timeline, skeleton, animationTime, mix, pose, timelinesRotation, i * 2,
 							firstFrame) -- FIXME passing ii * 2, indexing correct?
 					else
-						timeline:apply(skeleton, animationLast, animationTime, events, mix, timelineData[i] >= FIRST, false)
+						timeline:apply(skeleton, animationLast, animationTime, events, mix, pose, MixDirection._in)
 					end
 				end
 			end
@@ -412,9 +420,9 @@ function AnimationState:apply (skeleton)
   return applied
 end
 
-function AnimationState:applyMixingFrom (to, skeleton)
+function AnimationState:applyMixingFrom (to, skeleton, currentPose)
 	local from = to.mixingFrom
-	if from.mixingFrom then self:applyMixingFrom(from, skeleton) end
+	if from.mixingFrom then self:applyMixingFrom(from, skeleton, currentPose) end
 
 	local mix = 0
 	if to.mixDuration == 0 then -- Single frame mix to undo mixingFrom changes.
@@ -437,38 +445,39 @@ function AnimationState:applyMixingFrom (to, skeleton)
 	local firstFrame = #from.timelinesRotation == 0
 	local timelinesRotation = from.timelinesRotation
 
-	local first = false
+	local pose = MixPose.setup
 	local alphaDip = from.alpha * to.interruptAlpha
 	local alphaMix = alphaDip * (1 - mix)
 	local alpha = 0
 	from.totalAlpha = 0;
-	local skip = false
+
 	for i,timeline in ipairs(timelines) do
-		
+		local skipSubsequent = false;
 		if timelineData[i] == SUBSEQUENT then
-			first = false
+			if not attachments and timeline.type == Animation.TimelineType.attachment then skipSubsequent = true end
+			if not drawOrder and timeline.type == Animation.TimelineType.drawOrder then skipSubsequent = true end
+			pose = currentPose
 			alpha = alphaMix
 		elseif timelineData[i] == FIRST then
-			first = true
+			pose = MixPose.setup
 			alpha = alphaMix
 		elseif timelineData[i] == DIP then
-			first = true
+			pose = MixPose.setup
 			alpha = alphaDip
 		else
-			first = true
+			pose = MixPose.setup
 			alpha = alphaDip
 			local dipMix = timelineDipMix[i]
 			alpha = alpha * math_max(0, 1 - dipMix.mixtime / dipMix.mixDuration)
 		end
-		from.totalAlpha = from.totalAlpha + alpha
-		if timeline.type == Animation.TimelineType.rotate then
-			self:applyRotateTimeline(timeline, skeleton, animationTime, alpha, first, timelinesRotation, i * 2, firstFrame)
-		else
-			if not first then
-				if not attachments and timeline.type == Animation.TimelineType.attackment then skip = true end
-				if not drawOrder and timeline.type == Animation.TimelineType.drawOrder then skip = true end
+		
+		if not skipSubsequent then
+			from.totalAlpha = from.totalAlpha + alpha
+			if timeline.type == Animation.TimelineType.rotate then
+				self:applyRotateTimeline(timeline, skeleton, animationTime, alpha, pose, timelinesRotation, i * 2, firstFrame)
+			else
+				timeline:apply(skeleton, animationLast, animationTime, events, alpha, pose, MixDirection.out)
 			end
-			if not skip then timeline:apply(skeleton, animationLast, animationTime, events, alpha, first, true) end
 		end
 	end
 
@@ -480,14 +489,14 @@ function AnimationState:applyMixingFrom (to, skeleton)
 	return mix
 end
 
-function AnimationState:applyRotateTimeline (timeline, skeleton, time, alpha, setupPose, timelinesRotation, i, firstFrame)
+function AnimationState:applyRotateTimeline (timeline, skeleton, time, alpha, pose, timelinesRotation, i, firstFrame)
 	if firstFrame then 
 		timelinesRotation[i] = 0
 		timelinesRotation[i+1] = 0
 	end
 	
   if alpha == 1 then
-    timeline:apply(skeleton, 0, time, nil, 1, setupPose, false)
+    timeline:apply(skeleton, 0, time, nil, 1, pose, MixDirection._in)
     return
   end
 
@@ -495,7 +504,7 @@ function AnimationState:applyRotateTimeline (timeline, skeleton, time, alpha, se
   local frames = rotateTimeline.frames
   local bone = skeleton.bones[rotateTimeline.boneIndex]
   if time < frames[0] then
-		if setupPose then bone.rotation = bone.data.rotation end
+		if pose == MixPose.setup then bone.rotation = bone.data.rotation end
 		return
 	end
 
@@ -518,7 +527,7 @@ function AnimationState:applyRotateTimeline (timeline, skeleton, time, alpha, se
 
   -- Mix between rotations using the direction of the shortest route on the first frame while detecting crosses.
   local r1 = bone.rotation
-  if setupPose then r1 = bone.data.rotation end
+  if pose == MixPose.setup then r1 = bone.data.rotation end
   local total = 0
   local diff = r2 - r1
   if diff == 0 then

+ 8 - 0
spine-lua/attachments/VertexAttachment.lua

@@ -37,6 +37,9 @@ local setmetatable = setmetatable
 local AttachmentType = require "spine-lua.attachments.AttachmentType"
 local Attachment = require "spine-lua.attachments.Attachment"
 
+local nextID = 0;
+local SHL_11 = 2048;
+
 local VertexAttachment = {}
 VertexAttachment.__index = VertexAttachment
 setmetatable(VertexAttachment, { __index = Attachment })
@@ -46,6 +49,11 @@ function VertexAttachment.new (name, attachmentType)
 	self.bones = nil
 	self.vertices = nil
 	self.worldVerticesLength = 0
+	while nextID > 65535 do
+		nextID = nextID - 65535
+	end
+	self.id = nextID * SHL_11
+	nextID = nextID + 1
 	setmetatable(self, VertexAttachment)
 	return self
 end