From 3b650f8db14af6c6a719aeeda455972e408d943a Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Fri, 4 Jun 2021 22:15:32 -0400 Subject: [PATCH] [lua] Timelines 4.0 port. --- spine-lua/spine-lua/Animation.lua | 3670 +++++++++-------- spine-lua/spine-lua/AnimationState.lua | 34 +- spine-lua/spine-lua/Atlas.lua | 4 +- spine-lua/spine-lua/AtlasAttachmentLoader.lua | 4 +- spine-lua/spine-lua/IkConstraint.lua | 10 +- spine-lua/spine-lua/Interpolation.lua | 2 +- spine-lua/spine-lua/Skeleton.lua | 2 +- spine-lua/spine-lua/SkeletonBounds.lua | 4 +- spine-lua/spine-lua/SkeletonClipping.lua | 8 +- spine-lua/spine-lua/SkeletonJson.lua | 31 +- spine-lua/spine-lua/Skin.lua | 2 +- spine-lua/spine-lua/Triangulator.lua | 6 +- .../spine-lua/attachments/Attachment.lua | 2 +- .../attachments/VertexAttachment.lua | 1 + spine-lua/spine-lua/utils.lua | 8 +- .../spine-lua/vertexeffects/SwirlEffect.lua | 2 +- 16 files changed, 2121 insertions(+), 1669 deletions(-) diff --git a/spine-lua/spine-lua/Animation.lua b/spine-lua/spine-lua/Animation.lua index b9a127cc8..0e8278234 100644 --- a/spine-lua/spine-lua/Animation.lua +++ b/spine-lua/spine-lua/Animation.lua @@ -1,1611 +1,2059 @@ -------------------------------------------------------------------------------- --- Spine Runtimes License Agreement --- Last updated January 1, 2020. Replaces all prior versions. --- --- Copyright (c) 2013-2020, Esoteric Software LLC --- --- Integration of the Spine Runtimes into software or otherwise creating --- derivative works of the Spine Runtimes is permitted under the terms and --- conditions of Section 2 of the Spine Editor License Agreement: --- http://esotericsoftware.com/spine-editor-license --- --- Otherwise, it is permitted to integrate the Spine Runtimes into software --- or otherwise create derivative works of the Spine Runtimes (collectively, --- "Products"), provided that each user of the Products must obtain their own --- Spine Editor license and redistribution of the Products in any form must --- include this license and copyright notice. --- --- THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY --- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED --- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE --- DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY --- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES --- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, --- BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND --- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT --- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF --- THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- - --- FIXME --- All the indexing in this file is zero based. We use zlen() --- instead of the # operator. Initialization of number arrays --- is performed via utils.newNumberArrayZero. This needs --- to be rewritten using one-based indexing for better performance - -local utils = require "spine-lua.utils" -local AttachmentType = require "spine-lua.attachments.AttachmentType" -local math_floor = math.floor -local math_abs = math.abs -local math_signum = utils.signum - -local function zlen(array) - return #array + 1 -end - -local Animation = {} -function Animation.new (name, timelines, duration) - if not timelines then error("timelines cannot be nil", 2) end - - local self = { - name = name, - timelines = timelines, - timelineIds = {}, - duration = duration - } - - for i,timeline in ipairs(self.timelines) do - self.timelineIds[timeline:getPropertyId()] = true - end - - function self:hasTimeline(id) - return self.timelineIds[id] == true - end - - 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 - time = time % self.duration - if lastTime > 0 then lastTime = lastTime % self.duration end - end - - for i,timeline in ipairs(self.timelines) do - timeline:apply(skeleton, lastTime, time, events, alpha, blend, direction) - end - end - - return self -end - -local function binarySearch (values, target, step) - local low = 0 - local high = math.floor(zlen(values) / step - 2) - if high == 0 then return step end - local current = math.floor(high / 2) - while true do - if values[(current + 1) * step] <= target then - low = current + 1 - else - high = current - end - if low == high then return (low + 1) * step end - current = math.floor((low + high) / 2) - end -end -Animation.binarySearch = binarySearch - -local function binarySearch1 (values, target) - local low = 0 - local high = math.floor(zlen(values) - 2) - if high == 0 then return 1 end - local current = math.floor(high / 2) - while true do - if values[current + 1] <= target then - low = current + 1 - else - high = current - end - if low == high then return low + 1 end - current = math.floor((low + high) / 2) - end -end - -local function linearSearch (values, target, step) - local i = 0 - local last = zlen(values) - step - while i <= last do - if (values[i] > target) then return i end - i = i + step - end - return -1 -end - -Animation.MixBlend = { - setup = 0, - first = 1, - replace = 2, - add = 3 -} -local MixBlend = Animation.MixBlend - -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, - event = 7, drawOrder = 8, - ikConstraint = 9, transformConstraint = 10, - pathConstraintPosition = 11, pathConstraintSpacing = 12, pathConstraintMix = 13, - twoColor = 14 -} -local TimelineType = Animation.TimelineType -local SHL_24 = 16777216 -local SHL_27 = 134217728 - -Animation.CurveTimeline = {} -function Animation.CurveTimeline.new (frameCount) - local LINEAR = 0 - local STEPPED = 1 - local BEZIER = 2 - local BEZIER_SIZE = 10 * 2 - 1 - - local self = { - curves = utils.newNumberArrayZero((frameCount - 1) * BEZIER_SIZE) -- type, x, y, ... - } - - function self:getFrameCount () - return math.floor(zlen(self.curves) / BEZIER_SIZE) + 1 - end - - function self:setStepped (frameIndex) - self.curves[frameIndex * BEZIER_SIZE] = STEPPED - end - - function self:getCurveType (frameIndex) - local index = frameIndex * BEZIER_SIZE - if index == zlen(self.curves) then return LINEAR end - local type = self.curves[index] - if type == LINEAR then return LINEAR end - if type == STEPPED then return STEPPED end - return BEZIER - end - - function self:setCurve (frameIndex, cx1, cy1, cx2, cy2) - local tmpx = (-cx1 * 2 + cx2) * 0.03 - local tmpy = (-cy1 * 2 + cy2) * 0.03 - local dddfx = ((cx1 - cx2) * 3 + 1) * 0.006 - local dddfy = ((cy1 - cy2) * 3 + 1) * 0.006 - local ddfx = tmpx * 2 + dddfx - local ddfy = tmpy * 2 + dddfy - local dfx = cx1 * 0.3 + tmpx + dddfx * 0.16666667 - local dfy = cy1 * 0.3 + tmpy + dddfy * 0.16666667 - - local i = frameIndex * BEZIER_SIZE - local curves = self.curves - curves[i] = BEZIER - i = i + 1 - - local x = dfx - local y = dfy - local n = i + BEZIER_SIZE - 1 - while i < n do - curves[i] = x - curves[i + 1] = y - dfx = dfx + ddfx - dfy = dfy + ddfy - ddfx = ddfx + dddfx - ddfy = ddfy + dddfy - x = x + dfx - y = y + dfy - i = i + 2 - end - end - - function self:getCurvePercent (frameIndex, percent) - percent = utils.clamp(percent, 0, 1) - local curves = self.curves - local i = frameIndex * BEZIER_SIZE - local type = curves[i] - if type == LINEAR then return percent end - if type == STEPPED then return 0 end - i = i + 1 - local x - local n = i + BEZIER_SIZE - 1 - local start = i - while i < n do - x = curves[i] - if x >= percent then - local prevX, prevY - if i == start then - prevX = 0 - prevY = 0 - else - prevX = curves[i - 2] - prevY = curves[i - 1] - end - return prevY + (curves[i + 1] - prevY) * (percent - prevX) / (x - prevX) - end - i = i + 2 - end - local y = curves[i - 1] - return y + (1 - y) * (percent - x) / (1 - x) -- Last point is 1,1. - end - - return self -end - -Animation.RotateTimeline = {} -Animation.RotateTimeline.ENTRIES = 2 -Animation.RotateTimeline.PREV_TIME = -2 -Animation.RotateTimeline.PREV_ROTATION = -1 -Animation.RotateTimeline.ROTATION = 1 -function Animation.RotateTimeline.new (frameCount) - local ENTRIES = Animation.RotateTimeline.ENTRIES - local PREV_TIME = Animation.RotateTimeline.PREV_TIME - local PREV_ROTATION = Animation.RotateTimeline.PREV_ROTATION - local ROTATION = Animation.RotateTimeline.ROTATION - - local self = Animation.CurveTimeline.new(frameCount) - self.boneIndex = -1 - self.frames = utils.newNumberArrayZero(frameCount * 2) - self.type = TimelineType.rotate - - function self:getPropertyId () - return TimelineType.rotate * SHL_24 + self.boneIndex - end - - function self:setFrame (frameIndex, time, degrees) - frameIndex = frameIndex * 2 - self.frames[frameIndex] = time - self.frames[frameIndex + ROTATION] = degrees - end - - function self:apply (skeleton, lastTime, time, firedEvents, alpha, blend, direction) - local frames = self.frames - - local bone = skeleton.bones[self.boneIndex] - if not bone.active then return end - if time < frames[0] then - if blend == MixBlend.setup then - bone.rotation = bone.data.rotation - elseif blend == MixBlend.first then - local r = bone.data.rotation - bone.rotation - 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. - 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 - - -- Interpolate between the last frame and the current frame. - local frame = binarySearch(frames, time, ENTRIES) - local prevRotation = frames[frame + PREV_ROTATION] - local frameTime = frames[frame] - local percent = self:getCurvePercent((math.floor(frame / 2)) - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime)) - - local r = frames[frame + ROTATION] - prevRotation - 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 - - return self -end - -Animation.TranslateTimeline = {} -Animation.TranslateTimeline.ENTRIES = 3 -function Animation.TranslateTimeline.new (frameCount) - local ENTRIES = Animation.TranslateTimeline.ENTRIES - local PREV_TIME = -3 - local PREV_X = -2 - local PREV_Y = -1 - local X = 1 - local Y = 2 - - local self = Animation.CurveTimeline.new(frameCount) - self.frames = utils.newNumberArrayZero(frameCount * ENTRIES) - self.boneIndex = -1 - self.type = TimelineType.translate - - function self:getPropertyId () - return TimelineType.translate * SHL_24 + self.boneIndex - end - - function self:setFrame (frameIndex, time, x, y) - frameIndex = frameIndex * ENTRIES - self.frames[frameIndex] = time - self.frames[frameIndex + X] = x - self.frames[frameIndex + Y] = y - end - - function self:apply (skeleton, lastTime, time, firedEvents, alpha, blend, direction) - local frames = self.frames - - local bone = skeleton.bones[self.boneIndex] - if not bone.active then return end - - if time < frames[0] then - if blend == MixBlend.setup then - bone.x = bone.data.x - bone.y = bone.data.y - 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 - return - end - - local x = 0 - local y = 0 - if time >= frames[zlen(frames) - ENTRIES] then -- // Time is after last frame. - x = frames[zlen(frames) + PREV_X] - y = frames[zlen(frames) + PREV_Y] - else - -- Interpolate between the previous frame and the current frame. - local frame = binarySearch(frames, time, ENTRIES) - x = frames[frame + PREV_X] - y = frames[frame + PREV_Y] - local frameTime = frames[frame] - local percent = self:getCurvePercent(math_floor(frame / ENTRIES) - 1, - 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime)) - - x = x + (frames[frame + X] - x) * percent - y = y + (frames[frame + Y] - y) * percent - end - if blend == MixBlend.setup then - bone.x = bone.data.x + x * alpha - bone.y = bone.data.y + y * alpha - 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 - - return self -end - -Animation.ScaleTimeline = {} -Animation.ScaleTimeline.ENTRIES = Animation.TranslateTimeline.ENTRIES -function Animation.ScaleTimeline.new (frameCount) - local ENTRIES = Animation.ScaleTimeline.ENTRIES - local PREV_TIME = -3 - local PREV_X = -2 - local PREV_Y = -1 - local X = 1 - local Y = 2 - - local self = Animation.TranslateTimeline.new(frameCount) - self.type = TimelineType.scale - - function self:getPropertyId () - return TimelineType.scale * SHL_24 + self.boneIndex - end - - function self:apply (skeleton, lastTime, time, firedEvents, alpha, blend, direction) - local frames = self.frames - - local bone = skeleton.bones[self.boneIndex] - if not bone.active then return end - - if time < frames[0] then - if blend == MixBlend.setup then - bone.scaleX = bone.data.scaleX - bone.scaleY = bone.data.scaleY - 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 - return - end - - local x = 0 - local y = 0 - if time >= frames[zlen(frames) - ENTRIES] then -- Time is after last frame. - x = frames[zlen(frames) + PREV_X] * bone.data.scaleX - y = frames[zlen(frames) + PREV_Y] * bone.data.scaleY - else - -- Interpolate between the previous frame and the current frame. - local frame = binarySearch(frames, time, ENTRIES) - x = frames[frame + PREV_X] - y = frames[frame + PREV_Y] - local frameTime = frames[frame] - local percent = self:getCurvePercent(math_floor(frame / ENTRIES) - 1, - 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime)) - - x = (x + (frames[frame + X] - x) * percent) * bone.data.scaleX - y = (y + (frames[frame + Y] - y) * percent) * bone.data.scaleY - end - if alpha == 1 then - 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 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 - 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 - end - end - - return self -end - -Animation.ShearTimeline = {} -Animation.ShearTimeline.ENTRIES = Animation.TranslateTimeline.ENTRIES -function Animation.ShearTimeline.new (frameCount) - local ENTRIES = Animation.ShearTimeline.ENTRIES - local PREV_TIME = -3 - local PREV_X = -2 - local PREV_Y = -1 - local X = 1 - local Y = 2 - - local self = Animation.TranslateTimeline.new(frameCount) - self.type = TimelineType.shear - - function self:getPropertyId () - return TimelineType.shear * SHL_24 + self.boneIndex - end - - function self:apply (skeleton, lastTime, time, firedEvents, alpha, blend, direction) - local frames = self.frames - - local bone = skeleton.bones[self.boneIndex] - if not bone.active then return end - - if time < frames[0] then - if blend == MixBlend.setup then - bone.shearX = bone.data.shearX - bone.shearY = bone.data.shearY - 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 - return - end - - local x = 0 - local y = 0 - if time >= frames[zlen(frames) - ENTRIES] then -- // Time is after last frame. - x = frames[zlen(frames) + PREV_X] - y = frames[zlen(frames) + PREV_Y] - else - -- Interpolate between the previous frame and the current frame. - local frame = binarySearch(frames, time, ENTRIES) - x = frames[frame + PREV_X] - y = frames[frame + PREV_Y] - local frameTime = frames[frame] - local percent = self:getCurvePercent(math_floor(frame / ENTRIES) - 1, - 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime)) - - x = x + (frames[frame + X] - x) * percent - y = y + (frames[frame + Y] - y) * percent - end - if blend == MixBlend.setup then - bone.shearX = bone.data.shearX + x * alpha - bone.shearY = bone.data.shearY + y * alpha - 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 - - return self -end - -Animation.ColorTimeline = {} -Animation.ColorTimeline.ENTRIES = 5 -function Animation.ColorTimeline.new (frameCount) - local ENTRIES = Animation.ColorTimeline.ENTRIES - local PREV_TIME = -5 - local PREV_R = -4 - local PREV_G = -3 - local PREV_B = -2 - local PREV_A = -1 - local R = 1 - local G = 2 - local B = 3 - local A = 4 - - local self = Animation.CurveTimeline.new(frameCount) - self.frames = utils.newNumberArrayZero(frameCount * ENTRIES) - self.slotIndex = -1 - self.type = TimelineType.color - - function self:getPropertyId () - return TimelineType.color * SHL_24 + self.slotIndex - end - - function self:setFrame (frameIndex, time, r, g, b, a) - frameIndex = frameIndex * ENTRIES - self.frames[frameIndex] = time - self.frames[frameIndex + R] = r - self.frames[frameIndex + G] = g - self.frames[frameIndex + B] = b - self.frames[frameIndex + A] = a - end - - function self:apply (skeleton, lastTime, time, firedEvents, alpha, blend, direction) - local frames = self.frames - local slot = skeleton.slots[self.slotIndex] - if not slot.bone.active then return end - if time < frames[0] then - if blend == MixBlend.setup then - slot.color:setFrom(slot.data.color) - 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, - (setup.a - color.a) * alpha) - end - return - end - - local r, g, b, a - if time >= frames[zlen(frames) - ENTRIES] then -- Time is after last frame. - local i = zlen(frames) - r = frames[i + PREV_R] - g = frames[i + PREV_G] - b = frames[i + PREV_B] - a = frames[i + PREV_A] - else - -- Interpolate between the last frame and the current frame. - local frame = binarySearch(frames, time, ENTRIES) - r = frames[frame + PREV_R] - g = frames[frame + PREV_G] - b = frames[frame + PREV_B] - a = frames[frame + PREV_A] - local frameTime = frames[frame] - local percent = self:getCurvePercent(math.floor(frame / ENTRIES) - 1, - 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime)) - - r = r + (frames[frame + R] - r) * percent - g = g + (frames[frame + G] - g) * percent - b = b + (frames[frame + B] - b) * percent - a = a + (frames[frame + A] - a) * percent - end - if alpha == 1 then - slot.color:set(r, g, b, a) - else - local color = slot.color - 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 - - return self -end - -Animation.TwoColorTimeline = {} -Animation.TwoColorTimeline.ENTRIES = 8 -function Animation.TwoColorTimeline.new (frameCount) - local ENTRIES = Animation.TwoColorTimeline.ENTRIES - local PREV_TIME = -8 - local PREV_R = -7 - local PREV_G = -6 - local PREV_B = -5 - local PREV_A = -4 - local PREV_R2 = -3 - local PREV_G2 = -2 - local PREV_B2 = -1 - local R = 1 - local G = 2 - local B = 3 - local A = 4 - local R2 = 5 - local G2 = 6 - local B2 = 7 - - local self = Animation.CurveTimeline.new(frameCount) - self.frames = utils.newNumberArrayZero(frameCount * ENTRIES) - self.slotIndex = -1 - self.type = TimelineType.twoColor - - function self:getPropertyId () - return TimelineType.twoColor * SHL_24 + self.slotIndex - end - - function self:setFrame (frameIndex, time, r, g, b, a, r2, g2, b2) - frameIndex = frameIndex * ENTRIES - self.frames[frameIndex] = time - self.frames[frameIndex + R] = r - self.frames[frameIndex + G] = g - self.frames[frameIndex + B] = b - self.frames[frameIndex + A] = a - self.frames[frameIndex + R2] = r2 - self.frames[frameIndex + G2] = g2 - self.frames[frameIndex + B2] = b2 - end - - function self:apply (skeleton, lastTime, time, firedEvents, alpha, blend, direction) - local frames = self.frames - local slot = skeleton.slots[self.slotIndex] - if not slot.bone.active then return end - - if time < frames[0] then - if blend == MixBlend.setup then - slot.color:setFrom(slot.data.color) - slot.darkColor:setFrom(slot.data.darkColor) - elseif blend == MixBlend.first 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 - - local r, g, b, a, r2, g2, b2 - if time >= frames[zlen(frames) - ENTRIES] then -- Time is after last frame. - local i = zlen(frames) - r = frames[i + PREV_R] - g = frames[i + PREV_G] - b = frames[i + PREV_B] - a = frames[i + PREV_A] - r2 = frames[i + PREV_R2] - g2 = frames[i + PREV_G2] - b2 = frames[i + PREV_B2] - else - -- Interpolate between the last frame and the current frame. - local frame = binarySearch(frames, time, ENTRIES) - r = frames[frame + PREV_R] - g = frames[frame + PREV_G] - b = frames[frame + PREV_B] - a = frames[frame + PREV_A] - r2 = frames[frame + PREV_R2] - g2 = frames[frame + PREV_G2] - b2 = frames[frame + PREV_B2] - local frameTime = frames[frame] - local percent = self:getCurvePercent(math.floor(frame / ENTRIES) - 1, - 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime)) - - r = r + (frames[frame + R] - r) * percent - g = g + (frames[frame + G] - g) * percent - b = b + (frames[frame + B] - b) * percent - a = a + (frames[frame + A] - a) * percent - r2 = r2 + (frames[frame + R2] - r2) * percent - g2 = g2 + (frames[frame + G2] - g2) * percent - b2 = b2 + (frames[frame + B2] - b2) * percent - end - if alpha == 1 then - slot.color:set(r, g, b, a) - slot.darkColor:set(r2, g2, b2, 1) - else - local light = slot.color - local dark = slot.darkColor - if blend == MixBlend.setup then - light:setFrom(slot.data.color) - dark:setFrom(slot.data.darkColor) - end - light:add((r - light.r) * alpha, (g - light.g) * alpha, (b - light.b) * alpha, (a - light.a) * alpha) - dark:add((r2 - dark.r) * alpha, (g2 - dark.g) * alpha, (b2 - dark.b) * alpha, 0) - end - end - - return self -end - -Animation.AttachmentTimeline = {} -function Animation.AttachmentTimeline.new (frameCount) - local self = { - frames = utils.newNumberArrayZero(frameCount), -- time, ... - attachmentNames = {}, - slotIndex = -1, - type = TimelineType.attachment - } - - function self:getFrameCount () - return zlen(self.frames) - end - - function self:setFrame (frameIndex, time, attachmentName) - self.frames[frameIndex] = time - self.attachmentNames[frameIndex] = attachmentName - end - - function self:getPropertyId () - return TimelineType.attachment * SHL_24 + self.slotIndex - end - - function self:setAttachment(skeleton, slot, attachmentName) - attachmentName = slot.data.attachmentName - if not attachmentName then - slot:setAttachment(nil) - else - slot:setAttachment(skeleton:getAttachmentByIndex(self.slotIndex, attachmentName)) - end - end - - function self:apply (skeleton, lastTime, time, firedEvents, alpha, blend, direction) - local slot = skeleton.slots[self.slotIndex] - if not slot.bone.active then return end - local attachmentName - if direction == MixDirection.out then - if blend == MixBlend.setup then - self:setAttachment(skeleton, slot, slot.data.attachmentName) - end - return - end - - local frames = self.frames - if time < frames[0] then - if blend == MixBlend.setup or blend == MixBlend.first then - self:setAttachment(skeleton, slot, slot.data.attachmentName) - end - return - end - - local frameIndex = 0 - if time >= frames[zlen(frames) - 1] then - frameIndex = zlen(frames) - 1 - else - frameIndex = binarySearch(frames, time, 1) - 1 - end - - attachmentName = self.attachmentNames[frameIndex] - if not attachmentName then - slot:setAttachment(nil) - else - slot:setAttachment(skeleton:getAttachmentByIndex(self.slotIndex, attachmentName)) - end - end - - return self -end - -Animation.DeformTimeline = {} -function Animation.DeformTimeline.new (frameCount) - local self = Animation.CurveTimeline.new(frameCount) - self.frames = utils.newNumberArrayZero(frameCount) - self.frameVertices = utils.newNumberArrayZero(frameCount) - self.slotIndex = -1 - self.attachment = nil - self.type = TimelineType.deform - - function self:getPropertyId () - return TimelineType.deform * SHL_27 + self.attachment.id + self.slotIndex - end - - function self:setFrame (frameIndex, time, vertices) - self.frames[frameIndex] = time - self.frameVertices[frameIndex] = vertices - end - - function self:apply (skeleton, lastTime, time, firedEvents, alpha, blend, direction) - local slot = skeleton.slots[self.slotIndex] - if not slot.bone.active then return end - - local slotAttachment = slot.attachment - if not slotAttachment then return end - if not (slotAttachment.type == AttachmentType.mesh or slotAttachment.type == AttachmentType.linkedmesh or slotAttachment.type == AttachmentType.path or slotAttachment.type == AttachmentType.boundingbox) then return end - if slotAttachment.deformAttachment ~= self.attachment then return end - - local frames = self.frames - local deformArray = slot.deform - if #(deformArray) == 0 then blend = MixBlend.setup end - - local frameVertices = self.frameVertices - local vertexCount = #(frameVertices[0]) - - if time < frames[0] then - local vertexAttachment = slotAttachment - if blend == MixBlend.setup then - slot.deform = {} - return - elseif blend == MixBlend.first then - if (alpha == 1) then - slot.deform = {} - return - end - - local deform = utils.setArraySize(deformArray, vertexCount) - if (vertexAttachment.bones == nil) then - local setupVertices = vertexAttachment.vertices - local i = 1 - while i <= vertexCount do - deform[i] = deform[i] + (setupVertices[i] - deform[i]) * alpha - i = i + 1 - end - else - alpha = 1 - alpha - local i = 1 - while i <= vertexCount do - deform[i] = deform[i] * alpha - i = i + 1 - end - end - end - return - end - - local deform = utils.setArraySize(deformArray, vertexCount) - if time >= frames[zlen(frames) - 1] then -- Time is after last frame. - local lastVertices = frameVertices[zlen(frames) - 1] - if alpha == 1 then - 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 - deform[i] = deform[i] + lastVertices[i] - setupVertices[i] - i = i + 1 - end - else - -- Weighted deform offsets, with alpha. - local i = 1 - while i <= vertexCount do - deform[i] = deform[i] + lastVertices[i] - i = i + 1 - end - end - else - local i = 1 - while i <= vertexCount do - deform[i] = lastVertices[i] - i = i + 1 - end - 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 setup = setupVertices[i] - deform[i] = setup + (lastVertices[i] - setup) * alpha - i = i + 1 - end - else - -- Weighted deform offsets, with alpha. - local i = 1 - while i <= vertexCount do - deform[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 - deform[i] = deform[i] + (lastVertices[i] - deform[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 - deform[i] = deform[i] + (lastVertices[i] - setupVertices[i]) * alpha - i = i + 1 - end - else - -- Weighted deform offsets, with alpha. - local i = 1 - while i <= vertexCount do - deform[i] = deform[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 - deform[i] = deform[i] + (lastVertices[i] - setupVertices[i]) * alpha - i = i + 1 - end - else - -- Weighted deform offsets, with alpha. - local i = 1 - while i <= vertexCount do - deform[i] = deform[i] + lastVertices[i] * alpha - i = i + 1 - end - end - end - end - return - end - - -- Interpolate between the previous frame and the current frame. - local frame = binarySearch(frames, time, 1) - local prevVertices = frameVertices[frame - 1] - local nextVertices = frameVertices[frame] - local frameTime = frames[frame] - local percent = self:getCurvePercent(frame - 1, 1 - (time - frameTime) / (frames[frame - 1] - frameTime)) - - if alpha == 1 then - 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] - deform[i] = deform[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] - deform[i] = deform[i] + prev + (nextVertices[i] - prev) * percent - i = i + 1 - end - end - else - local i = 1 - while i <= vertexCount do - local prev = prevVertices[i] - deform[i] = prev + (nextVertices[i] - prev) * percent - i = i + 1 - end - 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] - deform[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] - deform[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] - deform[i] = deform[i] + (prev + (nextVertices[i] - prev) * percent - deform[i]) * alpha - i = i + 1 - 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 - local prev = prevVertices[i] - deform[i] = deform[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] - deform[i] = deform[i] + (prev + (nextVertices[i] - prev) * percent) * alpha - i = i + 1 - end - end - end - end - end - - return self -end - -Animation.EventTimeline = {} -function Animation.EventTimeline.new (frameCount) - local self = { - frames = utils.newNumberArrayZero(frameCount), - events = {}, - type = TimelineType.event - } - - function self:getPropertyId () - return TimelineType.event * SHL_24 - end - - function self:getFrameCount () - return zlen(self.frames) - end - - function self:setFrame (frameIndex, event) - self.frames[frameIndex] = event.time - self.events[frameIndex] = event - end - - -- Fires events for frames > lastTime and <= time. - 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, blend, direction) - lastTime = -1 - elseif lastTime >= frames[frameCount - 1] then -- Last time is after last frame. - return - end - if time < frames[0] then return end -- Time is before first frame. - - local frame - if lastTime < frames[0] then - frame = 0 - else - frame = binarySearch1(frames, lastTime) - local frame = frames[frame] - while frame > 0 do -- Fire multiple events with the same frame. - if frames[frame - 1] ~= frame then break end - frame = frame - 1 - end - end - local events = self.events - while frame < frameCount and time >= frames[frame] do - table.insert(firedEvents, events[frame]) - frame = frame + 1 - end - end - - return self -end - -Animation.DrawOrderTimeline = {} -function Animation.DrawOrderTimeline.new (frameCount) - local self = { - frames = utils.newNumberArrayZero(frameCount), - drawOrders = {}, - type = TimelineType.drawOrder - } - - function self:getPropertyId () - return TimelineType.drawOrder * SHL_24 - end - - function self:getFrameCount () - return zlen(self.frames) - end - - function self:setFrame (frameIndex, time, drawOrder) - self.frames[frameIndex] = time - self.drawOrders[frameIndex] = drawOrder - end - - function self:apply (skeleton, lastTime, time, firedEvents, alpha, blend, direction) - local drawOrder = skeleton.drawOrder - local slots = skeleton.slots - if direction == MixDirection.out then - if blend == MixBlend.setup then - for i,slot in ipairs(slots) do - drawOrder[i] = slots[i] - end - end - return - end - - local frames = self.frames - if time < frames[0] then - if blend == MixBlend.setup or blend == MixBlend.first then - for i,slot in ipairs(slots) do - drawOrder[i] = slots[i] - end - end - return - end - - local frame - if time >= frames[zlen(frames) - 1] then -- Time is after last frame. - frame = zlen(frames) - 1 - else - frame = binarySearch1(frames, time) - 1 - end - - local drawOrderToSetupIndex = self.drawOrders[frame] - if not drawOrderToSetupIndex then - for i,slot in ipairs(slots) do - drawOrder[i] = slots[i] - end - else - for i,setupIndex in ipairs(drawOrderToSetupIndex) do - drawOrder[i] = skeleton.slots[setupIndex] - end - end - end - - return self -end - -Animation.IkConstraintTimeline = {} -Animation.IkConstraintTimeline.ENTRIES = 6 -function Animation.IkConstraintTimeline.new (frameCount) - local ENTRIES = Animation.IkConstraintTimeline.ENTRIES - 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 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, softness, bendDirection, compress, stretch, ... - self.ikConstraintIndex = -1 - self.type = TimelineType.ikConstraint - - function self:getPropertyId () - return TimelineType.ikConstraint * SHL_24 + self.ikConstraintIndex - end - - 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 - else - self.frames[frameIndex + COMPRESS] = 0 - end - 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) - local frames = self.frames - - local constraint = skeleton.ikConstraints[self.ikConstraintIndex] - if not constraint.active then return end - 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 - end - return - end - - 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 - constraint.stretch = constraint.data.stretch - else - 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 - 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.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 - if (math_floor(frames[zlen(frames) + PREV_STRETCH]) == 1) then constraint.stretch = true else constraint.stretch = false end - end - end - return - end - - -- 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 - constraint.stretch = constraint.data.stretch - else - 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 - 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.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 - if (math_floor(frames[frame + PREV_STRETCH]) == 1) then constraint.stretch = true else constraint.stretch = false end - end - end - end - - return self -end - -Animation.TransformConstraintTimeline = {} -Animation.TransformConstraintTimeline.ENTRIES = 5 -function Animation.TransformConstraintTimeline.new (frameCount) - local ENTRIES = Animation.TransformConstraintTimeline.ENTRIES - local PREV_TIME = -5 - local PREV_ROTATE = -4 - local PREV_TRANSLATE = -3 - local PREV_SCALE = -2 - local PREV_SHEAR = -1 - local ROTATE = 1 - local TRANSLATE = 2 - local SCALE = 3 - local SHEAR = 4 - - local self = Animation.CurveTimeline.new(frameCount) - self.frames = utils.newNumberArrayZero(frameCount * ENTRIES) - self.transformConstraintIndex = -1 - self.type = TimelineType.transformConstraint - - function self:getPropertyId () - return TimelineType.transformConstraint * SHL_24 + self.transformConstraintIndex - end - - function self:setFrame (frameIndex, time, rotateMix, translateMix, scaleMix, shearMix) - frameIndex = frameIndex * ENTRIES - self.frames[frameIndex] = time - self.frames[frameIndex + ROTATE] = rotateMix - self.frames[frameIndex + TRANSLATE] = translateMix - self.frames[frameIndex + SCALE] = scaleMix - self.frames[frameIndex + SHEAR] = shearMix - end - - function self:apply (skeleton, lastTime, time, firedEvents, alpha, blend, direction) - local frames = self.frames - - local constraint = skeleton.transformConstraints[self.transformConstraintIndex] - if not constraint.active then return end - - if time < frames[0] then - local data = constraint.data - if blend == MixBlend.setup then - constraint.rotateMix = data.rotateMix - constraint.translateMix = data.translateMix - constraint.scaleMix = data.scaleMix - constraint.shearMix = data.shearMix - 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 - constraint.shearMix = constraint.shearMix + (data.shearMix - constraint.shearMix) * alpha - end - return - end - - local rotate = 0 - local translate = 0 - local scale = 0 - local shear = 0 - if time >= frames[zlen(frames) - ENTRIES] then -- Time is after last frame. - local i = zlen(frames) - rotate = frames[i + PREV_ROTATE] - translate = frames[i + PREV_TRANSLATE] - scale = frames[i + PREV_SCALE] - shear = frames[i + PREV_SHEAR] - else - -- Interpolate between the previous frame and the current frame. - local frame = binarySearch(frames, time, ENTRIES) - rotate = frames[frame + PREV_ROTATE] - translate = frames[frame + PREV_TRANSLATE] - scale = frames[frame + PREV_SCALE] - shear = frames[frame + PREV_SHEAR] - local frameTime = frames[frame] - local percent = self:getCurvePercent(math_floor(frame / ENTRIES) - 1, - 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime)) - - rotate = rotate + (frames[frame + ROTATE] - rotate) * percent - translate = translate + (frames[frame + TRANSLATE] - translate) * percent - scale = scale + (frames[frame + SCALE] - scale) * percent - shear = shear + (frames[frame + SHEAR] - shear) * percent - end - 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 - constraint.scaleMix = data.scaleMix + (scale - data.scaleMix) * alpha - constraint.shearMix = data.shearMix + (shear - data.shearMix) * alpha - else - constraint.rotateMix = constraint.rotateMix + (rotate - constraint.rotateMix) * alpha - constraint.translateMix = constraint.translateMix + (translate - constraint.translateMix) * alpha - constraint.scaleMix = constraint.scaleMix + (scale - constraint.scaleMix) * alpha - constraint.shearMix = constraint.shearMix + (shear - constraint.shearMix) * alpha - end - end - - return self -end - -Animation.PathConstraintPositionTimeline = {} -Animation.PathConstraintPositionTimeline.ENTRIES = 2 -function Animation.PathConstraintPositionTimeline.new (frameCount) - local ENTRIES = Animation.PathConstraintPositionTimeline.ENTRIES - local PREV_TIME = -2 - local PREV_VALUE = -1 - local VALUE = 1 - - local self = Animation.CurveTimeline.new(frameCount) - self.frames = utils.newNumberArrayZero(frameCount * ENTRIES) - self.pathConstraintIndex = -1 - self.type = TimelineType.pathConstraintPosition - - function self:getPropertyId () - return TimelineType.pathConstraintPosition * SHL_24 + self.pathConstraintIndex - end - - function self:setFrame (frameIndex, time, value) - frameIndex = frameIndex * ENTRIES - self.frames[frameIndex] = time - self.frames[frameIndex + VALUE] = value - end - - function self:apply (skeleton, lastTime, time, firedEvents, alpha, blend, direction) - local frames = self.frames - - local constraint = skeleton.pathConstraints[self.pathConstraintIndex] - if not constraint.active then return end - - if (time < frames[0]) then - if blend == MixBlend.setup then - constraint.position = constraint.data.position - elseif blend == MixBlend.first then - constraint.position = constraint.position + (constraint.data.position - constraint.position) * alpha - end - return - end - - local position = 0 - if time >= frames[zlen(frames) - ENTRIES] then -- Time is after last frame. - position = frames[zlen(frames) + PREV_VALUE] - else - -- Interpolate between the previous frame and the current frame. - local frame = binarySearch(frames, time, ENTRIES) - position = frames[frame + PREV_VALUE] - local frameTime = frames[frame] - local percent = self:getCurvePercent(math_floor(frame / ENTRIES) - 1, - 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime)) - - position = position + (frames[frame + VALUE] - position) * percent - end - if blend == MixBlend.setup then - constraint.position = constraint.data.position + (position - constraint.data.position) * alpha - else - constraint.position = constraint.position + (position - constraint.position) * alpha - end - end - - return self -end - -Animation.PathConstraintSpacingTimeline = {} -Animation.PathConstraintSpacingTimeline.ENTRIES = 2 -function Animation.PathConstraintSpacingTimeline.new (frameCount) - local ENTRIES = Animation.PathConstraintSpacingTimeline.ENTRIES - local PREV_TIME = -2 - local PREV_VALUE = -1 - local VALUE = 1 - - local self = Animation.CurveTimeline.new(frameCount) - self.frames = utils.newNumberArrayZero(frameCount * ENTRIES) - self.pathConstraintIndex = -1 - self.type = TimelineType.pathConstraintSpacing - - function self:getPropertyId () - return TimelineType.pathConstraintSpacing * SHL_24 + self.pathConstraintIndex - end - - function self:setFrame (frameIndex, time, value) - frameIndex = frameIndex * ENTRIES - self.frames[frameIndex] = time - self.frames[frameIndex + VALUE] = value - end - - function self:apply (skeleton, lastTime, time, firedEvents, alpha, blend, direction) - local frames = self.frames - - local constraint = skeleton.pathConstraints[self.pathConstraintIndex] - if not constraint.active then return end - - if (time < frames[0]) then - if blend == MixBlend.setup then - constraint.spacing = constraint.data.spacing - elseif blend == MixBlend.first then - constraint.spacing = constraint.spacing + (constraint.data.spacing - constraint.spacing) * alpha - end - return - end - - local spacing = 0 - if time >= frames[zlen(frames) - ENTRIES] then -- Time is after last frame. - spacing = frames[zlen(frames) + PREV_VALUE] - else - -- Interpolate between the previous frame and the current frame. - local frame = binarySearch(frames, time, ENTRIES) - spacing = frames[frame + PREV_VALUE] - local frameTime = frames[frame] - local percent = self:getCurvePercent(math_floor(frame / ENTRIES) - 1, - 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime)) - - spacing = spacing + (frames[frame + VALUE] - spacing) * percent - end - - if blend == MixBlend.setup then - constraint.spacing = constraint.data.spacing + (spacing - constraint.data.spacing) * alpha - else - constraint.spacing = constraint.spacing + (spacing - constraint.spacing) * alpha - end - end - - return self -end - -Animation.PathConstraintMixTimeline = {} -Animation.PathConstraintMixTimeline.ENTRIES = 3 -function Animation.PathConstraintMixTimeline.new (frameCount) - local ENTRIES = Animation.PathConstraintMixTimeline.ENTRIES - local PREV_TIME = -3 - local PREV_ROTATE = -2 - local PREV_TRANSLATE = -1 - local ROTATE = 1 - local TRANSLATE = 2 - - local self = Animation.CurveTimeline.new(frameCount) - self.frames = utils.newNumberArrayZero(frameCount * ENTRIES) - self.pathConstraintIndex = -1 - self.type = TimelineType.pathConstraintMix - - function self:getPropertyId () - return TimelineType.pathConstraintMix * SHL_24 + self.pathConstraintIndex - end - - function self:setFrame (frameIndex, time, rotateMix, translateMix) - frameIndex = frameIndex * ENTRIES - self.frames[frameIndex] = time - self.frames[frameIndex + ROTATE] = rotateMix - self.frames[frameIndex + TRANSLATE] = translateMix - end - - function self:apply (skeleton, lastTime, time, firedEvents, alpha, blend, direction) - local frames = self.frames - - local constraint = skeleton.pathConstraints[self.pathConstraintIndex] - if not constraint.active then return end - - if (time < frames[0]) then - if blend == MixBlend.setup then - constraint.rotateMix = constraint.data.rotateMix - constraint.translateMix = constraint.data.translateMix - 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 - return - end - - local rotate = 0 - local translate = 0 - if time >= frames[zlen(frames) - ENTRIES] then -- Time is after last frame. - rotate = frames[zlen(frames) + PREV_ROTATE] - translate = frames[zlen(frames) + PREV_TRANSLATE] - else - -- Interpolate between the previous frame and the current frame. - local frame = binarySearch(frames, time, ENTRIES) - rotate = frames[frame + PREV_ROTATE] - translate = frames[frame + PREV_TRANSLATE] - local frameTime = frames[frame] - local percent = self:getCurvePercent(math_floor(frame / ENTRIES) - 1, - 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime)) - - rotate = rotate + (frames[frame + ROTATE] - rotate) * percent - translate = translate + (frames[frame + TRANSLATE] - translate) * percent - end - - 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 - constraint.rotateMix = constraint.rotateMix + (rotate - constraint.rotateMix) * alpha - constraint.translateMix = constraint.translateMix + (translate - constraint.translateMix) * alpha - end - end - - return self -end - -return Animation +------------------------------------------------------------------------------- +-- Spine Runtimes License Agreement +-- Last updated January 1, 2020. Replaces all prior versions. +-- +-- Copyright (c) 2013-2020, Esoteric Software LLC +-- +-- Integration of the Spine Runtimes into software or otherwise creating +-- derivative works of the Spine Runtimes is permitted under the terms and +-- conditions of Section 2 of the Spine Editor License Agreement: +-- http://esotericsoftware.com/spine-editor-license +-- +-- Otherwise, it is permitted to integrate the Spine Runtimes into software +-- or otherwise create derivative works of the Spine Runtimes (collectively, +-- "Products"), provided that each user of the Products must obtain their own +-- Spine Editor license and redistribution of the Products in any form must +-- include this license and copyright notice. +-- +-- THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY +-- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +-- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +-- DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY +-- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +-- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, +-- BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND +-- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +-- THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +------------------------------------------------------------------------------- + +-- FIXME +-- All the indexing in this file is zero based. We use zlen() +-- instead of the # operator. Initialization of number arrays +-- is performed via utils.newNumberArrayZero. This needs +-- to be rewritten using one-based indexing for better performance + +local utils = require "spine-lua.utils" +local AttachmentType = require "spine-lua.attachments.AttachmentType" + +local setmetatable = setmetatable +local math_floor = math_floor +local math_abs = math.abs +local math_signum = utils.signum + +local function zlen(array) + return #array + 1 +end + +local Animation = {} + +function Animation.new (name, timelines, duration) + if not name then error("name cannot be nil", 2) end + if not timelines then error("timelines cannot be nil", 2) end + + local self = { + name = name, + timelines = timelines, + timelineIds = nil, + duration = duration + } + + function self:setTimelines (timelines) + self.timelines = timelines + + self.timelineIds = {} + for i,timeline in ipairs(self.timelines) do + for _,id in ipairs(timeline.propertyIds) do + timelineIds[id] = true + end + end + end + + function self:hasTimeline (ids) + for _,id in ipairs(ids) do + if timelineIds[id] then return true end + end + return false + end + + 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 + time = time % self.duration + if lastTime > 0 then lastTime = lastTime % self.duration end + end + + for i,timeline in ipairs(self.timelines) do + timeline:apply(skeleton, lastTime, time, events, alpha, blend, direction) + end + end + + self:setTimelines(timelines) + return self +end + +Animation.MixBlend = { + setup = 0, + first = 1, + replace = 2, + add = 3 +} +local MixBlend = Animation.MixBlend + +Animation.MixDirection = { + mixIn = 0, mixOut = 1 +} +local MixDirection = Animation.MixDirection + +Animation.Property = { + rotate = 0, + x = 1, + y = 2, + scaleX = 3, + scaleY = 4, + shearX = 5, + shearY = 6, + + rgb = 7, + alpha = 8, + rgb2 = 9, + + attachment = 10, + deform = 11, + + event = 12, + drawOrder = 13, + + ikConstraint = 14, + transformConstraint = 15, + + pathConstraintPosition = 16, + pathConstraintSpacing = 17, + pathConstraintMix = 18 +} +local Property = Animation.Property + +Animation.Timeline = {} +function Animation.Timeline.new (frameCount, propertyIds) + local self = { + propertyIds = propertyIds, + frames = utils.newNumberArrayZero((frameCount - 1) * self:getFrameEntries()) + } + + function self:getFrameEntries () + return 1 + end + + function self:getFrameCount () + return math_floor(zlen(self.frames) / self:getFrameEntries()) + end + + function self:getDuration () + return self.frames[zlen(self.frames) - self:getFrameEntries()] + end + + return self +end + +local function search1 (frames, time) + local n = zlen(frames) + while i <= n do + if frames[i] > time then return i - 1 end + i = i + 1 + end + return n - 1 +end +Animation.Timeline.search1 = search1 + +local function search (frames, time, step) + local n = zlen(frames) + local i = step + while i <= n do + if frames[i] > time then return i - step end + i = i + step + end + return n - step +end + +local LINEAR = 0 +local STEPPED = 1 +local BEZIER = 2 +local BEZIER_SIZE = 18 + +Animation.CurveTimeline = {} +function Animation.CurveTimeline.new (frameCount, bezierCount, propertyIds) + local LINEAR = 0 + local STEPPED = 1 + local BEZIER = 2 + local BEZIER_SIZE = 10 * 2 - 1 + + local self = Animation.Timeline.new(frameCount, propertyIds) + self.curves = utils.newNumberArrayZero(frameCount + bezierCount * BEZIER_SIZE) + + function self:getFrameCount () + return math_floor(zlen(self.curves) / BEZIER_SIZE) + 1 + end + + function self:setStepped (frame) + self.curves[frame] = STEPPED + end + + function self:setLinear (frame) + self.curves[frame] = LINEAR + end + + function self:shrink (bezierCount) + utils.setArraySize(self.curves, self:getFrameCount() + bezierCount * BEZIER_SIZE) + end + + function self:setBezier (bezier, frame, value, time1, value1, cx1, cy1, cx2, cy2, time2, value2) + local curves = self.curves + local i = self:getFrameCount() + bezier * BEZIER_SIZE + if value == 0 then curves[frame] = BEZIER + i end + local tmpx = (time1 - cx1 * 2 + cx2) * 0.03 + local tmpy = (value1 - cy1 * 2 + cy2) * 0.03 + local dddx = ((cx1 - cx2) * 3 - time1 + time2) * 0.006 + local dddy = ((cy1 - cy2) * 3 - value1 + value2) * 0.006 + local ddx = tmpx * 2 + dddx + local ddy = tmpy * 2 + dddy + local dx = (cx1 - time1) * 0.3 + tmpx + dddx * 0.16666667 + local dy = (cy1 - value1) * 0.3 + tmpy + dddy * 0.16666667 + local x = time1 + dx + local y = value1 + dy + local n = i + BEZIER_SIZE + while i < n do + curves[i] = x + curves[i + 1] = y + dx = dx + ddx + dy = dy + ddy + ddx = ddx + dddx + ddy = ddy + dddy + x = x + dx + y = y + dy + i = i + 2 + end + end + + function self:getBezierValue (time, frameIndex, valueOffset, i) + local curves = self.curves + if curves[i] > time then + local x = self.frames[frameIndex] + local y = self.frames[frameIndex + valueOffset] + return y + (time - x) / (curves[i] - x) * (curves[i + 1] - y) + end + local n = i + BEZIER_SIZE + while i < n do + if curves[i] >= time then + local x = curves[i - 2] + local y = curves[i - 1] + return y + (time - x) / (curves[i] - x) * (curves[i + 1] - y) + end + i = i + 2 + end + frameIndex = frameIndex + self:getFrameEntries() + local x = curves[n - 2] + local y = curves[n - 1] + return y + (time - x) / (self.frames[frameIndex] - x) * (self.frames[frameIndex + valueOffset] - y) + end + + return self +end + +Animation.CurveTimeline1 = {} +function Animation.CurveTimeline1.new (frameCount, bezierCount, propertyId) + local ENTRIES = 2 + local VALUE = 1 + + local self = Animation.CurveTimeline.new(frameCount, bezierCount, { propertyId }) + + function self:getFrameEntries () + return ENTRIES + end + + function self:setFrame (frame, time, value) + frame = frame * ENTRIES + self.frames[frame] = time + self.frames[frame + VALUE] = value + end + + function self:getCurveValue (time) + local frames = self.frames + local i = zlen(frames) - 2 + local ii = 2 + while ii <= i do + if frames[ii] > time then + i = ii - 2 + break + end + ii = ii + 2 + end + local curveType = self.curves[i / 2] + if curveType == LINEAR then + local before = frames[i] + local value = frames[i + VALUE] + return value + (time - before) / (frames[i + ENTRIES] - before) * (frames[i + ENTRIES + VALUE] - value) + elseif curveType == STEPPED then + return frames[i + VALUE] + end + return self:getBezierValue(time, i, VALUE, curveType - BEZIER) + end + + return self +end + +Animation.CurveTimeline2 = {} +function Animation.CurveTimeline2.new (frameCount, bezierCount, propertyId1, propertyId2) + local ENTRIES = 3 + local VALUE1 = 1 + local VALUE2 = 2 + + local self = Animation.CurveTimeline.new(frameCount, bezierCount, { propertyId1, propertyId2 }) + + function self:getFrameEntries () + return ENTRIES + end + + function self:setFrame (frame, time, value1, value2) + frame = frame * ENTRIES + self.frames[frame] = time + self.frames[frame + VALUE1] = value1 + self.frames[frame + VALUE2] = value2 + end + + return self +end + +Animation.RotateTimeline = {} +function Animation.RotateTimeline.new (frameCount, bezierCount, boneIndex) + local self = Animation.CurveTimeline1.new(frameCount, bezierCount, Property.rotate.."|"..boneIndex) + self.boneIndex = boneIndex + + function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) + local bone = skeleton.bones[self.boneIndex] + if not bone.active then return end + + local frames = self.frames + if time < frames[0] then + if blend == MixBlend.setup then + bone.rotation = bone.data.rotation + elseif blend == MixBlend.first then + bone.rotation = bone.rotation + (bone.data.rotation - bone.rotation) * alpha + end + return + end + + local r = self:getCurveValue(time) + 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 + elseif blend == MixBlend.add then + bone.rotation = bone.rotation + r * alpha + end + end + + return self +end + +Animation.TranslateTimeline = {} +function Animation.TranslateTimeline.new (frameCount, bezierCount, boneIndex) + local self = Animation.CurveTimeline2.new(frameCount, bezierCount, + Property.x.."|"..boneIndex, + Property.y.."|"..boneIndex + ) + self.boneIndex = boneIndex + + function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) + local bone = skeleton.bones[self.boneIndex] + if not bone.active then return end + + local frames = self.frames + if time < frames[0] then + if blend == MixBlend.setup then + bone.x = bone.data.x + bone.y = bone.data.y + 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 + return + end + + local x = 0 + local y = 0 + local frame = search2(frames, time, ENTRIES) + local curveType = self.curves[math_floor(i / ENTRIES)] + if curveType == LINEAR then + local before = frames[i] + x = frames[i + VALUE1] + y = frames[i + VALUE2] + local t = (time - before) / (frames[i + ENTRIES] - before) + x = x + (frames[i + ENTRIES + VALUE1] - x) * t + y = y + (frames[i + ENTRIES + VALUE2] - y) * t + elseif curveType == STEPPED then + x = frames[i + VALUE1] + y = frames[i + VALUE2] + else + x = self:getBezierValue(time, i, VALUE1, curveType - BEZIER) + y = self:getBezierValue(time, i, VALUE2, curveType + BEZIER_SIZE - BEZIER) + end + + if blend == MixBlend.setup then + bone.x = bone.data.x + x * alpha + bone.y = bone.data.y + y * alpha + 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 + + return self +end + +Animation.TranslateXTimeline = {} +function Animation.TranslateXTimeline.new (frameCount, bezierCount, boneIndex) + local self = Animation.CurveTimeline1.new(frameCount, bezierCount, Property.x.."|"..boneIndex) + self.boneIndex = boneIndex + + function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) + local bone = skeleton.bones[self.boneIndex] + if not bone.active then return end + + local frames = self.frames + if time < frames[0] then + if blend == MixBlend.setup then + bone.x = bone.data.x + elseif blend == MixBlend.first then + bone.x = bone.x + (bone.data.x - bone.x) * alpha + end + return + end + + local x = self:getCurveValue(time) + if blend == MixBlend.setup then + bone.x = bone.data.x + x * alpha + elseif blend == MixBlend.first or blend == MixBlend.replace then + bone.x = bone.x + (bone.data.x + x - bone.x) * alpha + elseif blend == MixBlend.add then + bone.x = bone.x + x * alpha + end + end + + return self +end + +Animation.TranslateYTimeline = {} +function Animation.TranslateYTimeline.new (frameCount, bezierCount, boneIndex) + local self = Animation.CurveTimeline1.new(frameCount, bezierCount, Property.x.."|"..boneIndex) + self.boneIndex = boneIndex + + function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) + local bone = skeleton.bones[self.boneIndex] + if not bone.active then return end + + local frames = self.frames + if time < frames[0] then + if blend == MixBlend.setup then + bone.y = bone.data.y + elseif blend == MixBlend.first then + bone.y = bone.y + (bone.data.y - bone.y) * alpha + end + return + end + + local y = self:getCurveValue(time) + if blend == MixBlend.setup then + bone.y = bone.data.y + y * alpha + elseif blend == MixBlend.first or blend == MixBlend.replace then + bone.y = bone.y + (bone.data.y + y - bone.y) * alpha + elseif blend == MixBlend.add then + bone.y = bone.y + y * alpha + end + end + + return self +end + +Animation.ScaleTimeline = {} +function Animation.ScaleTimeline.new (frameCount, bezierCount, boneIndex) + local self = Animation.CurveTimeline2.new(frameCount, bezierCount, + Property.scaleX.."|"..boneIndex, + Property.scaleY.."|"..boneIndex + ) + self.boneIndex = boneIndex + + function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) + local bone = skeleton.bones[self.boneIndex] + if not bone.active then return end + + local frames = self.frames + if time < frames[0] then + if blend == MixBlend.setup then + bone.scaleX = bone.data.scaleX + bone.scaleY = bone.data.scaleY + 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 + return + end + + local x = 0 + local y = 0 + local i = search2(frames, time, ENTRIES) + local curveType = self.curves[math_floor(i / ENTRIES)] + if curveType == LINEAR then + local before = frames[i] + x = frames[i + VALUE1] + y = frames[i + VALUE2] + local t = (time - before) / (frames[i + ENTRIES] - before) + x = x + (frames[i + ENTRIES + VALUE1] - x) * t + y = y + (frames[i + ENTRIES + VALUE2] - y) * t + elseif curveType == STEPPED then + x = frames[i + VALUE1] + y = frames[i + VALUE2] + else + x = self:getBezierValue(time, i, VALUE1, curveType - BEZIER) + y = self:getBezierValue(time, i, VALUE2, curveType + BEZIER_SIZE - BEZIER) + end + x = x * bone.data.scaleX + y = y * bone.data.scaleY + + if alpha == 1 then + 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 direction == MixDirection.mixOut 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 + 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 + end + end + + return self +end + +Animation.ScaleXTimeline = {} +function Animation.ScaleXTimeline.new (frameCount, bezierCount, boneIndex) + local self = Animation.CurveTimeline1.new(frameCount, bezierCount, Property.scaleX.."|"..boneIndex) + self.boneIndex = boneIndex + + function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) + local bone = skeleton.bones[self.boneIndex] + if not bone.active then return end + + local frames = self.frames + if time < frames[0] then + if blend == MixBlend.setup then + bone.scaleX = bone.data.scaleX + elseif blend == MixBlend.first then + bone.scaleX = bone.scaleX + (bone.data.scaleX - bone.scaleX) * alpha + end + return + end + + local x = self:getCurveValue(time) * bone.data.scaleX + if alpha == 1 then + if blend == MixBlend.add then + bone.scaleX = bone.scaleX + x - bone.data.scaleX + else + bone.scaleX = x + end + else + local bx = 0 + if direction == MixDirection.mixOut then + if blend == MixBlend.setup then + bx = bone.data.scaleX + bone.scaleX = bx + (math_abs(x) * math_signum(bx) - bx) * alpha + elseif blend == MixBlend.first or blend == MixBlend.replace then + bx = bone.scaleX + bone.scaleX = bx + (math_abs(x) * math_signum(bx) - bx) * alpha + elseif blend == MixBlend.add then + bx = bone.scaleX + bone.scaleX = bx + (math_abs(x) * math_signum(bx) - bone.data.scaleX) * alpha + end + else + if blend == MixBlend.setup then + bx = math_abs(bone.data.scaleX) * math_signum(x) + bone.scaleX = bx + (x - bx) * alpha + elseif blend == MixBlend.first or blend == MixBlend.replace then + bx = math_abs(bone.scaleX) * math_signum(x) + bone.scaleX = bx + (x - bx) * alpha + elseif blend == MixBlend.add then + bx = math_signum(x) + bone.scaleX = math_abs(bone.scaleX) * bx + (x - math_abs(bone.data.scaleX) * bx) * alpha + end + end + end + end + + return self +end + +Animation.ScaleYTimeline = {} +function Animation.ScaleYTimeline.new (frameCount, bezierCount, boneIndex) + local self = Animation.CurveTimeline1.new(frameCount, bezierCount, Property.scaleY.."|"..boneIndex) + self.boneIndex = boneIndex + + function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) + local bone = skeleton.bones[self.boneIndex] + if not bone.active then return end + + local frames = self.frames + if time < frames[0] then + if blend == MixBlend.setup then + bone.scaleY = bone.data.scaleY + elseif blend == MixBlend.first then + bone.scaleY = bone.scaleY + (bone.data.scaleY - bone.scaleY) * alpha + end + return + end + + local y = self:getCurveValue(time) * bone.data.scaleY + if alpha == 1 then + if blend == MixBlend.add then + bone.scaleY = bone.scaleY + y - bone.data.scaleY + else + bone.scaleY = y + end + else + local by = 0 + if direction == MixDirection.mixOut then + if blend == MixBlend.setup then + by = bone.data.scaleY + bone.scaleY = by + (math_abs(y) * math_signum(by) - by) * alpha + elseif blend == MixBlend.first or blend == MixBlend.replace then + by = bone.scaleY + bone.scaleY = by + (math_abs(y) * math_signum(by) - by) * alpha + elseif blend == MixBlend.add then + by = bone.scaleY + bone.scaleY = by + (math_abs(y) * math_signum(by) - bone.data.scaleY) * alpha + end + else + if blend == MixBlend.setup then + by = math_abs(bone.data.scaleY) * math_signum(y) + bone.scaleY = by + (y - by) * alpha + elseif blend == MixBlend.first or blend == MixBlend.replace then + by = math_abs(bone.scaleY) * math_signum(y) + bone.scaleY = by + (y - by) * alpha + elseif blend == MixBlend.add then + by = math_signum(y) + bone.scaleY = math_abs(bone.scaleY) * by + (y - math_abs(bone.data.scaleY) * by) * alpha + end + end + end + end + + return self +end + +Animation.ShearTimeline = {} +function Animation.ShearTimeline.new (frameCount) + local self = Animation.CurveTimeline2.new(frameCount, bezierCount, + Property.shearX.."|"..boneIndex, + Property.shearY.."|"..boneIndex + ) + self.boneIndex = boneIndex + + function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) + local bone = skeleton.bones[self.boneIndex] + if not bone.active then return end + + local frames = self.frames + if time < frames[0] then + if blend == MixBlend.setup then + bone.shearX = bone.data.shearX + bone.shearY = bone.data.shearY + 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 + return + end + + local x = 0 + local y = 0 + local i = search2(frames, time, ENTRIES) + local curveType = self.curves[math_floor(i / ENTRIES)] + if curveType == LINEAR then + local before = frames[i] + x = frames[i + VALUE1] + y = frames[i + VALUE2] + local t = (time - before) / (frames[i + ENTRIES] - before) + x = x + (frames[i + ENTRIES + VALUE1] - x) * t + y = y + (frames[i + ENTRIES + VALUE2] - y) * t + elseif curveType == STEPPED then + x = frames[i + VALUE1] + y = frames[i + VALUE2] + else + x = self:getBezierValue(time, i, VALUE1, curveType - BEZIER) + y = self:getBezierValue(time, i, VALUE2, curveType + BEZIER_SIZE - BEZIER) + end + + if blend == MixBlend.setup then + bone.shearX = bone.data.shearX + x * alpha + bone.shearY = bone.data.shearY + y * alpha + 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 + + return self +end + +Animation.ShearXTimeline = {} +function Animation.ShearXTimeline.new (frameCount) + local self = Animation.CurveTimeline1.new(frameCount, bezierCount, Property.shearX.."|"..boneIndex) + self.boneIndex = boneIndex + + function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) + local bone = skeleton.bones[self.boneIndex] + if not bone.active then return end + + local frames = self.frames + if time < frames[0] then + if blend == MixBlend.setup then + bone.shearX = bone.data.shearX + elseif blend == MixBlend.first then + bone.shearX = bone.shearX + (bone.data.shearX - bone.shearX) * alpha + end + return + end + + local x = self:getCurveValue(time) + if blend == MixBlend.setup then + bone.shearX = bone.data.shearX + x * alpha + elseif blend == MixBlend.first or blend == MixBlend.replace then + bone.shearX = bone.shearX + (bone.data.shearX + x - bone.shearX) * alpha + elseif blend == MixBlend.add then + bone.shearX = bone.shearX + x * alpha + end + end + + return self +end + +Animation.ShearYTimeline = {} +function Animation.ShearYTimeline.new (frameCount) + local self = Animation.CurveTimeline1.new(frameCount, bezierCount, Property.shearY.."|"..boneIndex) + self.boneIndex = boneIndex + + function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) + local bone = skeleton.bones[self.boneIndex] + if not bone.active then return end + + local frames = self.frames + if time < frames[0] then + if blend == MixBlend.setup then + bone.shearY = bone.data.shearY + elseif blend == MixBlend.first then + bone.shearY = bone.shearX + (bone.data.shearY - bone.shearY) * alpha + end + return + end + + local y = self:getCurveValue(time) + if blend == MixBlend.setup then + bone.shearY = bone.data.shearY + y * alpha + elseif blend == MixBlend.first or blend == MixBlend.replace then + bone.shearY = bone.shearY + (bone.data.shearY + y - bone.shearY) * alpha + elseif blend == MixBlend.add then + bone.shearY = bone.shearY + y * alpha + end + end + + return self +end + +Animation.RGBATimeline = {} +function Animation.RGBATimeline.new (frameCount, bezierCount, slotIndex) + local ENTRIES = 5 + local R = 1 + local G = 2 + local B = 3 + local A = 4 + + local self = Animation.CurveTimeline.new(frameCount, bezierCount, { + Property.rgb.."|"..slotIndex, + Property.alpha.."|"..slotIndex + }) + self.slotIndex = slotIndex + + function self:getFrameEntries () + return ENTRIES + end + + function self:setFrame (frame, time, r, g, b, a) + frame = frame * ENTRIES + self.frames[frame] = time + self.frames[frame + R] = r + self.frames[frame + G] = g + self.frames[frame + B] = b + self.frames[frame + A] = a + end + + function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) + local slot = skeleton.slots[self.slotIndex] + if not slot.bone.active then return end + + local frames = self.frames + local color = slot.color + if time < frames[0] then + local setup = slot.data.color + if blend == MixBlend.setup then + color:setFrom(setup) + elseif blend == MixBlend.first then + 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 + + local r, g, b, a + local i = search2(frames, time, ENTRIES) + local curveType = self.curves[i / ENTRIES] + if curveType == LINEAR then + local before = frames[i] + r = frames[i + R] + g = frames[i + G] + b = frames[i + B] + a = frames[i + A] + local t = (time - before) / (frames[i + ENTRIES] - before) + r = r + (frames[i + ENTRIES + R] - r) * t + g = g + (frames[i + ENTRIES + G] - g) * t + b = b + (frames[i + ENTRIES + B] - b) * t + a = a + (frames[i + ENTRIES + A] - a) * t + elseif curveType == STEPPED then + r = frames[i + R] + g = frames[i + G] + b = frames[i + B] + a = frames[i + A] + else + r = self:getBezierValue(time, i, R, curveType - BEZIER) + g = self:getBezierValue(time, i, G, curveType + BEZIER_SIZE - BEZIER) + b = self:getBezierValue(time, i, B, curveType + BEZIER_SIZE * 2 - BEZIER) + a = self:getBezierValue(time, i, A, curveType + BEZIER_SIZE * 3 - BEZIER) + end + + if alpha == 1 then + color:set(r, g, b, a) + else + 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 + + return self +end + +Animation.RGBTimeline = {} +function Animation.RGBTimeline.new (frameCount, bezierCount, slotIndex) + local ENTRIES = 4 + local R = 1 + local G = 2 + local B = 3 + + local self = Animation.CurveTimeline.new(frameCount, bezierCount, { Property.rgb.."|"..slotIndex }) + self.slotIndex = slotIndex + + function self:getFrameEntries () + return ENTRIES + end + + function self:setFrame (frame, time, r, g, b) + frame = frame * ENTRIES + self.frames[frame] = time + self.frames[frame + R] = r + self.frames[frame + G] = g + self.frames[frame + B] = b + end + + function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) + local slot = skeleton.slots[self.slotIndex] + if not slot.bone.active then return end + + local frames = self.frames + local color = slot.color + if time < frames[0] then + local setup = slot.data.color + if blend == MixBlend.setup then + color.r = setup.r + color.g = setup.g + color.b = setup.b + elseif blend == MixBlend.first then + color.r = color.r + (setup.r - color.r) * alpha + color.g = color.g + (setup.g - color.g) * alpha + color.b = color.b + (setup.b - color.b) * alpha + end + return + end + + local r, g, b + local i = search2(frames, time, ENTRIES) + local curveType = self.curves[i / ENTRIES] + if curveType == LINEAR then + local before = frames[i] + r = frames[i + R] + g = frames[i + G] + b = frames[i + B] + local t = (time - before) / (frames[i + ENTRIES] - before) + r = r + (frames[i + ENTRIES + R] - r) * t + g = g + (frames[i + ENTRIES + G] - g) * t + b = b + (frames[i + ENTRIES + B] - b) * t + elseif curveType == STEPPED then + r = frames[i + R] + g = frames[i + G] + b = frames[i + B] + else + r = self:getBezierValue(time, i, R, curveType - BEZIER) + g = self:getBezierValue(time, i, G, curveType + BEZIER_SIZE - BEZIER) + b = self:getBezierValue(time, i, B, curveType + BEZIER_SIZE * 2 - BEZIER) + end + + if alpha == 1 then + color.r = r + color.g = g + color.b = b + else + if blend == MixBlend.setup then + local setup = slot.data.color + color.r = setup.r + color.g = setup.g + color.b = setup.b + end + color.r = color.r + (r - color.r) * alpha + color.g = color.g + (g - color.g) * alpha + color.b = color.b + (b - color.b) * alpha + end + end + + return self +end + +Animation.AlphaTimeline = {} +function Animation.AlphaTimeline.new (frameCount, bezierCount, slotIndex) + local self = Animation.CurveTimeline1.new(frameCount, bezierCount, Property.alpha.."|"..slotIndex) + self.slotIndex = slotIndex + + function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) + local slot = skeleton.slots[self.slotIndex] + if not slot.bone.active then return end + + local color = slot.color + if time < frames[0] then + local setup = slot.data.color + if blend == MixBlend.setup then + color.a = setup.a + return + else + color.a = color.a + (setup.a - color.a) * alpha + end + return + end + + local a = self:getCurveValue(time) + if alpha == 1 then + color.a = a + else + if blend == MixBlend.setup then color.a = slot.data.color.a end + color.a = color.a + (a - color.a) * alpha + end + end + + return self +end + +Animation.RGBA2Timeline = {} +function Animation.RGBA2Timeline.new (frameCount, bezierCount, slotIndex) + local ENTRIES = 8 + local R = 1 + local G = 2 + local B = 3 + local A = 4 + local R2 = 5 + local G2 = 6 + local B2 = 7 + + local self = Animation.CurveTimeline.new(frameCount, bezierCount, { + Property.rgb.."|"..slotIndex, + Property.alpha.."|"..slotIndex, + Property.rgb2.."|"..slotIndex + }) + self.slotIndex = slotIndex + + function self:getFrameEntries () + return ENTRIES + end + + function self:setFrame (frame, time, r, g, b, a, r2, g2, b2) + frame = frame * ENTRIES + self.frames[frame] = time + self.frames[frame + R] = r + self.frames[frame + G] = g + self.frames[frame + B] = b + self.frames[frame + A] = a + self.frames[frame + R2] = r2 + self.frames[frame + G2] = g2 + self.frames[frame + B2] = b2 + end + + function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) + local slot = skeleton.slots[self.slotIndex] + if not slot.bone.active then return end + + local frames = self.frames + local light = slot.color + local dark = slot.darkColor + if time < frames[0] then + local setupLight = slot.data.color + local setupDark = slot.data.darkColor + if blend == MixBlend.setup then + light:setFrom(setupLight) + dark.r = setupDark.r + dark.g = setupDark.g + dark.b = setupDark.b + elseif blend == MixBlend.first then + light:add((setupLight.r - light.r) * alpha, (setupLight.g - light.g) * alpha, (setupLight.b - light.b) * alpha, + (setupLight.a - light.a) * alpha) + dark.r = dark.r + (setupDark.r - dark.r) * alpha + dark.g = dark.g + (setupDark.g - dark.g) * alpha + dark.b = dark.b + (setupDark.b - dark.b) * alpha + end + return + end + + local r, g, b, a, r2, g2, b2 + local i = search2(frames, time, ENTRIES) + local curveType = self.curves[math_floor(i / ENTRIES)] + if curveType == LINEAR then + local before = frames[i] + r = frames[i + R] + g = frames[i + G] + b = frames[i + B] + a = frames[i + A] + r2 = frames[i + R2] + g2 = frames[i + G2] + b2 = frames[i + B2] + local t = (time - before) / (frames[i + ENTRIES] - before) + r = r + (frames[i + ENTRIES + R] - r) * t + g = g + (frames[i + ENTRIES + G] - g) * t + b = b + (frames[i + ENTRIES + B] - b) * t + a = a + (frames[i + ENTRIES + A] - a) * t + r2 = r2 + (frames[i + ENTRIES + R2] - r2) * t + g2 = g2 + (frames[i + ENTRIES + G2] - g2) * t + b2 = b2 + (frames[i + ENTRIES + B2] - b2) * t + elseif curveType == STEPPED then + r = frames[i + R] + g = frames[i + G] + b = frames[i + B] + a = frames[i + A] + r2 = frames[i + R2] + g2 = frames[i + G2] + b2 = frames[i + B2] + else + r = self:getBezierValue(time, i, R, curveType - BEZIER) + g = self:getBezierValue(time, i, G, curveType + BEZIER_SIZE - BEZIER) + b = self:getBezierValue(time, i, B, curveType + BEZIER_SIZE * 2 - BEZIER) + a = self:getBezierValue(time, i, A, curveType + BEZIER_SIZE * 3 - BEZIER) + r2 = self:getBezierValue(time, i, R2, curveType + BEZIER_SIZE * 4 - BEZIER) + g2 = self:getBezierValue(time, i, G2, curveType + BEZIER_SIZE * 5 - BEZIER) + b2 = self:getBezierValue(time, i, B2, curveType + BEZIER_SIZE * 6 - BEZIER) + end + + if alpha == 1 then + light:set(r, g, b, a) + dark.r = r2 + dark.g = g2 + dark.b = b2 + else + if blend == MixBlend.setup then + light:setFrom(slot.data.color) + local setupDark = slot.data.darkColor + dark.r = setupDark.r + dark.g = setupDark.g + dark.b = setupDark.b + end + light:add((r - light.r) * alpha, (g - light.g) * alpha, (b - light.b) * alpha, (a - light.a) * alpha) + dark.r = dark.r + (r2 - dark.r) * alpha + dark.g = dark.g + (g2 - dark.g) * alpha + dark.b = dark.b + (b2 - dark.b) * alpha + end + end + + return self +end + +Animation.RGB2Timeline = {} +function Animation.RGB2Timeline.new (frameCount, bezierCount, slotIndex) + local ENTRIES = 7 + local R = 1 + local G = 2 + local B = 3 + local R2 = 4 + local G2 = 5 + local B2 = 6 + + local self = Animation.CurveTimeline.new(frameCount, bezierCount, { + Property.rgb.."|"..slotIndex, + Property.rgb2.."|"..slotIndex + }) + self.slotIndex = slotIndex + + function self:getFrameEntries () + return ENTRIES + end + + function self:setFrame (frame, time, r, g, b, r2, g2, b2) + frame = frame * ENTRIES + self.frames[frame] = time + self.frames[frame + R] = r + self.frames[frame + G] = g + self.frames[frame + B] = b + self.frames[frame + R2] = r2 + self.frames[frame + G2] = g2 + self.frames[frame + B2] = b2 + end + + function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) + local slot = skeleton.slots[self.slotIndex] + if not slot.bone.active then return end + + local frames = self.frames + local light = slot.color + local dark = slot.darkColor + if time < frames[0] then + local setupLight = slot.data.color + local setupDark = slot.data.darkColor + if blend == MixBlend.setup then + light.r = setupLight.r + light.g = setupLight.g + light.b = setupLight.b + dark.r = setupDark.r + dark.g = setupDark.g + dark.b = setupDark.b + elseif blend == MixBlend.first then + light.r = light.r + (setupLight.r - light.r) * alpha + light.g = light.g + (setupLight.g - light.g) * alpha + light.b = light.b + (setupLight.b - light.b) * alpha + dark.r = dark.r + (setupDark.r - dark.r) * alpha + dark.g = dark.g + (setupDark.g - dark.g) * alpha + dark.b = dark.b + (setupDark.b - dark.b) * alpha + end + return + end + + local r, g, b, r2, g2, b2 + local i = search2(frames, time, ENTRIES) + local curveType = self.curves[math_floor(i / ENTRIES)] + if curveType == LINEAR then + local before = frames[i] + r = frames[i + R] + g = frames[i + G] + b = frames[i + B] + r2 = frames[i + R2] + g2 = frames[i + G2] + b2 = frames[i + B2] + local t = (time - before) / (frames[i + ENTRIES] - before) + r = r + (frames[i + ENTRIES + R] - r) * t + g = g + (frames[i + ENTRIES + G] - g) * t + b = b + (frames[i + ENTRIES + B] - b) * t + r2 = r2 + (frames[i + ENTRIES + R2] - r2) * t + g2 = g2 + (frames[i + ENTRIES + G2] - g2) * t + b2 = b2 + (frames[i + ENTRIES + B2] - b2) * t + elseif curveType == STEPPED then + r = frames[i + R] + g = frames[i + G] + b = frames[i + B] + r2 = frames[i + R2] + g2 = frames[i + G2] + b2 = frames[i + B2] + else + r = self:getBezierValue(time, i, R, curveType - BEZIER) + g = self:getBezierValue(time, i, G, curveType + BEZIER_SIZE - BEZIER) + b = self:getBezierValue(time, i, B, curveType + BEZIER_SIZE * 2 - BEZIER) + r2 = self:getBezierValue(time, i, R2, curveType + BEZIER_SIZE * 4 - BEZIER) + g2 = self:getBezierValue(time, i, G2, curveType + BEZIER_SIZE * 5 - BEZIER) + b2 = self:getBezierValue(time, i, B2, curveType + BEZIER_SIZE * 6 - BEZIER) + end + + if alpha == 1 then + light.r = r + light.g = g + light.b = b + dark.r = r2 + dark.g = g2 + dark.b = b2 + else + if blend == MixBlend.setup then + local setupLight = slot.data.color + local setupDark = slot.data.darkColor + light.r = setupLight.r + light.g = setupLight.g + light.b = setupLight.b + dark.r = setupDark.r + dark.g = setupDark.g + dark.b = setupDark.b + end + light.r = light.r + (r - light.r) * alpha + light.g = light.g + (g - light.g) * alpha + light.b = light.b + (b - light.b) * alpha + dark.r = dark.r + (r2 - dark.r) * alpha + dark.g = dark.g + (g2 - dark.g) * alpha + dark.b = dark.b + (b2 - dark.b) * alpha + end + end + + return self +end + +Animation.AttachmentTimeline = {} +function Animation.AttachmentTimeline.new (frameCount, bezierCount, slotIndex) + local self = Animation.Timeline.new(frameCount, { Property.attachment + "|" + slotIndex }) + self.slotIndex = slotIndex + self.attachmentNames = {} + + function self:getFrameCount () + return zlen(self.frames) + end + + function self:setFrame (frame, time, attachmentName) + self.frames[frame] = time + self.attachmentNames[frame] = attachmentName + end + + function self:setAttachment(skeleton, slot, attachmentName) + attachmentName = slot.data.attachmentName + if not attachmentName then + slot:setAttachment(nil) + else + slot:setAttachment(skeleton:getAttachmentByIndex(self.slotIndex, attachmentName)) + end + end + + local function setAttachment (skeleton, slot, attachmentName) + local attachmentName = self.attachmentNames[frameIndex] + if not attachmentName then + slot:setAttachment(nil) + else + slot:setAttachment(skeleton:getAttachmentByIndex(self.slotIndex, attachmentName)) + end + end + + function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) + local slot = skeleton.slots[self.slotIndex] + if not slot.bone.active then return end + + if direction == MixDirection.mixOut then + if blend == MixBlend.setup then + self:setAttachment(skeleton, slot, slot.data.attachmentName) + end + return + end + + if time < self.frames[0] then + if blend == MixBlend.setup or blend == MixBlend.first then + self:setAttachment(skeleton, slot, slot.data.attachmentName) + end + return + end + + setAttachment(skeleton, slot, self.attachmentNames[search1(self.frames, time)]) + end + + return self +end + +Animation.DeformTimeline = {} +function Animation.DeformTimeline.new (frameCount, bezierCount, slotIndex, attachment) + local self = Animation.CurveTimeline.new(frameCount, bezierCount, { Property.deform + "|" + slotIndex + "|" + attachment.id }) + self.slotIndex = slotIndex + self.attachment = attachment + self.vertices = {} + + function self:getFrameCount () + return zlen(self.frames) + end + + function self:setFrame (frame, time, vertices) + self.frames[frame] = time + self.vertices[frame] = vertices + end + + function self:setBezier (bezier, frame, value, time1, value1, cx1, cy1, cx2, cy2, time2, value2) + local curves = self.curves + local i = self:getFrameCount() + bezier * BEZIER_SIZE + if value == 0 then curves[frame] = BEZIER + i end + local tmpx = (time1 - cx1 * 2 + cx2) * 0.03 + local tmpy = cy2 * 0.03 - cy1 * 0.06 + local dddx = ((cx1 - cx2) * 3 - time1 + time2) * 0.006 + local dddy = (cy1 - cy2 + 0.33333333) * 0.018 + local ddx = tmpx * 2 + dddx + local ddy = tmpy * 2 + dddy + local dx = (cx1 - time1) * 0.3 + tmpx + dddx * 0.16666667 + local dy = cy1 * 0.3 + tmpy + dddy * 0.16666667 + local x = time1 + dx + local y = dy + local n = i + BEZIER_SIZE + while i < n do + curves[i] = x + curves[i + 1] = y + dx = dx + ddx + dy = dy + ddy + ddx = ddx + dddx + ddy = ddy + dddy + x = x + dx + y = y + dy + i = i + 2 + end + end + + function getCurvePercent (time, frame) + local curves = self.curves + local i = curves[frame] + if i == LINEAR then + local x = self.frames[frame] + return (time - x) / (self.frames[frame + self:getFrameEntries()] - x) + elseif i == STEPPED then + return 0 + end + i = i - BEZIER + if curves[i] > time then + local x = self.frames[frame] + return curves[i + 1] * (time - x) / (curves[i] - x) + end + local n = i + BEZIER_SIZE + i = i + 2 + while i < n do + if curves[i] >= time then + local x = curves[i - 2] + local y = curves[i - 1] + return y + (time - x) / (curves[i] - x) * (curves[i + 1] - y) + end + i = i + 2 + end + local x = curves[n - 2] + local y = curves[n - 1] + return y + (1 - y) * (time - x) / (self.frames[frame + self:getFrameEntries()] - x) + end + + function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) + local slot = skeleton.slots[self.slotIndex] + if not slot.bone.active then return end + + local vertexAttachment = slot.attachment + if not vertexAttachment or not vertexAttachment.vertexAttachment or vertexAttachment.deformAttachment ~= self.attachment then return end + + local frames = self.frames + local deform = slot.deform + if #deform == 0 then blend = MixBlend.setup end + + local vertices = self.vertices + local vertexCount = #(vertices[0]) + + if time < frames[0] then + if blend == MixBlend.setup then + slot.deform = {} + return + elseif blend == MixBlend.first then + if alpha == 1 then + slot.deform = {} + return + end + utils.setArraySize(deform, vertexCount) + if not vertexAttachment.bones then + local setupVertices = vertexAttachment.vertices + local i = 1 + while i <= vertexCount do + deform[i] = deform[i] + (setupVertices[i] - deform[i]) * alpha + i = i + 1 + end + else + alpha = 1 - alpha + local i = 1 + while i <= vertexCount do + deform[i] = deform[i] * alpha + i = i + 1 + end + end + end + return + end + + utils.setArraySize(deform, vertexCount) + if time >= frames[zlen(frames) - 1] then -- Time is after last frame. + local lastVertices = vertices[zlen(frames) - 1] + if alpha == 1 then + if blend == MixBlend.add then + if vertexAttachment.bones == nil then + -- Unweighted vertex positions, with alpha. + local setupVertices = vertexAttachment.vertices + local i = 1 + while i <= vertexCount do + deform[i] = deform[i] + lastVertices[i] - setupVertices[i] + i = i + 1 + end + else + -- Weighted deform offsets, with alpha. + local i = 1 + while i <= vertexCount do + deform[i] = deform[i] + lastVertices[i] + i = i + 1 + end + end + else + local i = 1 + while i <= vertexCount do + deform[i] = lastVertices[i] + i = i + 1 + end + end + else + if blend == MixBlend.setup then + 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] + deform[i] = setup + (lastVertices[i] - setup) * alpha + i = i + 1 + end + else + -- Weighted deform offsets, with alpha. + local i = 1 + while i <= vertexCount do + deform[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 + deform[i] = deform[i] + (lastVertices[i] - deform[i]) * alpha + i = i + 1 + end + if vertexAttachment.bones == nil then + local setupVertices = vertexAttachment.vertices + local i = 1 + while i <= vertexCount do + deform[i] = deform[i] + (lastVertices[i] - setupVertices[i]) * alpha + i = i + 1 + end + else + -- Weighted deform offsets, with alpha. + local i = 1 + while i <= vertexCount do + deform[i] = deform[i] + lastVertices[i] * alpha + i = i + 1 + end + end + elseif blend == MixBlend.add then + if vertexAttachment.bones == nil then + local setupVertices = vertexAttachment.vertices + local i = 1 + while i <= vertexCount do + deform[i] = deform[i] + (lastVertices[i] - setupVertices[i]) * alpha + i = i + 1 + end + else + -- Weighted deform offsets, with alpha. + local i = 1 + while i <= vertexCount do + deform[i] = deform[i] + lastVertices[i] * alpha + i = i + 1 + end + end + end + end + return + end + + -- Interpolate between the previous frame and the current frame. + local frame = search1(frames, time) + local percent = self:getCurvePercent(time, frame) + local prevVertices = vertices[frame] + local nextVertices = vertices[frame + 1] + + if alpha == 1 then + if blend == MixBlend.add then + 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] + deform[i] = deform[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] + deform[i] = deform[i] + prev + (nextVertices[i] - prev) * percent + i = i + 1 + end + end + else + local i = 1 + while i <= vertexCount do + local prev = prevVertices[i] + deform[i] = prev + (nextVertices[i] - prev) * percent + i = i + 1 + end + end + else + if blend == MixBlend.setup then + 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] + deform[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] + deform[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] + deform[i] = deform[i] + (prev + (nextVertices[i] - prev) * percent - deform[i]) * alpha + i = i + 1 + end + elseif blend == MixBlend.add then + if vertexAttachment.bones == nil then + local setupVertices = vertexAttachment.vertices + local i = 1 + while i <= vertexCount do + local prev = prevVertices[i] + deform[i] = deform[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] + deform[i] = deform[i] + (prev + (nextVertices[i] - prev) * percent) * alpha + i = i + 1 + end + end + end + end + end + + return self +end + +Animation.EventTimeline = {} +local eventPropertyIds = { Property.event } +function Animation.EventTimeline.new (frameCount) + local self = Animation.Timeline.new(frameCount, eventPropertyIds) + self.events = {} + + function self:getFrameCount () + return zlen(self.frames) + end + + function self:setFrame (frame, event) + self.frames[frame] = event.time + self.events[frame] = event + end + + -- Fires events for frames > lastTime and <= time. + 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, blend, direction) + lastTime = -1 + elseif lastTime >= frames[frameCount - 1] then -- Last time is after last frame. + return + end + if time < frames[0] then return end -- Time is before first frame. + + local i + if lastTime < frames[0] then + i = 0 + else + i = binarySearch1(frames, lastTime) + local i = frames[i] + while i > 0 do -- Fire multiple events with the same frame. + if frames[i - 1] ~= i then break end + i = i - 1 + end + end + while i < frameCount and time >= frames[i] do + table.insert(firedEvents, self.events[i]) + i = i + 1 + end + end + + return self +end + +Animation.DrawOrderTimeline = {} +local drawOrderPropertyIds = { Property.drawOrder } +function Animation.DrawOrderTimeline.new (frameCount) + local self = Animation.Timeline.new(frameCount, drawOrderPropertyIds) + self.drawOrders = {} + + function self:getFrameCount () + return zlen(self.frames) + end + + function self:setFrame (frame, time, drawOrder) + self.frames[frame] = time + self.drawOrders[frame] = drawOrder + end + + function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) + if direction == MixDirection.mixOut then + if blend == MixBlend.setup then + for i,slot in ipairs(slots) do + drawOrder[i] = slots[i] + end + end + return + end + + if time < self.frames[0] then + if blend == MixBlend.setup or blend == MixBlend.first then + for i,slot in ipairs(slots) do + drawOrder[i] = slots[i] + end + end + return + end + + local drawOrderToSetupIndex = self.drawOrders[search1(self.frames, time)] + if not drawOrderToSetupIndex then + for i,slot in ipairs(slots) do + drawOrder[i] = slots[i] + end + else + local drawOrder = skeleton.drawOrder + local slots = skeleton.slots + for i,setupIndex in ipairs(drawOrderToSetupIndex) do + drawOrder[i] = skeleton.slots[setupIndex] + end + end + end + + return self +end + +Animation.IkConstraintTimeline = {} +function Animation.IkConstraintTimeline.new (frameCount, bezierCount, ikConstraintIndex) + local ENTRIES = 6 + local MIX = 1 + local SOFTNESS = 2 + local BEND_DIRECTION = 3 + local COMPRESS = 4 + local STRETCH = 5 + + local self = Animation.CurveTimeline.new(frameCount, bezierCount, { Property.ikConstraint + "|" + ikConstraintIndex }) + self.ikConstraintIndex = ikConstraintIndex + + function self:getFrameEntries () + return ENTRIES + end + + function self:setFrame (frame, time, mix, softness, bendDirection, compress, stretch) + frame = frame * ENTRIES + self.frames[frame] = time + self.frames[frame + MIX] = mix + self.frames[frame + SOFTNESS] = softness + self.frames[frame + BEND_DIRECTION] = bendDirection + if compress then + self.frames[frame + COMPRESS] = 1 + else + self.frames[frame + COMPRESS] = 0 + end + if stretch then + self.frames[frame + STRETCH] = 1 + else + self.frames[frame + STRETCH] = 0 + end + end + + function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) + local constraint = skeleton.ikConstraints[self.ikConstraintIndex] + if not constraint.active then return end + + local frames = self.frames + 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 + end + return + end + + local mix = 0 + local softness = 0 + local i = search(frames, time, ENTRIES) + local curveType = this.curves[i / ENTRIES] + if curveType == LINEAR then + local before = frames[i] + mix = frames[i + MIX] + softness = frames[i + SOFTNESS] + local t = (time - before) / (frames[i + ENTRIES] - before) + mix = mix + (frames[i + ENTRIES + MIX] - mix) * t + softness = softness + (frames[i + ENTRIES + SOFTNESS] - softness) * t + elseif curveType == STEPPED then + mix = frames[i + MIX] + softness = frames[i + SOFTNESS] + else + mix = self:getBezierValue(time, i, MIX, curveType - BEZIER) + softness = self:getBezierValue(time, i, SOFTNESS, curveType + BEZIER_SIZE - BEZIER) + end + + if blend == MixBlend.setup then + constraint.mix = constraint.data.mix + (mix - constraint.data.mix) * alpha + constraint.softness = constraint.data.softness + (softness - constraint.data.softness) * alpha + if direction == MixDirection.mixOut then + constraint.bendDirection = constraint.data.bendDirection + constraint.compress = constraint.data.compress + constraint.stretch = constraint.data.stretch + else + constraint.bendDirection = math_floor(frames[i + BEND_DIRECTION]) + if math_floor(frames[i + COMPRESS]) == 1 then constraint.compress = true else constraint.compress = false end + if math_floor(frames[i + STRETCH]) == 1 then constraint.stretch = true else constraint.stretch = false end + end + else + constraint.mix = constraint.mix + (mix - constraint.mix) * alpha + constraint.softness = constraint.softness + (softness - constraint.softness) * alpha + if direction == MixDirection.mixIn then + constraint.bendDirection = math_floor(frames[i + BEND_DIRECTION]) + if math_floor(frames[i + COMPRESS]) == 1 then constraint.compress = true else constraint.compress = false end + if math_floor(frames[i + STRETCH]) == 1 then constraint.stretch = true else constraint.stretch = false end + end + end + end + + return self +end + +Animation.TransformConstraintTimeline = {} +function Animation.TransformConstraintTimeline.new (frameCount, transformConstraintIndex) + local ENTRIES = 7 + local ROTATE = 1 + local X = 2 + local Y = 3 + local SCALEX = 4 + local SCALEY = 5 + local SHEARY = 6 + + local self = Animation.CurveTimeline.new(frameCount, bezierCount, { Property.transformConstraint + "|" + transformConstraintIndex }) + self.transformConstraintIndex = transformConstraintIndex + + function self:getFrameEntries () + return ENTRIES + end + + function self:setFrame (frame, time, mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY) + frame = frame * ENTRIES + self.frames[frame] = time + self.frames[frame + ROTATE] = mixRotate + self.frames[frame + X] = mixX + self.frames[frame + Y] = mixY + self.frames[frame + SCALEX] = mixScaleX + self.frames[frame + SCALEY] = mixScaleY + self.frames[frame + SHEARY] = mixShearY + end + + function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) + local constraint = skeleton.transformConstraints[self.transformConstraintIndex] + if not constraint.active then return end + + local frames = self.frames + if time < frames[0] then + local data = constraint.data + if blend == MixBlend.setup then + constraint.mixRotate = data.mixRotate + constraint.mixX = data.mixX + constraint.mixY = data.mixY + constraint.mixScaleX = data.mixScaleX + constraint.mixScaleY = data.mixScaleY + constraint.mixShearY = data.mixShearY + elseif blend == MixBlend.first then + constraint.mixRotate = constraint.mixRotate + (data.mixRotate - constraint.mixRotate) * alpha + constraint.mixX = constraint.mixX + (data.mixX - constraint.mixX) * alpha + constraint.mixY = constraint.mixY + (data.mixY - constraint.mixY) * alpha + constraint.mixScaleX = constraint.mixScaleX + (data.mixScaleX - constraint.mixScaleX) * alpha + constraint.mixScaleY = constraint.mixScaleY + (data.mixScaleY - constraint.mixScaleY) * alpha + constraint.mixShearY = constraint.mixShearY + (data.mixShearY - constraint.mixShearY) * alpha + end + return + end + + local rotate + local x + local y + local scaleX + local scaleY + local shearY + local i = search(frames, time, ENTRIES) + local curveType = this.curves[i / ENTRIES] + if curveType == LINEAR then + local before = frames[i] + rotate = frames[i + ROTATE] + x = frames[i + X] + y = frames[i + Y] + scaleX = frames[i + SCALEX] + scaleY = frames[i + SCALEY] + shearY = frames[i + SHEARY] + local t = (time - before) / (frames[i + ENTRIES] - before) + rotate = rotate + (frames[i + ENTRIES + ROTATE] - rotate) * t + x = x + (frames[i + ENTRIES + X] - x) * t + y = y + (frames[i + ENTRIES + Y] - y) * t + scaleX = scaleX + (frames[i + ENTRIES + SCALEX] - scaleX) * t + scaleY = scaleY + (frames[i + ENTRIES + SCALEY] - scaleY) * t + shearY = shearY + (frames[i + ENTRIES + SHEARY] - shearY) * t + elseif curveType == STEPPED then + rotate = frames[i + ROTATE] + x = frames[i + X] + y = frames[i + Y] + scaleX = frames[i + SCALEX] + scaleY = frames[i + SCALEY] + shearY = frames[i + SHEARY] + else + rotate = self:getBezierValue(time, i, ROTATE, curveType - BEZIER) + x = self:getBezierValue(time, i, X, curveType + BEZIER_SIZE - BEZIER) + y = self:getBezierValue(time, i, Y, curveType + BEZIER_SIZE * 2 - BEZIER) + scaleX = self:getBezierValue(time, i, SCALEX, curveType + BEZIER_SIZE * 3 - BEZIER) + scaleY = self:getBezierValue(time, i, SCALEY, curveType + BEZIER_SIZE * 4 - BEZIER) + shearY = self:getBezierValue(time, i, SHEARY, curveType + BEZIER_SIZE * 5 - BEZIER) + end + + if blend == MixBlend.setup then + local data = constraint.data + constraint.mixRotate = data.mixRotate + (rotate - data.mixRotate) * alpha + constraint.mixX = data.mixX + (x - data.mixX) * alpha + constraint.mixY = data.mixY + (y - data.mixY) * alpha + constraint.mixScaleX = data.mixScaleX + (scaleX - data.mixScaleX) * alpha + constraint.mixScaleY = data.mixScaleY + (scaleY - data.mixScaleY) * alpha + constraint.mixShearY = data.mixShearY + (shearY - data.mixShearY) * alpha + else + constraint.mixRotate = constraint.mixRotate + (rotate - constraint.mixRotate) * alpha + constraint.mixX = constraint.mixX + (x - constraint.mixX) * alpha + constraint.mixY = constraint.mixY + (y - constraint.mixY) * alpha + constraint.mixScaleX = constraint.mixScaleX + (scaleX - constraint.mixScaleX) * alpha + constraint.mixScaleY = constraint.mixScaleY + (scaleY - constraint.mixScaleY) * alpha + constraint.mixShearY = constraint.mixShearY + (shearY - constraint.mixShearY) * alpha + end + end + + return self +end + +Animation.PathConstraintPositionTimeline = {} +function Animation.PathConstraintPositionTimeline.new (frameCount, bezierCount, pathConstraintIndex) + local self = Animation.CurveTimeline1.new(frameCount, bezierCount, Property.pathConstraintPosition.."|"..pathConstraintIndex) + self.pathConstraintIndex = pathConstraintIndex + + function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) + local constraint = skeleton.pathConstraints[self.pathConstraintIndex] + if not constraint.active then return end + + local frames = self.frames + if time < frames[0] then + if blend == MixBlend.setup then + constraint.position = constraint.data.position + elseif blend == MixBlend.first then + constraint.position = constraint.position + (constraint.data.position - constraint.position) * alpha + end + return + end + + local position = self:getCurveValue(time) + if blend == MixBlend.setup then + constraint.position = constraint.data.position + (position - constraint.data.position) * alpha + else + constraint.position = constraint.position + (position - constraint.position) * alpha + end + end + + return self +end + +Animation.PathConstraintSpacingTimeline = {} +function Animation.PathConstraintSpacingTimeline.new (frameCount, bezierCount, pathConstraintIndex) + local self = Animation.CurveTimeline1.new(frameCount, bezierCount, Property.pathConstraintSpacing.."|"..pathConstraintIndex) + self.pathConstraintIndex = pathConstraintIndex + + function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) + local constraint = skeleton.pathConstraints[self.pathConstraintIndex] + if not constraint.active then return end + + local frames = self.frames + if time < frames[0] then + if blend == MixBlend.setup then + constraint.spacing = constraint.data.spacing + elseif blend == MixBlend.first then + constraint.spacing = constraint.spacing + (constraint.data.spacing - constraint.spacing) * alpha + end + return + end + + local spacing = self:getCurveValue(time) + if blend == MixBlend.setup then + constraint.spacing = constraint.data.spacing + (spacing - constraint.data.spacing) * alpha + else + constraint.spacing = constraint.spacing + (spacing - constraint.spacing) * alpha + end + end + + return self +end + +Animation.PathConstraintMixTimeline = {} +function Animation.PathConstraintMixTimeline.new (frameCount, bezierCount, pathConstraintIndex) + local ENTRIES = 4 + local ROTATE = 1 + local X = 2 + local Y = 3 + + local self = Animation.CurveTimeline.new(frameCount, bezierCount, Property.pathConstraintMix.."|"..pathConstraintIndex) + self.pathConstraintIndex = pathConstraintIndex + + function self:getFrameEntries () + return ENTRIES + end + + function self:setFrame (frame, time, mixRotate, mixX, mixY) + local frames = self.frames + frame = frame * ENTRIES + frames[frame] = time + frames[frame + ROTATE] = mixRotate + frames[frame + X] = mixX + frames[frame + Y] = mixY + end + + function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) + local constraint = skeleton.pathConstraints[self.pathConstraintIndex] + if not constraint.active then return end + + local frames = self.frames + if time < frames[0] then + if blend == MixBlend.setup then + constraint.mixRotate = constraint.data.mixRotate; + constraint.mixX = constraint.data.mixX; + constraint.mixY = constraint.data.mixY; + elseif blend == MixBlend.first then + constraint.mixRotate = constraint.mixRotate + (constraint.data.mixRotate - constraint.mixRotate) * alpha; + constraint.mixX = constraint.mixX + (constraint.data.mixX - constraint.mixX) * alpha; + constraint.mixY = constraint.mixY + (constraint.data.mixY - constraint.mixY) * alpha; + end + return + end + + local rotate + local x + local y + local i = search(frames, time, ENTRIES); + local curveType = self.curves[math_floor(i / 4)]; + if curveType == LINEAR then + local before = frames[i]; + rotate = frames[i + ROTATE]; + x = frames[i + X]; + y = frames[i + Y]; + local t = (time - before) / (frames[i + ENTRIES] - before); + rotate = rotate + (frames[i + ENTRIES + ROTATE] - rotate) * t; + x = x + (frames[i + ENTRIES + X] - x) * t; + y = y + (frames[i + ENTRIES + Y] - y) * t; + elseif curveType == STEPPED then + rotate = frames[i + ROTATE]; + x = frames[i + X]; + y = frames[i + Y]; + else + rotate = this.getBezierValue(time, i, ROTATE, curveType - BEZIER); + x = this.getBezierValue(time, i, X, curveType + BEZIER_SIZE - BEZIER); + y = this.getBezierValue(time, i, Y, curveType + BEZIER_SIZE * 2 - BEZIER); + end + + if blend == MixBlend.setup then + local data = constraint.data; + constraint.mixRotate = data.mixRotate + (rotate - data.mixRotate) * alpha; + constraint.mixX = data.mixX + (x - data.mixX) * alpha; + constraint.mixY = data.mixY + (y - data.mixY) * alpha; + else + constraint.mixRotate = constraint.mixRotate + (rotate - constraint.mixRotate) * alpha; + constraint.mixX = constraint.mixX + (x - constraint.mixX) * alpha; + constraint.mixY = constraint.mixY + (y - constraint.mixY) * alpha; + end + end + + return self +end + +return Animation diff --git a/spine-lua/spine-lua/AnimationState.lua b/spine-lua/spine-lua/AnimationState.lua index eabf6310e..b40574cf1 100644 --- a/spine-lua/spine-lua/AnimationState.lua +++ b/spine-lua/spine-lua/AnimationState.lua @@ -313,9 +313,9 @@ function AnimationState:updateMixingFrom (to, delta) from.trackLast = from.nextTrackLast -- Require mixTime > 0 to ensure the mixing from entry was applied at least once. - if (to.mixTime > 0 and to.mixTime >= to.mixDuration) then + if to.mixTime > 0 and to.mixTime >= to.mixDuration then -- Require totalAlpha == 0 to ensure mixing is complete, unless mixDuration == 0 (the transition is a single frame). - if (from.totalAlpha == 0 or to.mixDuration == 0) then + if from.totalAlpha == 0 or to.mixDuration == 0 then to.mixingFrom = from.mixingFrom if from.mixingFrom then from.mixingFrom.mixingTo = to end to.interruptAlpha = from.interruptAlpha @@ -365,7 +365,7 @@ function AnimationState:apply (skeleton) if timeline.type == Animation.TimelineType.attachment then self:applyAttachmentTimeline(timeline, skeleton, animationTime, blend, true) else - timeline:apply(skeleton, animationLast, animationTime, self.events, mix, blend, MixDirection._in) + timeline:apply(skeleton, animationLast, animationTime, self.events, mix, blend, MixDirection.mixIn) end end else @@ -383,7 +383,7 @@ function AnimationState:apply (skeleton) elseif timeline.type == Animation.TimelineType.attachment then self:applyAttachmentTimeline(timeline, skeleton, animationTime, timelineBlend, true) else - timeline:apply(skeleton, animationLast, animationTime, self.events, mix, timelineBlend, MixDirection._in) + timeline:apply(skeleton, animationLast, animationTime, self.events, mix, timelineBlend, MixDirection.mixIn) end end end @@ -444,7 +444,7 @@ function AnimationState:applyMixingFrom (to, skeleton, blend) if blend == MixBlend.add then for i,timeline in ipairs(timelines) do - timeline:apply(skeleton, animationLast, animationTime, events, alphaMix, blend, MixDirection.out) + timeline:apply(skeleton, animationLast, animationTime, events, alphaMix, blend, MixDirection.mixOut) end else local timelineMode = from.timelineMode @@ -457,7 +457,7 @@ function AnimationState:applyMixingFrom (to, skeleton, blend) for i,timeline in ipairs(timelines) do local skipSubsequent = false - local direction = MixDirection.out + local direction = MixDirection.mixOut local timelineBlend = MixBlend.setup local alpha = 0 if timelineMode[i] == SUBSEQUENT then @@ -486,8 +486,8 @@ function AnimationState:applyMixingFrom (to, skeleton, blend) elseif timeline.type == Animation.TimelineType.attachment then self:applyAttachmentTimeline(timeline, skeleton, animationTime, timelineBlend, attachments) else - if (drawOrder and timeline.type == Animation.TimelineType.drawOrder and timelineBlend == MixBlend.setup) then - direction = MixDirection._in + if drawOrder and timeline.type == Animation.TimelineType.drawOrder and timelineBlend == MixBlend.setup then + direction = MixDirection.mixIn end timeline:apply(skeleton, animationLast, animationTime, self.events, alpha, timelineBlend, direction) end @@ -495,7 +495,7 @@ function AnimationState:applyMixingFrom (to, skeleton, blend) end end - if (to.mixDuration > 0) then + if to.mixDuration > 0 then self:queueEvents(from, animationTime) end self.events = {} @@ -516,7 +516,7 @@ function AnimationState:applyAttachmentTimeline(timeline, skeleton, time, blend, end else local frameIndex = 0 - if (time >= frames[zlen(frames) - 1]) then -- Time is after last frame. + if time >= frames[zlen(frames) - 1] then -- Time is after last frame. frameIndex = zlen(frames) - 1 else frameIndex = Animation.binarySearch(frames, time, 1) - 1 @@ -529,7 +529,7 @@ function AnimationState:applyAttachmentTimeline(timeline, skeleton, time, blend, end function AnimationState:setAttachment(skeleton, slot, attachmentName, attachments) - if (attachmentName == nil) then + if attachmentName == nil then slot.attachment = nil else slot.attachment = skeleton:getAttachmentByIndex(slot.data.index, attachmentName) @@ -544,7 +544,7 @@ function AnimationState:applyRotateTimeline (timeline, skeleton, time, alpha, bl end if alpha == 1 then - timeline:apply(skeleton, 0, time, nil, 1, blend, MixDirection._in) + timeline:apply(skeleton, 0, time, nil, 1, blend, MixDirection.mixIn) return end @@ -730,7 +730,7 @@ function AnimationState:setAnimationByName (trackIndex, animationName, loop) end function AnimationState:setAnimation (trackIndex, animation, loop) - if not animation then error("animation cannot be null.") end + if not animation then error("animation cannot be null.", 2) end local interrupt = true local current = self:expandToIndex(trackIndex) local queue = self.queue @@ -756,12 +756,12 @@ end function AnimationState:addAnimationByName (trackIndex, animationName, loop, delay) local animation = self.data.skeletonData:findAnimation(animationName) - if not animation then error("Animation not found: " + animationName) end + if not animation then error("Animation not found: " + animationName, 2) end return self:addAnimation(trackIndex, animation, loop, delay) end function AnimationState:addAnimation (trackIndex, animation, loop, delay) - if not animation then error("animation cannot be null.") end + if not animation then error("animation cannot be null.", 2) end local last = self:expandToIndex(trackIndex) if last then @@ -911,7 +911,7 @@ function AnimationState:_animationsChanged () end repeat - if (entry.mixingTo == nil or entry.mixBlend ~= MixBlend.add) then + if entry.mixingTo == nil or entry.mixBlend ~= MixBlend.add then self:computeHold(entry) end entry = entry.mixingTo @@ -930,7 +930,7 @@ function AnimationState:computeHold(entry) local timelineHoldMix = entry.timelineHoldMix local propertyIDs = self.propertyIDs - if (to and to.holdPrevious) then + if to and to.holdPrevious then local i = 1 while i <= timelinesCount do local id = "" .. timelines[i]:getPropertyId() diff --git a/spine-lua/spine-lua/Atlas.lua b/spine-lua/spine-lua/Atlas.lua index ab22166a1..3a10a9d8f 100644 --- a/spine-lua/spine-lua/Atlas.lua +++ b/spine-lua/spine-lua/Atlas.lua @@ -43,13 +43,13 @@ function Atlas.parse(atlasPath, atlasBase) end if not atlasPath then - error("Error: " .. atlasPath .. ".atlas" .. " doesn't exist!") + error("Error: " .. atlasPath .. ".atlas" .. " doesn't exist!", 2) return nil end local atlasLines = spine.utils.readFile( atlasPath, atlasBase ) if not atlasLines then - error("Error: " .. atlasPath .. ".atlas" .. " unable to read!") + error("Error: " .. atlasPath .. ".atlas" .. " unable to read!", 2) return nil end diff --git a/spine-lua/spine-lua/AtlasAttachmentLoader.lua b/spine-lua/spine-lua/AtlasAttachmentLoader.lua index c787f08f6..56861fe90 100644 --- a/spine-lua/spine-lua/AtlasAttachmentLoader.lua +++ b/spine-lua/spine-lua/AtlasAttachmentLoader.lua @@ -51,7 +51,7 @@ end function AtlasAttachmentLoader:newRegionAttachment (skin, name, path) local region = self.atlas:findRegion(path) - if not region then error("Region not found in atlas: " .. path .. " (region attachment: " .. name .. ")") end + if not region then error("Region not found in atlas: " .. path .. " (region attachment: " .. name .. ")", 2) end region.renderObject = region local attachment = RegionAttachment.new(name) attachment:setRegion(region) @@ -61,7 +61,7 @@ end function AtlasAttachmentLoader:newMeshAttachment (skin, name, path) local region = self.atlas:findRegion(path) - if not region then error("Region not found in atlas: " .. path .. " (mesh attachment: " .. name .. ")") end + if not region then error("Region not found in atlas: " .. path .. " (mesh attachment: " .. name .. ")", 2) end region.renderObject = region local attachment = MeshAttachment.new(name) attachment.region = region diff --git a/spine-lua/spine-lua/IkConstraint.lua b/spine-lua/spine-lua/IkConstraint.lua index d799be5c1..29e0715fa 100644 --- a/spine-lua/spine-lua/IkConstraint.lua +++ b/spine-lua/spine-lua/IkConstraint.lua @@ -125,9 +125,9 @@ function IkConstraint:apply1 (bone, targetX, targetY, compress, stretch, uniform rotationIK = rotationIK + math_deg(math_atan2(ty, tx)) if bone.ascaleX < 0 then rotationIK = rotationIK + 180 end if rotationIK > 180 then - rotationIK = rotationIK - 360 - elseif (rotationIK < -180) then - rotationIK = rotationIK + 360 + rotationIK = rotationIK - 360 + elseif rotationIK < -180 then + rotationIK = rotationIK + 360 end local sx = bone.ascaleX local sy = bone.ascaleY @@ -262,7 +262,7 @@ function IkConstraint:apply2 (parent, child, targetX, targetY, bendDir, stretch, d = c1 * c1 - 4 * c2 * c if d >= 0 then local q = math_sqrt(d) - if (c1 < 0) then q = -q end + if c1 < 0 then q = -q end q = -(c1 + q) / 2 local r0 = q / c2 local r1 = c / q @@ -285,7 +285,7 @@ function IkConstraint:apply2 (parent, child, targetX, targetY, bendDir, stretch, local maxDist = maxX * maxX local maxY = 0 c = -a * l1 / (aa - bb) - if (c >= -1 and c <= 1) then + if c >= -1 and c <= 1 then c = math_acos(c) x = a * math_cos(c) + l1 y = b * math_sin(c) diff --git a/spine-lua/spine-lua/Interpolation.lua b/spine-lua/spine-lua/Interpolation.lua index 4650cb91e..ca935c643 100644 --- a/spine-lua/spine-lua/Interpolation.lua +++ b/spine-lua/spine-lua/Interpolation.lua @@ -36,7 +36,7 @@ function interpolation.apply (func, start, _end, a) end function interpolation.pow2(a) - if (a <= 0.5) then return math_pow(a * 2, 2) / 2 end + if a <= 0.5 then return math_pow(a * 2, 2) / 2 end return math_pow((a - 1) * 2, 2) / -2 + 1 end diff --git a/spine-lua/spine-lua/Skeleton.lua b/spine-lua/spine-lua/Skeleton.lua index cee3b0fa8..88be5675f 100644 --- a/spine-lua/spine-lua/Skeleton.lua +++ b/spine-lua/spine-lua/Skeleton.lua @@ -438,7 +438,7 @@ function Skeleton:setSkin (skinName) end function Skeleton:setSkinByReference(newSkin) - if (self.skin == newSkin) then return end + if self.skin == newSkin then return end if newSkin then if self.skin then newSkin:attachAll(self, self.skin) diff --git a/spine-lua/spine-lua/SkeletonBounds.lua b/spine-lua/spine-lua/SkeletonBounds.lua index 9e9ad41fe..a434537ad 100644 --- a/spine-lua/spine-lua/SkeletonBounds.lua +++ b/spine-lua/spine-lua/SkeletonBounds.lua @@ -59,7 +59,7 @@ function SkeletonBounds:update (skeleton, updateAabb) local slots = skeleton.slots for _,slot in ipairs(skeleton.slots) do - if (slot.bone.active) then + if slot.bone.active then local attachment = slot.attachment if attachment and attachment.type == AttachmentType.boundingbox then local boundingBox = attachment @@ -110,7 +110,7 @@ end function SkeletonBounds:aabbIntersectsSegment (x1, y1, x2, y2) local minX, minY, maxX, maxY = self.minX, self.minY, self.maxX, self.maxY if (x1 <= minX and x2 <= minX) or (y1 <= minY and y2 <= minY) or (x1 >= maxX and x2 >= maxX) or (y1 >= maxY and y2 >= maxY) then - return false + return false end local m = (y2 - y1) / (x2 - x1) local y = m * (minX - x1) + y1 diff --git a/spine-lua/spine-lua/SkeletonClipping.lua b/spine-lua/spine-lua/SkeletonClipping.lua index 92a5d9450..e0c3c32f2 100644 --- a/spine-lua/spine-lua/SkeletonClipping.lua +++ b/spine-lua/spine-lua/SkeletonClipping.lua @@ -128,9 +128,9 @@ function SkeletonClipping:clipTriangles(vertices, uvs, triangles, trianglesLengt while p <= polygonsCount do local s = #clippedVertices + 1 local clipOutput = {} - if (self:clip(x1, y1, x2, y2, x3, y3, polygons[p], clipOutput)) then + if self:clip(x1, y1, x2, y2, x3, y3, polygons[p], clipOutput) then local clipOutputLength = #clipOutput - if (clipOutputLength > 0) then + if clipOutputLength > 0 then local d0 = y2 - y3 local d1 = x3 - x2 local d2 = x1 - x3 @@ -295,7 +295,7 @@ function SkeletonClipping:clip(x1, y1, x2, y2, x3, y3, clippingArea, output) table_insert(output, output[1]) table_insert(output, output[2]) - if (i == clippingVerticesLast) then break end + if i == clippingVerticesLast then break end local temp = output output = input for i, _ in ipairs(output) do @@ -340,7 +340,7 @@ function SkeletonClipping:makeClockwise(polygon) area = area + p1x * p2y - p2x * p1y i = i + 2 end - if (area < 0) then return end + if area < 0 then return end i = 1 local lastX = verticesLength - 2 + 1 diff --git a/spine-lua/spine-lua/SkeletonJson.lua b/spine-lua/spine-lua/SkeletonJson.lua index 185ec16d7..63d815895 100644 --- a/spine-lua/spine-lua/SkeletonJson.lua +++ b/spine-lua/spine-lua/SkeletonJson.lua @@ -48,6 +48,9 @@ local TransformMode = require "spine-lua.TransformMode" local utils = require "spine-lua.utils" local Color = require "spine-lua.Color" +local math_max = math.max +local math_floor = math.floor + local SkeletonJson = {} function SkeletonJson.new (attachmentLoader) if not attachmentLoader then attachmentLoader = AttachmentLoader.new() end @@ -200,13 +203,13 @@ function SkeletonJson.new (attachmentLoader) for _,boneName in ipairs(constraintMap.bones) do local bone = skeletonData:findBone(boneName) - if not bone then error("Transform constraint bone not found: " .. boneName, 2) end + if not bone then error("Transform constraint bone not found: " .. boneName) end table_insert(data.bones, bone) end local targetName = constraintMap.target data.target = skeletonData:findBone(targetName) - if not data.target then error("Transform constraint target bone not found: " .. (targetName or "none"), 2) end + if not data.target then error("Transform constraint target bone not found: " .. (targetName or "none")) end data.local_ = getValue(constraintMap, "local", false) data.relative = getValue(constraintMap, "relative", false) @@ -237,13 +240,13 @@ function SkeletonJson.new (attachmentLoader) for _,boneName in ipairs(constraintMap.bones) do local bone = skeletonData:findBone(boneName) - if not bone then error("Path constraint bone not found: " .. boneName, 2) end + if not bone then error("Path constraint bone not found: " .. boneName) end table_insert(data.bones, bone) end local targetName = constraintMap.target data.target = skeletonData:findSlot(targetName) - if data.target == nil then error("Path target slot not found: " .. targetName, 2) end + if data.target == nil then error("Path target slot not found: " .. targetName) end data.positionMode = PathConstraintData.PositionMode[getValue(constraintMap, "positionMode", "percent"):lower()] data.spacingMode = PathConstraintData.SpacingMode[getValue(constraintMap, "spacingMode", "length"):lower()] @@ -269,7 +272,7 @@ function SkeletonJson.new (attachmentLoader) if skinMap["bones"] then for _, entry in ipairs(skinMap["bones"]) do local bone = skeletonData:findBone(entry) - if bone == nil then error("Skin bone not found: " .. entry, 2) end + if bone == nil then error("Skin bone not found: " .. entry) end table_insert(skin.bones, bone) end end @@ -277,7 +280,7 @@ function SkeletonJson.new (attachmentLoader) if skinMap["ik"] then for _, entry in ipairs(skinMap["ik"]) do local constraint = skeletonData:findIkConstraint(entry) - if constraint == nil then error("Skin IK constraint not found: " .. entry, 2) end + if constraint == nil then error("Skin IK constraint not found: " .. entry) end table_insert(skin.constraints, constraint) end end @@ -285,7 +288,7 @@ function SkeletonJson.new (attachmentLoader) if skinMap["transform"] then for _, entry in ipairs(skinMap["transform"]) do local constraint = skeletonData:findTransformConstraint(entry) - if constraint == nil then error("Skin transform constraint not found: " .. entry, 2) end + if constraint == nil then error("Skin transform constraint not found: " .. entry) end table_insert(skin.constraints, constraint) end end @@ -293,7 +296,7 @@ function SkeletonJson.new (attachmentLoader) if skinMap["path"] then for _, entry in ipairs(skinMap["path"]) do local constraint = skeletonData:findPathConstraint(entry) - if constraint == nil then error("Skin path constraint not found: " .. entry, 2) end + if constraint == nil then error("Skin path constraint not found: " .. entry) end table_insert(skin.constraints, constraint) end end @@ -915,7 +918,7 @@ function SkeletonJson.new (attachmentLoader) table_insert(timelines, readTimeline1(timelineMap, timeline, 0, timelineScale)) elseif timelineName == "spacing" then local timeline = Animation.PathConstraintSpacingTimeline.new(#timelineMap, #timelineMap, constraintIndex) - local timelineScale = 1; + local timelineScale = 1 if data.spacingMode == SpacingMode.Length or data.spacingMode == SpacingMode.Fixed then timelineScale = scale end table_insert(timelines, readTimeline1(timelineMap, timeline, 0, timelineScale)) elseif timelineName == "mix" then @@ -960,19 +963,19 @@ function SkeletonJson.new (attachmentLoader) if map.deform then for deformName, deformMap in pairs(map.deform) do local skin = skeletonData:findSkin(deformName) - if not skin then error("Skin not found: " .. deformName, 2) end + if not skin then error("Skin not found: " .. deformName) end for slotName,slotMap in pairs(deformMap) do local slotIndex = skeletonData:findSlot(slotName).index - if slotIndex == -1 then error("Slot not found: " .. slotMap.name, 2) end + if slotIndex == -1 then error("Slot not found: " .. slotMap.name) end for timelineName,timelineMap in pairs(slotMap) do local keyMap = timelineMap[1] if keyMap then local attachment = skin:getAttachment(slotIndex, timelineName) - if not attachment then error("Deform attachment not found: " .. timelineMap.name, 2) end + if not attachment then error("Deform attachment not found: " .. timelineMap.name) end local weighted = attachment.bones ~= nil local vertices = attachment.vertices local deformLength = #vertices - if weighted then deformLength = math.floor(deformLength / 3) * 2 end + if weighted then deformLength = math_floor(deformLength / 3) * 2 end local timeline = Animation.DeformTimeline.new(#timelineMap, #timelineMap, slotIndex, attachment) local bezier = 0 @@ -1104,7 +1107,7 @@ function SkeletonJson.new (attachmentLoader) local duration = 0 for _,timeline in ipairs(timelines) do - duration = math.max(duration, timeline:getDuration()) + duration = math_max(duration, timeline:getDuration()) end table_insert(skeletonData.animations, Animation.new(name, timelines, duration)) end diff --git a/spine-lua/spine-lua/Skin.lua b/spine-lua/spine-lua/Skin.lua index 3195cfa41..1e0da9297 100644 --- a/spine-lua/spine-lua/Skin.lua +++ b/spine-lua/spine-lua/Skin.lua @@ -187,7 +187,7 @@ function Skin:attachAll(skeleton, oldSkin) local slotAttachment = slot.attachment if slotAttachment then local dictionary = oldSkin.attachments[i] - if (dictionary) then + if dictionary then for key, value in pairs(dictionary) do local skinAttachment = value if slotAttachment == skinAttachment then diff --git a/spine-lua/spine-lua/Triangulator.lua b/spine-lua/spine-lua/Triangulator.lua index f5ac9ea8b..9c8de381e 100644 --- a/spine-lua/spine-lua/Triangulator.lua +++ b/spine-lua/spine-lua/Triangulator.lua @@ -111,7 +111,7 @@ function Triangulator:triangulate (verticesArray) end ii = (ii + 1) % vertexCount end - if (not goToHead) then + if not goToHead then breakLoop = true break end @@ -244,7 +244,7 @@ function Triangulator:decompose(verticesArray, triangles) n = #convexPolygons while i <= n do polygonIndices = convexPolygonsIndices[i] - if (#polygonIndices > 0) then + if #polygonIndices > 0 then local firstIndex = polygonIndices[1] local lastIndex = polygonIndices[#polygonIndices] @@ -265,7 +265,7 @@ function Triangulator:decompose(verticesArray, triangles) while ii <= n do if ii ~= i then local otherIndices = convexPolygonsIndices[ii] - if (#otherIndices == 3) then + if #otherIndices == 3 then local otherFirstIndex = otherIndices[1] local otherSecondIndex = otherIndices[2] local otherLastIndex = otherIndices[3] diff --git a/spine-lua/spine-lua/attachments/Attachment.lua b/spine-lua/spine-lua/attachments/Attachment.lua index 3f357ef47..2c8fc3eda 100644 --- a/spine-lua/spine-lua/attachments/Attachment.lua +++ b/spine-lua/spine-lua/attachments/Attachment.lua @@ -47,7 +47,7 @@ function Attachment.new (name, attachmentType) end function Attachment:copy () - error("Attachment copy not implemented.") + error("Attachment copy not implemented.", 2) end return Attachment diff --git a/spine-lua/spine-lua/attachments/VertexAttachment.lua b/spine-lua/spine-lua/attachments/VertexAttachment.lua index 75dd90da9..beda40fd1 100644 --- a/spine-lua/spine-lua/attachments/VertexAttachment.lua +++ b/spine-lua/spine-lua/attachments/VertexAttachment.lua @@ -45,6 +45,7 @@ setmetatable(VertexAttachment, { __index = Attachment }) function VertexAttachment.new (name, attachmentType) local self = Attachment.new(name, attachmentType) + self.vertexAttachment = true self.bones = nil self.vertices = nil self.worldVerticesLength = 0 diff --git a/spine-lua/spine-lua/utils.lua b/spine-lua/spine-lua/utils.lua index 300375380..4a00a6c3f 100644 --- a/spine-lua/spine-lua/utils.lua +++ b/spine-lua/spine-lua/utils.lua @@ -169,17 +169,17 @@ end function utils.randomTriangularWith(min, max, mode) local u = math.random() local d = max - min - if (u <= (mode - min) / d) then return min + math_sqrt(u * d * (mode - min)) end + if u <= (mode - min) / d then return min + math_sqrt(u * d * (mode - min)) end return max - math_sqrt((1 - u) * d * (max - mode)) end function utils.testBit(value, bit) - if (value == nil) then return 0 end + if value == nil then return 0 end return value % (2 * bit) >= bit end function utils.setBit(value, bit) - if (value == nil) then return 0 end + if value == nil then return 0 end if value % (2 * bit) >= bit then return value end @@ -187,7 +187,7 @@ function utils.setBit(value, bit) end function utils.clearBit(value, bit) - if (value == nil) then return 0 end + if value == nil then return 0 end if value % (2 * bit) >= bit then return value - bit end diff --git a/spine-lua/spine-lua/vertexeffects/SwirlEffect.lua b/spine-lua/spine-lua/vertexeffects/SwirlEffect.lua index 05ff640e6..25e26acb6 100644 --- a/spine-lua/spine-lua/vertexeffects/SwirlEffect.lua +++ b/spine-lua/spine-lua/vertexeffects/SwirlEffect.lua @@ -69,7 +69,7 @@ function SwirlEffect:transform (vertex) local x = vertex.x - self.worldX local y = vertex.y - self.worldY local dist = math_sqrt(x * x + y * y) - if (dist < self.radius) then + if dist < self.radius then local theta = interpolation.apply(self.interpolation, 0, self.angleRad, (self.radius - dist) / self.radius) local cos = math_cos(theta) local sin = math_sin(theta)