Răsfoiți Sursa

[lua] Ported additive blending, see #1060, see #1029

badlogic 7 ani în urmă
părinte
comite
54e99f1454
4 a modificat fișierele cu 334 adăugiri și 199 ștergeri
  1. 4 4
      spine-corona/main.lua
  2. 269 143
      spine-lua/Animation.lua
  3. 60 51
      spine-lua/AnimationState.lua
  4. 1 1
      spine-ts/core/src/Animation.ts

+ 4 - 4
spine-corona/main.lua

@@ -81,12 +81,12 @@ function loadSkeleton(atlasFile, jsonFile, x, y, scale, animation, skin)
 	return { skeleton = skeleton, state = animationState }
 end
 
--- table.insert(skeletons, loadSkeleton("coin.atlas", "coin-pro.json", 240, 300, 0.4, "rotate"))
--- table.insert(skeletons, loadSkeleton("spineboy.atlas", "spineboy-ess.json", 240, 300, 0.4, "walk"))
+table.insert(skeletons, loadSkeleton("coin.atlas", "coin-pro.json", 240, 300, 0.4, "rotate"))
+table.insert(skeletons, loadSkeleton("spineboy.atlas", "spineboy-ess.json", 240, 300, 0.4, "walk"))
 table.insert(skeletons, loadSkeleton("raptor.atlas", "raptor-pro.json", 200, 300, 0.25, "walk"))
--- table.insert(skeletons, loadSkeleton("goblins.atlas", "goblins-pro.json", 240, 300, 0.8, "walk", "goblin"))
+table.insert(skeletons, loadSkeleton("goblins.atlas", "goblins-pro.json", 240, 300, 0.8, "walk", "goblin"))
 table.insert(skeletons, loadSkeleton("stretchyman.atlas", "stretchyman-pro.json", 40, 300, 0.5, "sneak"))
--- table.insert(skeletons, loadSkeleton("tank.atlas", "tank-pro.json", 400, 300, 0.2, "drive"))
+table.insert(skeletons, loadSkeleton("tank.atlas", "tank-pro.json", 400, 300, 0.2, "drive"))
 table.insert(skeletons, loadSkeleton("vine.atlas", "vine-pro.json", 240, 300, 0.3, "grow"))
 
 local triangulator = spine.Triangulator.new()

+ 269 - 143
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, pose, direction)
+	function self:apply (skeleton, lastTime, time, loop, events, alpha, blend, 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, pose, direction)
+			timeline:apply(skeleton, lastTime, time, events, alpha, blend, direction)
 		end
 	end
 
@@ -113,12 +113,13 @@ local function linearSearch (values, target, step)
 	return -1
 end
 
