Pārlūkot izejas kodu

[lua] Finished porting AnimationState, needs testing and fixes

badlogic 9 gadi atpakaļ
vecāks
revīzija
15ae8183f7
2 mainītis faili ar 402 papildinājumiem un 1 dzēšanām
  1. 1 0
      spine-lua/Animation.lua
  2. 401 1
      spine-lua/AnimationState.lua

+ 1 - 0
spine-lua/Animation.lua

@@ -85,6 +85,7 @@ local function binarySearch (values, target, step)
 		current = math.floor((low + high) / 2)
 	end
 end
+Animation.binarySearch = binarySearch
 
 local function binarySearch1 (values, target)
 	local low = 0

+ 401 - 1
spine-lua/AnimationState.lua

@@ -37,6 +37,12 @@ local math_min = math.min
 local math_abs = math.abs
 local math_signum = utils.signum
 
+local function zlen(array)
+	return #array + 1
+end
+
+local EMPTY_ANIMATION = Animation.new("<empty>", {}, 0)
+
 local EventType = {
 	start = 0,
 	interrupt = 1,
@@ -371,6 +377,400 @@ function AnimationState:applyMixingFrom (entry, skeleton)
 	return mix
 end
 
--- CONTINUE WITH applyRotateTimeline here
+function AnimationState:applyRotateTimeline (timeline, skeleton, time, alpha, setupPose, timelinesRotation, i, firstFrame)
+  if alpha == 1 then
+    timeline:apply(skeleton, 0, time, nil, 1, setupPose, false)
+    return
+  end
+
+  local rotateTimeline = timeline
+  local frames = rotateTimeline.frames
+  if time < frames[0] then return end -- Time is before first frame.
+
+  local bone = skeleton.bones[rotateTimeline.boneIndex]
+
+  local r2 = 0
+  if time >= frames[zlen(frames) - Animation.RotateTimeline.ENTRIES] then -- Time is after last frame.
+    r2 = bone.data.rotation + frames[zlen(frames) + PREV_ROTATION]
+  else
+    -- Interpolate between the previous frame and the current frame.
+    local frame = Animation.binarySearch(frames, time, ENTRIES)
+    local prevRotation = frames[frame + PREV_ROTATION]
+    local frameTime = frames[frame]
+    local percent = rotateTimeline:getCurvePercent(math_floor(frame / 2) - 1,
+      1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime))
+
+    r2 = frames[frame + ROTATION] - prevRotation
+    r2 = r2 - (16384 - math_floor(16384.499999999996 - r2 / 360)) * 360
+    r2 = prevRotation + r2 * percent + bone.data.rotation
+    r2 = r2 - (16384 - math_floor(16384.499999999996 - r2 / 360)) * 360
+  end
+
+  -- Mix between rotations using the direction of the shortest route on the first frame while detecting crosses.
+  local r1 = bone.rotation
+  if setupPose then r1 = bone.data.rotation end
+  local total = 0
+  local diff = r2 - r1
+  if diff == 0 then
+    if firstFrame then
+      timelinesRotation[i] = 0
+      total = 0
+    else
+      total = timelinesRotation[i]
+    end
+  else
+    diff = diff - (16384 - math_floor(16384.499999999996 - diff / 360)) * 360
+    local lastTotal = 0
+    local lastDiff = 0
+    if firstFrame then
+      lastTotal = 0
+      lastDiff = diff
+    else
+      lastTotal = timelinesRotation[i] -- Angle and direction of mix, including loops.
+      lastDiff = timelinesRotation[i + 1] -- Difference between bones.
+    end
+    local current = diff > 0
+    local dir = lastTotal >= 0
+    -- Detect cross at 0 (not 180).
+    if math_signum(lastDiff) ~= math_signum(diff) and math_abs(lastDiff) <= 90 then
+      -- A cross after a 360 rotation is a loop.
+      if math_abs(lastTotal) > 180 then lastTotal = lastTotal + 360 * math_signum(lastTotal) end
+      dir = current
+    end
+    total = diff + lastTotal - math_ceil(lastTotal / 360 - 0.5) * 360 -- FIXME used to be %360, store loops as part of lastTotal.
+    if dir ~= current then total = total + 360 * Math.signum(lastTotal) end
+    timelinesRotation[i] = total
+  end
+  timelinesRotation[i + 1] = diff
+  r1 = r1 + total * alpha
+  bone.rotation = r1 - (16384 - math_floor(16384.499999999996 - r1 / 360)) * 360
+end
+
+function AnimationState:queueEvents (entry, animationTime)
+  local animationStart = entry.animationStart
+  local animationEnd = entry.animationEnd
+  local duration = animationEnd - animationStart
+  local trackLastWrapped = entry.trackLast % duration
+
+  -- Queue events before complete.
+  local events = self.events
+  local queue = self.queue
+  local i = 1
+  local n = #events
+  while i <= n do
+    local event = events[i]
+    if event.time < trackLastWrapped then break end
+    if not (event.time > animationEnd) then -- Discard events outside animation start/end.
+      queue:event(entry, event)
+    end
+    i = i + 1
+  end
+
+  -- Queue complete if completed a loop iteration or the animation.
+  local queueComplete = false
+  if entry.loop then 
+    queueComplete = (trackLastWrapped > entry.trackTime % duration)
+  else
+    queueComplete = (animationTime >= animationEnd and entry.animationLast < animationEnd)
+  end
+  if queueComplete then
+    queue:complete(entry)
+  end
+
+  -- Queue events after complete.
+  while i <= n do
+    local event = events[i]
+    if not (event.time < animationStart) then --// Discard events outside animation start/end.
+      queue.event(entry, event)
+    end
+    i = i + 1
+  end
+  events = {}
+end
+
+function AnimationState:clearTracks ()
+  local queue = self.queue
+  local tracks = self.tracks
+  queue.drainDisabled = true;
+  for i,track in pairs(tracks) do
+    self:clearTrack(i)
+  end
+  tracks = {}
+  queue.drainDisabled = false;
+  queue:drain();
+end
+
+function AnimationState:clearTrack (trackIndex)
+  local tracks = self.tracks
+  local queue = self.queue
+  local current = tracks[trackIndex]
+  if current == nil then return end
+
+  queue:_end(current)
+
+  self:disposeNext(current)
+
+  local entry = current;
+  while (true) do
+    local from = entry.mixingFrom
+    if from == nil then break end
+    queue:_end(from)
+    entry.mixingFrom = nil
+    entry = from
+  end
+
+  tracks[current.trackIndex] = nil
+
+  queue:drain()
+end
+
+function AnimationState:setCurrent (index, current)
+  local from = self:expandToIndex(index)
+  local tracks = self.tracks
+  local queue = self.queue
+  tracks[index] = current
+
+  if from then
+    queue:interrupt(from)
+    current.mixingFrom = from
+    current.mixTime = 0
+
+    -- If not completely mixed in, set mixAlpha so mixing out happens from current mix to zero.
+    if from.mixingFrom then current.mixAlpha = current.mixAlpha * math_min(from.mixTime / from.mixDuration, 1) end
+  end
+
+  queue:start(current)
+end
+
+function AnimationState:setAnimationByName (trackIndex, animationName, loop)
+		local animation = self.data.skeletonData:findAnimation(animationName)
+		if not animation then error("Animation not found: " .. animationName, 2) end
+		return self:setAnimation(trackIndex, animation, loop)
+end
+
+function AnimationState:setAnimation (trackIndex, animation, loop)
+  if not animation then error("animation cannot be null.") end
+  local current = self:expandToIndex(trackIndex)
+  local queue = self.queue
+  if current then
+    if current.nextTrackLast == -1 then
+      -- Don't mix from an entry that was never applied.
+      tracks[trackIndex] = nil
+      queue:interrupt(current)
+      queue:_end(current)
+      self:disposeNext(current)
+      current = nil
+    else
+      self:disposeNext(current)
+    end
+  end
+  local entry = self:trackEntry(trackIndex, animation, loop, current)
+  self:setCurrent(trackIndex, entry)
+  queue:drain()
+  return entry
+end
+
+function AnimationState:addAnimationByName (trackIndex, animationName, loop, delay)
+		local animation = data.skeletonData:findAnimation(animationName)
+		if not animation then error("Animation not found: " + animationName) end
+		return self:addAnimation(trackIndex, animation, loop, delay)
+end
+
+function AnimationState:addAnimation (trackIndex, animationName, loop, delay)
+  if not nimation then error("animation cannot be null.") end
+
+  local last = self:expandToIndex(trackIndex)
+  if last then
+    while last.next do
+      last = last.next
+    end
+  end
+
+  local entry = self:trackEntry(trackIndex, animation, loop, last)
+  local queue = self.queue
+  
+  if not last then
+    self:setCurrent(trackIndex, entry)
+    queue:drain()
+  else
+    last.next = entry
+    if delay <= 0 then
+      local duration = last.animationEnd - last.animationStart
+      if duration ~= 0 then
+        delay = delay + duration * (1 + math_floor(last.trackTime / duration)) - data:getMix(last.animation, animation)
+      else
+        delay = 0
+      end
+    end
+  end
+
+  entry.delay = delay
+  return entry
+end
+
+function AnimationState:setEmptyAnimation (trackIndex, mixDuration)
+  local entry = self:setAnimation(trackIndex, EMPTY_ANIMATION, false)
+  entry.mixDuration = mixDuration
+  entry.trackEnd = mixDuration
+  return entry
+end
+
+function AnimationState:addEmptyAnimation (trackIndex, mixDuration, delay)
+  if delay <= 0 then delay = delay - mixDuration end
+  local entry = self:addAnimation(trackIndex, EMPTY_ANIMATION, false, delay)
+  entry.mixDuration = mixDuration
+  entry.trackEnd = mixDuration
+  return entry
+end
+
+function AnimationState:setEmptyAnimations (mixDuration)
+  local queue = self.queue
+  queue.drainDisabled = true
+  for i,current in pairs(self.tracks) do
+    if current then self:setEmptyAnimation(current.trackIndex, mixDuration) end
+  end
+  queue.drainDisabled = false
+  queue:drain()
+end
+
+function AnimationState:expandToIndex (index)
+  return self.tracks[index]
+end
+
+function AnimationState:trackEntry (trackIndex, animation, loop, last)
+  local data = self.data
+  local entry = TrackEntry.new()
+  entry.trackIndex = trackIndex
+  entry.animation = animation
+  entry.loop = loop
+
+  entry.eventThreshold = 0
+  entry.attachmentThreshold = 0
+  entry.drawOrderThreshold = 0
+
+  entry.animationStart = 0
+  entry.animationEnd = animation.duration
+  entry.animationLast = -1
+  entry.nextAnimationLast = -1
+
+  entry.delay = 0
+  entry.trackTime = 0
+  entry.trackLast = -1
+  entry.nextTrackLast = -1
+  if loop then
+    entry.trackEnd = 999999999
+  else
+    entry.trackEnd = entry.animationEnd
+  end
+  entry.timeScale = 1
+
+  entry.alpha = 1
+  entry.mixAlpha = 1
+  entry.mixTime = 0
+  if not last then
+    entry.mixDuration = 0
+  else
+    entry.mixDuration = data:getMix(last.animation, animation)
+  end
+  return entry
+end
+
+function AnimationState:disposeNext (entry)
+  local _next = entry.next
+  local queue = self.queue
+  while _next do
+    queue:dispose(_next)
+    _next = next.next
+  end
+  entry.next = nil 
+end
+
+function AnimationState:animationsChanged ()
+  self.animationsChanged = false;
+
+  self.propertyIDs = {}
+  local propertyIDs = self.propertyIDs;
+
+  -- need to get the highest index cause Lua is funny
+  local highest = -1
+  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 = 1
+  local n = highest
+  local tracks = self.tracks
+  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
+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:checkTimlinesFirst (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
+  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)
+  return self.tracks[trackIndex]
+end
+
+function AnimationState:clearListeners ()
+  self.onStart = nil
+  self.onInterrupt = nil
+  self.onEnd = nil
+  self.onComplete = nil
+  self.onDispose = nil
+  self.onEvent = nil
+end
+
+function AnimationState:clearListenerNotificatin ()
+  self.queue:clear()
+end
 
 return AnimationState