[lua] 4.0 port, runs but LOVE is not rendering.

This commit is contained in:
Nathan Sweet 2021-06-05 16:55:59 -04:00
parent 700d2897a8
commit 137c3f69ed
16 changed files with 632 additions and 726 deletions

View File

@ -113,8 +113,8 @@ end
function love.load(arg) function love.load(arg)
if arg[#arg] == "-debug" then require("mobdebug").start() end if arg[#arg] == "-debug" then require("mobdebug").start() end
skeletonRenderer = spine.SkeletonRenderer.new(true) skeletonRenderer = spine.SkeletonRenderer.new(true)
table.insert(skeletons, loadSkeleton("mix-and-match-pro", "mix-and-match", "dance", nil, 0.5, 400, 500))
table.insert(skeletons, loadSkeleton("spineboy-pro", "spineboy", "walk", nil, 0.5, 400, 500)) table.insert(skeletons, loadSkeleton("spineboy-pro", "spineboy", "walk", nil, 0.5, 400, 500))
table.insert(skeletons, loadSkeleton("mix-and-match-pro", "mix-and-match", "dance", nil, 0.5, 400, 500))
table.insert(skeletons, loadSkeleton("stretchyman-pro", "stretchyman", "sneak", nil, 0.5, 200, 500)) table.insert(skeletons, loadSkeleton("stretchyman-pro", "stretchyman", "sneak", nil, 0.5, 200, 500))
table.insert(skeletons, loadSkeleton("coin-pro", "coin", "animation", nil, 0.5, 400, 300)) table.insert(skeletons, loadSkeleton("coin-pro", "coin", "animation", nil, 0.5, 400, 300))
table.insert(skeletons, loadSkeleton("raptor-pro", "raptor", "walk", nil, 0.3, 400, 500)) table.insert(skeletons, loadSkeleton("raptor-pro", "raptor", "walk", nil, 0.3, 400, 500))

View File

@ -37,7 +37,7 @@ local utils = require "spine-lua.utils"
local AttachmentType = require "spine-lua.attachments.AttachmentType" local AttachmentType = require "spine-lua.attachments.AttachmentType"
local setmetatable = setmetatable local setmetatable = setmetatable
local math_floor = math_floor local math_floor = math.floor
local math_abs = math.abs local math_abs = math.abs
local math_signum = utils.signum local math_signum = utils.signum
@ -64,14 +64,14 @@ function Animation.new (name, timelines, duration)
self.timelineIds = {} self.timelineIds = {}
for i,timeline in ipairs(self.timelines) do for i,timeline in ipairs(self.timelines) do
for _,id in ipairs(timeline.propertyIds) do for _,id in ipairs(timeline.propertyIds) do
timelineIds[id] = true self.timelineIds[id] = true
end end
end end
end end
function self:hasTimeline (ids) function self:hasTimeline (ids)
for _,id in ipairs(ids) do for _,id in ipairs(ids) do
if timelineIds[id] then return true end if self.timelineIds[id] then return true end
end end
return false return false
end end
@ -134,11 +134,28 @@ Animation.Property = {
} }
local Property = Animation.Property local Property = Animation.Property
Animation.TimelineType = {
rotate = 0,
translate = 1, translateX = 2, translateY = 3,
scale = 4, scaleX = 5, scaleY = 6,
shear = 7, shearX = 8, shearY = 9,
rgba = 10, rgb = 11, alpha = 12, rgba2 = 13, rgb2 = 14,
attachment = 15,
deform = 16,
event = 17,
drawOrder = 18,
ikConstraint = 19,
transformConstraint = 20,
pathConstraintPosition = 21, pathConstraintSpacing = 22, pathConstraintMix = 23
}
local TimelineType = Animation.TimelineType
Animation.Timeline = {} Animation.Timeline = {}
function Animation.Timeline.new (frameCount, propertyIds) function Animation.Timeline.new (timelineType, frameEntries, frameCount, propertyIds)
local self = { local self = {
timelineType = timelineType,
propertyIds = propertyIds, propertyIds = propertyIds,
frames = utils.newNumberArrayZero((frameCount - 1) * self:getFrameEntries()) frames = utils.newNumberArrayZero((frameCount - 1) * frameEntries)
} }
function self:getFrameEntries () function self:getFrameEntries ()
@ -158,7 +175,8 @@ end
local function search1 (frames, time) local function search1 (frames, time)
local n = zlen(frames) local n = zlen(frames)
while i <= n do local i = 1
while i < n do
if frames[i] > time then return i - 1 end if frames[i] > time then return i - 1 end
i = i + 1 i = i + 1
end end
@ -169,7 +187,7 @@ Animation.Timeline.search1 = search1
local function search (frames, time, step) local function search (frames, time, step)
local n = zlen(frames) local n = zlen(frames)
local i = step local i = step
while i <= n do while i < n do
if frames[i] > time then return i - step end if frames[i] > time then return i - step end
i = i + step i = i + step
end end
@ -182,14 +200,15 @@ local BEZIER = 2
local BEZIER_SIZE = 18 local BEZIER_SIZE = 18
Animation.CurveTimeline = {} Animation.CurveTimeline = {}
function Animation.CurveTimeline.new (frameCount, bezierCount, propertyIds) function Animation.CurveTimeline.new (timelineType, frameEntries, frameCount, bezierCount, propertyIds)
local LINEAR = 0 local LINEAR = 0
local STEPPED = 1 local STEPPED = 1
local BEZIER = 2 local BEZIER = 2
local BEZIER_SIZE = 10 * 2 - 1 local BEZIER_SIZE = 10 * 2 - 1
local self = Animation.Timeline.new(frameCount, propertyIds) local self = Animation.Timeline.new(timelineType, frameEntries, frameCount, propertyIds)
self.curves = utils.newNumberArrayZero(frameCount + bezierCount * BEZIER_SIZE) self.curves = utils.newNumberArrayZero(frameCount + bezierCount * BEZIER_SIZE)
self.curves[frameCount - 1] = STEPPED
function self:getFrameCount () function self:getFrameCount ()
return math_floor(zlen(self.curves) / BEZIER_SIZE) + 1 return math_floor(zlen(self.curves) / BEZIER_SIZE) + 1
@ -261,11 +280,11 @@ function Animation.CurveTimeline.new (frameCount, bezierCount, propertyIds)
end end
Animation.CurveTimeline1 = {} Animation.CurveTimeline1 = {}
function Animation.CurveTimeline1.new (frameCount, bezierCount, propertyId) function Animation.CurveTimeline1.new (timelineType, frameCount, bezierCount, propertyId)
local ENTRIES = 2 local ENTRIES = 2
local VALUE = 1 local VALUE = 1
local self = Animation.CurveTimeline.new(frameCount, bezierCount, { propertyId }) local self = Animation.CurveTimeline.new(timelineType, ENTRIES, frameCount, bezierCount, { propertyId })
function self:getFrameEntries () function self:getFrameEntries ()
return ENTRIES return ENTRIES
@ -303,12 +322,12 @@ function Animation.CurveTimeline1.new (frameCount, bezierCount, propertyId)
end end
Animation.CurveTimeline2 = {} Animation.CurveTimeline2 = {}
function Animation.CurveTimeline2.new (frameCount, bezierCount, propertyId1, propertyId2) function Animation.CurveTimeline2.new (timelineType, frameCount, bezierCount, propertyId1, propertyId2)
local ENTRIES = 3 local ENTRIES = 3
local VALUE1 = 1 local VALUE1 = 1
local VALUE2 = 2 local VALUE2 = 2
local self = Animation.CurveTimeline.new(frameCount, bezierCount, { propertyId1, propertyId2 }) local self = Animation.CurveTimeline.new(timelineType, ENTRIES, frameCount, bezierCount, { propertyId1, propertyId2 })
function self:getFrameEntries () function self:getFrameEntries ()
return ENTRIES return ENTRIES
@ -326,7 +345,7 @@ end
Animation.RotateTimeline = {} Animation.RotateTimeline = {}
function Animation.RotateTimeline.new (frameCount, bezierCount, boneIndex) function Animation.RotateTimeline.new (frameCount, bezierCount, boneIndex)
local self = Animation.CurveTimeline1.new(frameCount, bezierCount, Property.rotate.."|"..boneIndex) local self = Animation.CurveTimeline1.new(TimelineType.rotate, frameCount, bezierCount, Property.rotate.."|"..boneIndex)
self.boneIndex = boneIndex self.boneIndex = boneIndex
function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) function self:apply (skeleton, lastTime, time, events, alpha, blend, direction)
@ -358,7 +377,11 @@ end
Animation.TranslateTimeline = {} Animation.TranslateTimeline = {}
function Animation.TranslateTimeline.new (frameCount, bezierCount, boneIndex) function Animation.TranslateTimeline.new (frameCount, bezierCount, boneIndex)
local self = Animation.CurveTimeline2.new(frameCount, bezierCount, local ENTRIES = 3
local VALUE1 = 1
local VALUE2 = 2
local self = Animation.CurveTimeline2.new(TimelineType.translate, frameCount, bezierCount,
Property.x.."|"..boneIndex, Property.x.."|"..boneIndex,
Property.y.."|"..boneIndex Property.y.."|"..boneIndex
) )
@ -382,7 +405,7 @@ function Animation.TranslateTimeline.new (frameCount, bezierCount, boneIndex)
local x = 0 local x = 0
local y = 0 local y = 0
local frame = search2(frames, time, ENTRIES) local i = search(frames, time, ENTRIES)
local curveType = self.curves[math_floor(i / ENTRIES)] local curveType = self.curves[math_floor(i / ENTRIES)]
if curveType == LINEAR then if curveType == LINEAR then
local before = frames[i] local before = frames[i]
@ -416,7 +439,7 @@ end
Animation.TranslateXTimeline = {} Animation.TranslateXTimeline = {}
function Animation.TranslateXTimeline.new (frameCount, bezierCount, boneIndex) function Animation.TranslateXTimeline.new (frameCount, bezierCount, boneIndex)
local self = Animation.CurveTimeline1.new(frameCount, bezierCount, Property.x.."|"..boneIndex) local self = Animation.CurveTimeline1.new(TimelineType.translateX, frameCount, bezierCount, Property.x.."|"..boneIndex)
self.boneIndex = boneIndex self.boneIndex = boneIndex
function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) function self:apply (skeleton, lastTime, time, events, alpha, blend, direction)
@ -448,7 +471,7 @@ end
Animation.TranslateYTimeline = {} Animation.TranslateYTimeline = {}
function Animation.TranslateYTimeline.new (frameCount, bezierCount, boneIndex) function Animation.TranslateYTimeline.new (frameCount, bezierCount, boneIndex)
local self = Animation.CurveTimeline1.new(frameCount, bezierCount, Property.x.."|"..boneIndex) local self = Animation.CurveTimeline1.new(TimelineType.translateY, frameCount, bezierCount, Property.x.."|"..boneIndex)
self.boneIndex = boneIndex self.boneIndex = boneIndex
function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) function self:apply (skeleton, lastTime, time, events, alpha, blend, direction)
@ -480,7 +503,11 @@ end
Animation.ScaleTimeline = {} Animation.ScaleTimeline = {}
function Animation.ScaleTimeline.new (frameCount, bezierCount, boneIndex) function Animation.ScaleTimeline.new (frameCount, bezierCount, boneIndex)
local self = Animation.CurveTimeline2.new(frameCount, bezierCount, local ENTRIES = 3
local VALUE1 = 1
local VALUE2 = 2
local self = Animation.CurveTimeline2.new(TimelineType.scale, frameCount, bezierCount,
Property.scaleX.."|"..boneIndex, Property.scaleX.."|"..boneIndex,
Property.scaleY.."|"..boneIndex Property.scaleY.."|"..boneIndex
) )
@ -504,7 +531,7 @@ function Animation.ScaleTimeline.new (frameCount, bezierCount, boneIndex)
local x = 0 local x = 0
local y = 0 local y = 0
local i = search2(frames, time, ENTRIES) local i = search(frames, time, ENTRIES)
local curveType = self.curves[math_floor(i / ENTRIES)] local curveType = self.curves[math_floor(i / ENTRIES)]
if curveType == LINEAR then if curveType == LINEAR then
local before = frames[i] local before = frames[i]
@ -577,7 +604,7 @@ end
Animation.ScaleXTimeline = {} Animation.ScaleXTimeline = {}
function Animation.ScaleXTimeline.new (frameCount, bezierCount, boneIndex) function Animation.ScaleXTimeline.new (frameCount, bezierCount, boneIndex)
local self = Animation.CurveTimeline1.new(frameCount, bezierCount, Property.scaleX.."|"..boneIndex) local self = Animation.CurveTimeline1.new(TimelineType.scaleX, frameCount, bezierCount, Property.scaleX.."|"..boneIndex)
self.boneIndex = boneIndex self.boneIndex = boneIndex
function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) function self:apply (skeleton, lastTime, time, events, alpha, blend, direction)
@ -634,7 +661,7 @@ end
Animation.ScaleYTimeline = {} Animation.ScaleYTimeline = {}
function Animation.ScaleYTimeline.new (frameCount, bezierCount, boneIndex) function Animation.ScaleYTimeline.new (frameCount, bezierCount, boneIndex)
local self = Animation.CurveTimeline1.new(frameCount, bezierCount, Property.scaleY.."|"..boneIndex) local self = Animation.CurveTimeline1.new(TimelineType.scaleY, frameCount, bezierCount, Property.scaleY.."|"..boneIndex)
self.boneIndex = boneIndex self.boneIndex = boneIndex
function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) function self:apply (skeleton, lastTime, time, events, alpha, blend, direction)
@ -690,8 +717,12 @@ function Animation.ScaleYTimeline.new (frameCount, bezierCount, boneIndex)
end end
Animation.ShearTimeline = {} Animation.ShearTimeline = {}
function Animation.ShearTimeline.new (frameCount) function Animation.ShearTimeline.new (frameCount, bezierCount, boneIndex)
local self = Animation.CurveTimeline2.new(frameCount, bezierCount, local ENTRIES = 3
local VALUE1 = 1
local VALUE2 = 2
local self = Animation.CurveTimeline2.new(TimelineType.shear, frameCount, bezierCount,
Property.shearX.."|"..boneIndex, Property.shearX.."|"..boneIndex,
Property.shearY.."|"..boneIndex Property.shearY.."|"..boneIndex
) )
@ -715,7 +746,7 @@ function Animation.ShearTimeline.new (frameCount)
local x = 0 local x = 0
local y = 0 local y = 0
local i = search2(frames, time, ENTRIES) local i = search(frames, time, ENTRIES)
local curveType = self.curves[math_floor(i / ENTRIES)] local curveType = self.curves[math_floor(i / ENTRIES)]
if curveType == LINEAR then if curveType == LINEAR then
local before = frames[i] local before = frames[i]
@ -748,8 +779,8 @@ function Animation.ShearTimeline.new (frameCount)
end end
Animation.ShearXTimeline = {} Animation.ShearXTimeline = {}
function Animation.ShearXTimeline.new (frameCount) function Animation.ShearXTimeline.new (frameCount, bezierCount, boneIndex)
local self = Animation.CurveTimeline1.new(frameCount, bezierCount, Property.shearX.."|"..boneIndex) local self = Animation.CurveTimeline1.new(TimelineType.shearX, frameCount, bezierCount, Property.shearX.."|"..boneIndex)
self.boneIndex = boneIndex self.boneIndex = boneIndex
function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) function self:apply (skeleton, lastTime, time, events, alpha, blend, direction)
@ -780,8 +811,8 @@ function Animation.ShearXTimeline.new (frameCount)
end end
Animation.ShearYTimeline = {} Animation.ShearYTimeline = {}
function Animation.ShearYTimeline.new (frameCount) function Animation.ShearYTimeline.new (frameCount, bezierCount, boneIndex)
local self = Animation.CurveTimeline1.new(frameCount, bezierCount, Property.shearY.."|"..boneIndex) local self = Animation.CurveTimeline1.new(TimelineType.shearY, frameCount, bezierCount, Property.shearY.."|"..boneIndex)
self.boneIndex = boneIndex self.boneIndex = boneIndex
function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) function self:apply (skeleton, lastTime, time, events, alpha, blend, direction)
@ -819,7 +850,7 @@ function Animation.RGBATimeline.new (frameCount, bezierCount, slotIndex)
local B = 3 local B = 3
local A = 4 local A = 4
local self = Animation.CurveTimeline.new(frameCount, bezierCount, { local self = Animation.CurveTimeline.new(TimelineType.rgba, ENTRIES, frameCount, bezierCount, {
Property.rgb.."|"..slotIndex, Property.rgb.."|"..slotIndex,
Property.alpha.."|"..slotIndex Property.alpha.."|"..slotIndex
}) })
@ -856,7 +887,7 @@ function Animation.RGBATimeline.new (frameCount, bezierCount, slotIndex)
end end
local r, g, b, a local r, g, b, a
local i = search2(frames, time, ENTRIES) local i = search(frames, time, ENTRIES)
local curveType = self.curves[i / ENTRIES] local curveType = self.curves[i / ENTRIES]
if curveType == LINEAR then if curveType == LINEAR then
local before = frames[i] local before = frames[i]
@ -899,7 +930,7 @@ function Animation.RGBTimeline.new (frameCount, bezierCount, slotIndex)
local G = 2 local G = 2
local B = 3 local B = 3
local self = Animation.CurveTimeline.new(frameCount, bezierCount, { Property.rgb.."|"..slotIndex }) local self = Animation.CurveTimeline.new(TimelineType.rgb, ENTRIES, frameCount, bezierCount, { Property.rgb.."|"..slotIndex })
self.slotIndex = slotIndex self.slotIndex = slotIndex
function self:getFrameEntries () function self:getFrameEntries ()
@ -935,7 +966,7 @@ function Animation.RGBTimeline.new (frameCount, bezierCount, slotIndex)
end end
local r, g, b local r, g, b
local i = search2(frames, time, ENTRIES) local i = search(frames, time, ENTRIES)
local curveType = self.curves[i / ENTRIES] local curveType = self.curves[i / ENTRIES]
if curveType == LINEAR then if curveType == LINEAR then
local before = frames[i] local before = frames[i]
@ -978,7 +1009,7 @@ end
Animation.AlphaTimeline = {} Animation.AlphaTimeline = {}
function Animation.AlphaTimeline.new (frameCount, bezierCount, slotIndex) function Animation.AlphaTimeline.new (frameCount, bezierCount, slotIndex)
local self = Animation.CurveTimeline1.new(frameCount, bezierCount, Property.alpha.."|"..slotIndex) local self = Animation.CurveTimeline1.new(TimelineType.alpha, frameCount, bezierCount, Property.alpha.."|"..slotIndex)
self.slotIndex = slotIndex self.slotIndex = slotIndex
function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) function self:apply (skeleton, lastTime, time, events, alpha, blend, direction)
@ -1020,7 +1051,7 @@ function Animation.RGBA2Timeline.new (frameCount, bezierCount, slotIndex)
local G2 = 6 local G2 = 6
local B2 = 7 local B2 = 7
local self = Animation.CurveTimeline.new(frameCount, bezierCount, { local self = Animation.CurveTimeline.new(TimelineType.rgba2, ENTRIES, frameCount, bezierCount, {
Property.rgb.."|"..slotIndex, Property.rgb.."|"..slotIndex,
Property.alpha.."|"..slotIndex, Property.alpha.."|"..slotIndex,
Property.rgb2.."|"..slotIndex Property.rgb2.."|"..slotIndex
@ -1069,7 +1100,7 @@ function Animation.RGBA2Timeline.new (frameCount, bezierCount, slotIndex)
end end
local r, g, b, a, r2, g2, b2 local r, g, b, a, r2, g2, b2
local i = search2(frames, time, ENTRIES) local i = search(frames, time, ENTRIES)
local curveType = self.curves[math_floor(i / ENTRIES)] local curveType = self.curves[math_floor(i / ENTRIES)]
if curveType == LINEAR then if curveType == LINEAR then
local before = frames[i] local before = frames[i]
@ -1139,7 +1170,7 @@ function Animation.RGB2Timeline.new (frameCount, bezierCount, slotIndex)
local G2 = 5 local G2 = 5
local B2 = 6 local B2 = 6
local self = Animation.CurveTimeline.new(frameCount, bezierCount, { local self = Animation.CurveTimeline.new(TimelineType.rgb2, ENTRIES, frameCount, bezierCount, {
Property.rgb.."|"..slotIndex, Property.rgb.."|"..slotIndex,
Property.rgb2.."|"..slotIndex Property.rgb2.."|"..slotIndex
}) })
@ -1189,7 +1220,7 @@ function Animation.RGB2Timeline.new (frameCount, bezierCount, slotIndex)
end end
local r, g, b, r2, g2, b2 local r, g, b, r2, g2, b2
local i = search2(frames, time, ENTRIES) local i = search(frames, time, ENTRIES)
local curveType = self.curves[math_floor(i / ENTRIES)] local curveType = self.curves[math_floor(i / ENTRIES)]
if curveType == LINEAR then if curveType == LINEAR then
local before = frames[i] local before = frames[i]
@ -1254,7 +1285,7 @@ end
Animation.AttachmentTimeline = {} Animation.AttachmentTimeline = {}
function Animation.AttachmentTimeline.new (frameCount, bezierCount, slotIndex) function Animation.AttachmentTimeline.new (frameCount, bezierCount, slotIndex)
local self = Animation.Timeline.new(frameCount, { Property.attachment + "|" + slotIndex }) local self = Animation.Timeline.new(TimelineType.attachment, 1, frameCount, { Property.attachment.."|"..slotIndex })
self.slotIndex = slotIndex self.slotIndex = slotIndex
self.attachmentNames = {} self.attachmentNames = {}
@ -1311,7 +1342,7 @@ end
Animation.DeformTimeline = {} Animation.DeformTimeline = {}
function Animation.DeformTimeline.new (frameCount, bezierCount, slotIndex, attachment) function Animation.DeformTimeline.new (frameCount, bezierCount, slotIndex, attachment)
local self = Animation.CurveTimeline.new(frameCount, bezierCount, { Property.deform + "|" + slotIndex + "|" + attachment.id }) local self = Animation.CurveTimeline.new(TimelineType.deform, 1, frameCount, bezierCount, { Property.deform.."|"..slotIndex.."|"..attachment.id })
self.slotIndex = slotIndex self.slotIndex = slotIndex
self.attachment = attachment self.attachment = attachment
self.vertices = {} self.vertices = {}
@ -1353,7 +1384,7 @@ function Animation.DeformTimeline.new (frameCount, bezierCount, slotIndex, attac
end end
end end
function getCurvePercent (time, frame) function self:getCurvePercent (time, frame)
local curves = self.curves local curves = self.curves
local i = curves[frame] local i = curves[frame]
if i == LINEAR then if i == LINEAR then
@ -1387,7 +1418,7 @@ function Animation.DeformTimeline.new (frameCount, bezierCount, slotIndex, attac
if not slot.bone.active then return end if not slot.bone.active then return end
local vertexAttachment = slot.attachment local vertexAttachment = slot.attachment
if not vertexAttachment or not vertexAttachment.vertexAttachment or vertexAttachment.deformAttachment ~= self.attachment then return end if not vertexAttachment or not vertexAttachment.isVertexAttachment or vertexAttachment.deformAttachment ~= self.attachment then return end
local frames = self.frames local frames = self.frames
local deform = slot.deform local deform = slot.deform
@ -1604,7 +1635,7 @@ end
Animation.EventTimeline = {} Animation.EventTimeline = {}
local eventPropertyIds = { Property.event } local eventPropertyIds = { Property.event }
function Animation.EventTimeline.new (frameCount) function Animation.EventTimeline.new (frameCount)
local self = Animation.Timeline.new(frameCount, eventPropertyIds) local self = Animation.Timeline.new(TimelineType.event, 1, frameCount, eventPropertyIds)
self.events = {} self.events = {}
function self:getFrameCount () function self:getFrameCount ()
@ -1635,7 +1666,7 @@ function Animation.EventTimeline.new (frameCount)
if lastTime < frames[0] then if lastTime < frames[0] then
i = 0 i = 0
else else
i = binarySearch1(frames, lastTime) i = search1(frames, lastTime) + 1
local i = frames[i] local i = frames[i]
while i > 0 do -- Fire multiple events with the same frame. while i > 0 do -- Fire multiple events with the same frame.
if frames[i - 1] ~= i then break end if frames[i - 1] ~= i then break end
@ -1654,7 +1685,7 @@ end
Animation.DrawOrderTimeline = {} Animation.DrawOrderTimeline = {}
local drawOrderPropertyIds = { Property.drawOrder } local drawOrderPropertyIds = { Property.drawOrder }
function Animation.DrawOrderTimeline.new (frameCount) function Animation.DrawOrderTimeline.new (frameCount)
local self = Animation.Timeline.new(frameCount, drawOrderPropertyIds) local self = Animation.Timeline.new(TimelineType.drawOrder, 1, frameCount, drawOrderPropertyIds)
self.drawOrders = {} self.drawOrders = {}
function self:getFrameCount () function self:getFrameCount ()
@ -1667,6 +1698,9 @@ function Animation.DrawOrderTimeline.new (frameCount)
end end
function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) function self:apply (skeleton, lastTime, time, events, alpha, blend, direction)
local drawOrder = skeleton.drawOrder
local slots = skeleton.slots
if direction == MixDirection.mixOut then if direction == MixDirection.mixOut then
if blend == MixBlend.setup then if blend == MixBlend.setup then
for i,slot in ipairs(slots) do for i,slot in ipairs(slots) do
@ -1691,8 +1725,6 @@ function Animation.DrawOrderTimeline.new (frameCount)
drawOrder[i] = slots[i] drawOrder[i] = slots[i]
end end
else else
local drawOrder = skeleton.drawOrder
local slots = skeleton.slots
for i,setupIndex in ipairs(drawOrderToSetupIndex) do for i,setupIndex in ipairs(drawOrderToSetupIndex) do
drawOrder[i] = skeleton.slots[setupIndex] drawOrder[i] = skeleton.slots[setupIndex]
end end
@ -1711,7 +1743,7 @@ function Animation.IkConstraintTimeline.new (frameCount, bezierCount, ikConstrai
local COMPRESS = 4 local COMPRESS = 4
local STRETCH = 5 local STRETCH = 5
local self = Animation.CurveTimeline.new(frameCount, bezierCount, { Property.ikConstraint + "|" + ikConstraintIndex }) local self = Animation.CurveTimeline.new(TimelineType.ikConstraint, ENTRIES, frameCount, bezierCount, { Property.ikConstraint.."|"..ikConstraintIndex })
self.ikConstraintIndex = ikConstraintIndex self.ikConstraintIndex = ikConstraintIndex
function self:getFrameEntries () function self:getFrameEntries ()
@ -1804,7 +1836,7 @@ function Animation.IkConstraintTimeline.new (frameCount, bezierCount, ikConstrai
end end
Animation.TransformConstraintTimeline = {} Animation.TransformConstraintTimeline = {}
function Animation.TransformConstraintTimeline.new (frameCount, transformConstraintIndex) function Animation.TransformConstraintTimeline.new (frameCount, bezierCount, transformConstraintIndex)
local ENTRIES = 7 local ENTRIES = 7
local ROTATE = 1 local ROTATE = 1
local X = 2 local X = 2
@ -1813,7 +1845,7 @@ function Animation.TransformConstraintTimeline.new (frameCount, transformConstra
local SCALEY = 5 local SCALEY = 5
local SHEARY = 6 local SHEARY = 6
local self = Animation.CurveTimeline.new(frameCount, bezierCount, { Property.transformConstraint + "|" + transformConstraintIndex }) local self = Animation.CurveTimeline.new(TimelineType.transformConstraint, ENTRIES, frameCount, bezierCount, { Property.transformConstraint.."|"..transformConstraintIndex })
self.transformConstraintIndex = transformConstraintIndex self.transformConstraintIndex = transformConstraintIndex
function self:getFrameEntries () function self:getFrameEntries ()
@ -1918,7 +1950,7 @@ end
Animation.PathConstraintPositionTimeline = {} Animation.PathConstraintPositionTimeline = {}
function Animation.PathConstraintPositionTimeline.new (frameCount, bezierCount, pathConstraintIndex) function Animation.PathConstraintPositionTimeline.new (frameCount, bezierCount, pathConstraintIndex)
local self = Animation.CurveTimeline1.new(frameCount, bezierCount, Property.pathConstraintPosition.."|"..pathConstraintIndex) local self = Animation.CurveTimeline1.new(TimelineType.pathConstraintPosition, frameCount, bezierCount, Property.pathConstraintPosition.."|"..pathConstraintIndex)
self.pathConstraintIndex = pathConstraintIndex self.pathConstraintIndex = pathConstraintIndex
function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) function self:apply (skeleton, lastTime, time, events, alpha, blend, direction)
@ -1948,7 +1980,7 @@ end
Animation.PathConstraintSpacingTimeline = {} Animation.PathConstraintSpacingTimeline = {}
function Animation.PathConstraintSpacingTimeline.new (frameCount, bezierCount, pathConstraintIndex) function Animation.PathConstraintSpacingTimeline.new (frameCount, bezierCount, pathConstraintIndex)
local self = Animation.CurveTimeline1.new(frameCount, bezierCount, Property.pathConstraintSpacing.."|"..pathConstraintIndex) local self = Animation.CurveTimeline1.new(TimelineType.pathConstraintSpacing, frameCount, bezierCount, Property.pathConstraintSpacing.."|"..pathConstraintIndex)
self.pathConstraintIndex = pathConstraintIndex self.pathConstraintIndex = pathConstraintIndex
function self:apply (skeleton, lastTime, time, events, alpha, blend, direction) function self:apply (skeleton, lastTime, time, events, alpha, blend, direction)
@ -1983,7 +2015,7 @@ function Animation.PathConstraintMixTimeline.new (frameCount, bezierCount, pathC
local X = 2 local X = 2
local Y = 3 local Y = 3
local self = Animation.CurveTimeline.new(frameCount, bezierCount, Property.pathConstraintMix.."|"..pathConstraintIndex) local self = Animation.CurveTimeline.new(TimelineType.pathConstraintMix, ENTRIES, frameCount, bezierCount, Property.pathConstraintMix.."|"..pathConstraintIndex)
self.pathConstraintIndex = pathConstraintIndex self.pathConstraintIndex = pathConstraintIndex
function self:getFrameEntries () function self:getFrameEntries ()
@ -2006,13 +2038,13 @@ function Animation.PathConstraintMixTimeline.new (frameCount, bezierCount, pathC
local frames = self.frames local frames = self.frames
if time < frames[0] then if time < frames[0] then
if blend == MixBlend.setup then if blend == MixBlend.setup then
constraint.mixRotate = constraint.data.mixRotate; constraint.mixRotate = constraint.data.mixRotate
constraint.mixX = constraint.data.mixX; constraint.mixX = constraint.data.mixX
constraint.mixY = constraint.data.mixY; constraint.mixY = constraint.data.mixY
elseif blend == MixBlend.first then elseif blend == MixBlend.first then
constraint.mixRotate = constraint.mixRotate + (constraint.data.mixRotate - constraint.mixRotate) * alpha; constraint.mixRotate = constraint.mixRotate + (constraint.data.mixRotate - constraint.mixRotate) * alpha
constraint.mixX = constraint.mixX + (constraint.data.mixX - constraint.mixX) * alpha; constraint.mixX = constraint.mixX + (constraint.data.mixX - constraint.mixX) * alpha
constraint.mixY = constraint.mixY + (constraint.data.mixY - constraint.mixY) * alpha; constraint.mixY = constraint.mixY + (constraint.data.mixY - constraint.mixY) * alpha
end end
return return
end end
@ -2020,36 +2052,36 @@ function Animation.PathConstraintMixTimeline.new (frameCount, bezierCount, pathC
local rotate local rotate
local x local x
local y local y
local i = search(frames, time, ENTRIES); local i = search(frames, time, ENTRIES)
local curveType = self.curves[math_floor(i / 4)]; local curveType = self.curves[math_floor(i / 4)]
if curveType == LINEAR then if curveType == LINEAR then
local before = frames[i]; local before = frames[i]
rotate = frames[i + ROTATE]; rotate = frames[i + ROTATE]
x = frames[i + X]; x = frames[i + X]
y = frames[i + Y]; y = frames[i + Y]
local t = (time - before) / (frames[i + ENTRIES] - before); local t = (time - before) / (frames[i + ENTRIES] - before)
rotate = rotate + (frames[i + ENTRIES + ROTATE] - rotate) * t; rotate = rotate + (frames[i + ENTRIES + ROTATE] - rotate) * t
x = x + (frames[i + ENTRIES + X] - x) * t; x = x + (frames[i + ENTRIES + X] - x) * t
y = y + (frames[i + ENTRIES + Y] - y) * t; y = y + (frames[i + ENTRIES + Y] - y) * t
elseif curveType == STEPPED then elseif curveType == STEPPED then
rotate = frames[i + ROTATE]; rotate = frames[i + ROTATE]
x = frames[i + X]; x = frames[i + X]
y = frames[i + Y]; y = frames[i + Y]
else else
rotate = this.getBezierValue(time, i, ROTATE, curveType - BEZIER); rotate = this.getBezierValue(time, i, ROTATE, curveType - BEZIER)
x = this.getBezierValue(time, i, X, curveType + BEZIER_SIZE - BEZIER); x = this.getBezierValue(time, i, X, curveType + BEZIER_SIZE - BEZIER)
y = this.getBezierValue(time, i, Y, curveType + BEZIER_SIZE * 2 - BEZIER); y = this.getBezierValue(time, i, Y, curveType + BEZIER_SIZE * 2 - BEZIER)
end end
if blend == MixBlend.setup then if blend == MixBlend.setup then
local data = constraint.data; local data = constraint.data
constraint.mixRotate = data.mixRotate + (rotate - data.mixRotate) * alpha; constraint.mixRotate = data.mixRotate + (rotate - data.mixRotate) * alpha
constraint.mixX = data.mixX + (x - data.mixX) * alpha; constraint.mixX = data.mixX + (x - data.mixX) * alpha
constraint.mixY = data.mixY + (y - data.mixY) * alpha; constraint.mixY = data.mixY + (y - data.mixY) * alpha
else else
constraint.mixRotate = constraint.mixRotate + (rotate - constraint.mixRotate) * alpha; constraint.mixRotate = constraint.mixRotate + (rotate - constraint.mixRotate) * alpha
constraint.mixX = constraint.mixX + (x - constraint.mixX) * alpha; constraint.mixX = constraint.mixX + (x - constraint.mixX) * alpha
constraint.mixY = constraint.mixY + (y - constraint.mixY) * alpha; constraint.mixY = constraint.mixY + (y - constraint.mixY) * alpha
end end
end end

View File

@ -198,12 +198,22 @@ function TrackEntry:getAnimationTime ()
return math_min(self.trackTime + self.animationStart, self.animationEnd) return math_min(self.trackTime + self.animationStart, self.animationEnd)
end end
function TrackEntry:getTrackComplete ()
local duration = self.animationEnd - self.animationStart
if duration ~= 0 then
if self.loop then return duration * (1 + math_floor(self.trackTime / duration)) end -- Completion of next loop.
if self.trackTime < duration then return duration end -- Before duration.
end
return self.trackTime -- Next update.
end
function TrackEntry:resetRotationDirections () function TrackEntry:resetRotationDirections ()
self.timelinesRotation = {} self.timelinesRotation = {}
end end
local AnimationState = {} local AnimationState = {}
AnimationState.__index = AnimationState AnimationState.__index = AnimationState
AnimationState.TrackEntry = TrackEntry
function AnimationState.new (data) function AnimationState.new (data)
if not data then error("data cannot be nil", 2) end if not data then error("data cannot be nil", 2) end
@ -225,8 +235,6 @@ function AnimationState.new (data)
return self return self
end end
AnimationState.TrackEntry = TrackEntry
function AnimationState:update (delta) function AnimationState:update (delta)
delta = delta * self.timeScale delta = delta * self.timeScale
local tracks = self.tracks local tracks = self.tracks
@ -359,31 +367,35 @@ function AnimationState:apply (skeleton)
-- Apply current entry. -- Apply current entry.
local animationLast = current.animationLast local animationLast = current.animationLast
local animationTime = current:getAnimationTime() local animationTime = current:getAnimationTime()
local applyTime = animationTime
local applyEvents = self.events
if current.reverse then
applyTime = current.animation.duration - applyTime
applyEvents = nil
end
local timelines = current.animation.timelines local timelines = current.animation.timelines
if (i == 0 and mix == 1) or blend == MixBlend.add then if (i == 0 and mix == 1) or blend == MixBlend.add then
for i,timeline in ipairs(timelines) do for i,timeline in ipairs(timelines) do
if timeline.type == Animation.TimelineType.attachment then if timeline.type == Animation.TimelineType.attachment then
self:applyAttachmentTimeline(timeline, skeleton, animationTime, blend, true) self:applyAttachmentTimeline(timeline, skeleton, applyTime, blend, true)
else else
timeline:apply(skeleton, animationLast, animationTime, self.events, mix, blend, MixDirection.mixIn) timeline:apply(skeleton, animationLast, applyTime, applyEvents, mix, blend, MixDirection.mixIn)
end end
end end
else else
local timelineMode = current.timelineMode local timelineMode = current.timelineMode
local firstFrame = #current.timelinesRotation == 0 local firstFrame = #current.timelinesRotation ~= #timelines * 2
local timelinesRotation = current.timelinesRotation
for ii,timeline in ipairs(timelines) do for ii,timeline in ipairs(timelines) do
local timelineBlend = MixBlend.setup local timelineBlend = MixBlend.setup
if timelineMode[ii] == SUBSEQUENT then timelineBlend = blend end if timelineMode[ii] == SUBSEQUENT then timelineBlend = blend end
if timeline.type == Animation.TimelineType.rotate then if timeline.type == Animation.TimelineType.rotate then
self:applyRotateTimeline(timeline, skeleton, animationTime, mix, timelineBlend, timelinesRotation, ii * 2, self:applyRotateTimeline(timeline, skeleton, applyTime, mix, timelineBlend, current.timelinesRotation, ii * 2, firstFrame)
firstFrame)
elseif timeline.type == Animation.TimelineType.attachment then elseif timeline.type == Animation.TimelineType.attachment then
self:applyAttachmentTimeline(timeline, skeleton, animationTime, timelineBlend, true) self:applyAttachmentTimeline(timeline, skeleton, applyTime, timelineBlend, true)
else else
timeline:apply(skeleton, animationLast, animationTime, self.events, mix, timelineBlend, MixDirection.mixIn) timeline:apply(skeleton, animationLast, applyTime, applyEvents, mix, timelineBlend, MixDirection.mixIn)
end end
end end
end end
@ -401,7 +413,7 @@ function AnimationState:apply (skeleton)
-- the time is before the first key). -- the time is before the first key).
local setupState = self.unkeyedState + SETUP local setupState = self.unkeyedState + SETUP
local slots = skeleton.slots local slots = skeleton.slots
for _, slot in ipairs(slots) do for _,slot in ipairs(slots) do
if slot.attachmentState == setupState then if slot.attachmentState == setupState then
local attachmentName = slot.data.attachmentName local attachmentName = slot.data.attachmentName
if attachmentName == nil then if attachmentName == nil then
@ -432,25 +444,31 @@ function AnimationState:applyMixingFrom (to, skeleton, blend)
if blend ~= MixBlend.first then blend = from.mixBlend end if blend ~= MixBlend.first then blend = from.mixBlend end
end end
local events = nil
if mix < from.eventThreshold then events = self.events end
local attachments = mix < from.attachmentThreshold local attachments = mix < from.attachmentThreshold
local drawOrder = mix < from.drawOrderThreshold local drawOrder = mix < from.drawOrderThreshold
local animationLast = from.animationLast
local animationTime = from:getAnimationTime()
local timelines = from.animation.timelines local timelines = from.animation.timelines
local alphaHold = from.alpha * to.interruptAlpha local alphaHold = from.alpha * to.interruptAlpha
local alphaMix = alphaHold * (1 - mix) local alphaMix = alphaHold * (1 - mix)
local animationLast = from.animationLast
local animationTime = from:getAnimationTime()
local applyTime = animationTime
local events = nil
if from.reverse then
applyTime = from.animation.duration - applyTime
elseif mix < from.eventThreshold then
events = self.events
end
if blend == MixBlend.add then if blend == MixBlend.add then
for i,timeline in ipairs(timelines) do for i,timeline in ipairs(timelines) do
timeline:apply(skeleton, animationLast, animationTime, events, alphaMix, blend, MixDirection.mixOut) timeline:apply(skeleton, animationLast, applyTime, events, alphaMix, blend, MixDirection.mixOut)
end end
else else
local timelineMode = from.timelineMode local timelineMode = from.timelineMode
local timelineHoldMix = from.timelineHoldMix local timelineHoldMix = from.timelineHoldMix
local firstFrame = #from.timelinesRotation == 0 local firstFrame = #from.timelinesRotation ~= #timelines
local timelinesRotation = from.timelinesRotation local timelinesRotation = from.timelinesRotation
from.totalAlpha = 0 from.totalAlpha = 0
@ -473,7 +491,7 @@ function AnimationState:applyMixingFrom (to, skeleton, blend)
elseif timelineMode[i] == HOLD_FIRST then elseif timelineMode[i] == HOLD_FIRST then
timelineBlend = MixBlend.setup timelineBlend = MixBlend.setup
alpha = alphaHold alpha = alphaHold
else else -- HOLD_MIX
timelineBlend = MixBlend.setup timelineBlend = MixBlend.setup
local holdMix = timelineHoldMix[i] local holdMix = timelineHoldMix[i]
alpha = alphaHold * math_max(0, 1 - holdMix.mixtime / holdMix.mixDuration) alpha = alphaHold * math_max(0, 1 - holdMix.mixtime / holdMix.mixDuration)
@ -482,14 +500,14 @@ function AnimationState:applyMixingFrom (to, skeleton, blend)
if not skipSubsequent then if not skipSubsequent then
from.totalAlpha = from.totalAlpha + alpha from.totalAlpha = from.totalAlpha + alpha
if timeline.type == Animation.TimelineType.rotate then if timeline.type == Animation.TimelineType.rotate then
self:applyRotateTimeline(timeline, skeleton, animationTime, alpha, timelineBlend, timelinesRotation, i * 2, firstFrame) self:applyRotateTimeline(timeline, skeleton, applyTime, alpha, timelineBlend, timelinesRotation, i * 2, firstFrame)
elseif timeline.type == Animation.TimelineType.attachment then elseif timeline.type == Animation.TimelineType.attachment then
self:applyAttachmentTimeline(timeline, skeleton, animationTime, timelineBlend, attachments) self:applyAttachmentTimeline(timeline, skeleton, applyTime, timelineBlend, attachments)
else else
if drawOrder and timeline.type == Animation.TimelineType.drawOrder and timelineBlend == MixBlend.setup then if drawOrder and timeline.type == Animation.TimelineType.drawOrder and timelineBlend == MixBlend.setup then
direction = MixDirection.mixIn direction = MixDirection.mixIn
end end
timeline:apply(skeleton, animationLast, animationTime, self.events, alpha, timelineBlend, direction) timeline:apply(skeleton, animationLast, applyTime, events, alpha, timelineBlend, direction)
end end
end end
end end
@ -509,19 +527,12 @@ function AnimationState:applyAttachmentTimeline(timeline, skeleton, time, blend,
local slot = skeleton.slots[timeline.slotIndex] local slot = skeleton.slots[timeline.slotIndex]
if slot.bone.active == false then return end if slot.bone.active == false then return end
local frames = timeline.frames if time < timeline.frames[0] then -- Time is before first frame.
if time < frames[0] then -- Time is before first frame.
if blend == MixBlend.setup or blend == MixBlend.first then if blend == MixBlend.setup or blend == MixBlend.first then
self:setAttachment(skeleton, slot, slot.data.attachmentName, attachments) self:setAttachment(skeleton, slot, slot.data.attachmentName, attachments)
end end
else else
local frameIndex = 0 self:setAttachment(skeleton, slot, timeline.attachmentNames[Timeline.search1(timeline.frames, time)], attachments)
if time >= frames[zlen(frames) - 1] then -- Time is after last frame.
frameIndex = zlen(frames) - 1
else
frameIndex = Animation.binarySearch(frames, time, 1) - 1
end
self:setAttachment(skeleton, slot, timeline.attachmentNames[frameIndex], attachments)
end end
-- If an attachment wasn't set (ie before the first frame or attachments is false), set the setup attachment later. -- If an attachment wasn't set (ie before the first frame or attachments is false), set the setup attachment later.
@ -548,10 +559,9 @@ function AnimationState:applyRotateTimeline (timeline, skeleton, time, alpha, bl
return return
end end
local rotateTimeline = timeline local bone = skeleton.bones[timeline.boneIndex]
local frames = rotateTimeline.frames
local bone = skeleton.bones[rotateTimeline.boneIndex]
if not bone.active then return end if not bone.active then return end
local frames = timeline.frames
local r1 = 0 local r1 = 0
local r2 = 0 local r2 = 0
if time < frames[0] then if time < frames[0] then
@ -570,21 +580,7 @@ function AnimationState:applyRotateTimeline (timeline, skeleton, time, alpha, bl
else else
r1 = bone.rotation r1 = bone.rotation
end end
if time >= frames[zlen(frames) - Animation.RotateTimeline.ENTRIES] then -- Time is after last frame. r2 = bone.data.rotation + timeline:getCurveValue(time)
r2 = bone.data.rotation + frames[zlen(frames) + Animation.RotateTimeline.PREV_ROTATION]
else
-- Interpolate between the previous frame and the current frame.
local frame = Animation.binarySearch(frames, time, Animation.RotateTimeline.ENTRIES)
local prevRotation = frames[frame + Animation.RotateTimeline.PREV_ROTATION]
local frameTime = frames[frame]
local percent = rotateTimeline:getCurvePercent(math_floor(frame / 2) - 1,
1 - (time - frameTime) / (frames[frame + Animation.RotateTimeline.PREV_TIME] - frameTime))
r2 = frames[frame + Animation.RotateTimeline.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
end end
-- Mix between rotations using the direction of the shortest route on the first frame while detecting crosses. -- Mix between rotations using the direction of the shortest route on the first frame while detecting crosses.
@ -616,8 +612,7 @@ function AnimationState:applyRotateTimeline (timeline, skeleton, time, alpha, bl
timelinesRotation[i] = total timelinesRotation[i] = total
end end
timelinesRotation[i + 1] = diff timelinesRotation[i + 1] = diff
r1 = r1 + total * alpha bone.rotation = r1 + total * alpha
bone.rotation = r1 - (16384 - math_floor(16384.499999999996 - r1 / 360)) * 360
end end
function AnimationState:queueEvents (entry, animationTime) function AnimationState:queueEvents (entry, animationTime)
@ -654,7 +649,7 @@ function AnimationState:queueEvents (entry, animationTime)
-- Queue events after complete. -- Queue events after complete.
while i <= n do while i <= n do
local event = events[i] local event = events[i]
if not (event.time < animationStart) then --// Discard events outside animation start/end. if event.time >= animationStart then -- Discard events outside animation start/end.
queue:event(entry, event) queue:event(entry, event)
end end
i = i + 1 i = i + 1
@ -701,14 +696,17 @@ function AnimationState:clearTrack (trackIndex)
queue:drain() queue:drain()
end end
function AnimationState:clearNext (entry)
self:disposeNext(entry.next)
end
function AnimationState:setCurrent (index, current, interrupt) function AnimationState:setCurrent (index, current, interrupt)
local from = self:expandToIndex(index) local from = self:expandToIndex(index)
local tracks = self.tracks self.tracks[index] = current
local queue = self.queue current.previous = nil
tracks[index] = current
if from then if from then
if interrupt then queue:interrupt(from) end if interrupt then self.queue:interrupt(from) end
current.mixingFrom = from current.mixingFrom = from
from.mixingTo = current from.mixingTo = current
current.mixTime = 0 current.mixTime = 0
@ -720,7 +718,7 @@ function AnimationState:setCurrent (index, current, interrupt)
from.timelinesRotation = {} from.timelinesRotation = {}
end end
queue:start(current) self.queue:start(current)
end end
function AnimationState:setAnimationByName (trackIndex, animationName, loop) function AnimationState:setAnimationByName (trackIndex, animationName, loop)
@ -779,19 +777,8 @@ function AnimationState:addAnimation (trackIndex, animation, loop, delay)
queue:drain() queue:drain()
else else
last.next = entry last.next = entry
if delay <= 0 then entry.previous = last
local duration = last.animationEnd - last.animationStart if delay <= 0 then delay = delay + last:getTrackComplete() - entry.mixDuration end
if duration ~= 0 then
if last.loop then
delay = delay + duration * (1 + math_floor(last.trackTime / duration))
else
delay = delay + math_max(duration, last.trackTime)
end
delay = delay - data:getMix(last.animation, animation)
else
delay = last.trackTime
end
end
end end
entry.delay = delay entry.delay = delay
@ -806,10 +793,14 @@ function AnimationState:setEmptyAnimation (trackIndex, mixDuration)
end end
function AnimationState:addEmptyAnimation (trackIndex, mixDuration, delay) function AnimationState:addEmptyAnimation (trackIndex, mixDuration, delay)
if delay <= 0 then delay = delay - mixDuration end local addDelay = 1
local entry = self:addAnimation(trackIndex, EMPTY_ANIMATION, false, delay) if delay > 0 then addDelay = delay end
local entry = self:addAnimation(trackIndex, EMPTY_ANIMATION, false, addDelay)
entry.mixDuration = mixDuration entry.mixDuration = mixDuration
entry.trackEnd = mixDuration entry.trackEnd = mixDuration
if delay <= 0 and entry.previous then
entry.delay = entry.previous:getTrackComplete() - entry.mixDuration + delay
end
return entry return entry
end end
@ -895,28 +886,19 @@ function AnimationState:_animationsChanged ()
self.animationsChanged = false self.animationsChanged = false
self.propertyIDs = {} self.propertyIDs = {}
local highestIndex = -1
local tracks = self.tracks local tracks = self.tracks
local numTracks = getNumTracks(tracks)
local i = 0 local i = 0
while i <= numTracks do local n = zlen(tracks)
entry = tracks[i] while i < n do
local entry = tracks[i]
if entry then if entry then
if i > highestIndex then highestIndex = i end while entry.mixingFrom do
entry = entry.mixingFrom
if entry then
while entry.mixingFrom do
entry = entry.mixingFrom
end
repeat
if entry.mixingTo == nil or entry.mixBlend ~= MixBlend.add then
self:computeHold(entry)
end
entry = entry.mixingTo
until (entry == nil)
end end
repeat
if not entry.mixingTo or entry.mixBlend ~= MixBlend.add then self:computeHold(entry) end
entry = entry.mixingTo
until not entry
end end
i = i + 1 i = i + 1
end end
@ -925,57 +907,57 @@ end
function AnimationState:computeHold(entry) function AnimationState:computeHold(entry)
local to = entry.mixingTo local to = entry.mixingTo
local timelines = entry.animation.timelines local timelines = entry.animation.timelines
local timelinesCount = #entry.animation.timelines
local timelineMode = entry.timelineMode local timelineMode = entry.timelineMode
local timelineHoldMix = entry.timelineHoldMix local timelineHoldMix = entry.timelineHoldMix
local propertyIDs = self.propertyIDs local propertyIDs = self.propertyIDs
if to and to.holdPrevious then if to and to.holdPrevious then
local i = 1 for i,timeline in ipairs(timelines) do
while i <= timelinesCount do local mode = HOLD_SUBSEQUENT
local id = "" .. timelines[i]:getPropertyId() for _,id in ipairs(timeline.propertyIds) do
if propertyIDs[id] == nil then if not propertyIDs[id] then
propertyIDs[id] = id propertyIDs[id] = true
timelineMode[i] = HOLD_FIRST mode = HOLD_FIRST
else end
timelineMode[i] = HOLD_SUBSEQUENT
end end
timelineMode[i] = mode
end end
return return
end end
local i = 1 for i,timeline in ipairs(timelines) do
local skip local ids = timeline.propertyIds
while i <= timelinesCount do local added = false
local id = "" .. timelines[i]:getPropertyId() for _,id in ipairs(ids) do
if propertyIDs[id] then if not propertyIDs[id] then
timelineMode[i] = SUBSEQUENT propertyIDs[id] = true
else added = true
propertyIDs[id] = id
local timeline = timelines[i]
if to == nil or timeline.type == Animation.TimelineType.attachment
or timeline.type == Animation.TimelineType.drawOrder
or timeline.type == Animation.TimelineType.event
or not to.animation:hasTimeline(id) then
timelineMode[i] = FIRST
else
local next = to.mixingTo
skip = false
while next do
if not next.animation:hasTimeline(id) then
if entry.mixDuration > 0 then
timelineMode[i] = HOLD_MIX
timelineHoldMix[i] = next
skip = true
break
end
end
next = next.mixingTo
end
if not skip then timelineMode[i] = HOLD_FIRST end
end end
end end
i = i + 1 if not added then
timelineMode[i] = SUBSEQUENT
elseif not to
or timeline.type == Animation.TimelineType.attachment
or timeline.type == Animation.TimelineType.drawOrder
or timeline.type == Animation.TimelineType.event
or not to.animation:hasTimeline(ids) then
timelineMode[i] = FIRST
else
local next = to.mixingTo
local set
while next do
if not next.animation:hasTimeline(ids) then
if next.mixDuration > 0 then
timelineMode[i] = HOLD_MIX
timelineHoldMix[i] = next
set = true
end
break
end
next = next.mixingTo
end
if not set then timelineMode[i] = HOLD_FIRST end
end
end end
end end

View File

@ -1,104 +0,0 @@
-------------------------------------------------------------------------------
-- Spine Runtimes License Agreement
-- Last updated January 1, 2020. Replaces all prior versions.
--
-- Copyright (c) 2013-2020, Esoteric Software LLC
--
-- Integration of the Spine Runtimes into software or otherwise creating
-- derivative works of the Spine Runtimes is permitted under the terms and
-- conditions of Section 2 of the Spine Editor License Agreement:
-- http://esotericsoftware.com/spine-editor-license
--
-- Otherwise, it is permitted to integrate the Spine Runtimes into software
-- or otherwise create derivative works of the Spine Runtimes (collectively,
-- "Products"), provided that each user of the Products must obtain their own
-- Spine Editor license and redistribution of the Products in any form must
-- include this license and copyright notice.
--
-- THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 SOFTWARE LLC BE LIABLE FOR ANY
-- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
-- BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) 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
-- THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-------------------------------------------------------------------------------
local Atlas = {}
function Atlas.parse(atlasPath, atlasBase)
local function parseIntTuple4( l )
local a,b,c,d = string.match( l , " ? ?%a+: ([+-]?%d+), ?([+-]?%d+), ?([+-]?%d+), ?([+-]?%d+)" )
a,b,c,d = tonumber( a ), tonumber( b ), tonumber( c ), tonumber( d )
return a and b and c and d and {a, b, c ,d}
end
local function parseIntTuple2( l )
local a,b = string.match( l , " ? ?%a+: ([+-]?%d+), ?([+-]?%d+)" )
a,b = tonumber( a ), tonumber( b )
return a and b and {a, b}
end
if not atlasPath then
error("Error: " .. atlasPath .. ".atlas" .. " doesn't exist!", 2)
return nil
end
local atlasLines = spine.utils.readFile( atlasPath, atlasBase )
if not atlasLines then
error("Error: " .. atlasPath .. ".atlas" .. " unable to read!", 2)
return nil
end
local pages = {}
local it = string.gmatch(atlasLines, "(.-)\r?\n") -- iterate over lines
for l in it do
if #l == 0 then
l = it()
if l then
local page = { name = l }
l = it()
page.size = parseIntTuple2( l )
if page.size then
l = it()
end
page.format = string.match( l, "%a+: (.+)" )
page.filter = {string.match( it(), "%a+: (.+),(.+)" )}
page.wrap = string.match( it(), "%a+: (.+)" )
page.regions = {}
table.insert( pages, page )
else
break
end
else
local region = {name = l}
region.rotate = string.match( it(), "%a+: (.+)" ) == "true"
region.xy = parseIntTuple2( it() )
region.size = parseIntTuple2( it() )
l = it()
region.splits = parseIntTuple4(l)
if region.splits then
l = it()
region.pad = parseIntTuple4(l)
if region.pad then
l = it()
end
end
region.orig = parseIntTuple2( l )
region.offset = parseIntTuple2( it() )
region.index = tonumber( string.match( it() , "%a+: ([+-]?%d+)" ) )
table.insert( pages[#pages].regions, region )
end
end
return pages
end
return Atlas

View File

@ -27,6 +27,8 @@
-- THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
local TransformMode = require "spine-lua.TransformMode"
local setmetatable = setmetatable local setmetatable = setmetatable
local math_rad = math.rad local math_rad = math.rad
local math_deg = math.deg local math_deg = math.deg
@ -37,19 +39,6 @@ local math_sqrt = math.sqrt
local math_abs = math.abs local math_abs = math.abs
local math_pi = math.pi local math_pi = math.pi
local TransformMode = require "spine-lua.TransformMode"
function math.sign(x)
if x < 0 then
return -1
elseif x > 0 then
return 1
end
return 0
end
local math_sign = math.sign
local Bone = {} local Bone = {}
Bone.__index = Bone Bone.__index = Bone
@ -64,7 +53,6 @@ function Bone.new (data, skeleton, parent)
children = { }, children = { },
x = 0, y = 0, rotation = 0, scaleX = 1, scaleY = 1, shearX = 0, shearY = 0, x = 0, y = 0, rotation = 0, scaleX = 1, scaleY = 1, shearX = 0, shearY = 0,
ax = 0, ay = 0, arotation = 0, ascaleX = 0, ascaleY = 0, ashearX = 0, ashearY = 0, ax = 0, ay = 0, arotation = 0, ascaleX = 0, ascaleY = 0, ashearX = 0, ashearY = 0,
appliedValid = false,
a = 0, b = 0, worldX = 0, -- a b x a = 0, b = 0, worldX = 0, -- a b x
c = 0, d = 0, worldY = 0, -- c d y c = 0, d = 0, worldY = 0, -- c d y
@ -78,7 +66,7 @@ function Bone.new (data, skeleton, parent)
end end
function Bone:update () function Bone:update ()
self:updateWorldTransformWith(self.x, self.y, self.rotation, self.scaleX, self.scaleY, self.shearX, self.shearY) self:updateWorldTransformWith(self.ax, self.ay, self.arotation, self.ascaleX, self.ascaleY, self.ashearX, self.ashearY)
end end
function Bone:updateWorldTransform () function Bone:updateWorldTransform ()
@ -93,13 +81,12 @@ function Bone:updateWorldTransformWith (x, y, rotation, scaleX, scaleY, shearX,
self.ascaleY = scaleY self.ascaleY = scaleY
self.ashearX = shearX self.ashearX = shearX
self.ashearY = shearY self.ashearY = shearY
self.appliedValid = true
local sx = self.skeleton.scaleX local sx = self.skeleton.scaleX
local sy = self.skeleton.scaleY local sy = self.skeleton.scaleY
local parent = self.parent local parent = self.parent
if parent == nil then if not parent then
local rotationY = rotation + 90 + shearY local rotationY = rotation + 90 + shearY
local rotationRad = math_rad(rotation + shearX) local rotationRad = math_rad(rotation + shearX)
local rotationYRad = math_rad(rotationY) local rotationYRad = math_rad(rotationY)
@ -225,7 +212,7 @@ end
function Bone:updateAppliedTransform () function Bone:updateAppliedTransform ()
local parent = self.parent local parent = self.parent
if parent == nil then if not parent then
self.ax = self.worldX self.ax = self.worldX
self.ay = self.worldY self.ay = self.worldY
self.arotation = math_deg(math_atan2(self.c, self.a)) self.arotation = math_deg(math_atan2(self.c, self.a))
@ -268,15 +255,11 @@ function Bone:updateAppliedTransform ()
end end
function Bone:worldToLocal (world) function Bone:worldToLocal (world)
local a = self.a local invDet = 1 / (self.a * self.d - self.b * self.c)
local b = self.b
local c = self.c
local d = self.d
local invDet = 1 / (a * d - b * c)
local x = world[1] - self.worldX local x = world[1] - self.worldX
local y = world[2] - self.worldY local y = world[2] - self.worldY
world[1] = (x * d * invDet - y * b * invDet) world[1] = x * self.d * invDet - y * self.b * invDet
world[2] = (y * a * invDet - x * c * invDet) world[2] = y * self.a * invDet - x * self.c * invDet
return world return world
end end
@ -313,7 +296,6 @@ function Bone:rotateWorld (degrees)
self.b = cos * b - sin * d self.b = cos * b - sin * d
self.c = sin * a + cos * c self.c = sin * a + cos * c
self.d = sin * b + cos * d self.d = sin * b + cos * d
self.appliedValid = false
end end
return Bone return Bone

View File

@ -76,6 +76,7 @@ function IkConstraint:apply ()
end end
function IkConstraint:update () function IkConstraint:update ()
if self.mix == 0 then return end
local target = self.target local target = self.target
local bones = self.bones local bones = self.bones
local boneCount = #bones local boneCount = #bones
@ -87,9 +88,7 @@ function IkConstraint:update ()
end end
function IkConstraint:apply1 (bone, targetX, targetY, compress, stretch, uniform, alpha) function IkConstraint:apply1 (bone, targetX, targetY, compress, stretch, uniform, alpha)
if not bone.appliedValid then bone:updateAppliedTransform() end
local p = bone.parent local p = bone.parent
local pa = p.a local pa = p.a
local pb = p.b local pb = p.b
local pc = p.c local pc = p.c
@ -148,12 +147,6 @@ function IkConstraint:apply1 (bone, targetX, targetY, compress, stretch, uniform
end end
function IkConstraint:apply2 (parent, child, targetX, targetY, bendDir, stretch, softness, alpha) function IkConstraint:apply2 (parent, child, targetX, targetY, bendDir, stretch, softness, alpha)
if alpha == 0 then
child:updateWorldTransform()
return
end
if not parent.appliedValid then parent:updateAppliedTransform() end
if not child.appliedValid then child:updateAppliedTransform() end
local px = parent.ax local px = parent.ax
local py = parent.ay local py = parent.ay
local psx = parent.ascaleX local psx = parent.ascaleX

View File

@ -36,7 +36,7 @@ local AttachmentType = require "spine-lua.attachments.AttachmentType"
local PathConstraintData = require "spine-lua.PathConstraintData" local PathConstraintData = require "spine-lua.PathConstraintData"
local utils = require "spine-lua.utils" local utils = require "spine-lua.utils"
local math_pi = math.pi local math_pi = math.pi
local math_pi2 = math.pi * 2 local math_pi2 = math_pi * 2
local math_atan2 = math.atan2 local math_atan2 = math.atan2
local math_sqrt = math.sqrt local math_sqrt = math.sqrt
local math_acos = math.acos local math_acos = math.acos
@ -51,10 +51,10 @@ local math_max = math.max
local PathConstraint = {} local PathConstraint = {}
PathConstraint.__index = PathConstraint PathConstraint.__index = PathConstraint
PathConstraint.NONE = -1 local NONE = -1
PathConstraint.BEFORE = -2 local BEFORE = -2
PathConstraint.AFTER = -3 local AFTER = -3
PathConstraint.epsilon = 0.00001 local epsilon = 0.00001
function PathConstraint.new (data, skeleton) function PathConstraint.new (data, skeleton)
if not data then error("data cannot be nil", 2) end if not data then error("data cannot be nil", 2) end
@ -66,8 +66,9 @@ function PathConstraint.new (data, skeleton)
target = skeleton:findSlot(data.target.name), target = skeleton:findSlot(data.target.name),
position = data.position, position = data.position,
spacing = data.spacing, spacing = data.spacing,
rotateMix = data.rotateMix, mixRotate = data.mixRotate,
translateMix = data.translateMix, mixX = data.mixX,
mixY = data.mixY,
spaces = {}, spaces = {},
positions = {}, positions = {},
world = {}, world = {},
@ -85,81 +86,113 @@ function PathConstraint.new (data, skeleton)
return self return self
end end
function PathConstraint:apply ()
self:update()
end
function PathConstraint:update () function PathConstraint:update ()
local attachment = self.target.attachment local attachment = self.target.attachment
if not attachment or not (attachment.type == AttachmentType.path) then return end if not attachment or attachment.type ~= AttachmentType.path then return end
local rotateMix = self.rotateMix local mixRotate = self.mixRotate
local translateMix = self.translateMix local mixX = self.mixX
local translate = translateMix > 0 local mixY = self.mixY
local rotate = rotateMix > 0 if mixRotate == 0 and mixX == 0 and mixY == 0 then return end
if not translate and not rotate then return end
local data = self.data local data = self.data
local percentSpacing = data.spacingMode == PathConstraintData.SpacingMode.percent
local rotateMode = data.rotateMode
local tangents = rotateMode == PathConstraintData.RotateMode.tangent local tangents = rotateMode == PathConstraintData.RotateMode.tangent
local scale = rotateMode == PathConstraintData.RotateMode.chainscale local scale = rotateMode == PathConstraintData.RotateMode.chainscale
local bones = self.bones local bones = self.bones
local boneCount = #bones local boneCount = #bones
local spacesCount = boneCount + 1 local spacesCount = boneCount
if tangents then spacesCount = boneCount end if tangents then spacesCount = spacesCount + 1 end
local spaces = utils.setArraySize(self.spaces, spacesCount) local spaces = utils.setArraySize(self.spaces, spacesCount)
local lengths = nil local lengths = nil
if scale then lengths = Utils.setArraySize(this.lengths, boneCount) end
local spacing = self.spacing local spacing = self.spacing
if scale or not percentSpacing then
if scale then lengths = utils.setArraySize(self.lengths, boneCount) end if data.spacingMode == PathConstraintData.SpacingMode.percent then
local lengthSpacing = data.spacingMode == PathConstraintData.SpacingMode.length if scale then
local i = 0 local i = 0
local n = spacesCount - 1 local n = spacesCount - 1
while i < n do while i < n do
local bone = bones[i + 1] local bone = bones[i]
local setupLength = bone.data.length local setupLength = bone.data.length
if setupLength < PathConstraint.epsilon then if setupLength < epsilon then
if scale then lengths[i + 1] = 0 end lengths[i] = 0
i = i + 1 else
spaces[i + 1] = 0
elseif percentSpacing then
if scale then
local x = setupLength * bone.a local x = setupLength * bone.a
local y = setupLength * bone.c local y = setupLength * bone.c
local length = math_sqrt(x * x + y * y) lengths[i] = math_sqrt(x * x + y * y)
lengths[i + 1] = length
end end
i = i + 1 i = i + 1
spaces[i + 1] = spacing end
end
local i = 1
while i < spacesCount do
spaces[i] = spacing
i = i + 1
end
elseif data.spacingMode == PathConstraintData.SpacingMode.proportional then
local sum = 0
local i = 0
while i < boneCount do
local bone = bones[i]
local setupLength = bone.data.length
if setupLength < epsilon then
if scale then lengths[i] = 0 end
i = i + 1
spaces[i] = spacing
else else
local x = setupLength * bone.a local x = setupLength * bone.a
local y = setupLength * bone.c local y = setupLength * bone.c
local length = math_sqrt(x * x + y * y) local length = math_sqrt(x * x + y * y)
if scale then lengths[i + 1] = length end if scale then lengths[i] = length end
i = i + 1
spaces[i] = length
sum = sum + length
end
end
if sum > 0 then
sum = spacesCount / sum * spacing
local i = 1
while i < spacesCount do
spaces[i] = spaces[i] * sum
i = i + 1 i = i + 1
if lengthSpacing then
spaces[i + 1] = (setupLength + spacing) * length / setupLength
else
spaces[i + 1] = spacing * length / setupLength
end
end end
end end
else else
local lengthSpacing = data.spacingMode == PathConstraintData.SpacingMode.length
local i = 1 local i = 1
while i < spacesCount do local n = spacesCount - 1
spaces[i + 1] = spacing while i < n do
i = i + 1 local bone = bones[i]
local setupLength = bone.data.length
if setupLength < epsilon then
if scale then lengths[i] = 0 end
i = i + 1
spaces[i] = spacing
else
local x = setupLength * bone.a
local y = setupLength * bone.c
local length = math_sqrt(x * x + y * y)
if scale then lengths[i] = length end
i = i + 1
local s
if lengthSpacing then
s = setupLength + spacing
else
s = spacing
end
spaces[i] = s * length / setupLength
end
end end
end end
local positions = self:computeWorldPositions(attachment, spacesCount, tangents, data.positionMode == PathConstraintData.PositionMode.percent, percentSpacing) local positions = self:computeWorldPositions(attachment, spacesCount, tangents)
local boneX = positions[1] local boneX = positions[1]
local boneY = positions[2] local boneY = positions[2]
local offsetRotation = data.offsetRotation local offsetRotation = data.offsetRotation
local tip = false local tip = false
if offsetRotation == 0 then if offsetRotation == 0 then
tip = rotateMode == PathConstraintData.RotateMode.chain tip = data.rotateMode == PathConstraintData.RotateMode.chain
else else
tip = false tip = false
local p = self.target.bone local p = self.target.bone
@ -174,8 +207,8 @@ function PathConstraint:update ()
local p = 3 local p = 3
while i < boneCount do while i < boneCount do
local bone = bones[i + 1] local bone = bones[i + 1]
bone.worldX = bone.worldX + (boneX - bone.worldX) * translateMix bone.worldX = bone.worldX + (boneX - bone.worldX) * mixX
bone.worldY = bone.worldY + (boneY - bone.worldY) * translateMix bone.worldY = bone.worldY + (boneY - bone.worldY) * mixY
local x = positions[p + 1] local x = positions[p + 1]
local y = positions[p + 2] local y = positions[p + 2]
local dx = x - boneX local dx = x - boneX
@ -183,14 +216,14 @@ function PathConstraint:update ()
if scale then if scale then
local length = lengths[i + 1] local length = lengths[i + 1]
if length ~= 0 then if length ~= 0 then
local s = (math_sqrt(dx * dx + dy * dy) / length - 1) * rotateMix + 1 local s = (math_sqrt(dx * dx + dy * dy) / length - 1) * mixRotate + 1
bone.a = bone.a * s bone.a = bone.a * s
bone.c = bone.c * s bone.c = bone.c * s
end end
end end
boneX = x boneX = x
boneY = y boneY = y
if rotate then if mixRotate then
local a = bone.a local a = bone.a
local b = bone.b local b = bone.b
local c = bone.c local c = bone.c
@ -210,8 +243,8 @@ function PathConstraint:update ()
cos = math_cos(r) cos = math_cos(r)
sin = math_sin(r) sin = math_sin(r)
local length = bone.data.length local length = bone.data.length
boneX = boneX + (length * (cos * a - sin * c) - dx) * rotateMix boneX = boneX + (length * (cos * a - sin * c) - dx) * mixRotate
boneY = boneY + (length * (sin * a + cos * c) - dy) * rotateMix boneY = boneY + (length * (sin * a + cos * c) - dy) * mixRotate
else else
r = r + offsetRotation r = r + offsetRotation
end end
@ -220,21 +253,21 @@ function PathConstraint:update ()
elseif r < -math_pi then elseif r < -math_pi then
r = r + math_pi2 r = r + math_pi2
end end
r = r * rotateMix r = r * mixRotate
cos = math_cos(r) cos = math_cos(r)
sin = math.sin(r) sin = math_sin(r)
bone.a = cos * a - sin * c bone.a = cos * a - sin * c
bone.b = cos * b - sin * d bone.b = cos * b - sin * d
bone.c = sin * a + cos * c bone.c = sin * a + cos * c
bone.d = sin * b + cos * d bone.d = sin * b + cos * d
end end
bone.appliedValid = false bone:updateAppliedTransform()
i = i + 1 i = i + 1
p = p + 3 p = p + 3
end end
end end
function PathConstraint:computeWorldPositions (path, spacesCount, tangents, percentPosition, percentSpacing) function PathConstraint:computeWorldPositions (path, spacesCount, tangents)
local target = self.target local target = self.target
local position = self.position local position = self.position
local spaces = self.spaces local spaces = self.spaces
@ -250,20 +283,20 @@ function PathConstraint:computeWorldPositions (path, spacesCount, tangents, perc
local lengths = path.lengths local lengths = path.lengths
if closed then curveCount = curveCount - 1 else curveCount = curveCount - 2 end if closed then curveCount = curveCount - 1 else curveCount = curveCount - 2 end
local pathLength = lengths[curveCount + 1] local pathLength = lengths[curveCount + 1]
if percentPosition then position = position * pathLength end if self.data.positionMode == PathConstraintData.PositionMode.percent then position = position * pathLength end
if percentSpacing then
i = 1 local multiplier = 1
while i < spacesCount do if self.data.spacingMode == PathConstraintData.SpacingMode.percent then
spaces[i + 1] = spaces[i + 1] * pathLength multiplier = pathLength
i = i + 1 elseif self.data.spacingMode == PathConstraintData.SpacingMode.proportional then
end multiplier = pathLength / spacesCount
end end
world = utils.setArraySize(self.world, 8) world = utils.setArraySize(self.world, 8)
i = 0 i = 0
local o = 0 local o = 0
local curve = 0 local curve = 0
while i < spacesCount do while i < spacesCount do
local space = spaces[i + 1] local space = spaces[i + 1] * multiplier
position = position + space position = position + space
local p = position local p = position
@ -389,17 +422,14 @@ function PathConstraint:computeWorldPositions (path, spacesCount, tangents, perc
i = i + 1 i = i + 1
w = w + 6 w = w + 6
end end
if percentPosition then
position = position * pathLength if self.data.positionMode == PathConstraintData.PositionMode.percent then position = position * pathLength end
else
position = position * pathLength / path.lengths[curveCount] local multiplier = 1
end if self.data.spacingMode == PathConstraintData.SpacingMode.percent then
if percentSpacing then multiplier = pathLength
i = 1 elseif self.data.spacingMode == PathConstraintData.SpacingMode.proportional then
while i < spacesCount do multiplier = pathLength / spacesCount
spaces[i + 1] = spaces[i + 1] * pathLength
i = i + 1
end
end end
local segments = self.segments local segments = self.segments
@ -409,7 +439,7 @@ function PathConstraint:computeWorldPositions (path, spacesCount, tangents, perc
local curve = 0 local curve = 0
local segment = 0 local segment = 0
while i < spacesCount do while i < spacesCount do
local space = spaces[i + 1] local space = spaces[i + 1] * multiplier
position = position + space position = position + space
local p = position local p = position

View File

@ -43,8 +43,9 @@ function PathConstraintData.new (name)
offsetRotation = 0, offsetRotation = 0,
position = 0, position = 0,
spacing = 0, spacing = 0,
rotateMix = 0, mixRotate = 0,
translateMix = 0 mixX = 0,
mixY = 0
} }
return self return self
@ -58,7 +59,8 @@ PathConstraintData.PositionMode = {
PathConstraintData.SpacingMode = { PathConstraintData.SpacingMode = {
length = 0, length = 0,
fixed = 1, fixed = 1,
percent = 2 percent = 2,
proportional = 3
} }
PathConstraintData.RotateMode = { PathConstraintData.RotateMode = {

View File

@ -59,7 +59,6 @@ function Skeleton.new (data)
transformConstraints = {}, transformConstraints = {},
pathConstraints = {}, pathConstraints = {},
_updateCache = {}, _updateCache = {},
updateCacheReset = {},
skin = nil, skin = nil,
color = Color.newWith(1, 1, 1, 1), color = Color.newWith(1, 1, 1, 1),
time = 0, time = 0,
@ -110,7 +109,6 @@ end
function Skeleton:updateCache () function Skeleton:updateCache ()
local updateCache = {} local updateCache = {}
self._updateCache = updateCache self._updateCache = updateCache
self.updateCacheReset = {}
local bones = self.bones local bones = self.bones
for _, bone in ipairs(bones) do for _, bone in ipairs(bones) do
@ -122,11 +120,11 @@ function Skeleton:updateCache ()
local skinBones = self.skin.bones local skinBones = self.skin.bones
for i, boneData in ipairs(skinBones) do for i, boneData in ipairs(skinBones) do
local bone = bones[boneData.index] local bone = bones[boneData.index]
while bone do repeat
bone.sorted = false bone.sorted = false
bone.active = true bone.active = true
bone = bone.parent bone = bone.parent
end until not bone
end end
end end
@ -196,22 +194,18 @@ function Skeleton:sortIkConstraint (constraint)
local parent = constrained[1] local parent = constrained[1]
self:sortBone(parent) self:sortBone(parent)
if #constrained > 1 then if #constrained == 1 then
table_insert(self._updateCache, constraint)
self:sortReset(parent.children)
else
local child = constrained[#constrained] local child = constrained[#constrained]
local contains = false self:sortBone(child)
for _, updatable in ipairs(self._updateCache) do
if updatable == child then table_insert(self._updateCache, constraint)
contains = true
break self:sortReset(parent.children)
end child.sorted = true
end
if not contains then table_insert(self.updateCacheReset, child) end
end end
table_insert(self._updateCache, constraint)
self:sortReset(parent.children)
constrained[#constrained].sorted = true
end end
function Skeleton:sortPathConstraint(constraint) function Skeleton:sortPathConstraint(constraint)
@ -266,7 +260,7 @@ function Skeleton:sortTransformConstraint(constraint)
break break
end end
end end
if not contains then table_insert(self.updateCacheReset, child) end self:sortBone(child)
end end
else else
for _,bone in ipairs(constrained) do for _,bone in ipairs(constrained) do
@ -279,7 +273,6 @@ function Skeleton:sortTransformConstraint(constraint)
for _,bone in ipairs(constrained) do for _,bone in ipairs(constrained) do
self:sortReset(bone.children) self:sortReset(bone.children)
end end
for _,bone in ipairs(constrained) do for _,bone in ipairs(constrained) do
bone.sorted = true bone.sorted = true
end end
@ -333,8 +326,7 @@ end
-- Updates the world transform for each bone and applies IK constraints. -- Updates the world transform for each bone and applies IK constraints.
function Skeleton:updateWorldTransform () function Skeleton:updateWorldTransform ()
local updateCacheReset = self.updateCacheReset for _,bone in ipairs(self.bones) do
for _,bone in ipairs(updateCacheReset) do
bone.ax = bone.x bone.ax = bone.x
bone.ay = bone.y bone.ay = bone.y
bone.arotation = bone.rotation bone.arotation = bone.rotation
@ -342,11 +334,9 @@ function Skeleton:updateWorldTransform ()
bone.ascaleY = bone.scaleY bone.ascaleY = bone.scaleY
bone.ashearX = bone.shearX bone.ashearX = bone.shearX
bone.ashearY = bone.shearY bone.ashearY = bone.shearY
bone.appliedValid = true
end end
local updateCache = self._updateCache for _, updatable in ipairs(self._updateCache) do
for _, updatable in ipairs(updateCache) do
updatable:update() updatable:update()
end end
end end
@ -372,10 +362,12 @@ function Skeleton:setBonesToSetupPose ()
local transformConstraints = self.transformConstraints local transformConstraints = self.transformConstraints
for _, constraint in ipairs(transformConstraints) do for _, constraint in ipairs(transformConstraints) do
local data = constraint.data local data = constraint.data
constraint.rotateMix = data.rotateMix constraint.mixRotate = data.mixRotate
constraint.translateMix = data.translateMix constraint.mixX = data.mixX
constraint.scaleMix = data.scaleMix constraint.mixY = data.mixY
constraint.shearMix = data.shearMix constraint.mixScaleX = data.mixScaleX
constraint.mixScaleY = data.mixScaleY
constraint.mixShearY = data.mixShearY
end end
local pathConstraints = self.pathConstraints local pathConstraints = self.pathConstraints
@ -383,8 +375,9 @@ function Skeleton:setBonesToSetupPose ()
local data = constraint.data local data = constraint.data
constraint.position = data.position constraint.position = data.position
constraint.spacing = data.spacing constraint.spacing = data.spacing
constraint.rotateMix = data.rotateMix constraint.mixRotate = data.mixRotate
constraint.translateMix = data.translateMix constraint.mixX = data.mixX
constraint.mixY = data.mixY
end end
end end

View File

@ -558,9 +558,9 @@ function SkeletonJson.new (attachmentLoader)
for timelineName,timelineMap in pairs(slotMap) do for timelineName,timelineMap in pairs(slotMap) do
if not timelineMap then if not timelineMap then
elseif timelineName == "attachment" then elseif timelineName == "attachment" then
local timeline = Animation.AttachmentTimeline.new(#timelineMap, slotIndex) local timeline = Animation.AttachmentTimeline.new(#timelineMap, #timelineMap, slotIndex)
for i,keyMap in ipairs(timelineMap) do for i,keyMap in ipairs(timelineMap) do
timeline:setFrame(i + 1, getValue(keyMap, "time", 0), keyMap["name"]) timeline:setFrame(i - 1, getValue(keyMap, "time", 0), keyMap["name"])
end end
table_insert(timelines, timeline) table_insert(timelines, timeline)
elseif timelineName == "rgba" then elseif timelineName == "rgba" then
@ -765,7 +765,7 @@ function SkeletonJson.new (attachmentLoader)
table_insert(timelines, readTimeline1(timelineMap, timeline, 0, scale)) table_insert(timelines, readTimeline1(timelineMap, timeline, 0, scale))
elseif timelineName == "scale" then elseif timelineName == "scale" then
local timeline = Animation.ScaleTimeline.new(#timelineMap, #timelineMap * 2, boneIndex) local timeline = Animation.ScaleTimeline.new(#timelineMap, #timelineMap * 2, boneIndex)
table_insert(timelines, readTimeline2(timelineMap, "x", "y", 1, 1)) table_insert(timelines, readTimeline2(timelineMap, timeline, "x", "y", 1, 1))
elseif timelineName == "scalex" then elseif timelineName == "scalex" then
local timeline = Animation.ScaleXTimeline.new(#timelineMap, #timelineMap, boneIndex) local timeline = Animation.ScaleXTimeline.new(#timelineMap, #timelineMap, boneIndex)
table_insert(timelines, readTimeline1(timelineMap, timeline, 1, 1)) table_insert(timelines, readTimeline1(timelineMap, timeline, 1, 1))
@ -774,7 +774,7 @@ function SkeletonJson.new (attachmentLoader)
table_insert(timelines, readTimeline1(timelineMap, timeline, 1, 1)) table_insert(timelines, readTimeline1(timelineMap, timeline, 1, 1))
elseif timelineName == "shear" then elseif timelineName == "shear" then
local timeline = Animation.ShearTimeline.new(#timelineMap, #timelineMap * 2, boneIndex) local timeline = Animation.ShearTimeline.new(#timelineMap, #timelineMap * 2, boneIndex)
table_insert(timelines, readTimeline2(timelineMap, "x", "y", 0, 1)) table_insert(timelines, readTimeline2(timelineMap, timeline, "x", "y", 0, 1))
elseif timelineName == "shearx" then elseif timelineName == "shearx" then
local timeline = Animation.ShearXTimeline.new(#timelineMap, #timelineMap, boneIndex) local timeline = Animation.ShearXTimeline.new(#timelineMap, #timelineMap, boneIndex)
table_insert(timelines, readTimeline1(timelineMap, timeline, 0, 1)) table_insert(timelines, readTimeline1(timelineMap, timeline, 0, 1))
@ -901,7 +901,7 @@ function SkeletonJson.new (attachmentLoader)
if map.path then if map.path then
for constraintName,constraintMap in pairs(map.path) do for constraintName,constraintMap in pairs(map.path) do
local constraint, constraintIndex = -1 local constraint, constraintIndex = -1
for i,other in pairs(skeletonData.transformConstraints) do for i,other in pairs(skeletonData.pathConstraints) do
if other.name == constraintName then if other.name == constraintName then
constraintIndex = i constraintIndex = i
constraint = other constraint = other
@ -914,12 +914,12 @@ function SkeletonJson.new (attachmentLoader)
if timelineName == "position" then if timelineName == "position" then
local timeline = Animation.PathConstraintPositionTimeline.new(#timelineMap, #timelineMap, constraintIndex) local timeline = Animation.PathConstraintPositionTimeline.new(#timelineMap, #timelineMap, constraintIndex)
local timelineScale = 1 local timelineScale = 1
if constraint.positionMode == PositionMode.fixed then timelineScale = scale end if constraint.positionMode == PathConstraintData.PositionMode.fixed then timelineScale = scale end
table_insert(timelines, readTimeline1(timelineMap, timeline, 0, timelineScale)) table_insert(timelines, readTimeline1(timelineMap, timeline, 0, timelineScale))
elseif timelineName == "spacing" then elseif timelineName == "spacing" then
local timeline = Animation.PathConstraintSpacingTimeline.new(#timelineMap, #timelineMap, constraintIndex) local timeline = Animation.PathConstraintSpacingTimeline.new(#timelineMap, #timelineMap, constraintIndex)
local timelineScale = 1 local timelineScale = 1
if data.spacingMode == SpacingMode.Length or data.spacingMode == SpacingMode.Fixed then timelineScale = scale end if data.spacingMode == PathConstraintData.SpacingMode.Length or data.spacingMode == PathConstraintData.SpacingMode.Fixed then timelineScale = scale end
table_insert(timelines, readTimeline1(timelineMap, timeline, 0, timelineScale)) table_insert(timelines, readTimeline1(timelineMap, timeline, 0, timelineScale))
elseif timelineName == "mix" then elseif timelineName == "mix" then
local timeline = Animation.PathConstraintMixTimeline.new(#timelineMap, #timelineMap * 3, constraintIndex) local timeline = Animation.PathConstraintMixTimeline.new(#timelineMap, #timelineMap * 3, constraintIndex)
@ -978,6 +978,7 @@ function SkeletonJson.new (attachmentLoader)
if weighted then deformLength = math_floor(deformLength / 3) * 2 end if weighted then deformLength = math_floor(deformLength / 3) * 2 end
local timeline = Animation.DeformTimeline.new(#timelineMap, #timelineMap, slotIndex, attachment) local timeline = Animation.DeformTimeline.new(#timelineMap, #timelineMap, slotIndex, attachment)
local time = getValue(keyMap, "time", 0)
local bezier = 0 local bezier = 0
for i,keyMap in ipairs(timelineMap) do for i,keyMap in ipairs(timelineMap) do
local deform = nil local deform = nil
@ -1007,7 +1008,7 @@ function SkeletonJson.new (attachmentLoader)
end end
end end
local frame = i - 1 local frame = i - 1
timeline:setFrame(frame, time, mixRotate, mixX, mixY) timeline:setFrame(frame, time, deform)
local nextMap = timelineMap[frame + 1] local nextMap = timelineMap[frame + 1]
if not nextMap then if not nextMap then
timeline:shrink(bezier) timeline:shrink(bezier)
@ -1172,15 +1173,15 @@ function SkeletonJson.new (attachmentLoader)
readCurve = function (curve, timeline, bezier, frame, value, time1, time2, value1, value2, scale) readCurve = function (curve, timeline, bezier, frame, value, time1, time2, value1, value2, scale)
if curve == "stepped" then if curve == "stepped" then
if value ~= 0 then timeline.setStepped(frame) end if value ~= 0 then timeline:setStepped(frame) end
return bezier return bezier
end end
local i = value * 4 local i = value * 4 + 1
local cx1 = curve[i] local cx1 = curve[i]
local cy1 = curve[i + 1] * scale local cy1 = curve[i + 1] * scale
local cx2 = curve[i + 2] local cx2 = curve[i + 2]
local cy2 = curve[i + 3] * scale local cy2 = curve[i + 3] * scale
timeline.setBezier(bezier, frame, value, time1, value1, cx1, cy1, cx2, cy2, time2, value2) timeline:setBezier(bezier, frame, value, time1, value1, cx1, cy1, cx2, cy2, time2, value2)
return bezier + 1 return bezier + 1
end end

View File

@ -60,9 +60,16 @@ end
function Slot:setAttachment (attachment) function Slot:setAttachment (attachment)
if self.attachment == attachment then return end if self.attachment == attachment then return end
if not attachment
or not attachment.isVertexAttachment
or not self.attachment
or not self.attachment.isVertexAttachment
or attachment.deformAttachment ~= self.attachment.deformAttachment
then
self.deform = {}
end
self.attachment = attachment self.attachment = attachment
self.attachmentTime = self.bone.skeleton.time self.attachmentTime = self.bone.skeleton.time
self.deform = {}
end end
function Slot:setAttachmentTime (time) function Slot:setAttachmentTime (time)

View File

@ -7,7 +7,7 @@
-- Integration of the Spine Runtimes into software or otherwise creating -- Integration of the Spine Runtimes into software or otherwise creating
-- derivative works of the Spine Runtimes is permitted under the terms and -- derivative works of the Spine Runtimes is permitted under the terms and
-- conditions of Section 2 of the Spine Editor License Agreement: -- conditions of Section 2 of the Spine Editor License Agreement:
-- http://esotericsoftware.com/spine-editor-license -- http:--esotericsoftware.com/spine-editor-license
-- --
-- Otherwise, it is permitted to integrate the Spine Runtimes into software -- Otherwise, it is permitted to integrate the Spine Runtimes into software
-- or otherwise create derivative works of the Spine Runtimes (collectively, -- or otherwise create derivative works of the Spine Runtimes (collectively,
@ -28,6 +28,7 @@
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
local setmetatable = setmetatable local setmetatable = setmetatable
local tonumber = tonumber
local table_insert = table.insert local table_insert = table.insert
local math_abs = math.abs local math_abs = math.abs
@ -47,7 +48,8 @@ function TextureAtlasPage.new ()
vWrap = nil, vWrap = nil,
texture = nil, texture = nil,
width = 0, width = 0,
height = 0 height = 0,
pma = false
} }
setmetatable(self, TextureAtlasPage) setmetatable(self, TextureAtlasPage)
return self return self
@ -73,168 +75,178 @@ function TextureAtlas:parse (atlasContent, imageLoader)
if not atlasContent then error("atlasContent cannot be nil.", 2) end if not atlasContent then error("atlasContent cannot be nil.", 2) end
if not imageLoader then error("imageLoader cannot be nil.", 2) end if not imageLoader then error("imageLoader cannot be nil.", 2) end
function lineIterator(s) local readLine = atlasContent:gmatch("[ \t]*(.-)[ \t]*\r?\n")
if s:sub(-1)~="\n" then s=s.."\n" end
return s:gmatch("(.-)\n") local trim = function (value)
return value:match("^%s*(.-)%s*$")
end end
local lines = {} local entry = {}
local index = 0 local readEntry = function (entry, line)
local numLines = 0 if not line then return 0 end
for line in lineIterator(atlasContent) do if line:len() == 0 then return 0 end
lines[numLines] = line
numLines = numLines + 1
end
local readLine = function () local colon = line:find(":")
if index >= numLines then return nil end if not colon then return 0 end
local line = lines[index] entry[0] = trim(line:sub(1, colon))
index = index + 1 local lastMatch = colon + 1
return line
end
local readValue = function ()
local line = readLine()
local idx = line:find(":")
if not idx then error("Invalid line: " .. line, 2) end
return line:sub(idx + 1):match'^%s*(.*%S)' or ''
end
local readTuple = function ()
local line = readLine()
local idx = line:find(":")
if not idx then
error("Invalid line: " .. line, 2)
end
local i = 1 local i = 1
local lastMatch = idx + 1 while true do
local tuple = {}
while i <= 3 do
local comma = line:find(",", lastMatch) local comma = line:find(",", lastMatch)
if not comma then break end if not comma then
tuple[i] = line:sub(lastMatch, comma - 1):match'^%s*(.*%S)' or '' entry[i] = trim(line:sub(lastMatch))
return i
end
entry[i] = trim(line:sub(lastMatch, comma - lastMatch))
lastMatch = comma + 1 lastMatch = comma + 1
if i == 4 then return 4 end
i = i + 1 i = i + 1
end end
tuple[i] = line:sub(lastMatch):match'^%s*(.*%S)' or ''
return tuple
end end
local parseInt = function (str) local page
return tonumber(str) local region
local pageFields = {}
pageFields["size"] = function ()
page.width = tonumber(entry[1])
page.height = tonumber(entry[2])
end
pageFields["format"] = function ()
-- page.format = Format[tuple[0]] we don't need format in Lua
end
pageFields["filter"] = function ()
page.minFilter = TextureFilter[entry[1]]
page.magFilter = TextureFilter[entry[2]]
end
pageFields["repeat"] = function ()
if entry[1]:find("x") then page.uWrap = TextureWrap.Repeat end
if entry[1]:find("y") then page.vWrap = TextureWrap.Repeat end
end
pageFields["pma"] = function ()
page.pma = entry[1] == "true"
end end
local filterFromString = function (str) local regionFields = {}
str = str:lower() regionFields["xy"] = function () -- Deprecated, use bounds.
if str == "nearest" then return TextureFilter.Nearest region.x = tonumber(entry[1])
elseif str == "linear" then return TextureFilter.Linear region.y = tonumber(entry[2])
elseif str == "mipmap" then return TextureFilter.MipMap end
elseif str == "mipmapnearestnearest" then return TextureFilter.MipMapNearestNearest regionFields["size"] = function () -- Deprecated, use bounds.
elseif str == "mipmaplinearnearest" then return TextureFilter.MipMapLinearNearest region.width = tonumber(entry[1])
elseif str == "mipmapnearestlinear" then return TextureFilter.MipMapNearestLinear region.height = tonumber(entry[2])
elseif str == "mipmaplinearlinear" then return TextureFilter.MipMapLinearLinear end
else error("Unknown texture wrap: " .. str, 2) regionFields["bounds"] = function ()
region.x = tonumber(entry[1])
region.y = tonumber(entry[2])
region.width = tonumber(entry[3])
region.height = tonumber(entry[4])
end
regionFields["offset"] = function () -- Deprecated, use offsets.
region.offsetX = tonumber(entry[1])
region.offsetY = tonumber(entry[2])
end
regionFields["orig"] = function () -- Deprecated, use offsets.
region.originalWidth = tonumber(entry[1])
region.originalHeight = tonumber(entry[2])
end
regionFields["offsets"] = function ()
region.offsetX = tonumber(entry[1])
region.offsetY = tonumber(entry[2])
region.originalWidth = tonumber(entry[3])
region.originalHeight = tonumber(entry[4])
end
regionFields["rotate"] = function ()
local value = entry[1]
if value == "true" then
region.degrees = 90
elseif value ~= "false" then
region.degrees = tonumber(value)
end end
end end
regionFields["index"] = function ()
region.index = tonumber(entry[1])
end
local page = nil local line = readLine()
-- Ignore empty lines before first entry.
while line and line:len() == 0 do
line = readLine()
end
-- Header entries.
while true do
if not line or line:len() == 0 then break end
if readEntry(entry, line) == 0 then break end -- Silently ignore all header fields.
line = readLine()
end
-- Page and region entries.
local names
local values
while true do while true do
local line = readLine()
if not line then break end if not line then break end
line = line:match'^%s*(.*%S)' or ''
if line:len() == 0 then if line:len() == 0 then
page = nil page = nil
line = readLine()
elseif not page then elseif not page then
page = TextureAtlasPage.new() page = TextureAtlasPage.new()
page.name = line page.name = trim(line)
while true do
local tuple = readTuple() line = readLine()
if #tuple == 2 then if readEntry(entry, line) == 0 then break end
page.width = parseInt(tuple[1]) local field = pageFields[entry[0]]
page.height = parseInt(tuple[2]) if field then field() end
tuple = readTuple()
else
-- We only support atlases that have the page width/height
-- encoded in them. That way we don't rely on any special
-- wrapper objects for images to get the page size from
error("Atlas must specify page width/height. Please export to the latest atlas format", 2)
end end
page.texture = imageLoader(page.name)
tuple = readTuple() -- FIXME - Apply the filter and wrap settings to the texture.
page.minFilter = filterFromString(tuple[1]) -- page.texture:setFilters(page.minFilter, page.magFilter)
page.magFilter = filterFromString(tuple[2]) -- page.texture:setWraps(page.uWrap, page.vWrap)
local direction = readValue()
page.uWrap = TextureWrap.ClampToEdge
page.vWrap = TextureWrap.ClampToEdge
if direction == "x" then
page.uWrap = TextureWrap.Repeat
elseif direction == "y" then
page.vWrap = TextureWrap.Repeat
elseif direction == "xy" then
page.uWrap = TextureWrap.Repeat
page.vWrap = TextureWrap.Repeat
end
page.texture = imageLoader(line)
-- FIXME page.texture:setFilters(page.minFilter, page.magFilter)
-- FIXME page.texture:setWraps(page.uWrap, page.vWrap)
table_insert(self.pages, page) table_insert(self.pages, page)
else else
local region = TextureAtlasRegion.new() region = TextureAtlasRegion.new()
region.name = line
region.page = page region.page = page
region.name = line
local rotateValue = readValue() while true do
if rotateValue == "true" then line = readLine()
region.degrees = 90 local count = readEntry(entry, line)
elseif rotateValue == "false" then if count == 0 then break end
region.degrees = 0 local field = regionFields[entry[0]]
else if field then
region.degrees = tonumber(rotateValue) field()
end else
if region.degrees == 90 then region.rotate = true end if not names then
names = {}
local tuple = readTuple() values = {}
local x = parseInt(tuple[1]) end
local y = parseInt(tuple[2]) table_insert(names, entry[0])
local entryValues = {}
tuple = readTuple() local i = 0
local width = parseInt(tuple[1]) while i < count do
local height = parseInt(tuple[2]) table_insert(entryValues, tonumber(entry[i + 1]))
i = i + 1
region.u = x / page.width end
region.v = y / page.height table_insert(values, entryValues)
if region.rotate then
region.u2 = (x + height) / page.width
region.v2 = (y + width) / page.height
else
region.u2 = (x + width) / page.width
region.v2 = (y + height) / page.height
end
region.x = x
region.y = y
region.width = math_abs(width)
region.height = math_abs(height)
-- Read and skip optional splits
tuple = readTuple()
if #tuple == 4 then
tuple = readTuple()
if #tuple == 4 then
readTuple()
end end
end end
if region.originalWidth == 0 and region.originalHeight == 0 then
region.originalWidth = parseInt(tuple[1]) region.originalWidth = region.width
region.originalHeight = parseInt(tuple[2]) region.originalHeight = region.height
end
tuple = readTuple() if names and #names > 0 then
region.offsetX = parseInt(tuple[1]) region.names = names
region.offsetY = parseInt(tuple[2]) region.values = values
names = nil
region.index = parseInt(readValue()) values = nil
end
region.u = region.x / page.width
region.v = region.y / page.height
if region.degrees == 90 then
region.u2 = (region.x + region.height) / page.width
region.v2 = (region.y + region.width) / page.height
else
region.u2 = (region.x + region.width) / page.width
region.v2 = (region.y + region.height) / page.height
end
region.texture = page.texture region.texture = page.texture
table_insert(self.regions, region) table_insert(self.regions, region)
end end

View File

@ -42,9 +42,10 @@ function TextureAtlasRegion.new ()
self.x = 0 self.x = 0
self.y = 0 self.y = 0
self.index = 0 self.index = 0
self.rotate = false
self.degrees = 0 self.degrees = 0
self.texture = nil self.texture = nil
self.names = nil
self.values = nil
setmetatable(self, TextureAtlasRegion) setmetatable(self, TextureAtlasRegion)
return self return self

View File

@ -53,7 +53,7 @@ function TransformConstraint.new (data, skeleton)
data = data, data = data,
bones = {}, bones = {},
target = nil, target = nil,
rotateMix = data.rotateMix, translateMix = data.translateMix, scaleMix = data.scaleMix, shearMix = data.shearMix, mixRotate = data.mixRotate, mixX = data.mixX, mixY = data.mixY, mixScaleX = data.mixScaleX, mixScaleY = data.mixScaleY, mixShearY = data.mixShearY,
temp = { 0, 0 }, temp = { 0, 0 },
active = false active = false
} }
@ -67,11 +67,9 @@ function TransformConstraint.new (data, skeleton)
return self return self
end end
function TransformConstraint:apply ()
self:update()
end
function TransformConstraint:update () function TransformConstraint:update ()
if self.mixRotate == 0 and self.mixX == 0 and self.mixY == 0 and self.mixScaleX == 0 and self.mixScaleX == 0 and self.mixShearY == 0 then return end
if self.data.local_ then if self.data.local_ then
if self.data.relative then if self.data.relative then
self:applyRelativeLocal() self:applyRelativeLocal()
@ -88,10 +86,14 @@ function TransformConstraint:update ()
end end
function TransformConstraint:applyAbsoluteWorld () function TransformConstraint:applyAbsoluteWorld ()
local rotateMix = self.rotateMix local mixRotate = self.mixRotate
local translateMix = self.translateMix local mixX = self.mixX
local scaleMix = self.scaleMix local mixY = self.mixY
local shearMix = self.shearMix local mixScaleX = self.mixScaleX
local mixScaleY = self.mixScaleY
local mixShearY = self.mixShearY
local translate = mixX ~= 0 or mixY ~= 0
local target = self.target local target = self.target
local ta = target.a local ta = target.a
local tb = target.b local tb = target.b
@ -101,10 +103,10 @@ function TransformConstraint:applyAbsoluteWorld ()
if ta * td - tb * tc > 0 then degRadReflect = utils.degRad else degRadReflect = -utils.degRad end if ta * td - tb * tc > 0 then degRadReflect = utils.degRad else degRadReflect = -utils.degRad end
local offsetRotation = self.data.offsetRotation * degRadReflect local offsetRotation = self.data.offsetRotation * degRadReflect
local offsetShearY = self.data.offsetShearY * degRadReflect local offsetShearY = self.data.offsetShearY * degRadReflect
local bones = self.bones local bones = self.bones
for _, bone in ipairs(bones) do for _, bone in ipairs(bones) do
local modified = false if mixRotate ~= 0 then
if rotateMix ~= 0 then
local a = bone.a local a = bone.a
local b = bone.b local b = bone.b
local c = bone.c local c = bone.c
@ -115,45 +117,38 @@ function TransformConstraint:applyAbsoluteWorld ()
elseif r < -math_pi then elseif r < -math_pi then
r = r + math_pi2 r = r + math_pi2
end end
r = r * rotateMix r = r * mixRotate
local cos = math_cos(r) local cos = math_cos(r)
local sin = math_sin(r) local sin = math_sin(r)
bone.a = cos * a - sin * c bone.a = cos * a - sin * c
bone.b = cos * b - sin * d bone.b = cos * b - sin * d
bone.c = sin * a + cos * c bone.c = sin * a + cos * c
bone.d = sin * b + cos * d bone.d = sin * b + cos * d
modified = true
end end
if translateMix ~= 0 then if translate then
local temp = self.temp local temp = self.temp
temp[1] = self.data.offsetX temp[1] = self.data.offsetX
temp[2] = self.data.offsetY temp[2] = self.data.offsetY
target:localToWorld(temp) target:localToWorld(temp)
bone.worldX = bone.worldX + (temp[1] - bone.worldX) * translateMix bone.worldX = bone.worldX + (temp[1] - bone.worldX) * mixX
bone.worldY = bone.worldY + (temp[2] - bone.worldY) * translateMix bone.worldY = bone.worldY + (temp[2] - bone.worldY) * mixY
modified = true
end end
if scaleMix > 0 then if mixScaleX ~= 0 then
local s = math_sqrt(bone.a * bone.a + bone.c * bone.c) local s = math_sqrt(bone.a * bone.a + bone.c * bone.c)
local ts = math_sqrt(ta * ta + tc * tc) if s ~= 0 then s = (s + (math_sqrt(ta * ta + tc * tc) - s + self.data.offsetScaleX) * mixScaleX) / s end
if s > 0.00001 then
s = (s + (ts - s + self.data.offsetScaleX) * scaleMix) / s
end
bone.a = bone.a * s bone.a = bone.a * s
bone.c = bone.c * s bone.c = bone.c * s
s = math_sqrt(bone.b * bone.b + bone.d * bone.d) end
ts = math_sqrt(tb * tb + td * td) if mixScaleY ~= 0 then
if s > 0.00001 then local s = math_sqrt(bone.b * bone.b + bone.d * bone.d)
s = (s + (ts - s + self.data.offsetScaleY) * scaleMix) / s if s ~= 0 then s = (s + (math_sqrt(tb * tb + td * td) - s + self.data.offsetScaleY) * mixScaleY) / s end
end
bone.b = bone.b * s bone.b = bone.b * s
bone.d = bone.d * s bone.d = bone.d * s
modified = true
end end
if shearMix > 0 then if mixShearY > 0 then
local b = bone.b local b = bone.b
local d = bone.d local d = bone.d
local by = math_atan2(d, b) local by = math_atan2(d, b)
@ -163,22 +158,25 @@ function TransformConstraint:applyAbsoluteWorld ()
elseif r < -math_pi then elseif r < -math_pi then
r = r + math_pi2 r = r + math_pi2
end end
r = by + (r + offsetShearY) * shearMix r = by + (r + offsetShearY) * mixShearY
local s = math_sqrt(b * b + d * d) local s = math_sqrt(b * b + d * d)
bone.b = math_cos(r) * s bone.b = math_cos(r) * s
bone.d = math_sin(r) * s bone.d = math_sin(r) * s
modified = true
end end
if modified then bone.appliedValid = false end bone:updateAppliedTransform()
end end
end end
function TransformConstraint:applyRelativeWorld () function TransformConstraint:applyRelativeWorld ()
local rotateMix = self.rotateMix local mixRotate = self.mixRotate
local translateMix = self.translateMix local mixX = self.mixX
local scaleMix = self.scaleMix local mixY = self.mixY
local shearMix = self.shearMix local mixScaleX = self.mixScaleX
local mixScaleY = self.mixScaleY
local mixShearY = self.mixShearY
local translate = mixX ~= 0 or mixY ~= 0
local target = self.target local target = self.target
local ta = target.a local ta = target.a
local tb = target.b local tb = target.b
@ -188,11 +186,12 @@ function TransformConstraint:applyRelativeWorld ()
if ta * td - tb * tc > 0 then degRadReflect = utils.degRad else degRadReflect = -utils.degRad end if ta * td - tb * tc > 0 then degRadReflect = utils.degRad else degRadReflect = -utils.degRad end
local offsetRotation = self.data.offsetRotation * degRadReflect local offsetRotation = self.data.offsetRotation * degRadReflect
local offsetShearY = self.data.offsetShearY * degRadReflect local offsetShearY = self.data.offsetShearY * degRadReflect
local bones = self.bones local bones = self.bones
for _, bone in ipairs(bones) do for _, bone in ipairs(bones) do
local modified = false local modified = false
if rotateMix ~= 0 then if mixRotate ~= 0 then
local a = bone.a local a = bone.a
local b = bone.b local b = bone.b
local c = bone.c local c = bone.c
@ -203,37 +202,36 @@ function TransformConstraint:applyRelativeWorld ()
elseif r < -math_pi then elseif r < -math_pi then
r = r + math_pi2 r = r + math_pi2
end end
r = r * rotateMix r = r * mixRotate
local cos = math_cos(r) local cos = math_cos(r)
local sin = math_sin(r) local sin = math_sin(r)
bone.a = cos * a - sin * c bone.a = cos * a - sin * c
bone.b = cos * b - sin * d bone.b = cos * b - sin * d
bone.c = sin * a + cos * c bone.c = sin * a + cos * c
bone.d = sin * b + cos * d bone.d = sin * b + cos * d
modified = true
end end
if translateMix ~= 0 then if translate then
local temp = self.temp local temp = self.temp
temp[1] = self.data.offsetX temp[1] = self.data.offsetX
temp[2] = self.data.offsetY temp[2] = self.data.offsetY
target:localToWorld(temp) target:localToWorld(temp)
bone.worldX = bone.worldX + temp[1] * translateMix bone.worldX = bone.worldX + temp[1] * mixX
bone.worldY = bone.worldY + temp[2] * translateMix bone.worldY = bone.worldY + temp[2] * mixY
modified = true
end end
if scaleMix > 0 then if mixScaleX ~= 0 then
local s = (math_sqrt(ta * ta + tc * tc) - 1 + self.data.offsetScaleX) * scaleMix + 1 local s = (math_sqrt(ta * ta + tc * tc) - 1 + self.data.offsetScaleX) * mixScaleX + 1
bone.a = bone.a * s bone.a = bone.a * s
bone.c = bone.c * s bone.c = bone.c * s
s = (math_sqrt(tb * tb + td * td) - 1 + self.data.offsetScaleY) * scaleMix + 1 end
if mixScaleY ~= 0 then
local s = (math_sqrt(tb * tb + td * td) - 1 + self.data.offsetScaleY) * mixScaleY + 1
bone.b = bone.b * s bone.b = bone.b * s
bone.d = bone.d * s bone.d = bone.d * s
modified = true
end end
if shearMix > 0 then if mixShearY > 0 then
local r = math_atan2(td, tb) - math_atan2(tc, ta) local r = math_atan2(td, tb) - math_atan2(tc, ta)
if r > math_pi then if r > math_pi then
r = r - math_pi2 r = r - math_pi2
@ -242,59 +240,54 @@ function TransformConstraint:applyRelativeWorld ()
end end
local b = bone.b local b = bone.b
local d = bone.d local d = bone.d
r = math_atan2(d, b) + (r - math_pi / 2 + offsetShearY) * shearMix r = math_atan2(d, b) + (r - math_pi / 2 + offsetShearY) * mixShearY
local s = math_sqrt(b * b + d * d) local s = math_sqrt(b * b + d * d)
bone.b = math_cos(r) * s bone.b = math_cos(r) * s
bone.d = math_sin(r) * s bone.d = math_sin(r) * s
modified = true
end end
if modified then bone.appliedValid = false end bone:updateAppliedTransform()
end end
end end
function TransformConstraint:applyAbsoluteLocal () function TransformConstraint:applyAbsoluteLocal ()
local rotateMix = self.rotateMix local mixRotate = self.mixRotate
local translateMix = self.translateMix local mixX = self.mixX
local scaleMix = self.scaleMix local mixY = self.mixY
local shearMix = self.shearMix local mixScaleX = self.mixScaleX
local mixScaleY = self.mixScaleY
local mixShearY = self.mixShearY
local target = self.target local target = self.target
if not target.appliedValid then target:updatedAppliedTransform() end if not target.appliedValid then target:updatedAppliedTransform() end
local bones = self.bones local bones = self.bones
for _, bone in ipairs(bones) do for _, bone in ipairs(bones) do
local modified = false
if not bone.appliedValid then bone:updateAppliedTransform() end
local rotation = bone.arotation local rotation = bone.arotation
if rotateMix ~= 0 then if mixRotate ~= 0 then
local r = target.arotation - rotation + self.data.offsetRotation local r = target.arotation - rotation + self.data.offsetRotation
r = r - (16384 - math_floor(16384.499999999996 - r / 360)) * 360 r = r - (16384 - math_floor(16384.499999999996 - r / 360)) * 360
rotation = rotation + r * rotateMix rotation = rotation + r * mixRotate
end end
local x = bone.ax local x = bone.ax
local y = bone.ay local y = bone.ay
if translateMix ~= 0 then x = x + (target.ax - x + self.data.offsetX) * mixX
x = x + (target.ax - x + self.data.offsetX) * translateMix y = x + (target.ay - y + self.data.offsetY) * mixX
y = x + (target.ay - y + self.data.offsetY) * translateMix
end
local scaleX = bone.ascaleX local scaleX = bone.ascaleX
local scaleY = bone.ascaleY local scaleY = bone.ascaleY
if scaleMix ~= 0 then if mixScaleX ~= 0 and scaleX ~= 0 then
if scaleX > 0.00001 then scaleX = (scaleX + (target.ascaleX - scaleX + self.data.offsetScaleX) * mixScaleX) / scaleX
scaleX = (scaleX + (target.ascaleX - scaleX + self.data.offsetScaleX) * scaleMix) / scaleX end
end if mixScaleY ~= 0 and scaleY ~= 0 then
if scaleY > 0.00001 then scaleY = (scaleY + (target.ascaleY - scaleY + self.data.offsetScaleY) * mixScaleY) / scaleY
scaleY = (scaleY + (target.ascaleY - scaleY + self.data.offsetScaleY) * scaleMix) / scaleY
end
end end
local shearY = bone.ashearY local shearY = bone.ashearY
if shearMix ~= 0 then if mixShearY ~= 0 then
local r = target.ashearY - shearY + self.data.offsetShearY local r = target.ashearY - shearY + self.data.offsetShearY
r = r - (16384 - math_floor(16384.499999999996 - r / 360)) * 360 r = r - (16384 - math_floor(16384.499999999996 - r / 360)) * 360
bone.shearY = bone.shearY + r * shearMix bone.shearY = bone.shearY + r * mixShearY
end end
bone:updateWorldTransformWith(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY) bone:updateWorldTransformWith(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY)
@ -302,40 +295,23 @@ function TransformConstraint:applyAbsoluteLocal ()
end end
function TransformConstraint:applyRelativeLocal () function TransformConstraint:applyRelativeLocal ()
local rotateMix = self.rotateMix local mixRotate = self.mixRotate
local translateMix = self.translateMix local mixX = self.mixX
local scaleMix = self.scaleMix local mixY = self.mixY
local shearMix = self.shearMix local mixScaleX = self.mixScaleX
local mixScaleY = self.mixScaleY
local mixShearY = self.mixShearY
local target = self.target local target = self.target
if not target.appliedValid then target:updateAppliedTransform() end
local bones = self.bones local bones = self.bones
for _, bone in ipairs(bones) do for _, bone in ipairs(bones) do
if not bone.appliedValid then bone:updateAppliedTransform() end local rotation = bone.arotation + (target.arotation + this.data.offsetRotation) * mixRotate
local x = bone.ax + (target.ax + this.data.offsetX) * mixX
local rotation = bone.arotation local y = bone.ay + (target.ay + this.data.offsetY) * mixY
if rotateMix ~= 0 then rotation = rotation + (target.arotation + self.data.offsetRotation) * rotateMix end local scaleX = (bone.ascaleX * ((target.ascaleX - 1 + this.data.offsetScaleX) * mixScaleX) + 1)
local scaleY = (bone.ascaleY * ((target.ascaleY - 1 + this.data.offsetScaleY) * mixScaleY) + 1)
local x = bone.ax local shearY = bone.ashearY + (target.ashearY + this.data.offsetShearY) * mixShearY
local y = bone.ay
if translateMix ~= 0 then
x = x + (target.ax + self.data.offsetX) * translateMix
y = y + (target.ay + self.data.offsetY) * translateMix
end
local scaleX = bone.ascaleX
local scaleY = bone.ascaleY
if scaleMix ~= 0 then
if scaleX > 0.00001 then
scaleX = scaleX * (((target.ascaleX - 1 + self.data.offsetScaleX) * scaleMix) + 1)
end
if scaleY > 0.00001 then
scaleY = scaleY * (((target.ascaleY - 1 + self.data.offsetScaleY) * scaleMix) + 1)
end
end
local shearY = bone.ashearY
if shearMix ~= 0 then shearY = shearY + (target.ashearY + self.data.offsetShearY) * shearMix end
bone:updateWorldTransformWith(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY) bone:updateWorldTransformWith(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY)
end end
end end

View File

@ -37,7 +37,7 @@ function TransformConstraintData.new (name)
skinRequired = false, skinRequired = false,
bones = {}, bones = {},
target = nil, target = nil,
rotateMix = 0, translateMix = 0, scaleMix = 0, shearMix = 0, mixRotate = 0, mixX = 0, mixY = 0, mixScaleX = 0, mixScaleY = 0, mixShearY = 0,
offsetRotation = 0, offsetX = 0, offsetY = 0, offsetScaleX = 0, offsetScaleY = 0, offsetShearY = 0, offsetRotation = 0, offsetX = 0, offsetY = 0, offsetScaleX = 0, offsetScaleY = 0, offsetShearY = 0,
relative = false, relative = false,
local_ = false local_ = false

View File

@ -37,7 +37,6 @@ local AttachmentType = require "spine-lua.attachments.AttachmentType"
local Attachment = require "spine-lua.attachments.Attachment" local Attachment = require "spine-lua.attachments.Attachment"
local nextID = 0 local nextID = 0
local SHL_11 = 2048
local VertexAttachment = {} local VertexAttachment = {}
VertexAttachment.__index = VertexAttachment VertexAttachment.__index = VertexAttachment
@ -45,16 +44,16 @@ setmetatable(VertexAttachment, { __index = Attachment })
function VertexAttachment.new (name, attachmentType) function VertexAttachment.new (name, attachmentType)
local self = Attachment.new(name, attachmentType) local self = Attachment.new(name, attachmentType)
self.vertexAttachment = true
self.id = nextID
nextID = nextID + 1
self.isVertexAttachment = true
self.bones = nil self.bones = nil
self.vertices = nil self.vertices = nil
self.worldVerticesLength = 0 self.worldVerticesLength = 0
while nextID > 65535 do
nextID = nextID - 65535
end
self.id = nextID * SHL_11
self.deformAttachment = self self.deformAttachment = self
nextID = nextID + 1
setmetatable(self, VertexAttachment) setmetatable(self, VertexAttachment)
return self return self
end end