-Animation.MixPose = {
+Animation.MixBlend = {
 	setup = 0,
-	current = 1,
-	currentLayered = 2
+	first = 1,
+	replace = 2,
+	add = 3
 }
-local MixPose = Animation.MixPose
+local MixBlend = Animation.MixBlend
 
 Animation.MixDirection = {
 	_in = 0, out = 1
@@ -255,28 +256,30 @@ function Animation.RotateTimeline.new (frameCount)
 		self.frames[frameIndex + ROTATION] = degrees
 	end
 
-	function self:apply (skeleton, lastTime, time, firedEvents, alpha, pose, direction)
+	function self:apply (skeleton, lastTime, time, firedEvents, alpha, blend, direction)
 		local frames = self.frames
 
 		local bone = skeleton.bones[self.boneIndex]
 		if time < frames[0] then
-			if pose == MixPose.setup then
+			if blend == MixBlend.setup then
 				bone.rotation = bone.data.rotation
-			elseif pose == MixPose.current then
+			elseif blend == MixBlend.first 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
+				bone.rotation = bone.rotation + (r - (16384 - math_floor(16384.499999999996 - r / 360)) * 360) * alpha
 			end
 			return
 		end
 
 		if time >= frames[zlen(frames) - ENTRIES] then -- Time is after last frame.
-			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
+			local r = frames[zlen(frames) + PREV_ROTATION]
+			if blend == MixBlend.setup then
+				bone.rotation = bone.data.rotation + r * alpha
+			elseif blend == MixBlend.first or blend == MixBlend.replace then
+				r = 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;
+			elseif blend == MixBlend.add then
+				bone.rotation = bone.rotation + r * alpha;				
 			end
 			return;
 		end
@@ -288,15 +291,14 @@ function Animation.RotateTimeline.new (frameCount)
 		local percent = self:getCurvePercent((math.floor(frame / 2)) - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime))
 
 		local r = frames[frame + ROTATION] - prevRotation
-		r = r - (16384 - math_floor(16384.499999999996 - r / 360)) * 360
-		r = prevRotation + r * percent
-		if pose == MixPose.setup then
-			r = r - (16384 - math_floor(16384.499999999996 - r / 360)) * 360
-			bone.rotation = bone.data.rotation + r * alpha
-		else
-			r = bone.data.rotation + r - bone.rotation;
-			r = r - (16384 - math_floor(16384.499999999996 - r / 360)) * 360
-			bone.rotation = bone.rotation + r * alpha
+		r = prevRotation + (r - (16384 - math_floor(16384.499999999996 - r / 360)) * 360) * percent
+		if blend == MixBlend.setup then
+			bone.rotation = bone.data.rotation + (r - (16384 - math_floor(16384.499999999996 - r / 360)) * 360) * alpha
+		elseif blend == MixBlend.first or blend == MixBlend.replace then
+			r = r + bone.data.rotation - bone.rotation;
+			bone.rotation = bone.rotation + (r - (16384 - math_floor(16384.499999999996 - r / 360)) * 360) * alpha
+		elseif blend == MixBlend.add then
+			bone.rotation = bone.rotation + (r - (16384 - math_floor(16384.499999999996 - r / 360)) * 360) * alpha
 		end
 	end
 
@@ -329,15 +331,15 @@ function Animation.TranslateTimeline.new (frameCount)
 		self.frames[frameIndex + Y] = y
 	end
 
-	function self:apply (skeleton, lastTime, time, firedEvents, alpha, pose, direction)
+	function self:apply (skeleton, lastTime, time, firedEvents, alpha, blend, direction)
 		local frames = self.frames
 
 		local bone = skeleton.bones[self.boneIndex]
 		if time < frames[0] then 
-			if pose == MixPose.setup then
+			if blend == MixBlend.setup then
 				bone.x = bone.data.x
 				bone.y = bone.data.y
-			elseif pose == MixPose.current then
+			elseif blend == MixBlend.first then
 				bone.x = bone.x + (bone.data.x - bone.x) * alpha
 				bone.y = bone.y + (bone.data.y - bone.y) * alpha
 			end
@@ -361,12 +363,15 @@ function Animation.TranslateTimeline.new (frameCount)
 			x = x + (frames[frame + X] - x) * percent
 			y = y + (frames[frame + Y] - y) * percent
 		end
-		if pose == MixPose.setup then
+		if blend == MixBlend.setup then
 			bone.x = bone.data.x + x * alpha
 			bone.y = bone.data.y + y * alpha
-		else
+		elseif blend == MixBlend.first or blend == MixBlend.replace then
 			bone.x = bone.x + (bone.data.x + x - bone.x) * alpha
 			bone.y = bone.y + (bone.data.y + y - bone.y) * alpha
+		elseif blend == MixBlend.add then
+			bone.x = bone.x + x * alpha
+			bone.y = bone.y + y * alpha
 		end
 	end
 
@@ -390,15 +395,15 @@ function Animation.ScaleTimeline.new (frameCount)
 		return TimelineType.scale * SHL_24 + self.boneIndex
 	end
 
-	function self:apply (skeleton, lastTime, time, firedEvents, alpha, pose, direction)
+	function self:apply (skeleton, lastTime, time, firedEvents, alpha, blend, direction)
 		local frames = self.frames
 
 		local bone = skeleton.bones[self.boneIndex]
 		if time < frames[0] then
-			if pose == MixPose.setup then
+			if blend == MixBlend.setup then
 				bone.scaleX = bone.data.scaleX
 				bone.scaleY = bone.data.scaleY
-			elseif pose == MixPose.current then
+			elseif blend == MixBlend.first then
 				bone.scaleX = bone.scaleX + (bone.data.scaleX - bone.scaleX) * alpha
 				bone.scaleY = bone.scaleY + (bone.data.scaleY - bone.scaleY) * alpha
 			end
@@ -423,28 +428,51 @@ function Animation.ScaleTimeline.new (frameCount)
 			y = (y + (frames[frame + Y] - y) * percent) * bone.data.scaleY
 		end
 		if alpha == 1 then
-			bone.scaleX = x
-			bone.scaleY = y
+			if blend == MixBlend.add then
+				bone.scaleX = bone.scaleX + x - bone.data.scaleX
+				bone.scaleY = bone.scaleY + y - bone.data.scaleY
+			else
+				bone.scaleX = x
+				bone.scaleY = y
+			end
 		else
 			local bx = 0
 			local by = 0
-			if pose == MixPose.setup then
-				bx = bone.data.scaleX
-				by = bone.data.scaleY
-			else
-				bx = bone.scaleX
-				by = bone.scaleY
-			end
-			-- Mixing out uses sign of setup or current pose, else use sign of key.
-			if direction == MixDirection.out then
-				x = math_abs(x) * math_signum(bx)
-				y = math_abs(y) * math_signum(by)
+			if direction == MixDirection.out then 
+				if blend == MixBlend.setup then
+					bx = bone.data.scaleX
+					by = bone.data.scaleY
+					bone.scaleX = bx + (math_abs(x) * math_signum(bx) - bx) * alpha
+					bone.scaleY = by + (math_abs(y) * math_signum(by) - by) * alpha
+				elseif blend == MixBlend.first or blend == MixBlend.replace then
+					bx = bone.scaleX
+					by = bone.scaleY
+					bone.scaleX = bx + (math_abs(x) * math_signum(bx) - bx) * alpha
+					bone.scaleY = by + (math_abs(y) * math_signum(by) - by) * alpha
+				elseif blend == MixBlend.add then
+					bx = bone.scaleX
+					by = bone.scaleY
+					bone.scaleX = bx + (math_abs(x) * math_signum(bx) - bone.data.scaleX) * alpha
+					bone.scaleY = by + (math_abs(y) * math_signum(by) - bone.data.scaleY) * alpha
+				end
 			else
-				bx = math_abs(bx) * math_signum(x)
-				by = math_abs(by) * math_signum(y)
+				if blend == MixBlend.setup then
+					bx = math_abs(bone.data.scaleX) * math_signum(x)
+					by = math_abs(bone.data.scaleY) * math_signum(y)
+					bone.scaleX = bx + (x - bx) * alpha
+					bone.scaleY = by + (y - by) * alpha
+				elseif blend == MixBlend.first or blend == MixBlend.replace then
+					bx = math_abs(bone.scaleX) * math_signum(x)
+					by = math_abs(bone.scaleY) * math_signum(y)
+					bone.scaleX = bx + (x - bx) * alpha
+					bone.scaleY = by + (y - by) * alpha
+				elseif blend == MixBlend.add then
+					bx = math_signum(x)
+					by = math_signum(y)
+					bone.scaleX = math_abs(bone.scaleX) * bx + (x - math_abs(bone.data.scaleX) * bx) * alpha
+					bone.scaleY = math_abs(bone.scaleY) * by + (y - math_abs(bone.data.scaleY) * by) * alpha
+				end
 			end
-			bone.scaleX = bx + (x - bx) * alpha
-			bone.scaleY = by + (y - by) * alpha
 		end
 	end
 
@@ -468,15 +496,15 @@ function Animation.ShearTimeline.new (frameCount)
 		return TimelineType.shear * SHL_24 + self.boneIndex
 	end
 
-	function self:apply (skeleton, lastTime, time, firedEvents, alpha, pose, direction)
+	function self:apply (skeleton, lastTime, time, firedEvents, alpha, blend, direction)
 		local frames = self.frames
 
 		local bone = skeleton.bones[self.boneIndex]
 		if time < frames[0] then
-			if pose == MixPose.setup then
+			if blend == MixBlend.setup then
 				bone.shearX = bone.data.shearX
 				bone.shearY = bone.data.shearY
-			elseif pose == MixPose.current then
+			elseif blend == MixBlend.first then
 				bone.shearX = bone.shearX + (bone.data.shearX - bone.shearX) * alpha
 				bone.shearY = bone.shearX + (bone.data.shearY - bone.shearY) * alpha
 			end
@@ -500,12 +528,15 @@ function Animation.ShearTimeline.new (frameCount)
 			x = x + (frames[frame + X] - x) * percent
 			y = y + (frames[frame + Y] - y) * percent
 		end
-		if pose == MixPose.setup then
+		if blend == MixBlend.setup then
 			bone.shearX = bone.data.shearX + x * alpha
 			bone.shearY = bone.data.shearY + y * alpha
-		else
+		elseif blend == MixBlend.first or blend == MixBlend.replace then
 			bone.shearX = bone.shearX + (bone.data.shearX + x - bone.shearX) * alpha
 			bone.shearY = bone.shearY + (bone.data.shearY + y - bone.shearY) * alpha
+		elseif blend == MixBlend.add then
+			bone.shearX = bone.shearX + x * alpha
+			bone.shearY = bone.shearY + y * alpha
 		end
 	end
 
@@ -544,13 +575,13 @@ function Animation.ColorTimeline.new (frameCount)
 		self.frames[frameIndex + A] = a
 	end
 
-	function self:apply (skeleton, lastTime, time, firedEvents, alpha, pose, direction)
+	function self:apply (skeleton, lastTime, time, firedEvents, alpha, blend, direction)
 		local frames = self.frames
 		local slot = skeleton.slots[self.slotIndex]
 		if time < frames[0] then 
-			if pose == MixPose.setup then
+			if blend == MixBlend.setup then
 				slot.color:setFrom(slot.data.color)
-			elseif pose == MixPose.current then
+			elseif blend == MixBlend.first 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,
@@ -586,7 +617,7 @@ function Animation.ColorTimeline.new (frameCount)
 			slot.color:set(r, g, b, a)
 		else
 			local color = slot.color
-			if pose == MixPose.setup then color:setFrom(slot.data.color) end
+			if blend == MixBlend.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
@@ -635,14 +666,14 @@ function Animation.TwoColorTimeline.new (frameCount)
 		self.frames[frameIndex + B2] = b2
 	end
 
-	function self:apply (skeleton, lastTime, time, firedEvents, alpha, pose, direction)
+	function self:apply (skeleton, lastTime, time, firedEvents, alpha, blend, direction)
 		local frames = self.frames
 		local slot = skeleton.slots[self.slotIndex]
 		if time < frames[0] then 
-			if pose == MixPose.setup then
+			if blend == MixBlend.setup then
 				slot.color:setFrom(slot.data.color)
 				slot.darkColor:setFrom(slot.data.darkColor)
-			elseif pose == MixPose.current then
+			elseif blend == MixBlend.first then
 				local light = slot.color
 				local dark = slot.darkColor
 				local setupLight = slot.data.color
@@ -692,7 +723,7 @@ function Animation.TwoColorTimeline.new (frameCount)
 		else
 			local light = slot.color
 			local dark = slot.darkColor
-			if pose == MixPose.setup then 
+			if blend == MixBlend.setup then 
 				light:setFrom(slot.data.color)
 				dark:setFrom(slot.data.darkColor)
 			end
@@ -726,10 +757,10 @@ function Animation.AttachmentTimeline.new (frameCount)
 		return TimelineType.attachment * SHL_24 + self.slotIndex
 	end
 
-	function self:apply (skeleton, lastTime, time, firedEvents, alpha, pose, direction)
+	function self:apply (skeleton, lastTime, time, firedEvents, alpha, blend, direction)
 		local slot = skeleton.slots[self.slotIndex]
 		local attachmentName
-		if direction == MixDirection.out and pose == MixPose.setup then
+		if direction == MixDirection.out and blend == MixBlend.setup then
 			attachmentName = slot.data.attachmentName
 			if not attachmentName then
 				slot:setAttachment(nil)
@@ -741,7 +772,7 @@ function Animation.AttachmentTimeline.new (frameCount)
 		
 		local frames = self.frames
 		if time < frames[0] then 
-			if pose == MixPose.setup then
+			if blend == MixBlend.setup or blend == MixBlend.first then
 				attachmentName = slot.data.attachmentName
 				if not attachmentName then
 					slot:setAttachment(nil)
@@ -788,7 +819,7 @@ function Animation.DeformTimeline.new (frameCount)
 		self.frameVertices[frameIndex] = vertices
 	end
 
-	function self:apply (skeleton, lastTime, time, firedEvents, alpha, pose, direction)
+	function self:apply (skeleton, lastTime, time, firedEvents, alpha, blend, direction)
 		local slot = skeleton.slots[self.slotIndex]
 		local slotAttachment = slot.attachment
 		if not slotAttachment then return end
@@ -797,17 +828,17 @@ function Animation.DeformTimeline.new (frameCount)
 
 		local frames = self.frames
 		local verticesArray = slot.attachmentVertices
-    if #(verticesArray) == 0 then alpha = 1 end
+    if #(verticesArray) == 0 then blend = MixBlend.setup end
 
 		local frameVertices = self.frameVertices
 		local vertexCount = #(frameVertices[0])		
 		
 		if time < frames[0] then
 			local vertexAttachment = slotAttachment;
-			if pose == MixPose.setup then
+			if blend == MixBlend.setup then
         slot.attachmentVertices = {}
         return;
-			elseif pose == MixPose.current then
+			elseif blend == MixBlend.first then
         if (alpha == 1) then
           slot.attachmentVertices = {}
           return;
@@ -837,37 +868,90 @@ function Animation.DeformTimeline.new (frameCount)
 		if time >= frames[zlen(frames) - 1] then -- Time is after last frame.
 			local lastVertices = frameVertices[zlen(frames) - 1]
 			if alpha == 1 then
-				-- Vertex positions or deform offsets, no alpha.
-				local i = 1
-				while i <= vertexCount do
-					vertices[i] = lastVertices[i]
-					i = i + 1
-				end
-			elseif pose == MixPose.setup then
-				local vertexAttachment = slotAttachment
-				if vertexAttachment.bones == nil then
-					-- Unweighted vertex positions, with alpha.
-					local setupVertices = vertexAttachment.vertices
-					local i = 1
-					while i <= vertexCount do
-						local setup = setupVertices[i]
-						vertices[i] = setup + (lastVertices[i] - setup) * alpha
-						i = i + 1
+				if blend == MixBlend.add then
+					local vertexAttachment = slotAttachment
+					if vertexAttachment.bones == nil then
+						-- Unweighted vertex positions, with alpha.
+						local setupVertices = vertexAttachment.vertices
+						local i = 1
+						while i <= vertexCount do
+							vertices[i] = vertices[i] + lastVertices[i] - setupVertices[i]
+							i = i + 1
+						end
+					else
+						-- Weighted deform offsets, with alpha.
+						local i = 1
+						while i <= vertexCount do
+							vertices[i] = vertices[i] + lastVertices[i]
+							i = i + 1
+						end
 					end
 				else
-					-- Weighted deform offsets, with alpha.
 					local i = 1
 					while i <= vertexCount do
-						vertices[i] = lastVertices[i] * alpha
+						vertices[i] = lastVertices[i]
 						i = i + 1
 					end
 				end
 			else
-				-- Vertex positions or deform offsets, with alpha.
-				local i = 1
-				while i <= vertexCount do
-					vertices[i] = vertices[i] + (lastVertices[i] - vertices[i]) * alpha
-					i = i + 1
+				if blend == MixBlend.setup then
+					local vertexAttachment = slotAttachment
+					if vertexAttachment.bones == nil then
+						-- Unweighted vertex positions, with alpha.
+						local setupVertices = vertexAttachment.vertices
+						local i = 1
+						while i <= vertexCount do
+							local setup = setupVertices[i]
+							vertices[i] = setup + (lastVertices[i] - setup) * alpha
+							i = i + 1
+						end
+					else
+						-- Weighted deform offsets, with alpha.
+						local i = 1
+						while i <= vertexCount do
+							vertices[i] = lastVertices[i] * alpha
+							i = i + 1
+						end
+					end
+				elseif blend == MixBlend.first or blend == MixBlend.replace then
+					local i = 1
+					while i <= vertexCount do
+						vertices[i] = vertices[i] + (lastVertices[i] - vertices[i]) * alpha
+						i = i + 1
+					end
+					local vertexAttachment = slotAttachment
+					if vertexAttachment.bones == nil then
+						local setupVertices = vertexAttachment.vertices
+						local i = 1
+						while i <= vertexCount do
+							vertices[i] = vertices[i] + (lastVertices[i] - setupVertices[i]) * alpha
+							i = i + 1
+						end
+					else
+						-- Weighted deform offsets, with alpha.
+						local i = 1
+						while i <= vertexCount do
+							vertices[i] = vertices[i] + lastVertices[i] * alpha
+							i = i + 1
+						end
+					end
+				elseif blend == MixBlend.add then
+					local vertexAttachment = slotAttachment
+					if vertexAttachment.bones == nil then
+						local setupVertices = vertexAttachment.vertices
+						local i = 1
+						while i <= vertexCount do
+							vertices[i] = vertices[i] + (lastVertices[i] - setupVertices[i]) * alpha
+							i = i + 1
+						end
+					else
+						-- Weighted deform offsets, with alpha.
+						local i = 1
+						while i <= vertexCount do
+							vertices[i] = vertices[i] + lastVertices[i] * alpha
+							i = i + 1
+						end
+					end
 				end
 			end
 			return;
@@ -881,41 +965,82 @@ function Animation.DeformTimeline.new (frameCount)
 		local percent = self:getCurvePercent(frame - 1, 1 - (time - frameTime) / (frames[frame - 1] - frameTime))
 
 		if alpha == 1 then
-			-- Vertex positions or deform offsets, no alpha.
-			local i = 1
-			while i <= vertexCount do
-				local prev = prevVertices[i]
-				vertices[i] = prev + (nextVertices[i] - prev) * percent
-				i = i + 1
-			end
-		elseif pose == MixPose.setup then
-			local vertexAttachment = slotAttachment
-			if vertexAttachment.bones == nil then
-				-- Unweighted vertex positions, with alpha.
-				local setupVertices = vertexAttachment.vertices
+			if blend == MixBlend.add then
+				local vertexAttachment = slotAttachment
+				if vertexAttachment.bones == nil then
+					-- Unweighted vertex positions, with alpha.
+					local setupVertices = vertexAttachment.vertices
+					local i = 1
+					while i <= vertexCount do
+						local prev = prevVertices[i]
+						vertices[i] = vertices[i] + prev + (nextVertices[i] - prev) * precent - setupVertices[i]
+						i = i + 1
+					end
+				else
+					-- Weighted deform offsets, with alpha.
+					local i = 1
+					while i <= vertexCount do
+						local prev = prevVertices[i]
+						vertices[i] = vertices[i] + prev + (nextVertices[i] - prev) * percent
+						i = i + 1
+					end
+				end
+			else
 				local i = 1
 				while i <= vertexCount do
 					local prev = prevVertices[i]
-					local setup = setupVertices[i]
-					vertices[i] = setup + (prev + (nextVertices[i] - prev) * percent - setup) * alpha
+					vertices[i] = prev + (nextVertices[i] - prev) * percent
 					i = i + 1
 				end
-			else
-				-- Weighted deform offsets, with alpha.
+			end
+		else
+			if blend == MixBlend.setup then
+				local vertexAttachment = slotAttachment
+				if vertexAttachment.bones == nil then
+					-- Unweighted vertex positions, with alpha.
+					local setupVertices = vertexAttachment.vertices
+					local i = 1
+					while i <= vertexCount do
+						local prev = prevVertices[i]
+						local setup = setupVertices[i]
+						vertices[i] = setup + (prev + (nextVertices[i] - prev) * percent - setup) * alpha
+						i = i + 1
+					end
+				else
+					-- Weighted deform offsets, with alpha.
+					local i = 1
+					while i <= vertexCount do
+						local prev = prevVertices[i]
+						vertices[i] = (prev + (nextVertices[i] - prev) * percent) * alpha
+						i = i + 1
+					end
+				end
+			elseif blend == MixBlend.first or blend == MixBlend.replace then
 				local i = 1
 				while i <= vertexCount do
 					local prev = prevVertices[i]
-					vertices[i] = (prev + (nextVertices[i] - prev) * percent) * alpha
+					vertices[i] = vertices[i] + (prev + (nextVertices[i] - prev) * percent - vertices[i]) * alpha
 					i = i + 1
 				end
-			end
-		else
-			-- Vertex positions or deform offsets, with alpha.
-			local i = 1
-			while i <= vertexCount do
-				local prev = prevVertices[i]
-				vertices[i] = vertices[i] + (prev + (nextVertices[i] - prev) * percent - vertices[i]) * alpha
-				i = i + 1
+			elseif blend == MixBlend.add then
+				local vertexAttachment = slotAttachment
+				if vertexAttachment.bones == nil then
+					local setupVertices = vertexAttachment.vertices
+					local i = 1
+					while i <= vertexCount do
+						local prev = prevVertices[i]
+						vertices[i] = vertices[i] + (prev + (nextVertices[i] - prev) * percent - setupVertices[i]) * alpha
+						i = i + 1
+					end
+				else
+					-- Weighted deform offsets, with alpha.
+					local i = 1
+					while i <= vertexCount do
+						local prev = prevVertices[i]
+						vertices[i] = vertices[i] + (prev + (nextVertices[i] - prev) * percent) * alpha
+						i = i + 1
+					end
+				end
 			end
 		end
 	end
@@ -945,14 +1070,14 @@ function Animation.EventTimeline.new (frameCount)
 	end
 
 	-- Fires events for frames > lastTime and <= time.
-	function self:apply (skeleton, lastTime, time, firedEvents, alpha, pose, direction)
+	function self:apply (skeleton, lastTime, time, firedEvents, alpha, blend, 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, pose, direction)
+			self:apply(skeleton, lastTime, 999999, firedEvents, alpha, blend, direction)
 			lastTime = -1
 		elseif lastTime >= frames[frameCount - 1] then -- Last time is after last frame.
 			return
@@ -1001,18 +1126,19 @@ function Animation.DrawOrderTimeline.new (frameCount)
 		self.drawOrders[frameIndex] = drawOrder
 	end
 
-	function self:apply (skeleton, lastTime, time, firedEvents, alpha, pose, direction)
+	function self:apply (skeleton, lastTime, time, firedEvents, alpha, blend, direction)
 		local drawOrder = skeleton.drawOrder
 		local slots = skeleton.slots
-		if mixingOut and setupPose then
+		if direction == MixDirection.out and blend == MixBlend.setup then
 			for i,slot in ipairs(slots) do
 				drawOrder[i] = slots[i]
 			end
 			return;
 		end
+		
 		local frames = self.frames
 		if time < frames[0] then 
-			if pose == MixPose.setup then
+			if blend == MixBlend.setup or blend == MixBlend.first then
 				for i,slot in ipairs(slots) do
 					drawOrder[i] = slots[i]
 				end
@@ -1068,15 +1194,15 @@ function Animation.IkConstraintTimeline.new (frameCount)
 		self.frames[frameIndex + BEND_DIRECTION] = bendDirection
 	end
 
-	function self:apply (skeleton, lastTime, time, firedEvents, alpha, pose, direction)
+	function self:apply (skeleton, lastTime, time, firedEvents, alpha, blend, direction)
 		local frames = self.frames
 
 		local constraint = skeleton.ikConstraints[self.ikConstraintIndex]
 		if time < frames[0] then
-			if pose == MixPose.setup then
+			if blend == MixBlend.setup then
 				constraint.mix = constraint.data.mix
 				constraint.bendDirection = constraint.data.bendDirection
-			elseif pose == MixPose.current then
+			elseif blend == MixBlend.first then
 				constraint.mix = constraint.mix + (constraint.data.mix - constraint.mix) * alpha
 				constraint.bendDirection = constraint.data.bendDirection
 			end
@@ -1084,7 +1210,7 @@ function Animation.IkConstraintTimeline.new (frameCount)
 		end
 
 		if time >= frames[zlen(frames) - ENTRIES] then -- Time is after last frame.
-			if pose == MixPose.setup then
+			if blend == MixBlend.setup then
 				constraint.mix = constraint.data.mix + (frames[zlen(frames) + PREV_MIX] - constraint.data.mix) * alpha
 				if direction == MixDirection.out then 
 					constraint.bendDirection = constraint.data.bendDirection
@@ -1105,7 +1231,7 @@ function Animation.IkConstraintTimeline.new (frameCount)
 		local percent = self:getCurvePercent(math.floor(frame / ENTRIES) - 1,
 				1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime))
 
