diff --git a/spine-lua/Animation.lua b/spine-lua/Animation.lua index 590ca2f99..8b93b2b48 100644 --- a/spine-lua/Animation.lua +++ b/spine-lua/Animation.lua @@ -1181,21 +1181,23 @@ function Animation.DrawOrderTimeline.new (frameCount) end Animation.IkConstraintTimeline = {} -Animation.IkConstraintTimeline.ENTRIES = 5 +Animation.IkConstraintTimeline.ENTRIES = 6 function Animation.IkConstraintTimeline.new (frameCount) local ENTRIES = Animation.IkConstraintTimeline.ENTRIES - local PREV_TIME = -5 - local PREV_MIX = -4 + local PREV_TIME = -6 + local PREV_MIX = -5 + local PREV_SOFTNESS = -4 local PREV_BEND_DIRECTION = -3 local PREV_COMPRESS = -2 local PREV_STRETCH = -1 local MIX = 1 - local BEND_DIRECTION = 2 - local COMPRESS = 3 - local STRETCH = 4 + local SOFTNESS = 2 + local BEND_DIRECTION = 3 + local COMPRESS = 4 + local STRETCH = 5 local self = Animation.CurveTimeline.new(frameCount) - self.frames = utils.newNumberArrayZero(frameCount * ENTRIES) -- time, mix, bendDirection, compress, stretch, ... + self.frames = utils.newNumberArrayZero(frameCount * ENTRIES) -- time, mix, softness, bendDirection, compress, stretch, ... self.ikConstraintIndex = -1 self.type = TimelineType.ikConstraint @@ -1203,10 +1205,11 @@ function Animation.IkConstraintTimeline.new (frameCount) return TimelineType.ikConstraint * SHL_24 + self.ikConstraintIndex end - function self:setFrame (frameIndex, time, mix, bendDirection, compress, stretch) + function self:setFrame (frameIndex, time, mix, softness, bendDirection, compress, stretch) frameIndex = frameIndex * ENTRIES self.frames[frameIndex] = time self.frames[frameIndex + MIX] = mix + self.frames[frameIndex + SOFTNESS] = softness self.frames[frameIndex + BEND_DIRECTION] = bendDirection if (compress) then self.frames[frameIndex + COMPRESS] = 1 @@ -1228,11 +1231,13 @@ function Animation.IkConstraintTimeline.new (frameCount) if time < frames[0] then if blend == MixBlend.setup then constraint.mix = constraint.data.mix + constraint.softness = constraint.data.softness constraint.bendDirection = constraint.data.bendDirection constraint.compress = constraint.data.compress constraint.stretch = constraint.data.stretch elseif blend == MixBlend.first then constraint.mix = constraint.mix + (constraint.data.mix - constraint.mix) * alpha + constraint.softness = constraint.softness + (constraint.data.softness - constraint.softness) * alpha constraint.bendDirection = constraint.data.bendDirection constraint.compress = constraint.data.compress constraint.stretch = constraint.data.stretch @@ -1243,6 +1248,8 @@ function Animation.IkConstraintTimeline.new (frameCount) if time >= frames[zlen(frames) - ENTRIES] then -- Time is after last frame. if blend == MixBlend.setup then constraint.mix = constraint.data.mix + (frames[zlen(frames) + PREV_MIX] - constraint.data.mix) * alpha + constraint.softness = constraint.data.softness + + (frames[zlen(frames) + PREV_SOFTNESS] - constraint.data.softness) * alpha if direction == MixDirection.out then constraint.bendDirection = constraint.data.bendDirection constraint.compress = constraint.data.compress @@ -1253,7 +1260,8 @@ function Animation.IkConstraintTimeline.new (frameCount) 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[zlen(frames) + PREV_MIX] - constraint.mix) * alpha; + constraint.mix = constraint.mix + (frames[zlen(frames) + PREV_MIX] - constraint.mix) * alpha + constraint.softness = constraint.softness + (frames[zlen(frames) + PREV_SOFTNESS] - constraint.softness) * alpha if direction == MixDirection._in then constraint.bendDirection = math_floor(frames[zlen(frames) + PREV_BEND_DIRECTION]) if (math_floor(frames[zlen(frames) + PREV_COMPRESS]) == 1) then constraint.compress = true else constraint.compress = false end @@ -1266,12 +1274,15 @@ function Animation.IkConstraintTimeline.new (frameCount) -- Interpolate between the previous frame and the current frame. local frame = binarySearch(frames, time, ENTRIES) local mix = frames[frame + PREV_MIX] + local softness = frames[frame + PREV_SOFTNESS] local frameTime = frames[frame] local percent = self:getCurvePercent(math.floor(frame / ENTRIES) - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime)) if blend == MixBlend.setup then constraint.mix = constraint.data.mix + (mix + (frames[frame + MIX] - mix) * percent - constraint.data.mix) * alpha + constraint.softness = constraint.data.softness + + (softness + (frames[frame + SOFTNESS] - softness) * percent - constraint.data.softness) * alpha if direction == MixDirection.out then constraint.bendDirection = constraint.data.bendDirection constraint.compress = constraint.data.compress @@ -1282,7 +1293,8 @@ function Animation.IkConstraintTimeline.new (frameCount) 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; + constraint.mix = constraint.mix + (mix + (frames[frame + MIX] - mix) * percent - constraint.mix) * alpha + constraint.softness = constraint.softness + (softness + (frames[frame + SOFTNESS] - softness) * percent - constraint.softness) * alpha if direction == MixDirection._in then constraint.bendDirection = math_floor(frames[frame + PREV_BEND_DIRECTION]) if (math_floor(frames[frame + PREV_COMPRESS]) == 1) then constraint.compress = true else constraint.compress = false end diff --git a/spine-lua/IkConstraint.lua b/spine-lua/IkConstraint.lua index ef7126a2d..3ba7ffc57 100644 --- a/spine-lua/IkConstraint.lua +++ b/spine-lua/IkConstraint.lua @@ -34,6 +34,7 @@ local math_sqrt = math.sqrt local math_acos = math.acos local math_sin = math.sin local math_cos = math.cos +local math_min = math.min local table_insert = table.insert local math_deg = math.deg local math_rad = math.rad @@ -51,6 +52,7 @@ function IkConstraint.new (data, skeleton) bones = {}, target = nil, mix = data.mix, + softness = data.softness, compress = data.compress, stretch = data.stretch, bendDirection = data.bendDirection, @@ -78,7 +80,7 @@ function IkConstraint:update () if boneCount == 1 then self:apply1(bones[1], target.worldX, target.worldY, self.compress, self.stretch, self.data.uniform, self.mix) elseif boneCount == 2 then - self:apply2(bones[1], bones[2], target.worldX, target.worldY, self.bendDirection, self.stretch, self.mix) + self:apply2(bones[1], bones[2], target.worldX, target.worldY, self.bendDirection, self.stretch, self.softness, self.mix) end end @@ -111,7 +113,7 @@ function IkConstraint:apply1 (bone, targetX, targetY, compress, stretch, uniform bone:updateWorldTransformWith(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, sy, bone.ashearX, bone.ashearY) end -function IkConstraint:apply2 (parent, child, targetX, targetY, bendDir, stretch, alpha) +function IkConstraint:apply2 (parent, child, targetX, targetY, bendDir, stretch, softness, alpha) if alpha == 0 then child:updateWorldTransform() return @@ -169,19 +171,36 @@ function IkConstraint:apply2 (parent, child, targetX, targetY, bendDir, stretch, c = pp.c d = pp.d local id = 1 / (a * d - b * c) - local x = targetX - pp.worldX - 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 - local dy = (y * a - x * c) * id - py - local l1 = math_sqrt(dx * dx + dy * dy) - local l2 = child.data.length * csx - local a1 = 0 - local a2 = 0 + local x = cwx - pp.worldX + local y = cwy - pp.worldY + local dx = (x * d - y * b) * id - px + local dy = (y * a - x * c) * id - py + local l1 = math_sqrt(dx * dx + dy * dy) + local l2 = child.data.length * csx + local a1 = 0 + local a2 = 0 + if l1 < 0.0001 then + self:apply1(parent, targetX, targetY, false, stretch, false, alpha) + child:updateWorldTransformWith(cx, cy, 0, child.ascaleX, child.ascaleY, child.ashearX, child.ashearY) + return + end + x = targetX - pp.worldX + 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 + if softness ~= 0 then + softness = softness * (psx * (csx + 1) / 2) + local td = math_sqrt(dd) + local sd = td - l1 - l2 * psx + softness + if sd > 0 then + local p = math_min(1, sd / (softness * 2)) - 1 + p = (sd - softness * (1 - p * p)) / td + tx = tx - p * tx + ty = ty - p * ty + dd = tx * tx + ty * ty + end + end if u then l2 = l2 * psx diff --git a/spine-lua/IkConstraintData.lua b/spine-lua/IkConstraintData.lua index 57c03e0f6..d146d8240 100644 --- a/spine-lua/IkConstraintData.lua +++ b/spine-lua/IkConstraintData.lua @@ -41,7 +41,8 @@ function IkConstraintData.new (name) compress = false, stretch = false, uniform = false, - mix = 1 + mix = 1, + softness = 0 } return self diff --git a/spine-lua/Skeleton.lua b/spine-lua/Skeleton.lua index 7d2062d61..a4e5ba67f 100644 --- a/spine-lua/Skeleton.lua +++ b/spine-lua/Skeleton.lua @@ -363,6 +363,7 @@ function Skeleton:setBonesToSetupPose () for _,ikConstraint in ipairs(self.ikConstraints) do ikConstraint.mix = ikConstraint.data.mix + ikConstraint.softness = ikConstraint.data.softness ikConstraint.bendDirection = ikConstraint.data.bendDirection ikConstraint.compress = ikConstraint.data.compress ikConstraint.stretch = ikConstraint.data.stretch diff --git a/spine-lua/SkeletonJson.lua b/spine-lua/SkeletonJson.lua index c177fa38d..582c47546 100644 --- a/spine-lua/SkeletonJson.lua +++ b/spine-lua/SkeletonJson.lua @@ -168,6 +168,7 @@ function SkeletonJson.new (attachmentLoader) if not data.target then error("Target bone not found: " .. targetName) end data.mix = getValue(constraintMap, "mix", 1) + data.softness = getValue(constraintMap, "softness", 0) if constraintMap["bendPositive"] == nil or constraintMap["bendPositive"] == true then data.bendDirection = 1 else @@ -637,14 +638,16 @@ function SkeletonJson.new (attachmentLoader) local frameIndex = 0 for _,valueMap in ipairs(values) do local mix = 1 + local softness = 0 if valueMap["mix"] ~= nil then mix = valueMap["mix"] end + if valueMap["softness"] ~= nil then softness = valueMap["softness"] end local bendPositive = 1 if valueMap["bendPositive"] == false then bendPositive = -1 end local stretch = false if valueMap["stretch"] ~= nil then stretch = valueMap["stretch"] end local compress = false if valueMap["compress"] ~= nil then compress = valueMap["compress"] end - timeline:setFrame(frameIndex, getValue(valueMap, "time", 0), mix, bendPositive, compress, stretch) + timeline:setFrame(frameIndex, getValue(valueMap, "time", 0), mix, softness, bendPositive, compress, stretch) readCurve(valueMap, timeline, frameIndex) frameIndex = frameIndex + 1 end