|
@@ -33,6 +33,9 @@ local table_insert = table.insert
|
|
local utils = require "spine-lua.utils"
|
|
local utils = require "spine-lua.utils"
|
|
local Animation = require "spine-lua.Animation"
|
|
local Animation = require "spine-lua.Animation"
|
|
local AnimationStateData = require "spine-lua.AnimationStateData"
|
|
local AnimationStateData = require "spine-lua.AnimationStateData"
|
|
|
|
+local math_min = math.min
|
|
|
|
+local math_abs = math.abs
|
|
|
|
+local math_signum = utils.signum
|
|
|
|
|
|
local EventType = {
|
|
local EventType = {
|
|
start = 0,
|
|
start = 0,
|
|
@@ -142,6 +145,36 @@ function EventQueue:clear ()
|
|
end
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
+local TrackEntry = {}
|
|
|
|
+TrackEntry.__index = TrackEntry
|
|
|
|
+
|
|
|
|
+function TrackEntry.new ()
|
|
|
|
+ local self = {
|
|
|
|
+ animation = nil,
|
|
|
|
+ next = nil, mixingFrom = nil,
|
|
|
|
+ onStart = nil, onInterrupt = nil, onEnd = nil, onDispose = nil, onComplete = nil, onEvent = nil,
|
|
|
|
+ trackIndex = 0,
|
|
|
|
+ loop = false,
|
|
|
|
+ eventThreshold = 0, attachmentThreshold = 0, drawOrderThreshold = 0,
|
|
|
|
+ animationStart = 0, animationEnd = 0, animationLast = 0, nextAnimationLast = 0,
|
|
|
|
+ delay = 0, trackTime = 0, trackLast = 0, nextTrackLast = 0, trackEnd = 0, timeScale = 0,
|
|
|
|
+ alpha = 0, mixTime = 0, mixDuration = 0, mixAlpha = 0,
|
|
|
|
+ timelinesFirst = {},
|
|
|
|
+ timelinesRotation = {}
|
|
|
|
+ }
|
|
|
|
+ setmetatable(self, TrackEntry)
|
|
|
|
+ return self
|
|
|
|
+end
|
|
|
|
+
|
|
|
|
+function TrackEntry:getAnimationTime ()
|
|
|
|
+ if loop then
|
|
|
|
+ local duration = animationEnd - animationStart
|
|
|
|
+ if duration == 0 then return animationStart end
|
|
|
|
+ return (trackTime % duration) + animationStart
|
|
|
|
+ end
|
|
|
|
+ return math_min(trackTime + animationStart, animationEnd)
|
|
|
|
+end
|
|
|
|
+
|
|
local AnimationState = {}
|
|
local AnimationState = {}
|
|
AnimationState.__index = AnimationState
|
|
AnimationState.__index = AnimationState
|
|
|
|
|
|
@@ -163,4 +196,181 @@ function AnimationState.new (data)
|
|
return self
|
|
return self
|
|
end
|
|
end
|
|
|
|
|
|
|
|
+AnimationState.TrackEntry = TrackEntry
|
|
|
|
+
|
|
|
|
+function AnimationState:update (delta)
|
|
|
|
+ delta = delta * self.timeScale
|
|
|
|
+ local tracks = self.tracks
|
|
|
|
+ local queue = self.queue
|
|
|
|
+ for i,current in pairs(tracks) do
|
|
|
|
+ if current then
|
|
|
|
+ current.animationLast = current.nextAnimationLast
|
|
|
|
+ current.trackLast = current.nextTrackLast
|
|
|
|
+
|
|
|
|
+ local currentDelta = delta * current.timeScale
|
|
|
|
+
|
|
|
|
+ local skip = false
|
|
|
|
+ if current.delay > 0 then
|
|
|
|
+ current.delay = current.delay - currentDelta
|
|
|
|
+ if current.delay <= 0 then
|
|
|
|
+ skip = true
|
|
|
|
+ currentDelta = -current.delay
|
|
|
|
+ current.delay = 0
|
|
|
|
+ end
|
|
|
|
+ end
|
|
|
|
+
|
|
|
|
+ if not skip then
|
|
|
|
+ local next = current.next
|
|
|
|
+ if next then
|
|
|
|
+ -- When the next entry's delay is passed, change to the next entry, preserving leftover time.
|
|
|
|
+ local nextTime = current.trackLast - next.delay
|
|
|
|
+ if nextTime >= 0 then
|
|
|
|
+ next.delay = 0
|
|
|
|
+ next.trackTime = nextTime + delta * next.timeScale
|
|
|
|
+ current.trackTime = current.trackTime + currentDelta
|
|
|
|
+ self:setCurrent(i, next)
|
|
|
|
+ while next.mixingFrom do
|
|
|
|
+ next.mixTime = next.mixTime + currentDelta
|
|
|
|
+ next = next.mixingFrom
|
|
|
|
+ end
|
|
|
|
+ skip = true
|
|
|
|
+ end
|
|
|
|
+ if not skip then
|
|
|
|
+ self:updateMixingFrom(current, delta, true);
|
|
|
|
+ end
|
|
|
|
+ else
|
|
|
|
+ self:updateMixingFrom(current, delta, true)
|
|
|
|
+ -- Clear the track when there is no next entry, the track end time is reached, and there is no mixingFrom.
|
|
|
|
+ if current.trackLast >= current.trackEnd and current.mixingFrom == nil then
|
|
|
|
+ tracks[i] = nil
|
|
|
|
+ queue:_end(current)
|
|
|
|
+ self:disposeNext(current)
|
|
|
|
+ skip = true
|
|
|
|
+ end
|
|
|
|
+ end
|
|
|
|
+
|
|
|
|
+ if not skip then current.trackTime = current.trackTime + currentDelta end
|
|
|
|
+ end
|
|
|
|
+ end
|
|
|
|
+ end
|
|
|
|
+
|
|
|
|
+ queue:drain()
|
|
|
|
+end
|
|
|
|
+
|
|
|
|
+function AnimationState:updateMixingFrom (entry, delta, canEnd)
|
|
|
|
+ local from = entry.mixingFrom
|
|
|
|
+ if from == nil then return end
|
|
|
|
+
|
|
|
|
+ local queue = self.queue
|
|
|
|
+ if canEnd and entry.mixTime >= entry.mixDuration and entry.mixTime > 0 then
|
|
|
|
+ queue:_end(from)
|
|
|
|
+ local newFrom = from.mixingFrom
|
|
|
|
+ entry.mixingFrom = newFrom
|
|
|
|
+ if newFrom == nil then return end
|
|
|
|
+ entry.mixTime = from.mixTime;
|
|
|
|
+ entry.mixDuration = from.mixDuration;
|
|
|
|
+ from = newFrom;
|
|
|
|
+ end
|
|
|
|
+
|
|
|
|
+ from.animationLast = from.nextAnimationLast
|
|
|
|
+ from.trackLast = from.nextTrackLast
|
|
|
|
+ local mixingFromDelta = delta * from.timeScale
|
|
|
|
+ from.trackTime = from.trackTime + mixingFromDelta;
|
|
|
|
+ entry.mixTime = entry.mixtime + mixingFromDelta;
|
|
|
|
+
|
|
|
|
+ self:updateMixingFrom(from, delta, canEnd and from.alpha == 1)
|
|
|
|
+end
|
|
|
|
+
|
|
|
|
+function AnimationState:apply (skeleton)
|
|
|
|
+ if skeleton == nil then error("skeleton cannot be null.", 2) end
|
|
|
|
+ if self.animationsChanged then self:_animationsChanged() end
|
|
|
|
+
|
|
|
|
+ local events = self.events
|
|
|
|
+ local tracks = self.tracks
|
|
|
|
+ local queue = self.queue
|
|
|
|
+
|
|
|
|
+ for i,current in pairs(tracks) do
|
|
|
|
+ if not (current == nil or current.delay > 0) then
|
|
|
|
+ -- Apply mixing from entries first.
|
|
|
|
+ local mix = current.alpha
|
|
|
|
+ if current.mixingFrom then mix = mix * applyMixingFrom(current, skeleton) end
|
|
|
|
+
|
|
|
|
+ -- Apply current entry.
|
|
|
|
+ local animationLast = current.animationLast
|
|
|
|
+ local animationTime = current:getAnimationTime()
|
|
|
|
+ local timelines = current.animation.timelines
|
|
|
|
+ if mix == 1 then
|
|
|
|
+ for i,timeline in ipairs(timelines) do
|
|
|
|
+ timeline:apply(skeleton, animationLast, animationTime, events, 1, true, false)
|
|
|
|
+ end
|
|
|
|
+ else
|
|
|
|
+ local firstFrame = #current.timelinesRotation == 0
|
|
|
|
+ local timelinesRotation = current.timelinesRotation;
|
|
|
|
+ local timelinesFirst = current.timelinesFirst
|
|
|
|
+ for i,timeline in ipairs(timelines) do
|
|
|
|
+ if timeline.type == Animation.TimelineType.rotate then
|
|
|
|
+ self:applyRotateTimeline(timeline, skeleton, animationTime, mix, timelinesFirst[ii], timelinesRotation, ii * 2,
|
|
|
|
+ firstFrame) -- FIXME passing ii * 2, indexing correct?
|
|
|
|
+ else
|
|
|
|
+ timeline:apply(skeleton, animationLast, animationTime, events, mix, timelinesFirst[ii], false)
|
|
|
|
+ end
|
|
|
|
+ end
|
|
|
|
+ end
|
|
|
|
+ self:queueEvents(current, animationTime)
|
|
|
|
+ current.nextAnimationLast = animationTime
|
|
|
|
+ current.nextTrackLast = current.trackTime
|
|
|
|
+ end
|
|
|
|
+ end
|
|
|
|
+
|
|
|
|
+ queue:drain()
|
|
|
|
+end
|
|
|
|
+
|
|
|
|
+function AnimationState:applyMixingFrom (entry, skeleton)
|
|
|
|
+ local from = entry.mixingFrom
|
|
|
|
+ if from.mixingFrom then self:applyMixingFrom(from, skeleton) end
|
|
|
|
+
|
|
|
|
+ local mix = 0
|
|
|
|
+ if entry.mixDuration == 0 then -- Single frame mix to undo mixingFrom changes.
|
|
|
|
+ mix = 1
|
|
|
|
+ else
|
|
|
|
+ mix = entry.mixTime / entry.mixDuration
|
|
|
|
+ if mix > 1 then mix = 1 end
|
|
|
|
+ end
|
|
|
|
+
|
|
|
|
+ local events = nil
|
|
|
|
+ if mix < from.eventThreshold then events = self.events end
|
|
|
|
+ local attachments = mix < from.attachmentThreshold
|
|
|
|
+ local drawOrder = mix < from.drawOrderThreshold
|
|
|
|
+ local animationLast = from.animationLast
|
|
|
|
+ local animationTime = from:getAnimationTime()
|
|
|
|
+ local timelines = from.animation.timelines
|
|
|
|
+ local timelinesFirst = from.timelinesFirst;
|
|
|
|
+ local alpha = from.alpha * entry.mixAlpha * (1 - mix)
|
|
|
|
+
|
|
|
|
+ local firstFrame = #from.timelinesRotation.size == 0
|
|
|
|
+ local timelinesRotation = from.timelinesRotation
|
|
|
|
+
|
|
|
|
+ local skip = false
|
|
|
|
+ for i,timeline in ipairs(timelines) do
|
|
|
|
+ local setupPose = timelinesFirst[i]
|
|
|
|
+ if timeline.type == Animation.TimelineType.rotate then
|
|
|
|
+ self:applyRotateTimeline(timeline, skeleton, animationTime, alpha, setupPose, timelinesRotation, i * 2, firstFrame) -- FIXME passing i * 2, correct indexing?
|
|
|
|
+ else
|
|
|
|
+ if not setupPose then
|
|
|
|
+ if not attachments and timeline.type == Animation.TimelineType.attackment then skip = true end
|
|
|
|
+ if not drawOrder and timeline.type == Animation.TimelineType.drawOrder then skip = true end
|
|
|
|
+ end
|
|
|
|
+ if not skip then timeline:apply(skeleton, animationLast, animationTime, events, alpha, setupPose, true) end
|
|
|
|
+ end
|
|
|
|
+ end
|
|
|
|
+
|
|
|
|
+ self:queueEvents(from, animationTime)
|
|
|
|
+ from.nextAnimationLast = animationTime
|
|
|
|
+ from.nextTrackLast = from.trackTime
|
|
|
|
+
|
|
|
|
+ return mix
|
|
|
|
+end
|
|
|
|
+
|
|
|
|
+-- CONTINUE WITH applyRotateTimeline here
|
|
|
|
+
|
|
return AnimationState
|
|
return AnimationState
|