-		if pose == MixPose.setup then
+		if blend == MixBlend.setup then
 			constraint.mix = constraint.data.mix + (mix + (frames[frame + MIX] - mix) * percent - constraint.data.mix) * alpha
 			if direction == MixDirection.out then
 				constraint.bendDirection = constraint.data.bendDirection
@@ -1153,18 +1279,18 @@ function Animation.TransformConstraintTimeline.new (frameCount)
 		self.frames[frameIndex + SHEAR] = shearMix
 	end
 
-	function self:apply (skeleton, lastTime, time, firedEvents, alpha, pose, direction)
+	function self:apply (skeleton, lastTime, time, firedEvents, alpha, blend, direction)
 		local frames = self.frames
 
 		local constraint = skeleton.transformConstraints[self.transformConstraintIndex]
 		if time < frames[0] then
 			local data = constraint.data
-			if pose == MixPose.setup then
+			if blend == MixBlend.setup then
 				constraint.rotateMix = data.rotateMix
 				constraint.translateMix = data.translateMix
 				constraint.scaleMix = data.scaleMix
 				constraint.shearMix = data.shearMix
-			elseif pose == MixPose.current then
+			elseif blend == MixBlend.first 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
@@ -1199,7 +1325,7 @@ function Animation.TransformConstraintTimeline.new (frameCount)
 			scale = scale + (frames[frame + SCALE] - scale) * percent
 			shear = shear + (frames[frame + SHEAR] - shear) * percent
 		end
