This commit is contained in:
pharan 2017-05-20 10:18:09 +08:00
commit 5c50435ed2

View File

@ -34,6 +34,7 @@ local utils = require "spine-lua.utils"
local Animation = require "spine-lua.Animation"
local AnimationStateData = require "spine-lua.AnimationStateData"
local math_min = math.min
local math_max = math.max
local math_abs = math.abs
local math_signum = utils.signum
local math_floor = math.floor
@ -45,6 +46,9 @@ local function zlen(array)
end
local EMPTY_ANIMATION = Animation.new("<empty>", {}, 0)
local SUBSEQUENT = 0
local FIRST = 1
local DIP = 2
local EventType = {
start = 0,
@ -167,15 +171,63 @@ function TrackEntry.new ()
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 = {},
timelinesLast = {},
alpha = 0, mixTime = 0, mixDuration = 0, interruptAlpha = 0,
timelineData = {},
timelineDipMix = {},
timelinesRotation = {}
}
setmetatable(self, TrackEntry)
return self
end
function TrackEntry:setTimelineData(to, mixingToArray, propertyIDs)
if to then table_insert(mixingToArray, to) end
local lastEntry = self
if self.mixingFrom then lastEntry = self.mixingFrom:setTimelineData(self, mixingToArray, propertyIDs) end
if to then mixingToArray[#mixingToArray] = nil end
local mixingTo = mixingToArray
local mixingToLast = #mixingToArray
local timelines = self.animation.timelines
local timelinesCount = #self.animation.timelines
local timelineData = self.timelineData
local timelineDipMix = self.timelineDipMix
local i = 1
while i <= timelinesCount do
local id = "" .. timelines[i]:getPropertyId()
if not (propertyIDs[id] == nil) then
timelineData[i] = SUBSEQUENT
elseif (to == nil or not to:hasTimeline(id)) then
timelineData[i] = FIRST
else
timelineData[i] = DIP
local ii = mixingToLast
while ii > 0 do
local entry = mixingTo[ii]
local skip = false
if not entry:hasTimeline(id) then
if entry.mixDuration > 0 then timelineDipMix[i] = entry end
skip = true
break
end
ii = ii - 1
end
if not skip then timelineDipMix[i] = nil end
end
i = i + 1
end
return lastEntry
end
function TrackEntry:hasTimeline(id)
local timelines = self.animation.timelines
for i,timeline in ipairs(timelines) do
if timeline:getPropertyId() == id then return true end
end
return false
end
function TrackEntry:getAnimationTime ()
if self.loop then
local duration = self.animationEnd - self.animationStart
@ -204,7 +256,7 @@ function AnimationState.new (data)
propertyIDs = {},
animationsChanged = false,
timeScale = 1,
mixingMultiple = false
mixingTo = {}
}
self.queue = EventQueue.new(self)
setmetatable(self, AnimationState)
@ -260,8 +312,17 @@ function AnimationState:update (delta)
end
end
if not skip then
self:updateMixingFrom(current, delta)
if not skip then
if current.mixingFrom and self:updateMixingFrom(current, delta, 2) then
-- End mixing from entries once all have completed.
local from = current.mixingFrom
current.mixingFrom = nil
while from do
queue:_end(from)
from = from.mixingFrom
end
end
current.trackTime = current.trackTime + currentDelta
end
end
@ -271,23 +332,30 @@ function AnimationState:update (delta)
queue:drain()
end
function AnimationState:updateMixingFrom (entry, delta)
function AnimationState:updateMixingFrom (entry, delta, animationCount)
local from = entry.mixingFrom
if from == nil then return end
if from == nil then return true end
self:updateMixingFrom(from, delta)
local queue = self.queue
if entry.mixTime >= entry.mixDuration and from.mixingFrom == nil and entry.mixTime > 0 then
entry.mixingFrom = null
queue:_end(from)
return
local finished = self:updateMixingFrom(from, delta, animationCount + 1)
-- Require mixTime > 0 to ensure the mixing from entry was applied at least once.
if (entry.mixTime > 0 and (entry.mixTime >= entry.mixDuration or entry.timeScale == 0)) then
if (animationCount > 5 and from.mixingFrom == nil) then
-- Limit linked list by speeding up and removing old entries.
entry.interruptAlpha = math_max(0, entry.interruptAlpha - delta * 0.66)
if entry.interruptAlpha <= 0 then
entry.mixingFrom = nil
queue._end(from)
end
end
return finished
end
from.animationLast = from.nextAnimationLast
from.trackLast = from.nextTrackLast
from.trackTime = from.trackTime + delta * from.timeScale;
entry.mixTime = entry.mixTime + delta * entry.timeScale;
from.trackTime = from.trackTime + delta * from.timeScale
entry.mixTime = entry.mixTime + delta * entry.timeScale
return false;
end
function AnimationState:apply (skeleton)
@ -317,15 +385,16 @@ function AnimationState:apply (skeleton)
timeline:apply(skeleton, animationLast, animationTime, events, 1, true, false)
end
else
local timelineData = current.timelineData
local firstFrame = #current.timelinesRotation == 0
local timelinesRotation = current.timelinesRotation;
local timelinesFirst = current.timelinesFirst
local timelinesRotation = current.timelinesRotation
for i,timeline in ipairs(timelines) do
if timeline.type == Animation.TimelineType.rotate then
self:applyRotateTimeline(timeline, skeleton, animationTime, mix, timelinesFirst[i], timelinesRotation, i * 2,
self:applyRotateTimeline(timeline, skeleton, animationTime, mix, timelineData[i] > 0, timelinesRotation, i * 2,
firstFrame) -- FIXME passing ii * 2, indexing correct?
else
timeline:apply(skeleton, animationLast, animationTime, events, mix, timelinesFirst[i], false)
timeline:apply(skeleton, animationLast, animationTime, events, mix, timelineData[i] > 0, false)
end
end
end
@ -339,15 +408,15 @@ function AnimationState:apply (skeleton)
queue:drain()
end
function AnimationState:applyMixingFrom (entry, skeleton)
local from = entry.mixingFrom
function AnimationState:applyMixingFrom (to, skeleton)
local from = to.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.
if to.mixDuration == 0 then -- Single frame mix to undo mixingFrom changes.
mix = 1
else
mix = entry.mixTime / entry.mixDuration
mix = to.mixTime / to.mixDuration
if mix > 1 then mix = 1 end
end
@ -358,36 +427,45 @@ function AnimationState:applyMixingFrom (entry, skeleton)
local animationLast = from.animationLast
local animationTime = from:getAnimationTime()
local timelines = from.animation.timelines
local timelinesFirst = from.timelinesFirst
local timelinesLast = nil
if (self.multipleMixing == false) then timelinesLast = from.timelinesLast end
local alphaBase = from.alpha * entry.mixAlpha
local alphaMix = alphaBase * (1 - mix)
local timelineData = from.timelineData
local timelineDipMix = from.timelineDipMix
local firstFrame = #from.timelinesRotation == 0
local timelinesRotation = from.timelinesRotation
local first = false
local alphaDip = from.alpha * to.interruptAlpha
local alphaMix = alphaDip * (1 - mix)
local alpha = 0
local skip = false
for i,timeline in ipairs(timelines) do
local setupPose = timelinesFirst[i]
local alpha = 1;
if (timelinesLast ~= nil and setupPose and not timelinesLast[i]) then
alpha = alphaBase
else
if timelineData[i] == SUBSEQUENT then
first = false
alpha = alphaMix
elseif timelineData[i] == FIRST then
first = true
alpha = alphaMix
end
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
first = true
alpha = alphaDip
local dipMix = timelineDipMix[i]
if dipMix then alpha = alpha * math_max(0, 1 - dipMix.mixtime / dipMix.mixDuration) end
end
if timeline.type == Animation.TimelineType.rotate then
self:applyRotateTimeline(timeline, skeleton, animationTime, alpha, first, timelinesRotation, i * 2, firstFrame)
else
if not first 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
if not skip then timeline:apply(skeleton, animationLast, animationTime, events, alpha, first, true) end
end
end
if (entry.mixDuration > 0) then self:queueEvents(from, animationTime) end
if (to.mixDuration > 0) then self:queueEvents(from, animationTime) end
self.events = {};
from.nextAnimationLast = animationTime
from.nextTrackLast = from.trackTime
@ -555,24 +633,8 @@ function AnimationState:setCurrent (index, current, interrupt)
current.mixingFrom = from
current.mixTime = 0
local mixingFrom = from.mixingFrom
if (mixingFrom ~= nil and from.mixDuration > 0) then
if (self.multipleMixing) then
current.mixAlpha = current.mixAlpha * math_min(from.mixTime / from.mixDuration, 1)
else
if (from.mixTime / from.mixDuration < 0.5 and mixingFrom.animation ~= EMPTY_ANIMATION) then
current.mixingFrom = mixingFrom
mixingFrom.mixingFrom = from
mixingFrom.mixTime = from.mixDuration - from.mixTime
mixingFrom.mixDuration = from.mixDuration
from.mixingFrom = nil
from = mixingFrom
end
from.mixAlpha = 0;
from.mixTime = 0;
from.mixDuration = 0;
end
if from.mixingFrom and from.mixDuration > 0 then
current.interruptAlpha = current.interruptAlpha * math_min(1, from.mixTime / from.mixDuration)
end
from.timelinesRotation = {};
@ -705,7 +767,7 @@ function AnimationState:trackEntry (trackIndex, animation, loop, last)
entry.timeScale = 1
entry.alpha = 1
entry.mixAlpha = 1
entry.interruptAlpha = 1
entry.mixTime = 0
if not last then
entry.mixDuration = 0
@ -726,111 +788,19 @@ function AnimationState:disposeNext (entry)
end
function AnimationState:_animationsChanged ()
self.animationsChanged = false;
self.animationsChanged = false
self.propertyIDs = {}
local propertyIDs = self.propertyIDs;
-- need to get the highest index cause Lua is funny
local highest = -1
local tracks = self.tracks
for i,entry in pairs(tracks) do
if i > highest then highest = i end
end
-- Set timelinesFirst for all entries, from lowest track to highest.
local i = 0
local n = highest + 1
while i < n do
local entry = tracks[i]
if entry then
self:setTimelinesFirst(entry);
i = i + 1
break;
end
i = i + 1
end
while i < n do
local entry = tracks[i]
if entry then self:checkTimelinesFirst(entry) end
i = i + 1
end
local propertyIDs = self.propertyIDs
local mixingTo = self.mixingTo
if (self.multipleMixing) then return end
self.propertyIDs = {}
local lowestMixingFrom = n
i = 0;
while i < n do
entry = self.tracks[i]
if not (entry == nil or entry.mixingFrom == nil) then
lowestMixingFrom = i
i = n + 1 -- break
local lastEntry = nil
for i, entry in pairs(self.tracks) do
if entry then
entry:setTimelineData(lastEntry, mixingTo, propertyIDs)
lastEntry = entry
end
i = i + 1
end
i = n - 1
while i >= lowestMixingFrom do
local entry = self.tracks[i]
if (entry) then
local propertyIDs = self.propertyIDs
local timelines = entry.animation.timelines
local ii = 1
local nn = #entry.animation.timelines;
while ii <= nn do
local id = "" .. timelines[ii]:getPropertyId()
propertyIDs[id] = id
ii = ii + 1
end
entry = entry.mixingFrom
while (entry) do
self:checkTimelinesUsage(entry, entry.timelinesLast)
entry = entry.mixingFrom;
end
end
i = i - 1
end
end
function AnimationState:setTimelinesFirst (entry)
if entry.mixingFrom then
self:setTimelinesFirst(entry.mixingFrom)
self:checkTimelinesUsage(entry, entry.timelinesFirst)
return
end
local propertyIDs = self.propertyIDs
local n = #entry.animation.timelines
local timelines = entry.animation.timelines
entry.timelinesFirst = {}
local usage = entry.timelinesFirst;
local i = 1
while i <= n do
local id = "" .. timelines[i]:getPropertyId()
propertyIDs[id] = id
usage[i] = true;
i = i + 1
end
end
function AnimationState:checkTimelinesFirst (entry)
if entry.mixingFrom then self:checkTimelinesFirst(entry.mixingFrom) end
self:checkTimelinesUsage(entry, entry.timelinesFirst)
end
function AnimationState:checkTimelinesUsage (entry, usageArray)
local propertyIDs = self.propertyIDs
local n = #entry.animation.timelines
local timelines = entry.animation.timelines
local usage = usageArray
local i = 1
while i <= n do
local id = "" .. timelines[i]:getPropertyId()
local contained = propertyIDs[id] == id
propertyIDs[id] = id
usage[i] = not contained
i = i + 1
end
end
function AnimationState:getCurrent (trackIndex)