Parcourir la source

[lua] Ported stretchy IK.

badlogic il y a 7 ans
Parent
commit
fd7b0caec4

+ 1 - 0
spine-corona/main.lua

@@ -81,6 +81,7 @@ function loadSkeleton(atlasFile, jsonFile, x, y, scale, animation, skin)
 	return { skeleton = skeleton, state = animationState }
 end
 
+table.insert(skeletons, loadSkeleton("stretchyman.atlas", "stretchyman-stretchy-ik.json", 40, 300, 0.5, "sneak"))
 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"))

+ 26 - 7
spine-lua/Animation.lua

@@ -1169,14 +1169,16 @@ function Animation.DrawOrderTimeline.new (frameCount)
 end
 
 Animation.IkConstraintTimeline = {}
-Animation.IkConstraintTimeline.ENTRIES = 3
+Animation.IkConstraintTimeline.ENTRIES = 4
 function Animation.IkConstraintTimeline.new (frameCount)
 	local ENTRIES = Animation.IkConstraintTimeline.ENTRIES
-	local PREV_TIME = -3
-	local PREV_MIX = -2
-	local PREV_BEND_DIRECTION = -1
+	local PREV_TIME = -4
+	local PREV_MIX = -3
+	local PREV_BEND_DIRECTION = -2
+	local PREV_STRETCH = -1
 	local MIX = 1
 	local BEND_DIRECTION = 2
+	local STRETCH = 1
 
 	local self = Animation.CurveTimeline.new(frameCount)
 	self.frames = utils.newNumberArrayZero(frameCount * ENTRIES) -- time, mix, bendDirection, ...
@@ -1187,11 +1189,16 @@ function Animation.IkConstraintTimeline.new (frameCount)
 		return TimelineType.ikConstraint * SHL_24 + self.ikConstraintIndex
 	end
 
-	function self:setFrame (frameIndex, time, mix, bendDirection)
+	function self:setFrame (frameIndex, time, mix, bendDirection, stretch)
 		frameIndex = frameIndex * ENTRIES
 		self.frames[frameIndex] = time
 		self.frames[frameIndex + MIX] = mix
 		self.frames[frameIndex + BEND_DIRECTION] = bendDirection
+		if (stretch) then
+			self.frames[frameIndex + STRETCH] = 1
+		else
+			self.frames[frameIndex + STRETCH] = 0
+		end
 	end
 
 	function self:apply (skeleton, lastTime, time, firedEvents, alpha, blend, direction)
@@ -1202,9 +1209,11 @@ function Animation.IkConstraintTimeline.new (frameCount)
 			if blend == MixBlend.setup then
 				constraint.mix = constraint.data.mix
 				constraint.bendDirection = constraint.data.bendDirection
+				constraint.stretch = constraint.data.stretch
 			elseif blend == MixBlend.first then
 				constraint.mix = constraint.mix + (constraint.data.mix - constraint.mix) * alpha
 				constraint.bendDirection = constraint.data.bendDirection
+				constraint.stretch = constraint.data.stretch
 			end
 			return
 		end
@@ -1214,12 +1223,17 @@ function Animation.IkConstraintTimeline.new (frameCount)
 				constraint.mix = constraint.data.mix + (frames[zlen(frames) + PREV_MIX] - constraint.data.mix) * alpha
 				if direction == MixDirection.out then 
 					constraint.bendDirection = constraint.data.bendDirection
+					constraint.stretch = constraint.data.stretch
 				else
 					constraint.bendDirection = math_floor(frames[zlen(frames) + PREV_BEND_DIRECTION]);
+					if (math_floor(frames[zlen(frames) + PREV_STRETCH]) == 1) then constraint.stretch = true else constraint.stretch = false end
 				end
 			else
 				constraint.mix = constraint.mix + (frames[frames.length + PREV_MIX] - constraint.mix) * alpha;
