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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

@ -558,9 +558,9 @@ function SkeletonJson.new (attachmentLoader)
for timelineName,timelineMap in pairs(slotMap) do
if not timelineMap 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
timeline:setFrame(i + 1, getValue(keyMap, "time", 0), keyMap["name"])
timeline:setFrame(i - 1, getValue(keyMap, "time", 0), keyMap["name"])
end
table_insert(timelines, timeline)
elseif timelineName == "rgba" then
@ -765,7 +765,7 @@ function SkeletonJson.new (attachmentLoader)
table_insert(timelines, readTimeline1(timelineMap, timeline, 0, scale))
elseif timelineName == "scale" then
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
local timeline = Animation.ScaleXTimeline.new(#timelineMap, #timelineMap, boneIndex)
table_insert(timelines, readTimeline1(timelineMap, timeline, 1, 1))
@ -774,7 +774,7 @@ function SkeletonJson.new (attachmentLoader)
table_insert(timelines, readTimeline1(timelineMap, timeline, 1, 1))
elseif timelineName == "shear" then
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
local timeline = Animation.ShearXTimeline.new(#timelineMap, #timelineMap, boneIndex)
table_insert(timelines, readTimeline1(timelineMap, timeline, 0, 1))
@ -901,7 +901,7 @@ function SkeletonJson.new (attachmentLoader)
if map.path then
for constraintName,constraintMap in pairs(map.path) do
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
constraintIndex = i
constraint = other
@ -914,12 +914,12 @@ function SkeletonJson.new (attachmentLoader)
if timelineName == "position" then
local timeline = Animation.PathConstraintPositionTimeline.new(#timelineMap, #timelineMap, constraintIndex)
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))
elseif timelineName == "spacing" then
local timeline = Animation.PathConstraintSpacingTimeline.new(#timelineMap, #timelineMap, constraintIndex)
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))
elseif timelineName == "mix" then
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
local timeline = Animation.DeformTimeline.new(#timelineMap, #timelineMap, slotIndex, attachment)
local time = getValue(keyMap, "time", 0)
local bezier = 0
for i,keyMap in ipairs(timelineMap) do
local deform = nil
@ -1007,7 +1008,7 @@ function SkeletonJson.new (attachmentLoader)
end
end
local frame = i - 1
timeline:setFrame(frame, time, mixRotate, mixX, mixY)
timeline:setFrame(frame, time, deform)
local nextMap = timelineMap[frame + 1]
if not nextMap then
timeline:shrink(bezier)
@ -1172,15 +1173,15 @@ function SkeletonJson.new (attachmentLoader)
readCurve = function (curve, timeline, bezier, frame, value, time1, time2, value1, value2, scale)
if curve == "stepped" then
if value ~= 0 then timeline.setStepped(frame) end
if value ~= 0 then timeline:setStepped(frame) end
return bezier
end
local i = value * 4
local i = value * 4 + 1
local cx1 = curve[i]
local cy1 = curve[i + 1] * scale
local cx2 = curve[i + 2]
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
end

View File

@ -60,9 +60,16 @@ end
function Slot:setAttachment (attachment)
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.attachmentTime = self.bone.skeleton.time
self.deform = {}
end
function Slot:setAttachmentTime (time)

View File

@ -7,7 +7,7 @@
-- 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
-- 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,
@ -28,6 +28,7 @@
-------------------------------------------------------------------------------
local setmetatable = setmetatable
local tonumber = tonumber
local table_insert = table.insert
local math_abs = math.abs
@ -47,7 +48,8 @@ function TextureAtlasPage.new ()
vWrap = nil,
texture = nil,
width = 0,
height = 0
height = 0,
pma = false
}
setmetatable(self, TextureAtlasPage)
return self
@ -73,168 +75,178 @@ function TextureAtlas:parse (atlasContent, imageLoader)
if not atlasContent then error("atlasContent cannot be nil.", 2) end
if not imageLoader then error("imageLoader cannot be nil.", 2) end
function lineIterator(s)
if s:sub(-1)~="\n" then s=s.."\n" end
return s:gmatch("(.-)\n")
local readLine = atlasContent:gmatch("[ \t]*(.-)[ \t]*\r?\n")
local trim = function (value)
return value:match("^%s*(.-)%s*$")
end
local lines = {}
local index = 0
local numLines = 0
for line in lineIterator(atlasContent) do
lines[numLines] = line
numLines = numLines + 1
end
local entry = {}
local readEntry = function (entry, line)
if not line then return 0 end
if line:len() == 0 then return 0 end
local readLine = function ()
if index >= numLines then return nil end
local line = lines[index]
index = index + 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 colon = line:find(":")
if not colon then return 0 end
entry[0] = trim(line:sub(1, colon))
local lastMatch = colon + 1
local i = 1
local lastMatch = idx + 1
local tuple = {}
while i <= 3 do
while true do
local comma = line:find(",", lastMatch)
if not comma then break end
tuple[i] = line:sub(lastMatch, comma - 1):match'^%s*(.*%S)' or ''
if not comma then
entry[i] = trim(line:sub(lastMatch))
return i
end
entry[i] = trim(line:sub(lastMatch, comma - lastMatch))
lastMatch = comma + 1
if i == 4 then return 4 end
i = i + 1
end
tuple[i] = line:sub(lastMatch):match'^%s*(.*%S)' or ''
return tuple
end
local parseInt = function (str)
return tonumber(str)
local page
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
local filterFromString = function (str)
str = str:lower()
if str == "nearest" then return TextureFilter.Nearest
elseif str == "linear" then return TextureFilter.Linear
elseif str == "mipmap" then return TextureFilter.MipMap
elseif str == "mipmapnearestnearest" then return TextureFilter.MipMapNearestNearest
elseif str == "mipmaplinearnearest" then return TextureFilter.MipMapLinearNearest
elseif str == "mipmapnearestlinear" then return TextureFilter.MipMapNearestLinear
elseif str == "mipmaplinearlinear" then return TextureFilter.MipMapLinearLinear
else error("Unknown texture wrap: " .. str, 2)
local regionFields = {}
regionFields["xy"] = function () -- Deprecated, use bounds.
region.x = tonumber(entry[1])
region.y = tonumber(entry[2])
end
regionFields["size"] = function () -- Deprecated, use bounds.
region.width = tonumber(entry[1])
region.height = tonumber(entry[2])
end
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
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
local line = readLine()
if not line then break end
line = line:match'^%s*(.*%S)' or ''
if line:len() == 0 then
page = nil
line = readLine()
elseif not page then
page = TextureAtlasPage.new()
page.name = line
local tuple = readTuple()
if #tuple == 2 then
page.width = parseInt(tuple[1])
page.height = parseInt(tuple[2])
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)
page.name = trim(line)
while true do
line = readLine()
if readEntry(entry, line) == 0 then break end
local field = pageFields[entry[0]]
if field then field() end
end
tuple = readTuple()
page.minFilter = filterFromString(tuple[1])
page.magFilter = filterFromString(tuple[2])
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)
page.texture = imageLoader(page.name)
-- FIXME - Apply the filter and wrap settings to the texture.
-- page.texture:setFilters(page.minFilter, page.magFilter)
-- page.texture:setWraps(page.uWrap, page.vWrap)
table_insert(self.pages, page)
else
local region = TextureAtlasRegion.new()
region.name = line
region = TextureAtlasRegion.new()
region.page = page
local rotateValue = readValue()
if rotateValue == "true" then
region.degrees = 90
elseif rotateValue == "false" then
region.degrees = 0
else
region.degrees = tonumber(rotateValue)
end
if region.degrees == 90 then region.rotate = true end
local tuple = readTuple()
local x = parseInt(tuple[1])
local y = parseInt(tuple[2])
tuple = readTuple()
local width = parseInt(tuple[1])
local height = parseInt(tuple[2])
region.u = x / page.width
region.v = y / page.height
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()
region.name = line
while true do
line = readLine()
local count = readEntry(entry, line)
if count == 0 then break end
local field = regionFields[entry[0]]
if field then
field()
else
if not names then
names = {}
values = {}
end
table_insert(names, entry[0])
local entryValues = {}
local i = 0
while i < count do
table_insert(entryValues, tonumber(entry[i + 1]))
i = i + 1
end
table_insert(values, entryValues)
end
end
region.originalWidth = parseInt(tuple[1])
region.originalHeight = parseInt(tuple[2])
tuple = readTuple()
region.offsetX = parseInt(tuple[1])
region.offsetY = parseInt(tuple[2])
region.index = parseInt(readValue())
if region.originalWidth == 0 and region.originalHeight == 0 then
region.originalWidth = region.width
region.originalHeight = region.height
end
if names and #names > 0 then
region.names = names
region.values = values
names = nil
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
table_insert(self.regions, region)
end

View File

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

View File

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

View File

@ -37,7 +37,7 @@ function TransformConstraintData.new (name)
skinRequired = false,
bones = {},
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,
relative = false,
local_ = false

View File

@ -37,7 +37,6 @@ local AttachmentType = require "spine-lua.attachments.AttachmentType"
local Attachment = require "spine-lua.attachments.Attachment"
local nextID = 0
local SHL_11 = 2048
local VertexAttachment = {}
VertexAttachment.__index = VertexAttachment
@ -45,16 +44,16 @@ setmetatable(VertexAttachment, { __index = Attachment })
function VertexAttachment.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.vertices = nil
self.worldVerticesLength = 0
while nextID > 65535 do
nextID = nextID - 65535
end
self.id = nextID * SHL_11
self.deformAttachment = self
nextID = nextID + 1
setmetatable(self, VertexAttachment)
return self
end