-		if pose == MixPose.setup then
+		if blend == MixBlend.setup then
 			local data = constraint.data
 			constraint.rotateMix = data.rotateMix + (rotate - data.rotateMix) * alpha
 			constraint.translateMix = data.translateMix + (translate - data.translateMix) * alpha
@@ -1239,14 +1365,14 @@ function Animation.PathConstraintPositionTimeline.new (frameCount)
 		self.frames[frameIndex + VALUE] = value
 	end
 
-	function self:apply (skeleton, lastTime, time, firedEvents, alpha, pose, direction)
+	function self:apply (skeleton, lastTime, time, firedEvents, alpha, blend, direction)
 		local frames = self.frames
 
 		local constraint = skeleton.pathConstraints[self.pathConstraintIndex]
 		if (time < frames[0]) then
-			if pose == MixPose.setup then
+			if blend == MixBlend.setup then
 				constraint.position = constraint.data.position	
-			elseif pose == MixPose.current then
+			elseif blend == MixBlend.first then
 				constraint.position = constraint.position + (constraint.data.position - constraint.position) * alpha
 			end
 			return
@@ -1265,7 +1391,7 @@ function Animation.PathConstraintPositionTimeline.new (frameCount)
 
 			position = position + (frames[frame + VALUE] - position) * percent
 		end