-				if direction == MixDirection._in 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])
+					if (math_floor(frames[zlen(frames) + PREV_STRETCH]) == 1) then constraint.stretch = true else constraint.stretch = false end
+				end
 			end
 			return
 		end
@@ -1235,12 +1249,17 @@ function Animation.IkConstraintTimeline.new (frameCount)
 			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
+				constraint.stretch = constraint.data.stretch
 			else
 				constraint.bendDirection = math_floor(frames[frame + PREV_BEND_DIRECTION])
+				if (math_floor(frames[frame + PREV_STRETCH]) == 1) then constraint.stretch = true else constraint.stretch = false end
 			end
 		else
 			constraint.mix = constraint.mix + (mix + (frames[frame + MIX] - mix) * percent - constraint.mix) * alpha;
-			if direction == MixDirection._in 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])
+				if (math_floor(frames[frame + PREV_STRETCH]) == 1) then constraint.stretch = true else constraint.stretch = false end
+			end
 		end
 	end
 

+ 16 - 8
spine-lua/IkConstraint.lua

@@ -52,6 +52,7 @@ function IkConstraint.new (data, skeleton)
 		bones = {},
 		target = nil,
 		mix = data.mix,
+		stretch = data.stretch,
 		bendDirection = data.bendDirection,
 	}
 	setmetatable(self, IkConstraint)
@@ -74,13 +75,13 @@ function IkConstraint:update ()
 	local bones = self.bones
 	local boneCount = #bones
 	if boneCount == 1 then
-		self:apply1(bones[1], target.worldX, target.worldY, self.mix)
+		self:apply1(bones[1], target.worldX, target.worldY, self.stretch, self.mix)
 	elseif boneCount == 2 then
-		self:apply2(bones[1], bones[2], target.worldX, target.worldY, self.bendDirection, self.mix)
+		self:apply2(bones[1], bones[2], target.worldX, target.worldY, self.bendDirection, self.stretch, self.mix)
 	end
 end
 
-function IkConstraint:apply1 (bone, targetX, targetY, alpha)
+function IkConstraint:apply1 (bone, targetX, targetY, stretch, alpha)
 	if not bone.appliedValid then bone:updateAppliedTransform() end
 	local p = bone.parent
 	local id = 1 / (p.a * p.d - p.b * p.c)
@@ -95,10 +96,15 @@ function IkConstraint:apply1 (bone, targetX, targetY, alpha)
 	elseif (rotationIK < -180) then
 		rotationIK = rotationIK + 360
 	end
-	bone:updateWorldTransformWith(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, bone.ascaleX, bone.ascaleY, bone.ashearX, bone.ashearY)
+	local sx = bone.ascaleX
+	if stretch then
+		local dd = math_sqrt(tx * tx + ty * ty)
+		if dd > bone.data.length * sx then sx = sx * ((dd / (bone.data.length * sx) - 1) * alpha + 1) end
+	end
+	bone:updateWorldTransformWith(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, bone.ascaleY, bone.ashearX, bone.ashearY)
 end
 
-function IkConstraint:apply2 (parent, child, targetX, targetY, bendDir, alpha)
+function IkConstraint:apply2 (parent, child, targetX, targetY, bendDir, stretch, alpha)
 	if alpha == 0 then
 		child:updateWorldTransform()
 		return
@@ -108,6 +114,7 @@ function IkConstraint:apply2 (parent, child, targetX, targetY, bendDir, alpha)
 	local px = parent.ax
 	local py = parent.ay
 	local psx = parent.ascaleX
+	local sx = psx
 	local psy = parent.ascaleY
 	local csx = child.ascaleX
 	local os1 = 0
@@ -159,6 +166,7 @@ function IkConstraint:apply2 (parent, child, targetX, targetY, bendDir, alpha)
 	local y = targetY - pp.worldY
 	local tx = (x * d - y * b) * id - px
 	local ty = (y * a - x * c) * id - py
