123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704 |
- -------------------------------------------------------------------------------
- -- Spine Runtimes Software License
- -- Version 2.1
- --
- -- Copyright (c) 2013, Esoteric Software
- -- All rights reserved.
- --
- -- You are granted a perpetual, non-exclusive, non-sublicensable and
- -- non-transferable license to install, execute and perform the Spine Runtimes
- -- Software (the "Software") solely for internal use. Without the written
- -- permission of Esoteric Software (typically granted by licensing Spine), you
- -- may not (a) modify, translate, adapt or otherwise create derivative works,
- -- improvements of the Software or develop new applications using the Software
- -- or (b) remove, delete, alter or obscure any trademarks or any copyright,
- -- trademark, patent or other intellectual property or proprietary rights
- -- notices on or in the Software, including any copy thereof. Redistributions
- -- in binary or source form must include this license and terms.
- --
- -- THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "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 SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- -- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- -- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- -- OR BUSINESS INTERRUPTION) 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 THIS SOFTWARE, EVEN IF
- -- ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- -------------------------------------------------------------------------------
- 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,
- duration = duration
- }
- function self:apply (skeleton, lastTime, time, loop, events)
- if not skeleton then error("skeleton cannot be nil.", 2) end
- if loop and duration > 0 then
- time = time % self.duration
- lastTime = lastTime % self.duration
- end
- for i,timeline in ipairs(self.timelines) do
- timeline:apply(skeleton, lastTime, time, events, 1)
- end
- end
- function self:mix (skeleton, lastTime, time, loop, events, alpha)
- if not skeleton then error("skeleton cannot be nil.", 2) end
- if loop and duration > 0 then
- time = time % self.duration
- lastTime = lastTime % self.duration
- end
- for i,timeline in ipairs(self.timelines) do
- timeline:apply(skeleton, lastTime, time, events, alpha)
- end
- end
- return self
- end
- local function binarySearch (values, target, step)
- local low = 0
- local high = math.floor((#values + 1) / 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
- local function binarySearch1 (values, target)
- local low = 0
- local high = math.floor(#values - 1)
- 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)
- for i = 0, #values, step do
- if (values[i] > target) then return i end
- end
- return -1
- end
- Animation.CurveTimeline = {}
- function Animation.CurveTimeline.new ()
- local LINEAR = 0
- local STEPPED = -1
- local BEZIER_SEGMENTS = 10
- local self = {
- curves = {}
- }
- function self:setLinear (frameIndex)
- self.curves[frameIndex * 6] = LINEAR
- end
- function self:setStepped (frameIndex)
- self.curves[frameIndex * 6] = STEPPED
- end
- function self:setCurve (frameIndex, cx1, cy1, cx2, cy2)
- local subdiv_step = 1 / BEZIER_SEGMENTS
- local subdiv_step2 = subdiv_step * subdiv_step
- local subdiv_step3 = subdiv_step2 * subdiv_step
- local pre1 = 3 * subdiv_step
- local pre2 = 3 * subdiv_step2
- local pre4 = 6 * subdiv_step2
- local pre5 = 6 * subdiv_step3
- local tmp1x = -cx1 * 2 + cx2
- local tmp1y = -cy1 * 2 + cy2
- local tmp2x = (cx1 - cx2) * 3 + 1
- local tmp2y = (cy1 - cy2) * 3 + 1
- local i = frameIndex * 6
- local curves = self.curves
- curves[i] = cx1 * pre1 + tmp1x * pre2 + tmp2x * subdiv_step3
- curves[i + 1] = cy1 * pre1 + tmp1y * pre2 + tmp2y * subdiv_step3
- curves[i + 2] = tmp1x * pre4 + tmp2x * pre5
- curves[i + 3] = tmp1y * pre4 + tmp2y * pre5
- curves[i + 4] = tmp2x * pre5
- curves[i + 5] = tmp2y * pre5
- end
- function self:getCurvePercent (frameIndex, percent)
- local curveIndex = frameIndex * 6
- local curves = self.curves
- local dfx = curves[curveIndex]
- if not dfx then return percent end -- linear
- if dfx == STEPPED then return 0 end
- local dfy = curves[curveIndex + 1]
- local ddfx = curves[curveIndex + 2]
- local ddfy = curves[curveIndex + 3]
- local dddfx = curves[curveIndex + 4]
- local dddfy = curves[curveIndex + 5]
- local x = dfx
- local y = dfy
- local i = BEZIER_SEGMENTS - 2
- while true do
- if x >= percent then
- local lastX = x - dfx
- local lastY = y - dfy
- return lastY + (y - lastY) * (percent - lastX) / (x - lastX)
- end
- if i == 0 then break end
- i = i - 1
- dfx = dfx + ddfx
- dfy = dfy + ddfy
- ddfx = ddfx + dddfx
- ddfy = ddfy + dddfy
- x = x + dfx
- y = y + dfy
- end
- return y + (1 - y) * (percent - x) / (1 - x) -- Last point is 1,1.
- end
- return self
- end
- Animation.RotateTimeline = {}
- function Animation.RotateTimeline.new ()
- local LAST_FRAME_TIME = -2
- local FRAME_VALUE = 1
- local self = Animation.CurveTimeline.new()
- self.frames = {}
- self.boneIndex = -1
- function self:getDuration ()
- return self.frames[#self.frames - 1]
- end
- function self:getFrameCount ()
- return (#self.frames + 1) / 2
- end
- function self:setFrame (frameIndex, time, value)
- frameIndex = frameIndex * 2
- self.frames[frameIndex] = time
- self.frames[frameIndex + 1] = value
- end
- function self:apply (skeleton, lastTime, time, firedEvents, alpha)
- local frames = self.frames
- if time < frames[0] then return end -- Time is before first frame.
- local bone = skeleton.bones[self.boneIndex]
- if time >= frames[#frames - 1] then -- Time is after last frame.
- local amount = bone.data.rotation + frames[#frames] - bone.rotation
- while amount > 180 do
- amount = amount - 360
- end
- while amount < -180 do
- amount = amount + 360
- end
- bone.rotation = bone.rotation + amount * alpha
- return
- end
- -- Interpolate between the last frame and the current frame.
- local frameIndex = binarySearch(frames, time, 2)
- local lastFrameValue = frames[frameIndex - 1]
- local frameTime = frames[frameIndex]
- local percent = 1 - (time - frameTime) / (frames[frameIndex + LAST_FRAME_TIME] - frameTime)
- if percent < 0 then percent = 0 elseif percent > 1 then percent = 1 end
- percent = self:getCurvePercent(frameIndex / 2 - 1, percent)
- local amount = frames[frameIndex + FRAME_VALUE] - lastFrameValue
- while amount > 180 do
- amount = amount - 360
- end
- while amount < -180 do
- amount = amount + 360
- end
- amount = bone.data.rotation + (lastFrameValue + amount * percent) - bone.rotation
- while amount > 180 do
- amount = amount - 360
- end
- while amount < -180 do
- amount = amount + 360
- end
- bone.rotation = bone.rotation + amount * alpha
- end
- return self
- end
- Animation.TranslateTimeline = {}
- function Animation.TranslateTimeline.new ()
- local LAST_FRAME_TIME = -3
- local FRAME_X = 1
- local FRAME_Y = 2
- local self = Animation.CurveTimeline.new()
- self.frames = {}
- self.boneIndex = -1
- function self:getDuration ()
- return self.frames[#self.frames - 2]
- end
- function self:getFrameCount ()
- return (#self.frames + 1) / 3
- end
- function self:setFrame (frameIndex, time, x, y)
- frameIndex = frameIndex * 3
- self.frames[frameIndex] = time
- self.frames[frameIndex + 1] = x
- self.frames[frameIndex + 2] = y
- end
- function self:apply (skeleton, lastTime, time, firedEvents, alpha)
- local frames = self.frames
- if time < frames[0] then return end -- Time is before first frame.
- local bone = skeleton.bones[self.boneIndex]
-
- if time >= frames[#frames - 2] then -- Time is after last frame.
- bone.x = bone.x + (bone.data.x + frames[#frames - 1] - bone.x) * alpha
- bone.y = bone.y + (bone.data.y + frames[#frames] - bone.y) * alpha
- return
- end
- -- Interpolate between the last frame and the current frame.
- local frameIndex = binarySearch(frames, time, 3)
- local lastFrameX = frames[frameIndex - 2]
- local lastFrameY = frames[frameIndex - 1]
- local frameTime = frames[frameIndex]
- local percent = 1 - (time - frameTime) / (frames[frameIndex + LAST_FRAME_TIME] - frameTime)
- if percent < 0 then percent = 0 elseif percent > 1 then percent = 1 end
- percent = self:getCurvePercent(frameIndex / 3 - 1, percent)
- bone.x = bone.x + (bone.data.x + lastFrameX + (frames[frameIndex + FRAME_X] - lastFrameX) * percent - bone.x) * alpha
- bone.y = bone.y + (bone.data.y + lastFrameY + (frames[frameIndex + FRAME_Y] - lastFrameY) * percent - bone.y) * alpha
- end
- return self
- end
- Animation.ScaleTimeline = {}
- function Animation.ScaleTimeline.new ()
- local LAST_FRAME_TIME = -3
- local FRAME_X = 1
- local FRAME_Y = 2
- local self = Animation.TranslateTimeline.new()
- function self:apply (skeleton, lastTime, time, firedEvents, alpha)
- local frames = self.frames
- if time < frames[0] then return end -- Time is before first frame.
- local bone = skeleton.bones[self.boneIndex]
- if time >= frames[#frames - 2] then -- Time is after last frame.
- bone.scaleX = bone.scaleX + (bone.data.scaleX * frames[#frames - 1] - bone.scaleX) * alpha
- bone.scaleY = bone.scaleY + (bone.data.scaleY * frames[#frames] - bone.scaleY) * alpha
- return
- end
- -- Interpolate between the last frame and the current frame.
- local frameIndex = binarySearch(frames, time, 3)
- local lastFrameX = frames[frameIndex - 2]
- local lastFrameY = frames[frameIndex - 1]
- local frameTime = frames[frameIndex]
- local percent = 1 - (time - frameTime) / (frames[frameIndex + LAST_FRAME_TIME] - frameTime)
- if percent < 0 then percent = 0 elseif percent > 1 then percent = 1 end
- percent = self:getCurvePercent(frameIndex / 3 - 1, percent)
- bone.scaleX = bone.scaleX + (bone.data.scaleX * (lastFrameX + (frames[frameIndex + FRAME_X] - lastFrameX) * percent) - bone.scaleX) * alpha
- bone.scaleY = bone.scaleY + (bone.data.scaleY * (lastFrameY + (frames[frameIndex + FRAME_Y] - lastFrameY) * percent) - bone.scaleY) * alpha
- end
- return self
- end
- Animation.ColorTimeline = {}
- function Animation.ColorTimeline.new ()
- local LAST_FRAME_TIME = -5
- local FRAME_R = 1
- local FRAME_G = 2
- local FRAME_B = 3
- local FRAME_A = 4
- local self = Animation.CurveTimeline.new()
- self.frames = {}
- self.slotIndex = -1
- function self:getDuration ()
- return self.frames[#self.frames - 4]
- end
- function self:getFrameCount ()
- return (#self.frames + 1) / 5
- end
- function self:setFrame (frameIndex, time, r, g, b, a)
- frameIndex = frameIndex * 5
- self.frames[frameIndex] = time
- self.frames[frameIndex + 1] = r
- self.frames[frameIndex + 2] = g
- self.frames[frameIndex + 3] = b
- self.frames[frameIndex + 4] = a
- end
- function self:apply (skeleton, lastTime, time, firedEvents, alpha)
- local frames = self.frames
- if time < frames[0] then return end -- Time is before first frame.
- local r, g, b, a
- if time >= frames[#frames - 4] then -- Time is after last frame.
- r = frames[#frames - 3]
- g = frames[#frames - 2]
- b = frames[#frames - 1]
- a = frames[#frames]
- else
- -- Interpolate between the last frame and the current frame.
- local frameIndex = binarySearch(frames, time, 5)
- local lastFrameR = frames[frameIndex - 4]
- local lastFrameG = frames[frameIndex - 3]
- local lastFrameB = frames[frameIndex - 2]
- local lastFrameA = frames[frameIndex - 1]
- local frameTime = frames[frameIndex]
- local percent = 1 - (time - frameTime) / (frames[frameIndex + LAST_FRAME_TIME] - frameTime)
- if percent < 0 then percent = 0 elseif percent > 255 then percent = 255 end
- percent = self:getCurvePercent(frameIndex / 5 - 1, percent)
- r = lastFrameR + (frames[frameIndex + FRAME_R] - lastFrameR) * percent
- g = lastFrameG + (frames[frameIndex + FRAME_G] - lastFrameG) * percent
- b = lastFrameB + (frames[frameIndex + FRAME_B] - lastFrameB) * percent
- a = lastFrameA + (frames[frameIndex + FRAME_A] - lastFrameA) * percent
- end
- local slot = skeleton.slots[self.slotIndex]
- if alpha < 1 then
- slot:setColor(slot.r + (r - slot.r) * alpha, slot.g + (g - slot.g) * alpha, slot.b + (b - slot.b) * alpha, slot.a + (a - slot.a) * alpha)
- else
- slot:setColor(r, g, b, a)
- end
- end
- return self
- end
- Animation.AttachmentTimeline = {}
- function Animation.AttachmentTimeline.new ()
- local self = {
- frames = {},
- attachmentNames = {},
- slotName = nil
- }
- function self:getDuration ()
- return self.frames[#self.frames]
- end
- function self:getFrameCount ()
- return #self.frames + 1
- end
- function self:setFrame (frameIndex, time, attachmentName)
- self.frames[frameIndex] = time
- self.attachmentNames[frameIndex] = attachmentName
- end
- function self:apply (skeleton, lastTime, time, firedEvents, alpha)
- local frames = self.frames
- if time < frames[0] then return end -- Time is before first frame.
- local frameIndex
- if time >= frames[#frames] then -- Time is after last frame.
- frameIndex = #frames
- else
- frameIndex = binarySearch1(frames, time) - 1
- end
- local attachmentName = self.attachmentNames[frameIndex]
- local slot = skeleton.slotsByName[self.slotName]
- if attachmentName then
- if not slot.attachment then
- slot:setAttachment(skeleton:getAttachment(self.slotName, attachmentName))
- elseif slot.attachment.name ~= attachmentName then
- slot:setAttachment(skeleton:getAttachment(self.slotName, attachmentName))
- end
- else
- slot:setAttachment(nil)
- end
- end
- return self
- end
- Animation.EventTimeline = {}
- function Animation.EventTimeline.new ()
- local self = {
- frames = {},
- events = {}
- }
- function self:getDuration ()
- return self.frames[#self.frames]
- end
- function self:getFrameCount ()
- return #self.frames + 1
- end
- function self:setFrame (frameIndex, time, event)
- self.frames[frameIndex] = time
- self.events[frameIndex] = event
- end
- -- Fires events for frames > lastTime and <= time.
- function self:apply (skeleton, lastTime, time, firedEvents, alpha)
- if not firedEvents then return end
- local frames = self.frames
- local frameCount = #frames + 1
- if lastTime > time then -- Fire events after last time for looped animations.
- self:apply(skeleton, lastTime, 999999, firedEvents, alpha)
- 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 frameIndex
- if lastTime < frames[0] then
- frameIndex = 0
- else
- frameIndex = binarySearch1(frames, lastTime)
- local frame = frames[frameIndex]
- while frameIndex > 0 do -- Fire multiple events with the same frame.
- if frames[frameIndex - 1] ~= frame then break end
- frameIndex = frameIndex - 1
- end
- end
- local events = self.events
- while frameIndex < frameCount and time >= frames[frameIndex] do
- table.insert(firedEvents, events[frameIndex])
- frameIndex = frameIndex + 1
- end
- end
- return self
- end
- Animation.DrawOrderTimeline = {}
- function Animation.DrawOrderTimeline.new ()
- local self = {
- frames = {},
- drawOrders = {}
- }
- function self:getDuration ()
- return self.frames[#self.frames]
- end
- function self:getFrameCount ()
- return #self.frames + 1
- end
- function self:setFrame (frameIndex, time, drawOrder)
- self.frames[frameIndex] = time
- self.drawOrders[frameIndex] = drawOrder
- end
- function self:apply (skeleton, lastTime, time, firedEvents, alpha)
- local frames = self.frames
- if time < frames[0] then return end -- Time is before first frame.
- local frameIndex
- if time >= frames[#frames] then -- Time is after last frame.
- frameIndex = #frames
- else
- frameIndex = binarySearch1(frames, time) - 1
- end
- local drawOrder = skeleton.drawOrder
- local slots = skeleton.slots
- local drawOrderToSetupIndex = self.drawOrders[frameIndex]
- 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.FfdTimeline = {}
- function Animation.FfdTimeline.new ()
- local self = Animation.CurveTimeline.new()
- self.frames = {}
- self.frameVertices = {}
- self.slotIndex = -1
- function self:getDuration ()
- return self.frames[#self.frames]
- end
- function self:getFrameCount ()
- return #self.frames + 1
- end
- function self:setFrame (frameIndex, time, vertices)
- self.frames[frameIndex] = time
- self.frameVertices[frameIndex] = vertices
- end
- function self:apply (skeleton, lastTime, time, firedEvents, alpha)
- local slot = skeleton.slots[self.slotIndex]
- if slot.attachment ~= attachment then return end
- local frames = self.frames
- if time < frames[0] then -- Time is before first frame.
- slot.attachmentVerticesCount = 0
- return
- end
-
- local frameVertices = self.frameVertices
- local vertexCount = #frameVertices[0]
- local vertices = slot.attachmentVertices
- if #vertices < vertexCount then
- vertices = {}
- vertices[vertexCount] = 0
- slot.attachmentVertices = vertices
- elseif #vertices < vertexCount then
- alpha = 1 -- Don't mix from uninitialized slot vertices.
- end
- slot.attachmentVerticesCount = vertexCount
- if time >= frames[#frames] then -- Time is after last frame.
- local lastVertices = frameVertices[#frames.Length]
- if alpha < 1 then
- for i = 0, vertexCount do
- local vertex = vertices[i]
- vertices[i] = vertex + (lastVertices[i] - vertex) * alpha
- end
- else
- for i = 0, vertexCount do
- vertices[i] = lastVertices[i]
- end
- end
- return
- end
- -- Interpolate between the previous frame and the current frame.
- local frameIndex = binarySearch1(frames, time)
- local frameTime = frames[frameIndex]
- local percent = 1 - (time - frameTime) / (frames[frameIndex - 1] - frameTime)
- if percent < 0 then percent = 0 elseif percent > 1 then percent = 1 end
- percent = self:getCurvePercent(frameIndex - 1, percent)
- local prevVertices = frameVertices[frameIndex - 1]
- local nextVertices = frameVertices[frameIndex]
- if alpha < 1 then
- for i = 0, vertexCount do
- local prev = prevVertices[i]
- local vertices = vertices[i]
- vertices[i] = vertices + (prev + (nextVertices[i] - prev) * percent - vertices) * alpha
- end
- else
- for i = 0, vertexCount do
- local prev = prevVertices[i]
- vertices[i] = prev + (nextVertices[i] - prev) * percent
- end
- end
- end
- return self
- end
- Animation.FlipXTimeline = {}
- function Animation.FlipXTimeline.new ()
- local self = {
- frames = {}, -- time, flip, ...
- boneIndex = -1
- }
- function self:getDuration ()
- return self.frames[#self.frames - 1]
- end
- function self:getFrameCount ()
- return (#self.frames + 1) / 2
- end
- function self:setFrame (frameIndex, time, flip)
- frameIndex = frameIndex * 2
- self.frames[frameIndex] = time
- self.frames[frameIndex + 1] = flip
- end
- function self:apply (skeleton, lastTime, time, firedEvents, alpha)
- local frames = self.frames
- if time < frames[0] then
- if lastTime > time then self:apply(skeleton, lastTime, 999999, null, 0) end
- return
- elseif lastTime > time then
- lastTime = -1
- end
- local frameIndex
- if time >= frames[#frames - 1] then
- frameIndex = #frames - 1
- else
- frameIndex = binarySearch(frames, time, 2) - 2
- end
- if frames[frameIndex] < lastTime then return end
- self:setFlip(skeleton.bones[self.boneIndex], frames[frameIndex + 1])
- end
-
- function self:setFlip (bone, flip)
- bone.flipX = flip
- end
- return self
- end
- Animation.FlipYTimeline = {}
- function Animation.FlipYTimeline.new ()
- local self = Animation.FlipXTimeline.new()
- function self:setFlip (bone, flip)
- bone.flipY = flip
- end
- return self
- end
- return Animation
|