-		if pose == MixPose.setup then
+		if blend == MixBlend.setup then
 			constraint.position = constraint.data.position + (position - constraint.data.position) * alpha
 		else
 			constraint.position = constraint.position + (position - constraint.position) * alpha
@@ -1298,14 +1424,14 @@ function Animation.PathConstraintSpacingTimeline.new (frameCount)
 		self.frames[frameIndex + VALUE] = value
 	end
 
-	function self:apply (skeleton, lastTime, time, firedEvents, alpha, pose, direction)
+	function self:apply (skeleton, lastTime, time, firedEvents, alpha, blend, direction)
 		local frames = self.frames
 
 		local constraint = skeleton.pathConstraints[self.pathConstraintIndex]
 		if (time < frames[0]) then
-			if pose == MixPose.setup then
+			if blend == MixBlend.setup then
 				constraint.spacing = constraint.data.spacing
-			elseif pose == MixPose.current then
+			elseif blend == MixBlend.first then
 				constraint.spacing = constraint.spacing + (constraint.data.spacing - constraint.spacing) * alpha
 			end
 			return
@@ -1325,7 +1451,7 @@ function Animation.PathConstraintSpacingTimeline.new (frameCount)
 			spacing = spacing + (frames[frame + VALUE] - spacing) * percent
 		end
 
