mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-21 01:36:02 +08:00
Meshes, FFD and skinning for spine-lua.
This commit is contained in:
parent
c1a9b502bd
commit
81ae526a7b
@ -12,8 +12,8 @@ function skeleton:createImage (attachment)
|
||||
-- Customize where images are loaded.
|
||||
return display.newImage("examples/spineboy/images/" .. attachment.name .. ".png")
|
||||
end
|
||||
skeleton.group.x = 150
|
||||
skeleton.group.y = 325
|
||||
skeleton.group.x = display.contentWidth * 0.5
|
||||
skeleton.group.y = display.contentHeight * 0.9
|
||||
skeleton.flipX = false
|
||||
skeleton.flipY = false
|
||||
skeleton.debug = true -- Omit or set to false to not draw debug lines on top of the images.
|
||||
|
||||
@ -29,7 +29,7 @@
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
spine = {}
|
||||
|
||||
|
||||
spine.utils = require "spine-lua.utils"
|
||||
spine.SkeletonJson = require "spine-lua.SkeletonJson"
|
||||
spine.SkeletonData = require "spine-lua.SkeletonData"
|
||||
@ -37,6 +37,8 @@ spine.BoneData = require "spine-lua.BoneData"
|
||||
spine.SlotData = require "spine-lua.SlotData"
|
||||
spine.Skin = require "spine-lua.Skin"
|
||||
spine.RegionAttachment = require "spine-lua.RegionAttachment"
|
||||
spine.MeshAttachment = require "spine-lua.MeshAttachment"
|
||||
spine.SkinnedMeshAttachment = require "spine-lua.SkinnedMeshAttachment"
|
||||
spine.Skeleton = require "spine-lua.Skeleton"
|
||||
spine.Bone = require "spine-lua.Bone"
|
||||
spine.Slot = require "spine-lua.Slot"
|
||||
|
||||
@ -356,32 +356,30 @@ function Animation.ColorTimeline.new ()
|
||||
local frames = self.frames
|
||||
if time < frames[0] then return end -- Time is before first frame.
|
||||
|
||||
local slot = skeleton.slots[self.slotIndex]
|
||||
|
||||
local r, g, b, a
|
||||
if time >= frames[#frames - 4] then -- Time is after last frame.
|
||||
local r = frames[#frames - 3]
|
||||
local g = frames[#frames - 2]
|
||||
local b = frames[#frames - 1]
|
||||
local a = frames[#frames]
|
||||
slot:setColor(r, g, b, a)
|
||||
return
|
||||
r = frames[#frames - 3]
|
||||
g = frames[#frames - 2]
|
||||
b = frames[#frames - 1]
|
||||
a = frames[#frames]
|
||||
else
|
||||
-- Interpolate between the last frame and the current frame.
|
||||
local frameIndex = binarySearch(frames, time, 5)
|
||||
local lastFrameR = frames[frameIndex - 4]
|
||||
local lastFrameG = frames[frameIndex - 3]
|
||||
local lastFrameB = frames[frameIndex - 2]
|
||||
local lastFrameA = frames[frameIndex - 1]
|
||||
local frameTime = frames[frameIndex]
|
||||
local percent = 1 - (time - frameTime) / (frames[frameIndex + LAST_FRAME_TIME] - frameTime)
|
||||
if percent < 0 then percent = 0 elseif percent > 255 then percent = 255 end
|
||||
percent = self:getCurvePercent(frameIndex / 5 - 1, percent)
|
||||
|
||||
r = lastFrameR + (frames[frameIndex + FRAME_R] - lastFrameR) * percent
|
||||
g = lastFrameG + (frames[frameIndex + FRAME_G] - lastFrameG) * percent
|
||||
b = lastFrameB + (frames[frameIndex + FRAME_B] - lastFrameB) * percent
|
||||
a = lastFrameA + (frames[frameIndex + FRAME_A] - lastFrameA) * percent
|
||||
end
|
||||
|
||||
-- Interpolate between the last frame and the current frame.
|
||||
local frameIndex = binarySearch(frames, time, 5)
|
||||
local lastFrameR = frames[frameIndex - 4]
|
||||
local lastFrameG = frames[frameIndex - 3]
|
||||
local lastFrameB = frames[frameIndex - 2]
|
||||
local lastFrameA = frames[frameIndex - 1]
|
||||
local frameTime = frames[frameIndex]
|
||||
local percent = 1 - (time - frameTime) / (frames[frameIndex + LAST_FRAME_TIME] - frameTime)
|
||||
if percent < 0 then percent = 0 elseif percent > 255 then percent = 255 end
|
||||
percent = self:getCurvePercent(frameIndex / 5 - 1, percent)
|
||||
|
||||
local r = lastFrameR + (frames[frameIndex + FRAME_R] - lastFrameR) * percent
|
||||
local g = lastFrameG + (frames[frameIndex + FRAME_G] - lastFrameG) * percent
|
||||
local b = lastFrameB + (frames[frameIndex + FRAME_B] - lastFrameB) * percent
|
||||
local a = lastFrameA + (frames[frameIndex + FRAME_A] - lastFrameA) * percent
|
||||
local slot = skeleton.slots[self.slotIndex]
|
||||
if alpha < 1 then
|
||||
slot:setColor(slot.r + (r - slot.r) * alpha, slot.g + (g - slot.g) * alpha, slot.b + (b - slot.b) * alpha, slot.a + (a - slot.a) * alpha)
|
||||
else
|
||||
|
||||
@ -75,11 +75,11 @@ function AnimationState.new (data)
|
||||
for i = 0, self.trackCount do
|
||||
local current = self.tracks[i]
|
||||
if current then
|
||||
local trackDelta = delta * current.timeScale
|
||||
current.time = current.time + trackDelta
|
||||
current.time = current.time + delta * current.timeScale
|
||||
if current.previous then
|
||||
current.previous.time = current.previous.time + trackDelta
|
||||
current.mixTime = current.mixTime + trackDelta
|
||||
local previousDelta = delta * current.previous.timeScale
|
||||
current.previous.time = current.previous.time + previousDelta
|
||||
current.mixTime = current.mixTime + previousDelta
|
||||
end
|
||||
|
||||
local next = current.next
|
||||
|
||||
@ -34,8 +34,8 @@ function AnimationStateData.new (skeletonData)
|
||||
if not skeletonData then error("skeletonData cannot be nil", 2) end
|
||||
|
||||
local self = {
|
||||
animationToMixTime = {},
|
||||
skeletonData = skeletonData,
|
||||
animationToMixTime = {},
|
||||
defaultMix = 0
|
||||
}
|
||||
|
||||
|
||||
@ -32,20 +32,24 @@ local AttachmentType = require "spine-lua.AttachmentType"
|
||||
local RegionAttachment = require "spine-lua.RegionAttachment"
|
||||
local BoundingBoxAttachment = require "spine-lua.BoundingBoxAttachment"
|
||||
|
||||
local AttachmentLoader = {
|
||||
failed = {}
|
||||
}
|
||||
local AttachmentLoader = {}
|
||||
function AttachmentLoader.new ()
|
||||
local self = {}
|
||||
|
||||
function self:newAttachment (type, name)
|
||||
if type == AttachmentType.region then
|
||||
return RegionAttachment.new(name)
|
||||
end
|
||||
if type == AttachmentType.boundingbox then
|
||||
return BoundingBoxAttachment.new(name)
|
||||
end
|
||||
error("Unknown attachment type: " .. type .. " (" .. name .. ")")
|
||||
function self:newRegionAttachment (skin, name, path)
|
||||
return RegionAttachment.new(name)
|
||||
end
|
||||
|
||||
function self:newMeshAttachment (skin, name, path)
|
||||
return MeshAttachment.new(name)
|
||||
end
|
||||
|
||||
function self:newSkinningMeshAttachment (skin, name, path)
|
||||
return SkinningMeshAttachment.new(name)
|
||||
end
|
||||
|
||||
function self:newBoundingBoxAttachment (skin, name)
|
||||
return BoundingBoxAttachment.new(name)
|
||||
end
|
||||
|
||||
return self
|
||||
|
||||
@ -30,6 +30,8 @@
|
||||
|
||||
local AttachmentType = {
|
||||
region = 0,
|
||||
boundingbox = 1
|
||||
boundingbox = 1,
|
||||
mesh = 2,
|
||||
skinnedmesh = 3
|
||||
}
|
||||
return AttachmentType
|
||||
|
||||
93
spine-lua/MeshAttachment.lua
Normal file
93
spine-lua/MeshAttachment.lua
Normal file
@ -0,0 +1,93 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- Spine Runtimes Software License
|
||||
-- Version 2.1
|
||||
--
|
||||
-- Copyright (c) 2013, Esoteric Software
|
||||
-- All rights reserved.
|
||||
--
|
||||
-- You are granted a perpetual, non-exclusive, non-sublicensable and
|
||||
-- non-transferable license to install, execute and perform the Spine Runtimes
|
||||
-- Software (the "Software") solely for internal use. Without the written
|
||||
-- permission of Esoteric Software (typically granted by licensing Spine), you
|
||||
-- may not (a) modify, translate, adapt or otherwise create derivative works,
|
||||
-- improvements of the Software or develop new applications using the Software
|
||||
-- or (b) remove, delete, alter or obscure any trademarks or any copyright,
|
||||
-- trademark, patent or other intellectual property or proprietary rights
|
||||
-- notices on or in the Software, including any copy thereof. Redistributions
|
||||
-- in binary or source form must include this license and terms.
|
||||
--
|
||||
-- THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "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 SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
-- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
-- OR BUSINESS INTERRUPTION) 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 THIS SOFTWARE, 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 = 0, regionV2 = 0, 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 = 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.length
|
||||
if #slot.attachmentVertices == verticesCount then vertices = slot.attachmentVertices 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
|
||||
@ -36,9 +36,65 @@ function RegionAttachment.new (name)
|
||||
|
||||
local self = {
|
||||
name = name,
|
||||
type = AttachmentType.region
|
||||
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 = null,
|
||||
rendererObject = null,
|
||||
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
|
||||
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
|
||||
|
||||
@ -43,7 +43,10 @@ function Skeleton.new (skeletonData)
|
||||
slotsByName = {},
|
||||
drawOrder = {},
|
||||
r = 1, g = 1, b = 1, a = 1,
|
||||
x = 0, y = 0
|
||||
x = 0, y = 0,
|
||||
skin = nil,
|
||||
flipX = false, flipY = false,
|
||||
time = 0
|
||||
}
|
||||
|
||||
function self:updateWorldTransform ()
|
||||
@ -65,6 +68,7 @@ function Skeleton.new (skeletonData)
|
||||
|
||||
function self:setSlotsToSetupPose ()
|
||||
for i,slot in ipairs(self.slots) do
|
||||
self.drawOrder[i] = slot
|
||||
slot:setToSetupPose()
|
||||
end
|
||||
end
|
||||
@ -90,7 +94,7 @@ function Skeleton.new (skeletonData)
|
||||
local newSkin
|
||||
if skinName then
|
||||
newSkin = self.data:findSkin(skinName)
|
||||
if not newSkin then error("Skin not found: " .. skinName, 2) end
|
||||
if not newSkin then error("Skin not found = " .. skinName, 2) end
|
||||
if self.skin then
|
||||
-- Attach all attachments from the new skin if the corresponding attachment from the old skin is currently attached.
|
||||
for k,v in pairs(self.skin.attachments) do
|
||||
@ -103,6 +107,15 @@ function Skeleton.new (skeletonData)
|
||||
if newAttachment then slot:setAttachment(newAttachment) end
|
||||
end
|
||||
end
|
||||
else
|
||||
-- No previous skin, attach setup pose attachments.
|
||||
for i,slot in ipairs(self.slots) do
|
||||
local name = slot.data.attachmentName
|
||||
if name then
|
||||
local attachment = newSkin:getAttachment(i, name)
|
||||
if attachment then slot:setAttachment(attachment) end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
self.skin = newSkin
|
||||
@ -112,7 +125,7 @@ function Skeleton.new (skeletonData)
|
||||
if not slotName then error("slotName cannot be nil.", 2) end
|
||||
if not attachmentName then error("attachmentName cannot be nil.", 2) end
|
||||
local slotIndex = skeletonData.slotNameIndices[slotName]
|
||||
if slotIndex == -1 then error("Slot not found: " .. slotName, 2) end
|
||||
if slotIndex == -1 then error("Slot not found = " .. slotName, 2) end
|
||||
if self.skin then
|
||||
local attachment = self.skin:getAttachment(slotIndex, attachmentName)
|
||||
if attachment then return attachment end
|
||||
@ -135,13 +148,20 @@ function Skeleton.new (skeletonData)
|
||||
return
|
||||
end
|
||||
end
|
||||
error("Slot not found: " .. slotName, 2)
|
||||
error("Slot not found = " .. slotName, 2)
|
||||
end
|
||||
|
||||
function self:update (delta)
|
||||
self.time = self.time + delta
|
||||
end
|
||||
|
||||
function self:setColor (r, g, b, a)
|
||||
self.r = r
|
||||
self.g = g
|
||||
self.b = b
|
||||
self.a = a
|
||||
end
|
||||
|
||||
for i,boneData in ipairs(skeletonData.bones) do
|
||||
local parent
|
||||
if boneData.parent then parent = self.bones[spine.utils.indexOf(skeletonData.bones, boneData.parent)] end
|
||||
|
||||
@ -36,7 +36,8 @@ function SkeletonData.new ()
|
||||
slotNameIndices = {},
|
||||
skins = {},
|
||||
events = {},
|
||||
animations = {}
|
||||
animations = {},
|
||||
defaultSkin = nil
|
||||
}
|
||||
|
||||
function self:findBone (boneName)
|
||||
|
||||
@ -75,8 +75,16 @@ function SkeletonJson.new (attachmentLoader)
|
||||
boneData.x = (boneMap["x"] or 0) * self.scale
|
||||
boneData.y = (boneMap["y"] or 0) * self.scale
|
||||
boneData.rotation = (boneMap["rotation"] or 0)
|
||||
boneData.scaleX = (boneMap["scaleX"] or 1)
|
||||
boneData.scaleY = (boneMap["scaleY"] or 1)
|
||||
if boneMap["scaleX"] ~= nil then
|
||||
boneData.scaleX = boneMap["scaleX"]
|
||||
else
|
||||
boneData.scaleX = 1
|
||||
end
|
||||
if boneMap["scaleY"] ~= nil then
|
||||
boneData.scaleY = boneMap["scaleY"]
|
||||
else
|
||||
boneData.scaleY = 1
|
||||
end
|
||||
if boneMap["inheritScale"] == false then
|
||||
boneData.inheritScale = false
|
||||
else
|
||||
@ -124,7 +132,7 @@ function SkeletonJson.new (attachmentLoader)
|
||||
for slotName,slotMap in pairs(skinMap) do
|
||||
local slotIndex = skeletonData.slotNameIndices[slotName]
|
||||
for attachmentName,attachmentMap in pairs(slotMap) do
|
||||
local attachment = readAttachment(attachmentName, attachmentMap, self.scale)
|
||||
local attachment = readAttachment(attachmentName, attachmentMap)
|
||||
if attachment then
|
||||
skin:addAttachment(slotIndex, attachmentName, attachment)
|
||||
end
|
||||
@ -159,35 +167,171 @@ function SkeletonJson.new (attachmentLoader)
|
||||
return skeletonData
|
||||
end
|
||||
|
||||
readAttachment = function (name, map, scale)
|
||||
readAttachment = function (name, map)
|
||||
name = map["name"] or name
|
||||
local attachment
|
||||
local type = AttachmentType[map["type"] or "region"]
|
||||
attachment = attachmentLoader:newAttachment(type, name)
|
||||
if not attachment then return nil end
|
||||
|
||||
local type = AttachmentType[map["type"] or "region"]
|
||||
local path = map["path"] or name
|
||||
|
||||
local scale = self.scale
|
||||
if type == AttachmentType.region then
|
||||
attachment.x = (map["x"] or 0) * scale
|
||||
attachment.y = (map["y"] or 0) * scale
|
||||
attachment.scaleX = (map["scaleX"] or 1)
|
||||
attachment.scaleY = (map["scaleY"] or 1)
|
||||
attachment.rotation = (map["rotation"] or 0)
|
||||
attachment.width = map["width"] * scale
|
||||
attachment.height = map["height"] * scale
|
||||
local region = attachmentLoader:newRegionAttachment(type, name, path)
|
||||
if not region then return nil end
|
||||
region.x = (map["x"] or 0) * scale
|
||||
region.y = (map["y"] or 0) * scale
|
||||
if map["scaleX"] ~= nil then
|
||||
region.scaleX = map["scaleX"]
|
||||
else
|
||||
region.scaleX = 1
|
||||
end
|
||||
if map["scaleY"] ~= nil then
|
||||
region.scaleY = map["scaleY"]
|
||||
else
|
||||
region.scaleY = 1
|
||||
end
|
||||
region.rotation = (map["rotation"] or 0)
|
||||
region.width = map["width"] * scale
|
||||
region.height = map["height"] * scale
|
||||
|
||||
local color = map["color"]
|
||||
if color then
|
||||
region.r = tonumber(color:sub(1, 2), 16) / 255
|
||||
region.g = tonumber(color:sub(3, 4), 16) / 255
|
||||
region.b = tonumber(color:sub(5, 6), 16) / 255
|
||||
region.a = tonumber(color:sub(7, 8), 16) / 255
|
||||
end
|
||||
|
||||
region:updateOffset()
|
||||
return region
|
||||
|
||||
elseif type == AttachmentType.mesh then
|
||||
local mesh = attachmentLoader:newMeshAttachment(skin, name, path)
|
||||
if not mesh then return null end
|
||||
mesh.path = path
|
||||
mesh.vertices = getFloatArray(map, "vertices", scale)
|
||||
mesh.triangles = getIntArray(map, "triangles")
|
||||
mesh.regionUVs = getFloatArray(map, "uvs", 1)
|
||||
mesh:updateUVs()
|
||||
|
||||
local color = map["color"]
|
||||
if color then
|
||||
mesh.r = tonumber(color:sub(1, 2), 16) / 255
|
||||
mesh.g = tonumber(color:sub(3, 4), 16) / 255
|
||||
mesh.b = tonumber(color:sub(5, 6), 16) / 255
|
||||
mesh.a = tonumber(color:sub(7, 8), 16) / 255
|
||||
end
|
||||
|
||||
mesh.hullLength = (map["hull"] or 0) * 2
|
||||
if map["edges"] then mesh.edges = getIntArray(map, "edges") end
|
||||
mesh.width = (map["width"] or 0) * scale
|
||||
mesh.height = (map["height"] or 0) * scale
|
||||
return mesh
|
||||
|
||||
elseif type == AttachmentType.skinnedmesh then
|
||||
local mesh = self.attachmentLoader.newSkinnedMeshAttachment(skin, name, path)
|
||||
if not mesh then return null end
|
||||
mesh.path = path
|
||||
|
||||
local uvs = getFloatArray(map, "uvs", 1)
|
||||
vertices = getFloatArray(map, "vertices", 1)
|
||||
local weights = {}
|
||||
local bones = {}
|
||||
for i = 1, vertices do
|
||||
local boneCount = vertices[i]
|
||||
i = i + 1
|
||||
table.insert(bones, boneCount)
|
||||
for ii = 1, i + boneCount * 4 do
|
||||
table.insert(bones, vertices[i])
|
||||
table.insert(weights, vertices[i + 1] * scale)
|
||||
table.insert(weights, vertices[i + 2] * scale)
|
||||
table.insert(weights, vertices[i + 3])
|
||||
i = i + 4
|
||||
end
|
||||
end
|
||||
mesh.bones = bones
|
||||
mesh.weights = weights
|
||||
mesh.triangles = getIntArray(map, "triangles")
|
||||
mesh.regionUVs = uvs
|
||||
mesh:updateUVs()
|
||||
|
||||
local color = map["color"]
|
||||
if color then
|
||||
mesh.r = tonumber(color:sub(1, 2), 16) / 255
|
||||
mesh.g = tonumber(color:sub(3, 4), 16) / 255
|
||||
mesh.b = tonumber(color:sub(5, 6), 16) / 255
|
||||
mesh.a = tonumber(color:sub(7, 8), 16) / 255
|
||||
end
|
||||
|
||||
mesh.hullLength = (map["hull"] or 0) * 2
|
||||
if map["edges"] then mesh.edges = getIntArray(map, "edges") end
|
||||
mesh.width = (map["width"] or 0) * scale
|
||||
mesh.height = (map["height"] or 0) * scale
|
||||
return mesh
|
||||
|
||||
elseif type == AttachmentType.boundingbox then
|
||||
local box = attachmentLoader:newBoundingBoxAttachment(type, name)
|
||||
if not box then return nil end
|
||||
local vertices = map["vertices"]
|
||||
for i,point in ipairs(vertices) do
|
||||
table.insert(attachment.vertices, vertices[i] * scale)
|
||||
table.insert(box.vertices, vertices[i] * scale)
|
||||
end
|
||||
return box
|
||||
end
|
||||
|
||||
return attachment
|
||||
error("Unknown attachment type: " .. type .. " (" .. name .. ")")
|
||||
end
|
||||
|
||||
readAnimation = function (name, map, skeletonData)
|
||||
local timelines = {}
|
||||
local duration = 0
|
||||
|
||||
local slotsMap = map["slots"]
|
||||
if slotsMap then
|
||||
for slotName,timelineMap in pairs(slotsMap) do
|
||||
local slotIndex = skeletonData.slotNameIndices[slotName]
|
||||
|
||||
for timelineName,values in pairs(timelineMap) do
|
||||
if timelineName == "color" then
|
||||
local timeline = Animation.ColorTimeline.new()
|
||||
timeline.slotIndex = slotIndex
|
||||
|
||||
local frameIndex = 0
|
||||
for i,valueMap in ipairs(values) do
|
||||
local color = valueMap["color"]
|
||||
timeline:setFrame(
|
||||
frameIndex, valueMap["time"],
|
||||
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(timeline, frameIndex, valueMap)
|
||||
frameIndex = frameIndex + 1
|
||||
end
|
||||
table.insert(timelines, timeline)
|
||||
duration = math.max(duration, timeline:getDuration())
|
||||
|
||||
elseif timelineName == "attachment" then
|
||||
local timeline = Animation.AttachmentTimeline.new()
|
||||
timeline.slotName = slotName
|
||||
|
||||
local frameIndex = 0
|
||||
for i,valueMap in ipairs(values) do
|
||||
local attachmentName = valueMap["name"]
|
||||
if not attachmentName then attachmentName = nil end
|
||||
timeline:setFrame(frameIndex, valueMap["time"], attachmentName)
|
||||
frameIndex = frameIndex + 1
|
||||
end
|
||||
table.insert(timelines, timeline)
|
||||
duration = math.max(duration, timeline:getDuration())
|
||||
|
||||
else
|
||||
error("Invalid frame type for a slot: " .. timelineName .. " (" .. slotName .. ")")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local bonesMap = map["bones"]
|
||||
if bonesMap then
|
||||
for boneName,timelineMap in pairs(bonesMap) do
|
||||
@ -199,12 +343,11 @@ function SkeletonJson.new (attachmentLoader)
|
||||
local timeline = Animation.RotateTimeline.new()
|
||||
timeline.boneIndex = boneIndex
|
||||
|
||||
local keyframeIndex = 0
|
||||
local frameIndex = 0
|
||||
for i,valueMap in ipairs(values) do
|
||||
local time = valueMap["time"]
|
||||
timeline:setFrame(keyframeIndex, time, valueMap["angle"])
|
||||
readCurve(timeline, keyframeIndex, valueMap)
|
||||
keyframeIndex = keyframeIndex + 1
|
||||
timeline:setFrame(frameIndex, valueMap["time"], valueMap["angle"])
|
||||
readCurve(timeline, frameIndex, valueMap)
|
||||
frameIndex = frameIndex + 1
|
||||
end
|
||||
table.insert(timelines, timeline)
|
||||
duration = math.max(duration, timeline:getDuration())
|
||||
@ -220,14 +363,13 @@ function SkeletonJson.new (attachmentLoader)
|
||||
end
|
||||
timeline.boneIndex = boneIndex
|
||||
|
||||
local keyframeIndex = 0
|
||||
local frameIndex = 0
|
||||
for i,valueMap in ipairs(values) do
|
||||
local time = valueMap["time"]
|
||||
local x = (valueMap["x"] or 0) * timelineScale
|
||||
local y = (valueMap["y"] or 0) * timelineScale
|
||||
timeline:setFrame(keyframeIndex, time, x, y)
|
||||
readCurve(timeline, keyframeIndex, valueMap)
|
||||
keyframeIndex = keyframeIndex + 1
|
||||
timeline:setFrame(frameIndex, valueMap["time"], x, y)
|
||||
readCurve(timeline, frameIndex, valueMap)
|
||||
frameIndex = frameIndex + 1
|
||||
end
|
||||
table.insert(timelines, timeline)
|
||||
duration = math.max(duration, timeline:getDuration())
|
||||
@ -239,73 +381,71 @@ function SkeletonJson.new (attachmentLoader)
|
||||
end
|
||||
end
|
||||
|
||||
local slotsMap = map["slots"]
|
||||
if slotsMap then
|
||||
for slotName,timelineMap in pairs(slotsMap) do
|
||||
local slotIndex = skeletonData.slotNameIndices[slotName]
|
||||
|
||||
for timelineName,values in pairs(timelineMap) do
|
||||
if timelineName == "color" then
|
||||
local timeline = Animation.ColorTimeline.new()
|
||||
local ffd = map["ffd"]
|
||||
if ffd then
|
||||
for skinName,slotMap in pairs(ffd) do
|
||||
local skin = skeletonData.findSkin(skinName)
|
||||
for slotName,meshMap in pairs(slotMap) do
|
||||
local slotIndex = skeletonData.findSlotIndex(slotName)
|
||||
for meshName,values in pairs(meshMap) do
|
||||
local timeline = Animation.FfdTimeline.new()
|
||||
local attachment = skin:getAttachment(slotIndex, meshName)
|
||||
if not attachment then error("FFD attachment not found: " .. meshName) end
|
||||
timeline.slotIndex = slotIndex
|
||||
timeline.attachment = attachment
|
||||
|
||||
local keyframeIndex = 0
|
||||
for i,valueMap in ipairs(values) do
|
||||
local time = valueMap["time"]
|
||||
local color = valueMap["color"]
|
||||
timeline:setFrame(
|
||||
keyframeIndex, time,
|
||||
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(timeline, keyframeIndex, valueMap)
|
||||
keyframeIndex = keyframeIndex + 1
|
||||
local isMesh = attachment.type == AttachmentType.mesh
|
||||
local vertexCount
|
||||
if isMesh then
|
||||
vertexCount = attachment.vertices.length
|
||||
else
|
||||
vertexCount = attachment.weights.length / 3 * 2
|
||||
end
|
||||
table.insert(timelines, timeline)
|
||||
duration = math.max(duration, timeline:getDuration())
|
||||
|
||||
elseif timelineName == "attachment" then
|
||||
local timeline = Animation.AttachmentTimeline.new()
|
||||
timeline.slotName = slotName
|
||||
|
||||
local frameIndex = 0
|
||||
for i,valueMap in ipairs(values) do
|
||||
local time = valueMap["time"]
|
||||
local attachmentName = valueMap["name"]
|
||||
if not attachmentName then attachmentName = nil end
|
||||
timeline:setFrame(frameIndex, time, attachmentName)
|
||||
local vertices
|
||||
if not valueMap["vertices"] then
|
||||
if isMesh then
|
||||
vertices = attachment.vertices
|
||||
else
|
||||
vertices = {}
|
||||
vertices.length = vertexCount
|
||||
end
|
||||
else
|
||||
local verticesValue = valueMap["vertices"]
|
||||
local vertices = {}
|
||||
local start = valueMap["offset"] or 0
|
||||
if scale == 1 then
|
||||
for ii = 1, #verticesValue do
|
||||
vertices[ii + start] = verticesValue[ii]
|
||||
end
|
||||
else
|
||||
for ii = 1, #verticesValue do
|
||||
vertices[ii + start] = verticesValue[ii] * scale
|
||||
end
|
||||
end
|
||||
if isMesh then
|
||||
local meshVertices = attachment.vertices
|
||||
for ii = 1, vertexCount do
|
||||
vertices[ii] = vertices[ii] + meshVertices[ii]
|
||||
end
|
||||
elseif #verticesValue < vertexCount then
|
||||
vertices[vertexCount] = 0
|
||||
end
|
||||
end
|
||||
|
||||
timeline:setFrame(frameIndex, valueMap["time"], vertices)
|
||||
readCurve(timeline, frameIndex, valueMap)
|
||||
frameIndex = frameIndex + 1
|
||||
end
|
||||
table.insert(timelines, timeline)
|
||||
duration = math.max(duration, timeline:getDuration())
|
||||
|
||||
else
|
||||
error("Invalid frame type for a slot: " .. timelineName .. " (" .. slotName .. ")")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local events = map["events"]
|
||||
if events then
|
||||
local timeline = Animation.EventTimeline.new(#events)
|
||||
local frameIndex = 0
|
||||
for i,eventMap in ipairs(events) do
|
||||
local eventData = skeletonData:findEvent(eventMap["name"])
|
||||
if not eventData then error("Event not found: " .. eventMap["name"]) end
|
||||
local event = Event.new(eventData)
|
||||
event.intValue = eventMap["int"] or eventData.intValue
|
||||
event.floatValue = eventMap["float"] or eventData.floatValue
|
||||
event.stringValue = eventMap["string"] or eventData.stringValue
|
||||
timeline:setFrame(frameIndex, eventMap["time"], event)
|
||||
frameIndex = frameIndex + 1
|
||||
end
|
||||
table.insert(timelines, timeline)
|
||||
duration = math.max(duration, timeline:getDuration())
|
||||
end
|
||||
|
||||
local drawOrderValues = map["draworder"]
|
||||
if drawOrderValues then
|
||||
local timeline = Animation.DrawOrderTimeline.new(#drawOrderValues)
|
||||
@ -353,6 +493,36 @@ function SkeletonJson.new (attachmentLoader)
|
||||
duration = math.max(duration, timeline:getDuration())
|
||||
end
|
||||
|
||||
local events = map["events"]
|
||||
if events then
|
||||
local timeline = Animation.EventTimeline.new(#events)
|
||||
local frameIndex = 0
|
||||
for i,eventMap in ipairs(events) do
|
||||
local eventData = skeletonData:findEvent(eventMap["name"])
|
||||
if not eventData then error("Event not found: " .. eventMap["name"]) end
|
||||
local event = Event.new(eventData)
|
||||
if eventMap["int"] ~= nil then
|
||||
event.intValue = eventMap["int"]
|
||||
else
|
||||
event.intValue = eventData.intValue
|
||||
end
|
||||
if eventMap["float"] ~= nil then
|
||||
event.floatValue = eventMap["float"]
|
||||
else
|
||||
event.floatValue = eventData.floatValue
|
||||
end
|
||||
if eventMap["string"] ~= nil then
|
||||
event.stringValue = eventMap["string"]
|
||||
else
|
||||
event.stringValue = eventData.stringValue
|
||||
end
|
||||
timeline:setFrame(frameIndex, eventMap["time"], event)
|
||||
frameIndex = frameIndex + 1
|
||||
end
|
||||
table.insert(timelines, timeline)
|
||||
duration = math.max(duration, timeline:getDuration())
|
||||
end
|
||||
|
||||
table.insert(skeletonData.animations, Animation.new(name, timelines, duration))
|
||||
end
|
||||
|
||||
|
||||
131
spine-lua/SkinnedMeshAttachment.lua
Normal file
131
spine-lua/SkinnedMeshAttachment.lua
Normal file
@ -0,0 +1,131 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- Spine Runtimes Software License
|
||||
-- Version 2.1
|
||||
--
|
||||
-- Copyright (c) 2013, Esoteric Software
|
||||
-- All rights reserved.
|
||||
--
|
||||
-- You are granted a perpetual, non-exclusive, non-sublicensable and
|
||||
-- non-transferable license to install, execute and perform the Spine Runtimes
|
||||
-- Software (the "Software") solely for internal use. Without the written
|
||||
-- permission of Esoteric Software (typically granted by licensing Spine), you
|
||||
-- may not (a) modify, translate, adapt or otherwise create derivative works,
|
||||
-- improvements of the Software or develop new applications using the Software
|
||||
-- or (b) remove, delete, alter or obscure any trademarks or any copyright,
|
||||
-- trademark, patent or other intellectual property or proprietary rights
|
||||
-- notices on or in the Software, including any copy thereof. Redistributions
|
||||
-- in binary or source form must include this license and terms.
|
||||
--
|
||||
-- THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "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 SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
-- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
-- OR BUSINESS INTERRUPTION) 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 THIS SOFTWARE, EVEN IF
|
||||
-- ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local AttachmentType = require "spine-lua.AttachmentType"
|
||||
|
||||
local SkinnedMeshAttachment = {}
|
||||
function SkinnedMeshAttachment.new (name)
|
||||
if not name then error("name cannot be nil", 2) end
|
||||
|
||||
local self = {
|
||||
name = name,
|
||||
type = AttachmentType.mesh,
|
||||
bones = nil,
|
||||
weights = 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 = 0, regionV2 = 0, 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 skeletonBones = slot.skeleton.bones
|
||||
local weights = self.weights
|
||||
local bones = self.bones
|
||||
|
||||
local w, v, b, f = 0, 0, 0, 0
|
||||
local n = bones.length
|
||||
local wx, wy, bone, vx, vy, weight
|
||||
if #slot.attachmentVertices == 0 then
|
||||
while v < n do
|
||||
wx = 0
|
||||
wy = 0
|
||||
local nn = bones[v] + v
|
||||
v = v + 1
|
||||
while v <= nn do
|
||||
bone = skeletonBones[bones[v]]
|
||||
vx = weights[b]
|
||||
vy = weights[b + 1]
|
||||
weight = weights[b + 2]
|
||||
wx = wx + (vx * bone.m00 + vy * bone.m01 + bone.worldX) * weight
|
||||
wy = wy + (vx * bone.m10 + vy * bone.m11 + bone.worldY) * weight
|
||||
v = v + 1
|
||||
b = b + 3
|
||||
end
|
||||
worldVertices[w] = wx + x
|
||||
worldVertices[w + 1] = wy + y
|
||||
w = w + 2
|
||||
end
|
||||
else
|
||||
local ffd = slot.attachmentVertices
|
||||
while v < n do
|
||||
wx = 0
|
||||
wy = 0
|
||||
local nn = bones[v] + v
|
||||
v = v + 1
|
||||
while v <= nn do
|
||||
bone = skeletonBones[bones[v]]
|
||||
vx = weights[b] + ffd[f]
|
||||
vy = weights[b + 1] + ffd[f + 1]
|
||||
weight = weights[b + 2]
|
||||
wx = wx + (vx * bone.m00 + vy * bone.m01 + bone.worldX) * weight
|
||||
wy = wy + (vx * bone.m10 + vy * bone.m11 + bone.worldY) * weight
|
||||
v = v + 1
|
||||
b = b + 3
|
||||
f = f + 2
|
||||
end
|
||||
worldVertices[w] = wx + x
|
||||
worldVertices[w + 1] = wy + y
|
||||
w = w + 2
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
return MeshAttachment
|
||||
@ -38,7 +38,10 @@ function Slot.new (slotData, skeleton, bone)
|
||||
data = slotData,
|
||||
skeleton = skeleton,
|
||||
bone = bone,
|
||||
r = 1, g = 1, b = 1, a = 1
|
||||
r = 1, g = 1, b = 1, a = 1,
|
||||
attachment = nil,
|
||||
attachmentTime = 0,
|
||||
attachmentVertices = nil
|
||||
}
|
||||
|
||||
function self:setColor (r, g, b, a)
|
||||
|
||||
@ -36,7 +36,9 @@ function SlotData.new (name, boneData)
|
||||
local self = {
|
||||
name = name,
|
||||
boneData = boneData,
|
||||
r = 1, g = 1, b = 1, a = 1
|
||||
r = 1, g = 1, b = 1, a = 1,
|
||||
attachmentName = nil,
|
||||
additiveBlending = false
|
||||
}
|
||||
|
||||
function self:setColor (r, g, b, a)
|
||||
@ -46,6 +48,6 @@ function SlotData.new (name, boneData)
|
||||
self.a = a
|
||||
end
|
||||
|
||||
return self;
|
||||
return self
|
||||
end
|
||||
return SlotData
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user