From 44f5540d37d81a1839d5c5dac43859a0445180ac Mon Sep 17 00:00:00 2001 From: badlogic Date: Mon, 31 Oct 2016 16:21:39 +0100 Subject: [PATCH] [lua] Ported half of AnimationState --- spine-lua/Animation.lua | 20 +++- spine-lua/AnimationState.lua | 210 +++++++++++++++++++++++++++++++++++ 2 files changed, 227 insertions(+), 3 deletions(-) diff --git a/spine-lua/Animation.lua b/spine-lua/Animation.lua index 6b48954a4..84b510fde 100644 --- a/spine-lua/Animation.lua +++ b/spine-lua/Animation.lua @@ -228,6 +228,7 @@ function Animation.RotateTimeline.new (frameCount) 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 @@ -291,6 +292,7 @@ function Animation.TranslateTimeline.new (frameCount) 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 @@ -349,6 +351,7 @@ function Animation.ScaleTimeline.new (frameCount) local Y = 2 local self = Animation.TranslateTimeline.new(frameCount) + self.type = TimelineType.scale function self:getPropertyId () return TimelineType.scale * SHL_24 + self.boneIndex @@ -417,6 +420,7 @@ function Animation.ShearTimeline.new (frameCount) local Y = 2 local self = Animation.TranslateTimeline.new(frameCount) + self.type = TimelineType.shear function self:getPropertyId () return TimelineType.shear * SHL_24 + self.boneIndex @@ -474,6 +478,7 @@ function Animation.ColorTimeline.new (frameCount) 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 @@ -533,7 +538,8 @@ function Animation.AttachmentTimeline.new (frameCount) local self = { frames = utils.newNumberArrayZero(frameCount), -- time, ... attachmentNames = {}, - slotIndex = -1 + slotIndex = -1, + type = TimelineType.attachment } function self:getFrameCount () @@ -591,6 +597,7 @@ function Animation.DeformTimeline.new (frameCount) self.frameVertices = utils.newNumberArrayZero(frameCount) self.slotIndex = -1 self.attachment = nil + self.type = TimelineType.deform function self:getPropertyId () return TimelineType.deform * SHL_24 + self.slotIndex @@ -711,7 +718,8 @@ Animation.EventTimeline = {} function Animation.EventTimeline.new (frameCount) local self = { frames = utils.newNumberArrayZero(frameCount), - events = {} + events = {}, + type = TimelineType.event } function self:getPropertyId () @@ -767,7 +775,8 @@ Animation.DrawOrderTimeline = {} function Animation.DrawOrderTimeline.new (frameCount) local self = { frames = utils.newNumberArrayZero(frameCount), - drawOrders = {} + drawOrders = {}, + type = TimelineType.drawOrder } function self:getPropertyId () @@ -830,6 +839,7 @@ function Animation.IkConstraintTimeline.new (frameCount) local self = Animation.CurveTimeline.new(frameCount) self.frames = utils.newNumberArrayZero(frameCount * ENTRIES) -- time, mix, bendDirection, ... self.ikConstraintIndex = -1 + self.type = TimelineType.ikConstraint function self:getPropertyId () return TimelineType.ikConstraint * SHL_24 + self.ikConstraintIndex @@ -903,6 +913,7 @@ function Animation.TransformConstraintTimeline.new (frameCount) 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 @@ -977,6 +988,7 @@ function Animation.PathConstraintPositionTimeline.new (frameCount) 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 @@ -1028,6 +1040,7 @@ function Animation.PathConstraintSpacingTimeline.new (frameCount) 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 @@ -1082,6 +1095,7 @@ function Animation.PathConstraintMixTimeline.new (frameCount) 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 diff --git a/spine-lua/AnimationState.lua b/spine-lua/AnimationState.lua index a32ee7b5c..e18e3a698 100644 --- a/spine-lua/AnimationState.lua +++ b/spine-lua/AnimationState.lua @@ -33,6 +33,9 @@ local table_insert = table.insert 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_abs = math.abs +local math_signum = utils.signum local EventType = { start = 0, @@ -142,6 +145,36 @@ function EventQueue:clear () 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 = {} AnimationState.__index = AnimationState @@ -163,4 +196,181 @@ function AnimationState.new (data) return self 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