[lua] SkeletonJson 4.0 port, clean up.

This commit is contained in:
Nathan Sweet 2021-06-03 21:55:17 -04:00
parent 7df74c2fa4
commit 3c0a43687b
18 changed files with 2258 additions and 2263 deletions

File diff suppressed because it is too large Load Diff

View File

@ -159,7 +159,7 @@ function EventQueue:drain ()
end end
self:clear() self:clear()
self.drainDisabled = false; self.drainDisabled = false
end end
function EventQueue:clear () function EventQueue:clear ()
@ -326,7 +326,7 @@ function AnimationState:updateMixingFrom (to, delta)
from.trackTime = from.trackTime + delta * from.timeScale from.trackTime = from.trackTime + delta * from.timeScale
to.mixTime = to.mixTime + delta to.mixTime = to.mixTime + delta
return false; return false
end end
function AnimationState:apply (skeleton) function AnimationState:apply (skeleton)
@ -388,7 +388,7 @@ function AnimationState:apply (skeleton)
end end
end end
self:queueEvents(current, animationTime) self:queueEvents(current, animationTime)
self.events = {}; self.events = {}
current.nextAnimationLast = animationTime current.nextAnimationLast = animationTime
current.nextTrackLast = current.trackTime current.nextTrackLast = current.trackTime
end end
@ -400,7 +400,7 @@ function AnimationState:apply (skeleton)
-- subsequent timelines see any deform, but the subsequent timelines don't set an attachment (eg they are also mixing out or -- subsequent timelines see any deform, but the subsequent timelines don't set an attachment (eg they are also mixing out or
-- 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
@ -411,7 +411,7 @@ function AnimationState:apply (skeleton)
end end
end end
end end
self.unkeyedState = self.unkeyedState + 2; -- Increasing after each use avoids the need to reset attachmentState for every slot. self.unkeyedState = self.unkeyedState + 2 -- Increasing after each use avoids the need to reset attachmentState for every slot.
queue:drain() queue:drain()
@ -453,11 +453,11 @@ function AnimationState:applyMixingFrom (to, skeleton, blend)
local firstFrame = #from.timelinesRotation == 0 local firstFrame = #from.timelinesRotation == 0
local timelinesRotation = from.timelinesRotation local timelinesRotation = from.timelinesRotation
from.totalAlpha = 0; from.totalAlpha = 0
for i,timeline in ipairs(timelines) do for i,timeline in ipairs(timelines) do
local skipSubsequent = false; local skipSubsequent = false
local direction = MixDirection.out; local direction = MixDirection.out
local timelineBlend = MixBlend.setup local timelineBlend = MixBlend.setup
local alpha = 0 local alpha = 0
if timelineMode[i] == SUBSEQUENT then if timelineMode[i] == SUBSEQUENT then
@ -498,7 +498,7 @@ function AnimationState:applyMixingFrom (to, skeleton, blend)
if (to.mixDuration > 0) then if (to.mixDuration > 0) then
self:queueEvents(from, animationTime) self:queueEvents(from, animationTime)
end end
self.events = {}; self.events = {}
from.nextAnimationLast = animationTime from.nextAnimationLast = animationTime
from.nextTrackLast = from.trackTime from.nextTrackLast = from.trackTime
@ -506,20 +506,20 @@ function AnimationState:applyMixingFrom (to, skeleton, blend)
end end
function AnimationState:applyAttachmentTimeline(timeline, skeleton, time, blend, attachments) function AnimationState:applyAttachmentTimeline(timeline, skeleton, time, blend, attachments)
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 local frames = timeline.frames
if time < 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 local frameIndex = 0
if (time >= frames[zlen(frames) - 1]) then -- Time is after last frame. if (time >= frames[zlen(frames) - 1]) then -- Time is after last frame.
frameIndex = zlen(frames) - 1; frameIndex = zlen(frames) - 1
else else
frameIndex = Animation.binarySearch(frames, time, 1) - 1; frameIndex = Animation.binarySearch(frames, time, 1) - 1
end end
self:setAttachment(skeleton, slot, timeline.attachmentNames[frameIndex], attachments) self:setAttachment(skeleton, slot, timeline.attachmentNames[frameIndex], attachments)
end end
@ -665,7 +665,7 @@ function AnimationState:clearTracks ()
local queue = self.queue local queue = self.queue
local tracks = self.tracks local tracks = self.tracks
local oldDrainDisabled = queue.drainDisabled local oldDrainDisabled = queue.drainDisabled
queue.drainDisabled = true; queue.drainDisabled = true
local numTracks = getNumTracks(tracks) local numTracks = getNumTracks(tracks)
local i = 0 local i = 0
while i <= numTracks do while i <= numTracks do
@ -673,7 +673,7 @@ function AnimationState:clearTracks ()
end end
tracks = {} tracks = {}
queue.drainDisabled = oldDrainDisabled queue.drainDisabled = oldDrainDisabled
queue:drain(); queue:drain()
end end
function AnimationState:clearTrack (trackIndex) function AnimationState:clearTrack (trackIndex)
@ -686,7 +686,7 @@ function AnimationState:clearTrack (trackIndex)
self:disposeNext(current) self:disposeNext(current)
local entry = current; local entry = current
while (true) do while (true) do
local from = entry.mixingFrom local from = entry.mixingFrom
if from == nil then break end if from == nil then break end
@ -717,7 +717,7 @@ function AnimationState:setCurrent (index, current, interrupt)
current.interruptAlpha = current.interruptAlpha * math_min(1, from.mixTime / from.mixDuration) current.interruptAlpha = current.interruptAlpha * math_min(1, from.mixTime / from.mixDuration)
end end
from.timelinesRotation = {}; from.timelinesRotation = {}
end end
queue:start(current) queue:start(current)
@ -731,7 +731,7 @@ end
function AnimationState:setAnimation (trackIndex, animation, loop) function AnimationState:setAnimation (trackIndex, animation, loop)
if not animation then error("animation cannot be null.") end if not animation then error("animation cannot be null.") end
local interrupt = true; local interrupt = true
local current = self:expandToIndex(trackIndex) local current = self:expandToIndex(trackIndex)
local queue = self.queue local queue = self.queue
local tracks = self.tracks local tracks = self.tracks
@ -743,7 +743,7 @@ function AnimationState:setAnimation (trackIndex, animation, loop)
queue:_end(current) queue:_end(current)
self:disposeNext(current) self:disposeNext(current)
current = current.mixingFrom current = current.mixingFrom
interrupt = false; interrupt = false
else else
self:disposeNext(current) self:disposeNext(current)
end end

View File

@ -40,13 +40,12 @@ local math_pi = math.pi
local TransformMode = require "spine-lua.TransformMode" local TransformMode = require "spine-lua.TransformMode"
function math.sign(x) function math.sign(x)
if x<0 then if x < 0 then
return -1 return -1
elseif x>0 then elseif x > 0 then
return 1 return 1
else
return 0
end end
return 0
end end
local math_sign = math.sign local math_sign = math.sign
@ -96,8 +95,8 @@ function Bone:updateWorldTransformWith (x, y, rotation, scaleX, scaleY, shearX,
self.ashearY = shearY self.ashearY = shearY
self.appliedValid = true 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 parent == nil then
@ -132,7 +131,7 @@ function Bone:updateWorldTransformWith (x, y, rotation, scaleX, scaleY, shearX,
self.b = pa * lb + pb * ld self.b = pa * lb + pb * ld
self.c = pc * la + pd * lc self.c = pc * la + pd * lc
self.d = pc * lb + pd * ld self.d = pc * lb + pd * ld
return; return
elseif transformMode == TransformMode.onlyTranslation then elseif transformMode == TransformMode.onlyTranslation then
local rotationY = rotation + 90 + shearY local rotationY = rotation + 90 + shearY
self.a = math_cos(math_rad(rotation + shearX)) * scaleX self.a = math_cos(math_rad(rotation + shearX)) * scaleX
@ -148,11 +147,11 @@ function Bone:updateWorldTransformWith (x, y, rotation, scaleX, scaleY, shearX,
pc = pc / self.skeleton.scaleY pc = pc / self.skeleton.scaleY
pb = pc * s pb = pc * s
pd = pa * s pd = pa * s
prx = math_deg(math_atan2(pc, pa)); prx = math_deg(math_atan2(pc, pa))
else else
pa = 0; pa = 0
pc = 0; pc = 0
prx = 90 - math_deg(math_atan2(pd, pb)); prx = 90 - math_deg(math_atan2(pd, pb))
end end
local rx = rotation + shearX - prx local rx = rotation + shearX - prx
local ry = rotation + shearY - prx + 90 local ry = rotation + shearY - prx + 90
@ -181,10 +180,10 @@ function Bone:updateWorldTransformWith (x, y, rotation, scaleX, scaleY, shearX,
local r = math_pi / 2 + math_atan2(zc, za) local r = math_pi / 2 + math_atan2(zc, za)
local zb = math_cos(r) * s local zb = math_cos(r) * s
local zd = math_sin(r) * s local zd = math_sin(r) * s
local la = math_cos(math_rad(shearX)) * scaleX; local la = math_cos(math_rad(shearX)) * scaleX
local lb = math_cos(math_rad(90 + shearY)) * scaleY; local lb = math_cos(math_rad(90 + shearY)) * scaleY
local lc = math_sin(math_rad(shearX)) * scaleX; local lc = math_sin(math_rad(shearX)) * scaleX
local ld = math_sin(math_rad(90 + shearY)) * scaleY; local ld = math_sin(math_rad(90 + shearY)) * scaleY
self.a = za * la + zb * lc self.a = za * la + zb * lc
self.b = za * lb + zb * ld self.b = za * lb + zb * ld
self.c = zc * la + zd * lc self.c = zc * la + zd * lc

View File

@ -47,7 +47,8 @@ function BoneData.new (index, name, parent)
shearX = 0, shearY = 0, shearX = 0, shearY = 0,
inheritRotation = true, inheritRotation = true,
inheritScale = true, inheritScale = true,
skinRequired = false skinRequired = false,
color = nil
} }
return self return self

View File

@ -1,61 +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 AttachmentType = require "spine-lua.AttachmentType"
local BoundingBoxAttachment = {}
function BoundingBoxAttachment.new (name)
if not name then error("name cannot be nil", 2) end
local self = {
name = name,
type = AttachmentType.boundingbox,
vertices = {}
}
function self:computeWorldVertices (x, y, bone, worldVertices)
x = x + bone.worldX
y = y + bone.worldY
local m00 = bone.m00
local m01 = bone.m01
local m10 = bone.m10
local m11 = bone.m11
local vertices = self.vertices
local count = #vertices
for i = 1, count, 2 do
local px = vertices[i]
local py = vertices[i + 1]
worldVertices[i] = px * m00 + py * m01 + x
worldVertices[i + 1] = px * m10 + py * m11 + y
end
end
return self
end
return BoundingBoxAttachment

View File

@ -102,12 +102,12 @@ function IkConstraint:apply1 (bone, targetX, targetY, compress, stretch, uniform
tx = targetX - bone.worldX tx = targetX - bone.worldX
ty = targetY - bone.worldY ty = targetY - bone.worldY
elseif bone.data.transformMode == TransformMode.noRotationOrReflection then elseif bone.data.transformMode == TransformMode.noRotationOrReflection then
local s = math_abs(pa * pd - pb * pc) / (pa * pa + pc * pc); local s = math_abs(pa * pd - pb * pc) / (pa * pa + pc * pc)
local sa = pa / bone.skeleton.scaleX; local sa = pa / bone.skeleton.scaleX
local sc = pc / bone.skeleton.scaleY; local sc = pc / bone.skeleton.scaleY
pb = -sc * s * bone.skeleton.scaleX; pb = -sc * s * bone.skeleton.scaleX
pd = sa * s * bone.skeleton.scaleY; pd = sa * s * bone.skeleton.scaleY
rotationIK = rotationIK + math_deg(math_atan2(sc, sa)); rotationIK = rotationIK + math_deg(math_atan2(sc, sa))
local x = targetX - p.worldX local x = targetX - p.worldX
@ -255,13 +255,13 @@ function IkConstraint:apply2 (parent, child, targetX, targetY, bendDir, stretch,
b = psy * l2 b = psy * l2
local aa = a * a local aa = a * a
local bb = b * b local bb = b * b
local ta = math_atan2(ty, tx); local ta = math_atan2(ty, tx)
c = bb * l1 * l1 + aa * dd - aa * bb c = bb * l1 * l1 + aa * dd - aa * bb
local c1 = -2 * bb * l1 local c1 = -2 * bb * l1
local c2 = bb - aa local c2 = bb - aa
d = c1 * c1 - 4 * c2 * c d = c1 * c1 - 4 * c2 * c
if d >= 0 then if d >= 0 then
local q = math_sqrt(d); local q = math_sqrt(d)
if (c1 < 0) then q = -q end if (c1 < 0) then q = -q end
q = -(c1 + q) / 2 q = -(c1 + q) / 2
local r0 = q / c2 local r0 = q / c2
@ -279,7 +279,7 @@ function IkConstraint:apply2 (parent, child, targetX, targetY, bendDir, stretch,
local minAngle = math_pi local minAngle = math_pi
local minX = l1 - a local minX = l1 - a
local minDist = minX * minX local minDist = minX * minX
local minY = 0; local minY = 0
local maxAngle = 0 local maxAngle = 0
local maxX = l1 + a local maxX = l1 + a
local maxDist = maxX * maxX local maxDist = maxX * maxX
@ -328,7 +328,7 @@ function IkConstraint:apply2 (parent, child, targetX, targetY, bendDir, stretch,
elseif a2 < -180 then elseif a2 < -180 then
a2 = a2 + 360 a2 = a2 + 360
end end
child:updateWorldTransformWith(cx, cy, rotation + a2 * alpha, child.ascaleX, child.ascaleY, child.ashearX, child.ashearY); child:updateWorldTransformWith(cx, cy, rotation + a2 * alpha, child.ascaleX, child.ascaleY, child.ashearX, child.ashearY)
end end
return IkConstraint return IkConstraint

View File

@ -1,93 +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 AttachmentType = require "spine-lua.AttachmentType"
local MeshAttachment = {}
function MeshAttachment.new (name)
if not name then error("name cannot be nil", 2) end
local self = {
name = name,
type = AttachmentType.mesh,
vertices = nil,
uvs = nil,
regionUVs = nil,
triangles = nil,
hullLength = 0,
r = 1, g = 1, b = 1, a = 1,
path = nil,
rendererObject = nil,
regionU = 0, regionV = 0, regionU2 = 1, regionV2 = 1, regionRotate = false,
regionOffsetX = 0, regionOffsetY = 0,
regionWidth = 0, regionHeight = 0,
regionOriginalWidth = 0, regionOriginalHeight = 0,
edges = nil,
width = 0, height = 0
}
function self:updateUVs ()
local width, height = self.regionU2 - self.regionU, self.regionV2 - self.regionV
local n = #self.regionUVs
if not self.uvs or #self.uvs ~= n then
self.uvs = {}
end
if self.regionRotate then
for i = 1, n, 2 do
self.uvs[i] = self.regionU + self.regionUVs[i + 1] * width
self.uvs[i + 1] = self.regionV + height - self.regionUVs[i] * height
end
else
for i = 1, n, 2 do
self.uvs[i] = self.regionU + self.regionUVs[i] * width
self.uvs[i + 1] = self.regionV + self.regionUVs[i + 1] * height
end
end
end
function self:computeWorldVertices (x, y, slot, worldVertices)
local bone = slot.bone
x,y=slot.bone.skeleton.x,slot.bone.skeleton.y
x = x + bone.worldX
y = y + bone.worldY
local m00, m01, m10, m11 = bone.m00, bone.m01, bone.m10, bone.m11
local vertices = self.vertices
local verticesCount = #vertices
if slot.deform and #slot.deform == verticesCount then vertices = slot.deform end
for i = 1, verticesCount, 2 do
local vx = vertices[i]
local vy = vertices[i + 1]
worldVertices[i] = vx * m00 + vy * m01 + x
worldVertices[i + 1] = vx * m10 + vy * m11 + y
end
end
return self
end
return MeshAttachment

View File

@ -99,7 +99,7 @@ function PathConstraint:update ()
local rotate = rotateMix > 0 local rotate = rotateMix > 0
if not translate and not rotate 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 percentSpacing = data.spacingMode == PathConstraintData.SpacingMode.percent
local rotateMode = data.rotateMode local rotateMode = data.rotateMode
local tangents = rotateMode == PathConstraintData.RotateMode.tangent local tangents = rotateMode == PathConstraintData.RotateMode.tangent
@ -117,7 +117,7 @@ function PathConstraint:update ()
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 + 1]
local setupLength = bone.data.length local setupLength = bone.data.length
if setupLength < PathConstraint.epsilon then if setupLength < PathConstraint.epsilon then
if scale then lengths[i + 1] = 0 end if scale then lengths[i + 1] = 0 end
@ -157,12 +157,12 @@ function PathConstraint:update ()
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 = rotateMode == PathConstraintData.RotateMode.chain
else else
tip = false; tip = false
local p = self.target.bone; local p = self.target.bone
if p.a * p.d - p.b * p.c > 0 then if p.a * p.d - p.b * p.c > 0 then
offsetRotation = offsetRotation * utils.degRad offsetRotation = offsetRotation * utils.degRad
else else
@ -210,8 +210,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) * rotateMix
boneY = boneY + (length * (sin * a + cos * c) - dy) * rotateMix; boneY = boneY + (length * (sin * a + cos * c) - dy) * rotateMix
else else
r = r + offsetRotation r = r + offsetRotation
end end
@ -249,7 +249,7 @@ function PathConstraint:computeWorldPositions (path, spacesCount, tangents, perc
if not path.constantSpeed then if not path.constantSpeed then
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 percentPosition then position = position * pathLength end
if percentSpacing then if percentSpacing then
i = 1 i = 1
@ -258,12 +258,12 @@ function PathConstraint:computeWorldPositions (path, spacesCount, tangents, perc
i = i + 1 i = i + 1
end end
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]
position = position + space position = position + space
local p = position local p = position
@ -331,14 +331,14 @@ function PathConstraint:computeWorldPositions (path, spacesCount, tangents, perc
world[verticesLength - 1 + 1] = world[1 + 1] world[verticesLength - 1 + 1] = world[1 + 1]
else else
curveCount = curveCount - 1 curveCount = curveCount - 1
verticesLength = verticesLength - 4; verticesLength = verticesLength - 4
world = utils.setArraySize(self.world, verticesLength) world = utils.setArraySize(self.world, verticesLength)
path:computeWorldVertices(target, 2, verticesLength, world, 0, 2) path:computeWorldVertices(target, 2, verticesLength, world, 0, 2)
end end
-- Curve lengths. -- Curve lengths.
local curves = utils.setArraySize(self.curves, curveCount) local curves = utils.setArraySize(self.curves, curveCount)
local pathLength = 0; local pathLength = 0
local x1 = world[0 + 1] local x1 = world[0 + 1]
local y1 = world[1 + 1] local y1 = world[1 + 1]
local cx1 = 0 local cx1 = 0
@ -392,7 +392,7 @@ function PathConstraint:computeWorldPositions (path, spacesCount, tangents, perc
if percentPosition then if percentPosition then
position = position * pathLength position = position * pathLength
else else
position = position * pathLength / path.lengths[curveCount]; position = position * pathLength / path.lengths[curveCount]
end end
if percentSpacing then if percentSpacing then
i = 1 i = 1
@ -496,7 +496,7 @@ function PathConstraint:computeWorldPositions (path, spacesCount, tangents, perc
local prev = segments[segment - 1 + 1] local prev = segments[segment - 1 + 1]
p = segment + (p - prev) / (length - prev) p = segment + (p - prev) / (length - prev)
end end
break; break
end end
segment = segment + 1 segment = segment + 1
end end
@ -536,7 +536,7 @@ function PathConstraint:addCurvePosition(p, x1, y1, cx1, cy1, cx2, cy2, x2, y2,
out[o + 1] = x1 out[o + 1] = x1
out[o + 2] = y1 out[o + 2] = y1
out[o + 3] = math_atan2(cy1 - y1, cx1 - x1) out[o + 3] = math_atan2(cy1 - y1, cx1 - x1)
return; return
end end
local tt = p * p local tt = p * p
local ttt = tt * p local ttt = tt * p

View File

@ -1,100 +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 AttachmentType = require "spine-lua.AttachmentType"
local RegionAttachment = {}
function RegionAttachment.new (name)
if not name then error("name cannot be nil", 2) end
local self = {
name = name,
type = AttachmentType.region,
x = 0, y = 0,
rotation = 0,
scaleX = 1, scaleY = 1,
width = 0, height = 0,
offset = {},
uvs = {},
r = 1, g = 1, b = 1, a = 1,
path = nil,
rendererObject = nil,
regionOffsetX = 0, regionOffsetY = 0,
regionWidth = 0, regionHeight = 0,
regionOriginalWidth = 0, regionOriginalHeight = 0
}
function self:updateOffset ()
local regionScaleX = self.width / self.regionOriginalWidth * self.scaleX
local regionScaleY = self.height / self.regionOriginalHeight * self.scaleY
local localX = -self.width / 2 * self.scaleX + self.regionOffsetX * regionScaleX
local localY = -self.height / 2 * self.scaleY + self.regionOffsetY * regionScaleY
local localX2 = localX + self.regionWidth * regionScaleX
local localY2 = localY + self.regionHeight * regionScaleY
local radians = self.rotation * math.pi / 180
local cos = math.cos(radians)
local sin = math.sin(radians)
local localXCos = localX * cos + self.x
local localXSin = localX * sin
local localYCos = localY * cos + self.y
local localYSin = localY * sin
local localX2Cos = localX2 * cos + self.x
local localX2Sin = localX2 * sin
local localY2Cos = localY2 * cos + self.y
local localY2Sin = localY2 * sin
local offset = self.offset
offset[0] = localXCos - localYSin -- X1
offset[1] = localYCos + localXSin -- Y1
offset[2] = localXCos - localY2Sin -- X2
offset[3] = localY2Cos + localXSin -- Y2
offset[4] = localX2Cos - localY2Sin -- X3
offset[5] = localY2Cos + localX2Sin -- Y3
offset[6] = localX2Cos - localYSin -- X4
offset[7] = localYCos + localX2Sin -- Y4
end
function self:computeWorldVertices (x, y, bone, worldVertices)
x = x + bone.worldX
y = y + bone.worldY
local m00, m01, m10, m11 = bone.m00, bone.m01, bone.m10, bone.m11
local offset = self.offset
local vertices = self.vertices;
vertices[0] = offset[0] * m00 + offset[1] * m01 + x
vertices[1] = offset[0] * m10 + offset[1] * m11 + y
vertices[2] = offset[2] * m00 + offset[3] * m01 + x
vertices[3] = offset[2] * m10 + offset[3] * m11 + y
vertices[4] = offset[4] * m00 + offset[5] * m01 + x
vertices[5] = offset[4] * m10 + offset[5] * m11 + y
vertices[6] = offset[6] * m00 + offset[7] * m01 + x
vertices[7] = offset[6] * m10 + offset[7] * m11 + y
end
return self
end
return RegionAttachment

View File

@ -460,7 +460,7 @@ function Skeleton:setSkinByReference(newSkin)
end end
function Skeleton:getAttachment (slotName, attachmentName) function Skeleton:getAttachment (slotName, attachmentName)
return self:getAttachmentByIndex(self.data.slotNameIndices[slotName], attachmentName) return self:getAttachmentByIndex(self.data.nameToSlot[slotName].index, attachmentName)
end end
function Skeleton:getAttachmentByIndex (slotIndex, attachmentName) function Skeleton:getAttachmentByIndex (slotIndex, attachmentName)
@ -520,7 +520,7 @@ end
function Skeleton:getBounds(offset, size) function Skeleton:getBounds(offset, size)
if not offset then error("offset cannot be null.", 2) end if not offset then error("offset cannot be null.", 2) end
if not size then error("size cannot be null.", 2) end if not size then error("size cannot be null.", 2) end
local drawOrder = self.drawOrder; local drawOrder = self.drawOrder
local minX = 99999999 local minX = 99999999
local minY = 99999999 local minY = 99999999
local maxX = -99999999 local maxX = -99999999

View File

@ -118,7 +118,7 @@ function SkeletonClipping:clipTriangles(vertices, uvs, triangles, trianglesLengt
local u2 = uvs[vertexOffset] local u2 = uvs[vertexOffset]
local v2 = uvs[vertexOffset + 1] local v2 = uvs[vertexOffset + 1]
vertexOffset = (triangles[i + 2] - 1) * 2 + 1; vertexOffset = (triangles[i + 2] - 1) * 2 + 1
local x3 = vertices[vertexOffset] local x3 = vertices[vertexOffset]
local y3 = vertices[vertexOffset + 1] local y3 = vertices[vertexOffset + 1]
local u3 = uvs[vertexOffset] local u3 = uvs[vertexOffset]
@ -135,7 +135,7 @@ function SkeletonClipping:clipTriangles(vertices, uvs, triangles, trianglesLengt
local d1 = x3 - x2 local d1 = x3 - x2
local d2 = x1 - x3 local d2 = x1 - x3
local d4 = y3 - y1 local d4 = y3 - y1
local d = 1 / (d0 * d2 + d1 * (y1 - y3)); local d = 1 / (d0 * d2 + d1 * (y1 - y3))
local clipOutputCount = clipOutputLength / 2 local clipOutputCount = clipOutputLength / 2
local clipOutputItems = clipOutput local clipOutputItems = clipOutput
@ -193,7 +193,7 @@ function SkeletonClipping:clipTriangles(vertices, uvs, triangles, trianglesLengt
clippedTrianglesItems[s] = index clippedTrianglesItems[s] = index
clippedTrianglesItems[s + 1] = index + 1 clippedTrianglesItems[s + 1] = index + 1
clippedTrianglesItems[s + 2] = index + 2 clippedTrianglesItems[s + 2] = index + 2
index = index + 3; index = index + 3
break break
end end
p = p + 1 p = p + 1
@ -246,7 +246,7 @@ function SkeletonClipping:clip(x1, y1, x2, y2, x3, y3, clippingArea, output)
local inputX2 = inputVertices[ii + 2] local inputX2 = inputVertices[ii + 2]
local inputY2 = inputVertices[ii + 3] local inputY2 = inputVertices[ii + 3]
local side2 = deltaX * (inputY2 - edgeY2) - deltaY * (inputX2 - edgeX2) > 0 local side2 = deltaX * (inputY2 - edgeY2) - deltaY * (inputX2 - edgeX2) > 0
local continue = false; local continue = false
if deltaX * (inputY - edgeY2) - deltaY * (inputX - edgeX2) > 0 then if deltaX * (inputY - edgeY2) - deltaY * (inputX - edgeX2) > 0 then
if side2 then -- v1 inside, v2 inside if side2 then -- v1 inside, v2 inside
table_insert(output, inputX2) table_insert(output, inputX2)

View File

@ -37,6 +37,7 @@ function SkeletonData.new ()
name, name,
bones = {}, bones = {},
slots = {}, slots = {},
nameToSlot = {},
skins = {}, skins = {},
defaultSkin = nil, defaultSkin = nil,
events = {}, events = {},
@ -45,8 +46,7 @@ function SkeletonData.new ()
transformConstraints = {}, transformConstraints = {},
pathConstraints = {}, pathConstraints = {},
x, y, width, height, x, y, width, height,
version, hash, imagesPath, version, hash, imagesPath
slotNameIndices = {}
} }
setmetatable(self, SkeletonData) setmetatable(self, SkeletonData)
@ -71,15 +71,7 @@ end
function SkeletonData:findSlot (slotName) function SkeletonData:findSlot (slotName)
if not slotName then error("slotName cannot be nil.", 2) end if not slotName then error("slotName cannot be nil.", 2) end
for i,slot in ipairs(self.slots) do return self.nameToSlot[slotName]
if slot.name == slotName then return slot end
end
return nil
end
function SkeletonData:findSlotIndex (slotName)
if not slotName then error("slotName cannot be nil.", 2) end
return self.slotNameIndices[slotName] or -1
end end
function SkeletonData:findSkin (skinName) function SkeletonData:findSkin (skinName)
@ -130,12 +122,4 @@ function SkeletonData:findPathConstraint (constraintName)
return nil return nil
end end
function SkeletonData:findPathConstraintIndex (constraintName)
if not constraintName then error("constraintName cannot be nil.", 2) end
for i,constraint in ipairs(self.pathConstraints) do
if constraint.name == constraintName then return i end
end
return -1
end
return SkeletonData return SkeletonData

View File

@ -65,6 +65,8 @@ function SkeletonJson.new (attachmentLoader)
local readAttachment local readAttachment
local readAnimation local readAnimation
local readCurve local readCurve
local readTimeline1
local readTimeline2
local getArray local getArray
local getValue = function (map, name, default) local getValue = function (map, name, default)
@ -83,9 +85,6 @@ function SkeletonJson.new (attachmentLoader)
if skeletonMap then if skeletonMap then
skeletonData.hash = skeletonMap["hash"] skeletonData.hash = skeletonMap["hash"]
skeletonData.version = skeletonMap["spine"] skeletonData.version = skeletonMap["spine"]
if ("3.8.75" == skeletonData.version) then
error("Unsupported skeleton data, please export with a newer version of Spine.")
end
skeletonData.x = skeletonMap["x"] skeletonData.x = skeletonMap["x"]
skeletonData.y = skeletonMap["y"] skeletonData.y = skeletonMap["y"]
skeletonData.width = skeletonMap["width"] skeletonData.width = skeletonMap["width"]
@ -105,17 +104,25 @@ function SkeletonJson.new (attachmentLoader)
if not parent then error("Parent bone not found: " .. parentName) end if not parent then error("Parent bone not found: " .. parentName) end
end end
local data = BoneData.new(i, boneName, parent) local data = BoneData.new(i, boneName, parent)
data.length = getValue(boneMap, "length", 0) * scale; data.length = getValue(boneMap, "length", 0) * scale
data.x = getValue(boneMap, "x", 0) * scale; data.x = getValue(boneMap, "x", 0) * scale
data.y = getValue(boneMap, "y", 0) * scale; data.y = getValue(boneMap, "y", 0) * scale
data.rotation = getValue(boneMap, "rotation", 0); data.rotation = getValue(boneMap, "rotation", 0)
data.scaleX = getValue(boneMap, "scaleX", 1); data.scaleX = getValue(boneMap, "scaleX", 1)
data.scaleY = getValue(boneMap, "scaleY", 1); data.scaleY = getValue(boneMap, "scaleY", 1)
data.shearX = getValue(boneMap, "shearX", 0); data.shearX = getValue(boneMap, "shearX", 0)
data.shearY = getValue(boneMap, "shearY", 0); data.shearY = getValue(boneMap, "shearY", 0)
data.transformMode = TransformMode[getValue(boneMap, "transform", "normal")] data.transformMode = TransformMode[getValue(boneMap, "transform", "normal")]
data.skinRequired = getValue(boneMap, "skin", false) data.skinRequired = getValue(boneMap, "skin", false)
local color = boneMap["color"]
if color then
data.color = Color.newWith(tonumber(color:sub(1, 2), 16) / 255,
tonumber(color:sub(3, 4), 16) / 255,
tonumber(color:sub(5, 6), 16) / 255,
tonumber(color:sub(7, 8), 16) / 255)
end
table_insert(skeletonData.bones, data) table_insert(skeletonData.bones, data)
end end
@ -138,18 +145,17 @@ function SkeletonJson.new (attachmentLoader)
local dark = slotMap["dark"] local dark = slotMap["dark"]
if dark then if dark then
data.darkColor = Color.newWith(1, 1, 1, 1) data.darkColor = Color.newWith(
data.darkColor:set(tonumber(dark:sub(1, 2), 16) / 255, tonumber(dark:sub(1, 2), 16) / 255,
tonumber(dark:sub(3, 4), 16) / 255, tonumber(dark:sub(3, 4), 16) / 255,
tonumber(dark:sub(5, 6), 16) / 255, tonumber(dark:sub(5, 6), 16) / 255, 0)
0)
end end
data.attachmentName = getValue(slotMap, "attachment", nil) data.attachmentName = getValue(slotMap, "attachment", nil)
data.blendMode = BlendMode[getValue(slotMap, "blend", "normal")] data.blendMode = BlendMode[getValue(slotMap, "blend", "normal")]
table_insert(skeletonData.slots, data) table_insert(skeletonData.slots, data)
skeletonData.slotNameIndices[data.name] = #skeletonData.slots skeletonData.nameToSlot[data.name] = data
end end
end end
@ -204,17 +210,19 @@ function SkeletonJson.new (attachmentLoader)
data.local_ = getValue(constraintMap, "local", false) data.local_ = getValue(constraintMap, "local", false)
data.relative = getValue(constraintMap, "relative", false) data.relative = getValue(constraintMap, "relative", false)
data.offsetRotation = getValue(constraintMap, "rotation", 0); data.offsetRotation = getValue(constraintMap, "rotation", 0)
data.offsetX = getValue(constraintMap, "x", 0) * scale; data.offsetX = getValue(constraintMap, "x", 0) * scale
data.offsetY = getValue(constraintMap, "y", 0) * scale; data.offsetY = getValue(constraintMap, "y", 0) * scale
data.offsetScaleX = getValue(constraintMap, "scaleX", 0); data.offsetScaleX = getValue(constraintMap, "scaleX", 0)
data.offsetScaleY = getValue(constraintMap, "scaleY", 0); data.offsetScaleY = getValue(constraintMap, "scaleY", 0)
data.offsetShearY = getValue(constraintMap, "shearY", 0); data.offsetShearY = getValue(constraintMap, "shearY", 0)
data.rotateMix = getValue(constraintMap, "rotateMix", 1); data.mixRotate = getValue(constraintMap, "rotateMix", 1)
data.translateMix = getValue(constraintMap, "translateMix", 1); data.mixX = getValue(constraintMap, "mixX", 1)
data.scaleMix = getValue(constraintMap, "scaleMix", 1); data.mixY = getValue(constraintMap, "mixY", data.mixX)
data.shearMix = getValue(constraintMap, "shearMix", 1); data.mixScaleX = getValue(constraintMap, "mixScaleX", 1)
data.mixScaleY = getValue(constraintMap, "mixScaleY", data.mixScaleX)
data.mixShearY = getValue(constraintMap, "mixShearY", 1)
table_insert(skeletonData.transformConstraints, data) table_insert(skeletonData.transformConstraints, data)
end end
@ -222,8 +230,8 @@ function SkeletonJson.new (attachmentLoader)
-- Path constraints -- Path constraints
if root["path"] then if root["path"] then
for _,constraintMap in ipairs(root.path) do for _,constraintMap in ipairs(root["path"]) do
local data = PathConstraintData.new(constraintMap.name); local data = PathConstraintData.new(constraintMap.name)
data.order = getValue(constraintMap, "order", 0) data.order = getValue(constraintMap, "order", 0)
data.skinRequired = getValue(constraintMap, "skin", false) data.skinRequired = getValue(constraintMap, "skin", false)
@ -233,20 +241,21 @@ function SkeletonJson.new (attachmentLoader)
table_insert(data.bones, bone) table_insert(data.bones, bone)
end end
local targetName = constraintMap.target; local targetName = constraintMap.target
data.target = skeletonData:findSlot(targetName) data.target = skeletonData:findSlot(targetName)
if data.target == nil then error("Path target slot not found: " .. targetName, 2) end if data.target == nil then error("Path target slot not found: " .. targetName, 2) end
data.positionMode = PathConstraintData.PositionMode[getValue(constraintMap, "positionMode", "percent"):lower()] data.positionMode = PathConstraintData.PositionMode[getValue(constraintMap, "positionMode", "percent"):lower()]
data.spacingMode = PathConstraintData.SpacingMode[getValue(constraintMap, "spacingMode", "length"):lower()] data.spacingMode = PathConstraintData.SpacingMode[getValue(constraintMap, "spacingMode", "length"):lower()]
data.rotateMode = PathConstraintData.RotateMode[getValue(constraintMap, "rotateMode", "tangent"):lower()] data.rotateMode = PathConstraintData.RotateMode[getValue(constraintMap, "rotateMode", "tangent"):lower()]
data.offsetRotation = getValue(constraintMap, "rotation", 0); data.offsetRotation = getValue(constraintMap, "rotation", 0)
data.position = getValue(constraintMap, "position", 0); data.position = getValue(constraintMap, "position", 0)
if data.positionMode == PathConstraintData.PositionMode.fixed then data.position = data.position * scale end if data.positionMode == PathConstraintData.PositionMode.fixed then data.position = data.position * scale end
data.spacing = getValue(constraintMap, "spacing", 0); data.spacing = getValue(constraintMap, "spacing", 0)
if data.spacingMode == PathConstraintData.SpacingMode.length or data.spacingMode == PathConstraintData.SpacingMode.fixed then data.spacing = data.spacing * scale end if data.spacingMode == PathConstraintData.SpacingMode.length or data.spacingMode == PathConstraintData.SpacingMode.fixed then data.spacing = data.spacing * scale end
data.rotateMix = getValue(constraintMap, "rotateMix", 1); data.mixRotate = getValue(constraintMap, "mixRotate", 1)
data.translateMix = getValue(constraintMap, "translateMix", 1); data.mixX = getValue(constraintMap, "mixX", 1)
data.mixY = getValue(constraintMap, "mixY", data.mixX)
table_insert(skeletonData.pathConstraints, data) table_insert(skeletonData.pathConstraints, data)
end end
@ -290,7 +299,7 @@ function SkeletonJson.new (attachmentLoader)
end end
for slotName,slotMap in pairs(skinMap.attachments) do for slotName,slotMap in pairs(skinMap.attachments) do
local slotIndex = skeletonData.slotNameIndices[slotName] local slotIndex = skeletonData:findSlot(slotName).index
for attachmentName,attachmentMap in pairs(slotMap) do for attachmentName,attachmentMap in pairs(slotMap) do
local attachment = readAttachment(attachmentMap, skin, slotIndex, attachmentName, skeletonData) local attachment = readAttachment(attachmentMap, skin, slotIndex, attachmentName, skeletonData)
if attachment then if attachment then
@ -360,11 +369,11 @@ function SkeletonJson.new (attachmentLoader)
region.path = path region.path = path
region.x = getValue(map, "x", 0) * scale region.x = getValue(map, "x", 0) * scale
region.y = getValue(map, "y", 0) * scale region.y = getValue(map, "y", 0) * scale
region.scaleX = getValue(map, "scaleX", 1); region.scaleX = getValue(map, "scaleX", 1)
region.scaleY = getValue(map, "scaleY", 1); region.scaleY = getValue(map, "scaleY", 1)
region.rotation = getValue(map, "rotation", 0); region.rotation = getValue(map, "rotation", 0)
region.width = map.width * scale; region.width = map.width * scale
region.height = map.height * scale; region.height = map.height * scale
local color = map["color"] local color = map["color"]
if color then if color then
@ -453,7 +462,7 @@ function SkeletonJson.new (attachmentLoader)
tonumber(color:sub(5, 6), 16) / 255, tonumber(color:sub(5, 6), 16) / 255,
tonumber(color:sub(7, 8), 16) / 255) tonumber(color:sub(7, 8), 16) / 255)
end end
return path; return path
elseif type == AttachmentType.point then elseif type == AttachmentType.point then
local point = self.attachmentLoader:newPointAttachment(skin, name) local point = self.attachmentLoader:newPointAttachment(skin, name)
@ -536,124 +545,239 @@ function SkeletonJson.new (attachmentLoader)
readAnimation = function (map, name, skeletonData) readAnimation = function (map, name, skeletonData)
local timelines = {} local timelines = {}
local duration = 0
local scale = self.scale local scale = self.scale
-- Slot timelines -- Slot timelines.
local slotsMap = map["slots"] local slotsMap = map["slots"]
if slotsMap then if slotsMap then
for slotName,timelineMap in pairs(slotsMap) do for slotName,slotMap in pairs(slotsMap) do
local slotIndex = skeletonData.slotNameIndices[slotName] local slotIndex = skeletonData:findSlot(slotName).index
for timelineName,timelineMap in pairs(slotMap) do
for timelineName,values in pairs(timelineMap) do if not timelineMap then
if timelineName == "color" then
local timeline = Animation.ColorTimeline.new(#values)
timeline.slotIndex = slotIndex
local frameIndex = 0
for _,valueMap in ipairs(values) do
local color = valueMap["color"]
timeline:setFrame(
frameIndex, getValue(valueMap, "time", 0),
tonumber(color:sub(1, 2), 16) / 255,
tonumber(color:sub(3, 4), 16) / 255,
tonumber(color:sub(5, 6), 16) / 255,
tonumber(color:sub(7, 8), 16) / 255
)
readCurve(valueMap, timeline, frameIndex)
frameIndex = frameIndex + 1
end
table_insert(timelines, timeline)
duration = math.max(duration, timeline.frames[(timeline:getFrameCount() - 1) * Animation.ColorTimeline.ENTRIES])
elseif timelineName == "twoColor" then
local timeline = Animation.TwoColorTimeline.new(#values)
timeline.slotIndex = slotIndex
local frameIndex = 0
for _,valueMap in ipairs(values) do
local light = valueMap["light"]
local dark = valueMap["dark"]
timeline:setFrame(
frameIndex, getValue(valueMap, "time", 0),
tonumber(light:sub(1, 2), 16) / 255,
tonumber(light:sub(3, 4), 16) / 255,
tonumber(light:sub(5, 6), 16) / 255,
tonumber(light:sub(7, 8), 16) / 255,
tonumber(dark:sub(1, 2), 16) / 255,
tonumber(dark:sub(3, 4), 16) / 255,
tonumber(dark:sub(5, 6), 16) / 255
)
readCurve(valueMap, timeline, frameIndex)
frameIndex = frameIndex + 1
end
table_insert(timelines, timeline)
duration = math.max(duration, timeline.frames[(timeline:getFrameCount() - 1) * Animation.TwoColorTimeline.ENTRIES])
elseif timelineName == "attachment" then elseif timelineName == "attachment" then
local timeline = Animation.AttachmentTimeline.new(#values) local timeline = Animation.AttachmentTimeline.new(#timelineMap, slotIndex)
timeline.slotIndex = slotIndex for i,keyMap in ipairs(timelineMap) do
timeline:setFrame(i + 1, getValue(keyMap, "time", 0), keyMap["name"])
local frameIndex = 0 end
for _,valueMap in ipairs(values) do table_insert(timelines, timeline)
local attachmentName = valueMap["name"] elseif timelineName == "rgba" then
timeline:setFrame(frameIndex, getValue(valueMap, "time", 0), attachmentName) local timeline = Animation.RGBATimeline.new(#timelineMap, #timelineMap * 4, slotIndex)
frameIndex = frameIndex + 1 local keyMap = timelineMap[1]
local time = getValue(keyMap, "time", 0)
local color = keyMap["color"]
local r = tonumber(color:sub(1, 2), 16) / 255
local g = tonumber(color:sub(3, 4), 16) / 255
local b = tonumber(color:sub(5, 6), 16) / 255
local a = tonumber(color:sub(7, 8), 16) / 255
local bezier = 0
for i,keyMap in ipairs(timelineMap) do
local frame = i - 1
timeline:setFrame(frame, time, r, g, b, a)
local nextMap = timelineMap[i + 1]
if not nextMap then
timeline:shrink(bezier)
break
end
local time2 = getValue(nextMap, "time", 0)
color = nextMap["color"]
local nr = tonumber(color:sub(1, 2), 16) / 255
local ng = tonumber(color:sub(3, 4), 16) / 255
local nb = tonumber(color:sub(5, 6), 16) / 255
local na = tonumber(color:sub(7, 8), 16) / 255
local curve = keyMap.curve
if curve then
bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, r, nr, 1)
bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, g, ng, 1)
bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, b, nb, 1)
bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, a, na, 1)
end
time = time2
r = nr
g = ng
b = nb
a = na
end
table_insert(timelines, timeline)
elseif timelineName == "rgb" then
local timeline = Animation.RGBTimeline.new(#timelineMap, #timelineMap * 3, slotIndex)
local keyMap = timelineMap[1]
local time = getValue(keyMap, "time", 0)
local color = keyMap["color"]
local r = tonumber(color:sub(1, 2), 16) / 255
local g = tonumber(color:sub(3, 4), 16) / 255
local b = tonumber(color:sub(5, 6), 16) / 255
local bezier = 0
for i,keyMap in ipairs(timelineMap) do
local frame = i - 1
timeline:setFrame(frame, time, r, g, b)
local nextMap = timelineMap[i + 1]
if not nextMap then
timeline:shrink(bezier)
break
end
local time2 = getValue(nextMap, "time", 0)
color = nextMap["color"]
local nr = tonumber(color:sub(1, 2), 16) / 255
local ng = tonumber(color:sub(3, 4), 16) / 255
local nb = tonumber(color:sub(5, 6), 16) / 255
local curve = keyMap.curve
if curve then
bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, r, nr, 1)
bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, g, ng, 1)
bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, b, nb, 1)
end
time = time2
r = nr
g = ng
b = nb
end
table_insert(timelines, timeline)
elseif timelineName == "alpha" then
table_insert(timelines, readTimeline1(timelineMap, Animation.AlphaTimeline.new(#timelineMap, #timelineMap, slotIndex), 0, 1))
elseif timelineName == "rgba2" then
local timeline = Animation.RGBA2Timeline.new(#timelineMap, #timelineMap * 7, slotIndex)
local keyMap = timelineMap[1]
local time = getValue(keyMap, "time", 0)
local color = keyMap["light"]
local r = tonumber(color:sub(1, 2), 16) / 255
local g = tonumber(color:sub(3, 4), 16) / 255
local b = tonumber(color:sub(5, 6), 16) / 255
local a = tonumber(color:sub(7, 8), 16) / 255
color = keyMap["dark"]
local r2 = tonumber(color:sub(1, 2), 16) / 255
local g2 = tonumber(color:sub(3, 4), 16) / 255
local b2 = tonumber(color:sub(5, 6), 16) / 255
local bezier = 0
for i,keyMap in ipairs(timelineMap) do
local frame = i - 1
timeline:setFrame(frame, time, r, g, b, a, r2, g2, b2)
local nextMap = timelineMap[i + 1]
if not nextMap then
timeline:shrink(bezier)
break
end
local time2 = getValue(nextMap, "time", 0)
color = nextMap["light"]
local nr = tonumber(color:sub(1, 2), 16) / 255
local ng = tonumber(color:sub(3, 4), 16) / 255
local nb = tonumber(color:sub(5, 6), 16) / 255
local na = tonumber(color:sub(7, 8), 16) / 255
color = nextMap["dark"]
local nr2 = tonumber(color:sub(1, 2), 16) / 255
local ng2 = tonumber(color:sub(3, 4), 16) / 255
local nb2 = tonumber(color:sub(5, 6), 16) / 255
local curve = keyMap.curve
if curve then
bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, r, nr, 1)
bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, g, ng, 1)
bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, b, nb, 1)
bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, a, na, 1)
bezier = readCurve(curve, timeline, bezier, frame, 4, time, time2, r2, nr2, 1)
bezier = readCurve(curve, timeline, bezier, frame, 5, time, time2, g2, ng2, 1)
bezier = readCurve(curve, timeline, bezier, frame, 6, time, time2, b2, nb2, 1)
end
time = time2
r = nr
g = ng
b = nb
a = na
r2 = nr2
g2 = ng2
b2 = nb2
end
table_insert(timelines, timeline)
elseif timelineName == "rgb2" then
local timeline = Animation.RGB2Timeline.new(#timelineMap, #timelineMap * 6, slotIndex)
local keyMap = timelineMap[1]
local time = getValue(keyMap, "time", 0)
local color = keyMap["light"]
local r = tonumber(color:sub(1, 2), 16) / 255
local g = tonumber(color:sub(3, 4), 16) / 255
local b = tonumber(color:sub(5, 6), 16) / 255
color = keyMap["dark"]
local r2 = tonumber(color:sub(1, 2), 16) / 255
local g2 = tonumber(color:sub(3, 4), 16) / 255
local b2 = tonumber(color:sub(5, 6), 16) / 255
local bezier = 0
for i,keyMap in ipairs(timelineMap) do
local frame = i - 1
timeline:setFrame(frame, time, r, g, b, r2, g2, b2)
local nextMap = timelineMap[i + 1]
if not nextMap then
timeline:shrink(bezier)
break
end
local time2 = getValue(nextMap, "time", 0)
color = nextMap["light"]
local nr = tonumber(color:sub(1, 2), 16) / 255
local ng = tonumber(color:sub(3, 4), 16) / 255
local nb = tonumber(color:sub(5, 6), 16) / 255
color = nextMap["dark"]
local nr2 = tonumber(color:sub(1, 2), 16) / 255
local ng2 = tonumber(color:sub(3, 4), 16) / 255
local nb2 = tonumber(color:sub(5, 6), 16) / 255
local curve = keyMap.curve
if curve then
bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, r, nr, 1)
bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, g, ng, 1)
bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, b, nb, 1)
bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, r2, nr2, 1)
bezier = readCurve(curve, timeline, bezier, frame, 4, time, time2, g2, ng2, 1)
bezier = readCurve(curve, timeline, bezier, frame, 5, time, time2, b2, nb2, 1)
end
time = time2
r = nr
g = ng
b = nb
r2 = nr2
g2 = ng2
b2 = nb2
end end
table_insert(timelines, timeline) table_insert(timelines, timeline)
duration = math.max(duration, timeline.frames[timeline:getFrameCount() - 1])
else else
error("Invalid frame type for a slot: " .. timelineName .. " (" .. slotName .. ")") error("Invalid timeline type for a slot: " .. timelineName .. " (" .. slotName .. ")")
end end
end end
end end
end end
-- Bone timelines -- Bone timelines.
local bonesMap = map["bones"] local bonesMap = map["bones"]
if bonesMap then if bonesMap then
for boneName,timelineMap in pairs(bonesMap) do for boneName,boneMap in pairs(bonesMap) do
local boneIndex = skeletonData:findBoneIndex(boneName) local boneIndex = skeletonData:findBoneIndex(boneName)
if boneIndex == -1 then error("Bone not found: " .. boneName) end if boneIndex == -1 then error("Bone not found: " .. boneName) end
for timelineName,timelineMap in pairs(boneMap) do
for timelineName,values in pairs(timelineMap) do if not timelineMap then
if timelineName == "rotate" then elseif timelineName == "rotate" then
local timeline = Animation.RotateTimeline.new(#values) table_insert(timelines, readTimeline1(timelineMap, Animation.RotateTimeline.new(#timelineMap, #timelineMap, boneIndex), 0, 1))
timeline.boneIndex = boneIndex elseif timelineName == "translate" then
local timeline = Animation.TranslateTimeline.new(#timelineMap, #timelineMap * 2, boneIndex)
local frameIndex = 0 table_insert(timelines, readTimeline2(timelineMap, timeline, "x", "y", 0, scale))
for _,valueMap in ipairs(values) do elseif timelineName == "translatex" then
timeline:setFrame(frameIndex, getValue(valueMap, "time", 0), getValue(valueMap, "angle", 0)) local timeline = Animation.TranslateXTimeline.new(#timelineMap, #timelineMap, boneIndex)
readCurve(valueMap, timeline, frameIndex) table_insert(timelines, readTimeline1(timelineMap, timeline, 0, scale))
frameIndex = frameIndex + 1 elseif timelineName == "translatey" then
end local timeline = Animation.TranslateYTimeline.new(#timelineMap, #timelineMap, boneIndex)
table_insert(timelines, timeline) table_insert(timelines, readTimeline1(timelineMap, timeline, 0, scale))
duration = math.max(duration, timeline.frames[(timeline:getFrameCount() - 1) * Animation.RotateTimeline.ENTRIES]) elseif timelineName == "scale" then
local timeline = Animation.ScaleTimeline.new(#timelineMap, #timelineMap * 2, boneIndex)
elseif timelineName == "translate" or timelineName == "scale" or timelineName == "shear" then table_insert(timelines, readTimeline2(timelineMap, "x", "y", 1, 1))
local timeline elseif timelineName == "scalex" then
local timelineScale = 1 local timeline = Animation.ScaleXTimeline.new(#timelineMap, #timelineMap, boneIndex)
local defaultValue = 0 table_insert(timelines, readTimeline1(timelineMap, timeline, 1, 1))
if timelineName == "scale" then elseif timelineName == "scaley" then
timeline = Animation.ScaleTimeline.new(#values) local timeline = Animation.ScaleYTimeline.new(#timelineMap, #timelineMap, boneIndex)
defaultValue = 1 table_insert(timelines, readTimeline1(timelineMap, timeline, 1, 1))
elseif timelineName == "shear" then elseif timelineName == "shear" then
timeline = Animation.ShearTimeline.new(#values) local timeline = Animation.ShearTimeline.new(#timelineMap, #timelineMap * 2, boneIndex)
else table_insert(timelines, readTimeline2(timelineMap, "x", "y", 0, 1))
timeline = Animation.TranslateTimeline.new(#values) elseif timelineName == "shearx" then
timelineScale = self.scale local timeline = Animation.ShearXTimeline.new(#timelineMap, #timelineMap, boneIndex)
end table_insert(timelines, readTimeline1(timelineMap, timeline, 0, 1))
timeline.boneIndex = boneIndex elseif timelineName == "sheary" then
local timeline = Animation.ShearYTimeline.new(#timelineMap, #timelineMap, boneIndex)
local frameIndex = 0 table_insert(timelines, readTimeline1(timelineMap, timeline, 0, 1))
for _,valueMap in ipairs(values) do
local x = getValue(valueMap, "x", defaultValue) * timelineScale
local y = getValue(valueMap, "y", defaultValue) * timelineScale
timeline:setFrame(frameIndex, getValue(valueMap, "time", 0), x, y)
readCurve(valueMap, timeline, frameIndex)
frameIndex = frameIndex + 1
end
table_insert(timelines, timeline)
duration = math.max(duration, timeline.frames[(timeline:getFrameCount() - 1) * Animation.TranslateTimeline.ENTRIES])
else else
error("Invalid timeline type for a bone: " .. timelineName .. " (" .. boneName .. ")") error("Invalid timeline type for a bone: " .. timelineName .. " (" .. boneName .. ")")
end end
@ -664,96 +788,169 @@ function SkeletonJson.new (attachmentLoader)
-- IK timelines. -- IK timelines.
local ik = map["ik"] local ik = map["ik"]
if ik then if ik then
for ikConstraintName,values in pairs(ik) do for constraintName,timelineMap in pairs(ik) do
local ikConstraint = skeletonData:findIkConstraint(ikConstraintName) local keyMap = timelineMap[1]
local timeline = Animation.IkConstraintTimeline.new(#values) if keyMap then
for i,other in pairs(skeletonData.ikConstraints) do local constraintIndex = -1
if other == ikConstraint then for i,other in pairs(skeletonData.ikConstraints) do
timeline.ikConstraintIndex = i if other.name == constraintName then
break constraintIndex = i
break
end
end
local timeline = Animation.IkConstraintTimeline.new(#timelineMap, #timelineMap * 2, constraintIndex)
local time = getValue(keyMap, "time", 0)
local mix = getValue(keyMap, "mix", 1)
local softness = getValue(keyMap, "softness", 0) * scale
local bezier = 0
for i,keyMap in ipairs(timelineMap) do
local frame = i - 1
local bendPositive = 1
local compress = false
local stretch = false
if keyMap["bendPositive"] == false then bendPositive = -1 end
if keyMap["compress"] ~= nil then compress = keyMap["compress"] end
if keyMap["stretch"] ~= nil then stretch = keyMap["stretch"] end
timeline:setFrame(frame, time, mix, softness, bendPositive, compress, stretch)
local nextMap = timelineMap[i + 1]
if not nextMap then
timeline:shrink(bezier)
break
end
local time2 = getValue(nextMap, "time", 0)
color = nextMap["color"]
local mix2 = getValue(nextMap, "mix", 1)
local softness2 = getValue(nextMap, "softness", 0) * scale
local curve = keyMap.curve
if curve then
bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, mix, mix2, 1)
bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, softness, softness2, scale)
end
time = time2
mix = mix2
softness = softness2
end end
end end
local frameIndex = 0
for _,valueMap in ipairs(values) do
local mix = 1
local softness = 0
if valueMap["mix"] ~= nil then mix = valueMap["mix"] end
if valueMap["softness"] ~= nil then softness = valueMap["softness"] * scale end
local bendPositive = 1
if valueMap["bendPositive"] == false then bendPositive = -1 end
local stretch = false
if valueMap["stretch"] ~= nil then stretch = valueMap["stretch"] end
local compress = false
if valueMap["compress"] ~= nil then compress = valueMap["compress"] end
timeline:setFrame(frameIndex, getValue(valueMap, "time", 0), mix, softness, bendPositive, compress, stretch)
readCurve(valueMap, timeline, frameIndex)
frameIndex = frameIndex + 1
end
table_insert(timelines, timeline) table_insert(timelines, timeline)
duration = math.max(duration, timeline.frames[(timeline:getFrameCount() - 1) * Animation.IkConstraintTimeline.ENTRIES])
end end
end end
-- Transform constraint timelines. -- Transform constraint timelines.
local transform = map["transform"] local transform = map["transform"]
if transform then if transform then
for constraintName, values in pairs(transform) do for constraintName, timelineMap in pairs(transform) do
local constraint = skeletonData:findTransformConstraint(constraintName) local keyMap = timelineMap[1]
local timeline = Animation.TransformConstraintTimeline.new(#values) if keyMap then
for i,other in pairs(skeletonData.transformConstraints) do local constraintIndex = -1
if other == constraint then for i,other in pairs(skeletonData.transformConstraints) do
timeline.transformConstraintIndex = i if other.name == constraintName then
break constraintIndex = i
break
end
end end
local timeline = Animation.TransformConstraintTimeline.new(#timelineMap, #timelineMap * 4, constraintIndex)
local time = getValue(keyMap, "time", 0)
local mixRotate = getValue(keyMap, "mixRotate", 0)
local mixX = getValue(keyMap, "mixX", 1)
local mixY = getValue(keyMap, "mixY", mixX)
local mixScaleX = getValue(keyMap, "mixScaleX", 1)
local mixScaleY = getValue(keyMap, "mixScaleY", mixScaleX)
local mixShearY = getValue(keyMap, "mixShearY", 1)
local bezier = 0
for i,keyMap in ipairs(timelineMap) do
local frame = i - 1
timeline:setFrame(frame, time, mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY)
local nextMap = timelineMap[frame + 1]
if not nextMap then
timeline:shrink(bezier)
break
end
local time2 = getValue(nextMap, "time", 0)
local mixRotate2 = getValue(nextMap, "mixRotate", 1)
local mixX2 = getValue(nextMap, "mixX", 1)
local mixY2 = getValue(nextMap, "mixY", mixX2)
local mixScaleX2 = getValue(nextMap, "mixScaleX", 1)
local mixScaleY2 = getValue(nextMap, "mixScaleY", mixScaleX2)
local mixShearY2 = getValue(nextMap, "mixShearY", 1)
local curve = keyMap.curve
if curve then
bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, mixRotate, mixRotate2, 1)
bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, mixX, mixX2, 1)
bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, mixY, mixY2, 1)
bezier = readCurve(curve, timeline, bezier, frame, 3, time, time2, mixScaleX, mixScaleX2, 1)
bezier = readCurve(curve, timeline, bezier, frame, 4, time, time2, mixScaleY, mixScaleY2, 1)
bezier = readCurve(curve, timeline, bezier, frame, 5, time, time2, mixShearY, mixShearY2, 1)
end
time = time2
mixRotate = mixRotate2
mixX = mixX2
mixY = mixY2
mixScaleX = mixScaleX2
mixScaleY = mixScaleY2
mixScaleX = mixScaleX2
end
table_insert(timelines, timeline)
end end
local frameIndex = 0
for _,valueMap in ipairs(values) do
timeline:setFrame(frameIndex, getValue(valueMap, "time", 0), getValue(valueMap, "rotateMix", 1), getValue(valueMap, "translateMix", 1), getValue(valueMap, "scaleMix", 1), getValue(valueMap, "shearMix", 1))
readCurve(valueMap, timeline, frameIndex)
frameIndex = frameIndex + 1
end
table_insert(timelines, timeline)
duration = math.max(duration, timeline.frames[(timeline:getFrameCount() - 1) * Animation.TransformConstraintTimeline.ENTRIES])
end end
end end
-- Path constraint timelines. -- Path constraint timelines.
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 index = skeletonData:findPathConstraintIndex(constraintName) local constraint, constraintIndex = -1
if index == -1 then error("Path constraint not found: " .. constraintName, 2) end for i,other in pairs(skeletonData.transformConstraints) do
local data = skeletonData.pathConstraints[index] if other.name == constraintName then
constraintIndex = i
constraint = other
break
end
end
for timelineName, timelineMap in pairs(constraintMap) do for timelineName, timelineMap in pairs(constraintMap) do
if timelineName == "position" or timelineName == "spacing" then local keyMap = timelineMap[1]
local timeline = nil if keyMap then
local timelineScale = 1 if timelineName == "position" then
if timelineName == "spacing" then local timeline = Animation.PathConstraintPositionTimeline.new(#timelineMap, #timelineMap, constraintIndex)
timeline = Animation.PathConstraintSpacingTimeline.new(#timelineMap) local timelineScale = 1
if data.spacingMode == PathConstraintData.SpacingMode.length or data.spacingMode == PathConstraintData.SpacingMode.fixed then timelineScale = scale end if constraint.positionMode == PositionMode.fixed then timelineScale = scale end
else table_insert(timelines, readTimeline1(timelineMap, timeline, 0, timelineScale))
timeline = Animation.PathConstraintPositionTimeline.new(#timelineMap) elseif timelineName == "spacing" then
if data.positionMode == PathConstraintData.PositionMode.fixed then timelineScale = scale end 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
table_insert(timelines, readTimeline1(timelineMap, timeline, 0, timelineScale))
elseif timelineName == "mix" then
local timeline = Animation.PathConstraintMixTimeline.new(#timelineMap, #timelineMap * 3, constraintIndex)
local time = getValue(keyMap, "time", 0)
local mixRotate = getValue(keyMap, "mixRotate", 1)
local mixX = getValue(keyMap, "mixX", 1)
local mixY = getValue(keyMap, "mixY", mixX)
local bezier = 0
for i,keyMap in ipairs(timelineMap) do
local frame = i - 1
timeline:setFrame(frame, time, mixRotate, mixX, mixY)
local nextMap = timelineMap[frame + 1]
if not nextMap then
timeline:shrink(bezier)
break
end
local time2 = getValue(nextMap, "time", 0)
local mixRotate2 = getValue(nextMap, "mixRotate", 1)
local mixX2 = getValue(nextMap, "mixX", 1)
local mixY2 = getValue(nextMap, "mixY", mixX2)
local curve = keyMap.curve
if curve then
bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, mixRotate, mixRotate2, 1)
bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, mixX, mixX2, 1)
bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, mixY, mixY2, 1)
end
time = time2
mixRotate = mixRotate2
mixX = mixX2
mixY = mixY2
keyMap = nextMap
end
table_insert(timelines, timeline)
end end
timeline.pathConstraintIndex = index
local frameIndex = 0
for _,valueMap in ipairs(timelineMap) do
timeline:setFrame(frameIndex, getValue(valueMap, "time", 0), getValue(valueMap, timelineName, 0) * timelineScale)
readCurve(valueMap, timeline, frameIndex)
frameIndex = frameIndex + 1
end
table_insert(timelines, timeline)
duration = math.max(duration, timeline.frames[(timeline:getFrameCount() - 1) * Animation.PathConstraintPositionTimeline.ENTRIES])
elseif timelineName == "mix" then
local timeline = Animation.PathConstraintMixTimeline.new(#timelineMap)
timeline.pathConstraintIndex = index
local frameIndex = 0
for _,valueMap in ipairs(timelineMap) do
timeline:setFrame(frameIndex, getValue(valueMap, "time", 0), getValue(valueMap, "rotateMix", 1), getValue(valueMap, "translateMix", 1))
readCurve(valueMap, timeline, frameIndex)
frameIndex = frameIndex + 1
end
table_insert(timelines, timeline)
duration = math.max(duration, timeline.frames[(timeline:getFrameCount() - 1) * Animation.PathConstraintMixTimeline.ENTRIES])
end end
end end
end end
@ -765,68 +962,72 @@ function SkeletonJson.new (attachmentLoader)
local skin = skeletonData:findSkin(deformName) local skin = skeletonData:findSkin(deformName)
if not skin then error("Skin not found: " .. deformName, 2) end if not skin then error("Skin not found: " .. deformName, 2) end
for slotName,slotMap in pairs(deformMap) do for slotName,slotMap in pairs(deformMap) do
local slotIndex = skeletonData:findSlotIndex(slotName) local slotIndex = skeletonData:findSlot(slotName).index
if slotIndex == -1 then error("Slot not found: " .. slotMap.name, 2) end if slotIndex == -1 then error("Slot not found: " .. slotMap.name, 2) end
for timelineName,timelineMap in pairs(slotMap) do for timelineName,timelineMap in pairs(slotMap) do
local attachment = skin:getAttachment(slotIndex, timelineName) local keyMap = timelineMap[1]
if not attachment then error("Deform attachment not found: " .. timelineMap.name, 2) end if keyMap then
local weighted = attachment.bones ~= nil local attachment = skin:getAttachment(slotIndex, timelineName)
local vertices = attachment.vertices; if not attachment then error("Deform attachment not found: " .. timelineMap.name, 2) end
local deformLength = #vertices local weighted = attachment.bones ~= nil
if weighted then deformLength = math.floor(#vertices / 3) * 2 end local vertices = attachment.vertices
local deformLength = #vertices
if weighted then deformLength = math.floor(deformLength / 3) * 2 end
local timeline = Animation.DeformTimeline.new(#timelineMap) local timeline = Animation.DeformTimeline.new(#timelineMap, #timelineMap, slotIndex, attachment)
timeline.slotIndex = slotIndex local bezier = 0
timeline.attachment = attachment for i,keyMap in ipairs(timelineMap) do
local deform = nil
local frameIndex = 0 local verticesValue = getValue(keyMap, "vertices", nil)
for _,valueMap in ipairs(timelineMap) do if verticesValue == nil then
local deform = nil deform = vertices
local verticesValue = getValue(valueMap, "vertices", nil) if weighted then deform = utils.newNumberArray(deformLength) end
if verticesValue == nil then else
deform = vertices deform = utils.newNumberArray(deformLength)
if weighted then deform = utils.newNumberArray(deformLength) end local start = getValue(keyMap, "offset", 0) + 1
else utils.arrayCopy(verticesValue, 1, deform, start, #verticesValue)
deform = utils.newNumberArray(deformLength) if scale ~= 1 then
local start = getValue(valueMap, "offset", 0) + 1 local i = start
utils.arrayCopy(verticesValue, 1, deform, start, #verticesValue) local n = i + #verticesValue
if scale ~= 1 then while i < n do
local i = start deform[i] = deform[i] * scale
local n = i + #verticesValue i = i + 1
while i < n do end
deform[i] = deform[i] * scale end
i = i + 1 if not weighted then
local i = 1
local n = i + deformLength
while i < n do
deform[i] = deform[i] + vertices[i]
i = i + 1
end
end end
end end
if not weighted then local frame = i - 1
local i = 1 timeline:setFrame(frame, time, mixRotate, mixX, mixY)
local n = i + deformLength local nextMap = timelineMap[frame + 1]
while i < n do if not nextMap then
deform[i] = deform[i] + vertices[i] timeline:shrink(bezier)
i = i + 1 break
end
end end
local time2 = getValue(nextMap, "time", 0)
local curve = keyMap.curve
if curve then bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, 0, 1, 1) end
time = time2
end end
table_insert(timelines, timeline)
timeline:setFrame(frameIndex, getValue(valueMap, "time", 0), deform)
readCurve(valueMap, timeline, frameIndex)
frameIndex = frameIndex + 1
end end
table_insert(timelines, timeline)
duration = math.max(duration, timeline.frames[timeline:getFrameCount() - 1])
end end
end end
end end
end end
-- Draworder timeline. -- Draw order timelines.
local drawOrderValues = map["drawOrder"] if map["drawOrder"] then
if not drawOrderValues then drawOrderValues = map["draworder"] end local timeline = Animation.DrawOrderTimeline.new(#map["drawOrder"])
if drawOrderValues then
local timeline = Animation.DrawOrderTimeline.new(#drawOrderValues)
local slotCount = #skeletonData.slots local slotCount = #skeletonData.slots
local frameIndex = 0 local frame = 0
for _,drawOrderMap in ipairs(drawOrderValues) do for _,drawOrderMap in ipairs(map["drawOrder"]) do
local drawOrder = nil local drawOrder = nil
local offsets = drawOrderMap["offsets"] local offsets = drawOrderMap["offsets"]
if offsets then if offsets then
@ -835,7 +1036,7 @@ function SkeletonJson.new (attachmentLoader)
local originalIndex = 1 local originalIndex = 1
local unchangedIndex = 1 local unchangedIndex = 1
for _,offsetMap in ipairs(offsets) do for _,offsetMap in ipairs(offsets) do
local slotIndex = skeletonData:findSlotIndex(offsetMap["slot"]) local slotIndex = skeletonData:findSlot(offsetMap["slot"]).index
if slotIndex == -1 then error("Slot not found: " .. offsetMap["slot"]) end if slotIndex == -1 then error("Slot not found: " .. offsetMap["slot"]) end
-- Collect unchanged items. -- Collect unchanged items.
while originalIndex ~= slotIndex do while originalIndex ~= slotIndex do
@ -861,18 +1062,17 @@ function SkeletonJson.new (attachmentLoader)
end end
end end
end end
timeline:setFrame(frameIndex, getValue(drawOrderMap, "time", 0), drawOrder) timeline:setFrame(frame, getValue(drawOrderMap, "time", 0), drawOrder)
frameIndex = frameIndex + 1 frame = frame + 1
end end
table_insert(timelines, timeline) table_insert(timelines, timeline)
duration = math.max(duration, timeline.frames[timeline:getFrameCount() - 1])
end end
-- Event timeline. -- Event timelines.
local events = map["events"] local events = map["events"]
if events then if events then
local timeline = Animation.EventTimeline.new(#events) local timeline = Animation.EventTimeline.new(#events)
local frameIndex = 0 local frame = 0
for _,eventMap in ipairs(events) do for _,eventMap in ipairs(events) do
local eventData = skeletonData:findEvent(eventMap["name"]) local eventData = skeletonData:findEvent(eventMap["name"])
if not eventData then error("Event not found: " .. eventMap["name"]) end if not eventData then error("Event not found: " .. eventMap["name"]) end
@ -896,26 +1096,91 @@ function SkeletonJson.new (attachmentLoader)
event.volume = getValue(eventMap, "volume", 1) event.volume = getValue(eventMap, "volume", 1)
event.balance = getValue(eventMap, "balance", 0) event.balance = getValue(eventMap, "balance", 0)
end end
timeline:setFrame(frameIndex, event) timeline:setFrame(frame, event)
frameIndex = frameIndex + 1 frame = frame + 1
end end
table_insert(timelines, timeline) table_insert(timelines, timeline)
duration = math.max(duration, timeline.frames[timeline:getFrameCount() - 1])
end end
local duration = 0
for _,timeline in ipairs(timelines) do
duration = math.max(duration, timeline:getDuration())
end
table_insert(skeletonData.animations, Animation.new(name, timelines, duration)) table_insert(skeletonData.animations, Animation.new(name, timelines, duration))
end end
readCurve = function (map, timeline, frameIndex) readCurve = function (map, timeline, frame)
local curve = map["curve"] local curve = map["curve"]
if not curve then return end if not curve then return end
if curve == "stepped" then if curve == "stepped" then
timeline:setStepped(frameIndex) timeline:setStepped(frame)
else else
timeline:setCurve(frameIndex, getValue(map, "curve", 0), getValue(map, "c2", 0), getValue(map, "c3", 1), getValue(map, "c4", 1)) timeline:setCurve(frame, getValue(map, "curve", 0), getValue(map, "c2", 0), getValue(map, "c3", 1), getValue(map, "c4", 1))
end end
end end
readTimeline1 = function (keys, timeline, defaultValue, scale)
local keyMap = keys[1]
local time = getValue(keyMap, "time", 0)
local value = getValue(keyMap, "value", defaultValue) * scale
local bezier = 0
for i,keyMap in ipairs(keys) do
local frame = i - 1
timeline:setFrame(frame, time, value)
local nextMap = keys[frame + 1]
if not nextMap then break end
local time2 = getValue(nextMap, "time", 0)
local value2 = getValue(nextMap, "value", defaultValue) * scale
local curve = keyMap.curve
if curve then bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, value, value2, scale) end
time = time2
value = value2
end
timeline:shrink(bezier)
return timeline
end
readTimeline2 = function (keys, timeline, name1, name2, defaultValue, scale)
local keyMap = keys[1]
local time = getValue(keyMap, "time", 0)
local value1 = getValue(keyMap, name1, defaultValue) * scale
local value2 = getValue(keyMap, name2, defaultValue) * scale
local bezier = 0
for i,keyMap in ipairs(keys) do
local frame = i - 1
timeline:setFrame(frame, time, value1, value2)
local nextMap = keys[frame + 1]
if not nextMap then break end
local time2 = getValue(nextMap, "time", 0)
local nvalue1 = getValue(nextMap, name1, defaultValue) * scale
local nvalue2 = getValue(nextMap, name2, defaultValue) * scale
local curve = keyMap.curve
if curve then
bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, value1, nvalue1, scale)
bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, value2, nvalue2, scale)
end
time = time2
value1 = nvalue1
value2 = nvalue2
end
timeline:shrink(bezier)
return timeline
end
readCurve = function (curve, timeline, bezier, frame, value, time1, time2, value1, value2, scale)
if curve == "stepped" then
if value ~= 0 then timeline.setStepped(frame) end
return bezier
end
local i = value * 4
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)
return bezier + 1
end
getArray = function (map, name, scale) getArray = function (map, name, scale)
local list = map[name] local list = map[name]
local values = {} local values = {}

View File

@ -97,7 +97,7 @@ function TransformConstraint:applyAbsoluteWorld ()
local tb = target.b local tb = target.b
local tc = target.c local tc = target.c
local td = target.d local td = target.d
local degRadReflect = 0; local degRadReflect = 0
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
@ -184,7 +184,7 @@ function TransformConstraint:applyRelativeWorld ()
local tb = target.b local tb = target.b
local tc = target.c local tc = target.c
local td = target.d local td = target.d
local degRadReflect = 0; local degRadReflect = 0
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
@ -242,7 +242,7 @@ 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) * shearMix
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
@ -297,7 +297,7 @@ function TransformConstraint:applyAbsoluteLocal ()
bone.shearY = bone.shearY + r * shearMix bone.shearY = bone.shearY + r * shearMix
end 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

@ -74,7 +74,7 @@ function Triangulator:triangulate (verticesArray)
end end
self.triangles = {} self.triangles = {}
local triangles = self.triangles; local triangles = self.triangles
while vertexCount > 3 do while vertexCount > 3 do
-- Find ear tip. -- Find ear tip.
@ -122,7 +122,7 @@ function Triangulator:triangulate (verticesArray)
if _next == 0 then if _next == 0 then
repeat repeat
if not isConcave[i] then if not isConcave[i] then
break; break
end end
i = i - 1 i = i - 1
until i == 0 until i == 0
@ -171,10 +171,10 @@ function Triangulator:decompose(verticesArray, triangles)
local vertices = verticesArray local vertices = verticesArray
self.convexPolygons = {} self.convexPolygons = {}
local convexPolygons = self.convexPolygons; local convexPolygons = self.convexPolygons
self.convexPolygonsIndices = {} self.convexPolygonsIndices = {}
local convexPolygonsIndices = self.convexPolygonsIndices; local convexPolygonsIndices = self.convexPolygonsIndices
local polygonIndices = {} local polygonIndices = {}
local polygon = {} local polygon = {}
@ -197,12 +197,12 @@ function Triangulator:decompose(verticesArray, triangles)
local y3 = vertices[t3 + 1] local y3 = vertices[t3 + 1]
-- If the base of the last triangle is the same as this triangle, check if they form a convex polygon (triangle fan). -- If the base of the last triangle is the same as this triangle, check if they form a convex polygon (triangle fan).
local merged = false; local merged = false
if fanBaseIndex == t1 then if fanBaseIndex == t1 then
local o = #polygon - 4 + 1; local o = #polygon - 4 + 1
local p = polygon; local p = polygon
local winding1 = self:winding(p[o], p[o + 1], p[o + 2], p[o + 3], x3, y3); local winding1 = self:winding(p[o], p[o + 1], p[o + 2], p[o + 3], x3, y3)
local winding2 = self:winding(x3, y3, p[1], p[2], p[3], p[4]); local winding2 = self:winding(x3, y3, p[1], p[2], p[3], p[4])
if winding1 == lastWinding and winding2 == lastWinding then if winding1 == lastWinding and winding2 == lastWinding then
table_insert(polygon, x3) table_insert(polygon, x3)
table_insert(polygon, y3) table_insert(polygon, y3)
@ -226,8 +226,8 @@ function Triangulator:decompose(verticesArray, triangles)
table_insert(polygon, y3) table_insert(polygon, y3)
polygonIndices = {} polygonIndices = {}
table_insert(polygonIndices, t1) table_insert(polygonIndices, t1)
table_insert(polygonIndices, t2); table_insert(polygonIndices, t2)
table_insert(polygonIndices, t3); table_insert(polygonIndices, t3)
lastWinding = self:winding(x1, y1, x2, y2, x3, y3) lastWinding = self:winding(x1, y1, x2, y2, x3, y3)
fanBaseIndex = t1 fanBaseIndex = t1
end end
@ -266,11 +266,11 @@ function Triangulator:decompose(verticesArray, triangles)
if ii ~= i then if ii ~= i then
local otherIndices = convexPolygonsIndices[ii] local otherIndices = convexPolygonsIndices[ii]
if (#otherIndices == 3) then if (#otherIndices == 3) then
local otherFirstIndex = otherIndices[1]; local otherFirstIndex = otherIndices[1]
local otherSecondIndex = otherIndices[2]; local otherSecondIndex = otherIndices[2]
local otherLastIndex = otherIndices[3]; local otherLastIndex = otherIndices[3]
local otherPoly = convexPolygons[ii]; local otherPoly = convexPolygons[ii]
local x3 = otherPoly[#otherPoly - 2 + 1] local x3 = otherPoly[#otherPoly - 2 + 1]
local y3 = otherPoly[#otherPoly - 1 + 1] local y3 = otherPoly[#otherPoly - 1 + 1]
@ -308,14 +308,14 @@ function Triangulator:decompose(verticesArray, triangles)
i = i - 1 i = i - 1
end end
return convexPolygons; return convexPolygons
end end
function Triangulator:isConcave(index, vertexCount, vertices, indices) function Triangulator:isConcave(index, vertexCount, vertices, indices)
local previous = indices[(vertexCount + index - 1) % vertexCount] * 2 + 1; local previous = indices[(vertexCount + index - 1) % vertexCount] * 2 + 1
local current = indices[index] * 2 + 1; local current = indices[index] * 2 + 1
local _next = indices[(index + 1) % vertexCount] * 2 + 1; local _next = indices[(index + 1) % vertexCount] * 2 + 1
return not self:positiveArea(vertices[previous], vertices[previous + 1], vertices[current], vertices[current + 1], vertices[_next],vertices[_next + 1]); return not self:positiveArea(vertices[previous], vertices[previous + 1], vertices[current], vertices[current + 1], vertices[_next],vertices[_next + 1])
end end
function Triangulator:positiveArea(p1x, p1y, p2x, p2y, p3x, p3y) function Triangulator:positiveArea(p1x, p1y, p2x, p2y, p3x, p3y)
@ -328,7 +328,7 @@ function Triangulator:winding(p1x, p1y, p2x, p2y, p3x, p3y)
if p3x * py - p3y * px + px * p1y - p1x * py >= 0 then if p3x * py - p3y * px + px * p1y - p1x * py >= 0 then
return 1 return 1
else else
return -1; return -1
end end
end end

View File

@ -84,8 +84,8 @@ function MeshAttachment:updateUVs ()
local i = 0 local i = 0
local n = #uvs local n = #uvs
while i < n do while i < n do
uvs[i + 1] = u + regionUVs[i + 2] * width; uvs[i + 1] = u + regionUVs[i + 2] * width
uvs[i + 2] = v + (1 - regionUVs[i + 1]) * height; uvs[i + 2] = v + (1 - regionUVs[i + 1]) * height
i = i + 2 i = i + 2
end end
elseif region.degrees == 180 then elseif region.degrees == 180 then
@ -96,8 +96,8 @@ function MeshAttachment:updateUVs ()
local i = 0 local i = 0
local n = #uvs local n = #uvs
while i < n do while i < n do
uvs[i + 1] = u + (1 - regionUVs[i + 1]) * width; uvs[i + 1] = u + (1 - regionUVs[i + 1]) * width
uvs[i + 2] = v + (1 - regionUVs[i + 2]) * height; uvs[i + 2] = v + (1 - regionUVs[i + 2]) * height
i = i + 2 i = i + 2
end end
elseif region.degrees == 270 then elseif region.degrees == 270 then
@ -108,20 +108,20 @@ function MeshAttachment:updateUVs ()
local i = 0 local i = 0
local n = #uvs local n = #uvs
while i < n do while i < n do
uvs[i + 1] = u + (1 - regionUVs[i + 2]) * width; uvs[i + 1] = u + (1 - regionUVs[i + 2]) * width
uvs[i + 2] = v + regionUVs[i + 1] * height; uvs[i + 2] = v + regionUVs[i + 1] * height
i = i + 2 i = i + 2
end end
else else
u = region.u - region.offsetX / textureWidth; u = region.u - region.offsetX / textureWidth
v = region.v - (region.originalHeight - region.offsetY - region.height) / textureHeight; v = region.v - (region.originalHeight - region.offsetY - region.height) / textureHeight
width = region.originalWidth / textureWidth; width = region.originalWidth / textureWidth
height = region.originalHeight / textureHeight; height = region.originalHeight / textureHeight
local i = 0 local i = 0
local n = #uvs local n = #uvs
while i < n do while i < n do
uvs[i + 1] = u + regionUVs[i + 1] * width; uvs[i + 1] = u + regionUVs[i + 1] * width
uvs[i + 2] = v + regionUVs[i + 2] * height; uvs[i + 2] = v + regionUVs[i + 2] * height
i = i + 2 i = i + 2
end end
end end

View File

@ -155,6 +155,7 @@ function RegionAttachment.new (name)
end end
function RegionAttachment:updateOffset () function RegionAttachment:updateOffset ()
if not self.region then return end
local regionScaleX = self.width / self.region.originalWidth * self.scaleX local regionScaleX = self.width / self.region.originalWidth * self.scaleX
local regionScaleY = self.height / self.region.originalHeight * self.scaleY local regionScaleY = self.height / self.region.originalHeight * self.scaleY
local localX = -self.width / 2 * self.scaleX + self.region.offsetX * regionScaleX local localX = -self.width / 2 * self.scaleX + self.region.offsetX * regionScaleX

View File

@ -36,8 +36,8 @@ local utils = require "spine-lua.utils"
local AttachmentType = require "spine-lua.attachments.AttachmentType" 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 SHL_11 = 2048
local VertexAttachment = {} local VertexAttachment = {}
VertexAttachment.__index = VertexAttachment VertexAttachment.__index = VertexAttachment