-		if pose == MixPose.setup then
+		if blend == MixBlend.setup then
 			constraint.spacing = constraint.data.spacing + (spacing - constraint.data.spacing) * alpha
 		else
 			constraint.spacing = constraint.spacing + (spacing - constraint.spacing) * alpha
@@ -1361,15 +1487,15 @@ function Animation.PathConstraintMixTimeline.new (frameCount)
 		self.frames[frameIndex + TRANSLATE] = translateMix
 	end
 
-	function self:apply (skeleton, lastTime, time, firedEvents, alpha, pose, direction)
+	function self:apply (skeleton, lastTime, time, firedEvents, alpha, blend, direction)
 		local frames = self.frames
 
 		local constraint = skeleton.pathConstraints[self.pathConstraintIndex]
 		if (time < frames[0]) then
-			if pose == MixPose.setup then
+			if blend == MixBlend.setup then
 				constraint.rotateMix = constraint.data.rotateMix
 				constraint.translateMix = constraint.data.translateMix
-			elseif pose == MixPose.current then
+			elseif blend == MixBlend.first then
 				constraint.rotateMix = constraint.rotateMix + (constraint.data.rotateMix - constraint.rotateMix) * alpha
 				constraint.translateMix = constraint.translateMix + (constraint.data.translateMix - constraint.translateMix) * alpha
 			end