+	local dd = tx * tx + ty * ty
 	x = cwx - pp.worldX
 	y = cwy - pp.worldY
 	local dx = (x * d - y * b) * id - px
@@ -170,11 +178,12 @@ function IkConstraint:apply2 (parent, child, targetX, targetY, bendDir, alpha)
 
 	if u then
 		l2 = l2 * psx
-		local cos = (tx * tx + ty * ty - l1 * l1 - l2 * l2) / (2 * l1 * l2)
+		local cos = (dd - l1 * l1 - l2 * l2) / (2 * l1 * l2)
 		if cos < -1 then
 			cos = -1
 		elseif cos > 1 then
 			cos = 1
+			if stretch then sx = sx * ((math_sqrt(dd) / (l1 + l2) - 1) * alpha + 1) end
 		end
 		a2 = math_acos(cos) * bendDir
 		a = l1 + l2 * cos
@@ -186,7 +195,6 @@ function IkConstraint:apply2 (parent, child, targetX, targetY, bendDir, alpha)
 		b = psy * l2
 		local aa = a * a
 		local bb = b * b
-		local dd = tx * tx + ty * ty
 		local ta = math_atan2(ty, tx);
 		c = bb * l1 * l1 + aa * dd - aa * bb
 		local c1 = -2 * bb * l1
@@ -252,7 +260,7 @@ function IkConstraint:apply2 (parent, child, targetX, targetY, bendDir, alpha)
 	elseif a1 < -180 then
 		a1 = a1 + 360
 	end
-	parent:updateWorldTransformWith(px, py, rotation + a1 * alpha, parent.ascaleX, parent.ascaleY, 0, 0)
+	parent:updateWorldTransformWith(px, py, rotation + a1 * alpha, sx, parent.ascaleY, 0, 0)
 	rotation = child.rotation
 	a2 = (math_deg(a2 + os) - child.ashearX) * s2 + os2 - rotation
 	if a2 > 180 then

+ 1 - 0
spine-lua/IkConstraintData.lua

@@ -38,6 +38,7 @@ function IkConstraintData.new (name)
 		bones = {},
 		target = nil,
 		bendDirection = 1,
+		stretch = false,
 		mix = 1
 	}
 

+ 1 - 0
spine-lua/Skeleton.lua

@@ -339,6 +339,7 @@ function Skeleton:setBonesToSetupPose ()
 
 	for _,ikConstraint in ipairs(self.ikConstraints) do
 		ikConstraint.bendDirection = ikConstraint.data.bendDirection
+		ikConstraint.stretch = ikConstraint.data.stretch
 		ikConstraint.mix = ikConstraint.data.mix
 	end
 

+ 4 - 1
spine-lua/SkeletonJson.lua

@@ -165,6 +165,7 @@ function SkeletonJson.new (attachmentLoader)
 				if not data.target then error("Target bone not found: " .. targetName) end
 
 				if constraintMap["bendPositive"] == false then data.bendDirection = -1 else data.bendDirection = 1 end
+				if constraintMap["stretch"] == false then data.stretch = false else data.stretch = true end
 				data.mix = getValue(constraintMap, "mix", 1)
 
 				table_insert(skeletonData.ikConstraints, data)
@@ -612,7 +613,9 @@ function SkeletonJson.new (attachmentLoader)
 					if valueMap["mix"] ~= nil then mix = valueMap["mix"] end
 					local bendPositive = 1
 					if valueMap["bendPositive"] == false then bendPositive = -1 end
-					timeline:setFrame(frameIndex, valueMap["time"], mix, bendPositive)
+					local stretch = true
+					if valueMap["stretch"] == false then stretch = false end
+					timeline:setFrame(frameIndex, valueMap["time"], mix, bendPositive, stretch)
 					readCurve(valueMap, timeline, frameIndex)
 					frameIndex = frameIndex + 1
 				end