diff --git a/spine-corona/main.lua b/spine-corona/main.lua index 687b28f70..78da32c25 100644 --- a/spine-corona/main.lua +++ b/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")) diff --git a/spine-lua/Animation.lua b/spine-lua/Animation.lua index b0b33ac68..b4af403e4 100644 --- a/spine-lua/Animation.lua +++ b/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 diff --git a/spine-lua/IkConstraint.lua b/spine-lua/IkConstraint.lua index c5315ba0e..8bba44edf 100644 --- a/spine-lua/IkConstraint.lua +++ b/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 diff --git a/spine-lua/IkConstraintData.lua b/spine-lua/IkConstraintData.lua index b7497d63b..842fef327 100644 --- a/spine-lua/IkConstraintData.lua +++ b/spine-lua/IkConstraintData.lua @@ -38,6 +38,7 @@ function IkConstraintData.new (name) bones = {}, target = nil, bendDirection = 1, + stretch = false, mix = 1 } diff --git a/spine-lua/Skeleton.lua b/spine-lua/Skeleton.lua index cb468861e..1e2ae96e1 100644 --- a/spine-lua/Skeleton.lua +++ b/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 diff --git a/spine-lua/SkeletonJson.lua b/spine-lua/SkeletonJson.lua index 57116c61d..536f526db 100644 --- a/spine-lua/SkeletonJson.lua +++ b/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