@@ -1394,7 +1520,7 @@ function Animation.PathConstraintMixTimeline.new (frameCount)
 			translate = translate + (frames[frame + TRANSLATE] - translate) * percent
 		end
 
-		if pose == MixPose.setup then
+		if blend == MixBlend.setup then
 			constraint.rotateMix = constraint.data.rotateMix + (rotate - constraint.data.rotateMix) * alpha
 			constraint.translateMix = constraint.data.translateMix + (translate - constraint.data.translateMix) * alpha
 		else

+ 60 - 51
spine-lua/AnimationState.lua

@@ -32,7 +32,7 @@ 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 MixBlend = Animation.MixBlend
 local MixDirection = Animation.MixDirection
 local AnimationStateData = require "spine-lua.AnimationStateData"
 local math_min = math.min
@@ -175,6 +175,7 @@ function TrackEntry.new ()
 		animationStart = 0, animationEnd = 0, animationLast = 0, nextAnimationLast = 0,
 		delay = 0, trackTime = 0, trackLast = 0, nextTrackLast = 0, trackEnd = 0, timeScale = 0,
 		alpha = 0, mixTime = 0, mixDuration = 0, interruptAlpha = 0, totalAlpha = 0,
+		mixBlend = MixBlend.replace,
 		timelineData = {},
 		timelineDipMix = {},
 		timelinesRotation = {}
@@ -374,13 +375,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
+			local blend = current.mixBlend
+			if i == 0 then blend = MixBlend.first end
 			
 			-- Apply mixing from entries first.
 			local mix = current.alpha
 			if current.mixingFrom then 
-				mix = mix * self:applyMixingFrom(current, skeleton, currentPose)
+				mix = mix * self:applyMixingFrom(current, skeleton, blend)
 			elseif current.trackTime >= current.trackEnd and current.next == nil then
 				mix = 0
 			end
@@ -389,9 +390,9 @@ function AnimationState:apply (skeleton)
 			local animationLast = current.animationLast
 			local animationTime = current:getAnimationTime()
 			local timelines = current.animation.timelines
-			if mix == 1 then
+			if mix == 1 or blend == MixBlend.add then
 				for i,timeline in ipairs(timelines) do
-					timeline:apply(skeleton, animationLast, animationTime, events, 1, MixPose.setup, MixDirection._in)
+					timeline:apply(skeleton, animationLast, animationTime, events, mix, blend, MixDirection._in)
 				end
 			else
 				local timelineData = current.timelineData
@@ -399,14 +400,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
+					local timelineBlend = MixBlend.setup
+					if timelineData[i] == SUBSEQUENT then timelineBlend = blend end
 					
 					if timeline.type == Animation.TimelineType.rotate then
-						self:applyRotateTimeline(timeline, skeleton, animationTime, mix, pose, timelinesRotation, i * 2,
+						self:applyRotateTimeline(timeline, skeleton, animationTime, mix, timelineBlend, timelinesRotation, i * 2,
 							firstFrame) -- FIXME passing ii * 2, indexing correct?
 					else
-						timeline:apply(skeleton, animationLast, animationTime, events, mix, pose, MixDirection._in)
+						timeline:apply(skeleton, animationLast, animationTime, events, mix, timelineBlend, MixDirection._in)
 					end
 				end
 			end
@@ -421,17 +422,18 @@ function AnimationState:apply (skeleton)
   return applied
 end
 
-function AnimationState:applyMixingFrom (to, skeleton, currentPose)
+function AnimationState:applyMixingFrom (to, skeleton, blend)
 	local from = to.mixingFrom
-	if from.mixingFrom then self:applyMixingFrom(from, skeleton, currentPose) end
+	if from.mixingFrom then self:applyMixingFrom(from, skeleton, blend) end
 
 	local mix = 0
 	if to.mixDuration == 0 then -- Single frame mix to undo mixingFrom changes.
 		mix = 1
-    currentPose = MixPose.setup
+    if blend == MixBlend.first then blend = MixBlend.setup end
 	else
 		mix = to.mixTime / to.mixDuration
 		if mix > 1 then mix = 1 end
+		if blend ~= MixBlend.first then blend = from.mixBlend end
 	end
 
 	local events = nil
@@ -441,44 +443,50 @@ function AnimationState:applyMixingFrom (to, skeleton, currentPose)
 	local animationLast = from.animationLast
 	local animationTime = from:getAnimationTime()
 	local timelines = from.animation.timelines
-	local timelineData = from.timelineData
-	local timelineDipMix = from.timelineDipMix
-
-	local firstFrame = #from.timelinesRotation == 0
-	local timelinesRotation = from.timelinesRotation
-
-	local pose = MixPose.setup
 	local alphaDip = from.alpha * to.interruptAlpha
 	local alphaMix = alphaDip * (1 - mix)
-	local alpha = 0
-	from.totalAlpha = 0;
-
-	for i,timeline in ipairs(timelines) do
-		local skipSubsequent = false;
-		if timelineData[i] == SUBSEQUENT then
-			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
-			pose = MixPose.setup
-			alpha = alphaMix
-		elseif timelineData[i] == DIP then
-			pose = MixPose.setup
-			alpha = alphaDip
-		else
-			pose = MixPose.setup
-			alpha = alphaDip
-			local dipMix = timelineDipMix[i]
-			alpha = alpha * math_max(0, 1 - dipMix.mixtime / dipMix.mixDuration)
+	
+	if blend == MixBlend.add then
+		for i,timeline in ipairs(timelines) do
+			timeline:apply(skeleton, animationLast, animationTime, events, alphaMix, blend, MixDirection.out)
 		end
+	else
+		local timelineData = from.timelineData
+		local timelineDipMix = from.timelineDipMix
+
+		local firstFrame = #from.timelinesRotation == 0
+		local timelinesRotation = from.timelinesRotation
 		
-		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)
+		from.totalAlpha = 0;
+
+		for i,timeline in ipairs(timelines) do
+			local skipSubsequent = false;
+			local timelineBlend = MixBlend.setup
+			local alpha = 0
+			if timelineData[i] == SUBSEQUENT then
+				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
+				timelineBlend = blend
+				alpha = alphaMix
+			elseif timelineData[i] == FIRST then
+				timelineBlend = MixBlend.setup
+				alpha = alphaMix
+			elseif timelineData[i] == DIP then
+				timelineBlend = MixBlend.setup
+				alpha = alphaDip
 			else
-				timeline:apply(skeleton, animationLast, animationTime, events, alpha, pose, MixDirection.out)
+				timelineBlend = MixBlend.setup
+				local dipMix = timelineDipMix[i]
+				alpha = alphaDip * math_max(0, 1 - dipMix.mixtime / dipMix.mixDuration)
+			end
+			
+			if not skipSubsequent then
+				from.totalAlpha = from.totalAlpha + alpha
+				if timeline.type == Animation.TimelineType.rotate then
+					self:applyRotateTimeline(timeline, skeleton, animationTime, alpha, timelineBlend, timelinesRotation, i * 2, firstFrame)
+				else
+					timeline:apply(skeleton, animationLast, animationTime, events, alpha, timelineBlend, MixDirection.out)
+				end
 			end
 		end
 	end
@@ -491,14 +499,14 @@ function AnimationState:applyMixingFrom (to, skeleton, currentPose)
 	return mix
 end
 
-function AnimationState:applyRotateTimeline (timeline, skeleton, time, alpha, pose, timelinesRotation, i, firstFrame)
+function AnimationState:applyRotateTimeline (timeline, skeleton, time, alpha, blend, 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, pose, MixDirection._in)
+    timeline:apply(skeleton, 0, time, nil, 1, blend, MixDirection._in)
     return
   end
 
@@ -506,7 +514,7 @@ function AnimationState:applyRotateTimeline (timeline, skeleton, time, alpha, po
   local frames = rotateTimeline.frames
   local bone = skeleton.bones[rotateTimeline.boneIndex]
   if time < frames[0] then
-		if pose == MixPose.setup then bone.rotation = bone.data.rotation end
+		if blend == MixBlend.setup then bone.rotation = bone.data.rotation end
 		return
 	end
 
@@ -529,7 +537,7 @@ function AnimationState:applyRotateTimeline (timeline, skeleton, time, alpha, po
 
   -- Mix between rotations using the direction of the shortest route on the first frame while detecting crosses.
   local r1 = bone.rotation
-  if pose == MixPose.setup then r1 = bone.data.rotation end
+  if blend == MixBlend.setup then r1 = bone.data.rotation end
   local total = 0
   local diff = r2 - r1
   if diff == 0 then
@@ -797,6 +805,7 @@ function AnimationState:trackEntry (trackIndex, animation, loop, last)
   else
     entry.mixDuration = data:getMix(last.animation, animation)
   end
+	entry.mixBlend = MixBlend.replace
   return entry
 end
 
@@ -818,7 +827,7 @@ function AnimationState:_animationsChanged ()
 	local mixingTo = self.mixingTo
 	  
 	for i, entry in pairs(self.tracks) do
-		if entry then
+		if entry and (i == 0 or entry.mixBlend ~= MixBlend.add) then
 			entry:setTimelineData(nil, mixingTo, propertyIDs)			
 		end
 	end

+ 1 - 1
spine-ts/core/src/Animation.ts

@@ -833,7 +833,7 @@ module spine {
 					case MixBlend.first:
 					case MixBlend.replace:
 						for (let i = 0; i < vertexCount; i++)
-						vertices[i] += (lastVertices[i] - vertices[i]) * alpha;
+							vertices[i] += (lastVertices[i] - vertices[i]) * alpha;
 					case MixBlend.add:
 						let vertexAttachment = slotAttachment as VertexAttachment;
 						if (vertexAttachment.bones == null) {