| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640 |
- -------------------------------------------------------------------------------
- -- 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 utils = require "lib/spine-lua/utils"
- local SkeletonData = require "lib/spine-lua/SkeletonData"
- local BoneData = require "lib/spine-lua/BoneData"
- local SlotData = require "lib/spine-lua/SlotData"
- local Skin = require "lib/spine-lua/Skin"
- local AttachmentLoader = require "lib/spine-lua/AttachmentLoader"
- local Animation = require "lib/spine-lua/Animation"
- local IkConstraintData = require "lib/spine-lua/IkConstraintData"
- local IkConstraint = require "lib/spine-lua/IkConstraint"
- local EventData = require "lib/spine-lua/EventData"
- local Event = require "lib/spine-lua/Event"
- local AttachmentType = require "lib/spine-lua/AttachmentType"
- local SkeletonJson = {}
- function SkeletonJson.new (attachmentLoader)
- if not attachmentLoader then attachmentLoader = AttachmentLoader.new() end
- local self = {
- attachmentLoader = attachmentLoader,
- scale = 1
- }
- function self:readSkeletonDataFile (fileName, base)
- return self:readSkeletonData(utils.readFile(fileName, base))
- end
- local readAttachment
- local readAnimation
- local readCurve
- local getArray
- function self:readSkeletonData (json)
- local skeletonData = SkeletonData.new(self.attachmentLoader)
- root = json
- -- Skeleton.
- if root["skeleton"] then
- local skeletonMap = root["skeleton"]
- skeletonData.hash = skeletonMap["hash"]
- skeletonData.version = skeletonMap["spine"]
- skeletonData.width = skeletonMap["width"] or 0
- skeletonData.height = skeletonMap["height"] or 0
- end
- -- Bones.
- for i,boneMap in ipairs(root["bones"]) do
- local boneName = boneMap["name"]
- local parent = nil
- local parentName = boneMap["parent"]
- if parentName then
- parent = skeletonData:findBone(parentName)
- if not parent then error("Parent bone not found: " .. parentName) end
- end
- local boneData = BoneData.new(boneName, parent)
- boneData.length = (boneMap["length"] or 0) * self.scale
- boneData.x = (boneMap["x"] or 0) * self.scale
- boneData.y = (boneMap["y"] or 0) * self.scale
- boneData.rotation = (boneMap["rotation"] or 0)
- 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
- boneData.flipX = boneMap["flipX"] or false
- boneData.flipY = boneMap["flipY"] or false
- if boneMap["inheritScale"] == false then
- boneData.inheritScale = false
- else
- boneData.inheritScale = true
- end
- if boneMap["inheritRotation"] == false then
- boneData.inheritRotation = false
- else
- boneData.inheritRotation = true
- end
- table.insert(skeletonData.bones, boneData)
- end
- -- IK constraints.
- if root["ik"] then
- for i,ikMap in ipairs(root["ik"]) do
- local ikConstraintData = IkConstraintData.new(ikMap["name"])
- for i,boneName in ipairs(ikMap["bones"]) do
- local bone = skeletonData:findBone(boneName)
- if not bone then error("IK bone not found: " .. boneName) end
- table.insert(ikConstraintData.bones, bone)
- end
- local targetName = ikMap["target"]
- ikConstraintData.target = skeletonData:findBone(targetName)
- if not ikConstraintData.target then error("Target bone not found: " .. targetName) end
- if ikMap["bendPositive"] == false then ikConstraintData.bendDirection = -1 end
- if ikMap["mix"] ~= nil then ikConstraintData.mix = ikMap["mix"] end
- table.insert(skeletonData.ikConstraints, ikConstraintData)
- end
- end
- -- Slots.
- if root["slots"] then
- for i,slotMap in ipairs(root["slots"]) do
- local slotName = slotMap["name"]
- local boneName = slotMap["bone"]
- local boneData = skeletonData:findBone(boneName)
- if not boneData then error("Slot bone not found: " .. boneName) end
- local slotData = SlotData.new(slotName, boneData)
- local color = slotMap["color"]
- if color then
- slotData:setColor(
- 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
- slotData.attachmentName = slotMap["attachment"]
- slotData.additiveBlending = slotMap["additive"]
- table.insert(skeletonData.slots, slotData)
- skeletonData.slotNameIndices[slotData.name] = #skeletonData.slots
- end
- end
- -- Skins.
- if root["skins"] then
- for skinName,skinMap in pairs(root["skins"]) do
- local skin = Skin.new(skinName)
- for slotName,slotMap in pairs(skinMap) do
- local slotIndex = skeletonData.slotNameIndices[slotName]
- for attachmentName,attachmentMap in pairs(slotMap) do
- local attachment = readAttachment(attachmentName, attachmentMap)
- if attachment then
- skin:addAttachment(slotIndex, attachmentName, attachment)
- end
- end
- end
- if skin.name == "default" then
- skeletonData.defaultSkin = skin
- else
- table.insert(skeletonData.skins, skin)
- end
- end
- end
- -- Events.
- if root["events"] then
- for eventName,eventMap in pairs(root["events"]) do
- local eventData = EventData.new(eventName)
- eventData.intValue = eventMap["int"] or 0
- eventData.floatValue = eventMap["float"] or 0
- eventData.stringValue = eventMap["string"]
- table.insert(skeletonData.events, eventData)
- end
- end
- -- Animations.
- if root["animations"] then
- for animationName,animationMap in pairs(root["animations"]) do
- readAnimation(animationName, animationMap, skeletonData)
- end
- end
- return skeletonData
- end
- readAttachment = function (name, map)
- name = map["name"] or name
- local type = AttachmentType[map["type"] or "region"]
- local path = map["path"] or name
- local scale = self.scale
- if type == AttachmentType.region then
- 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 = getArray(map, "vertices", scale)
- mesh.triangles = getArray(map, "triangles", 1)
- mesh.regionUVs = getArray(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 = getArray(map, "edges", 1) 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 = getArray(map, "uvs", 1)
- vertices = getArray(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 = getArray(map, "triangles", 1)
- 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 = getArray(map, "edges", 1) 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(box.vertices, vertices[i] * scale)
- end
- return box
- end
- 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
- local boneIndex = skeletonData:findBoneIndex(boneName)
- if boneIndex == -1 then error("Bone not found: " .. boneName) end
- for timelineName,values in pairs(timelineMap) do
- if timelineName == "rotate" then
- local timeline = Animation.RotateTimeline.new()
- timeline.boneIndex = boneIndex
- local frameIndex = 0
- for i,valueMap in ipairs(values) do
- 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())
- elseif timelineName == "translate" or timelineName == "scale" then
- local timeline
- local timelineScale = 1
- if timelineName == "scale" then
- timeline = Animation.ScaleTimeline.new()
- else
- timeline = Animation.TranslateTimeline.new()
- timelineScale = self.scale
- end
- timeline.boneIndex = boneIndex
- local frameIndex = 0
- for i,valueMap in ipairs(values) do
- local x = (valueMap["x"] or 0) * timelineScale
- local y = (valueMap["y"] or 0) * timelineScale
- 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())
- elseif timelineName == "flipX" or timelineName == "flipY" then
- local x = timelineName == "flipX"
- local timeline, field
- if x then
- timeline = Animation.FlipXTimeline.new()
- field = "x"
- else
- timeline = Animation.FlipYTimeline.new();
- field = "y"
- end
- timeline.boneIndex = boneIndex
- local frameIndex = 0
- for i,valueMap in ipairs(values) do
- timeline:setFrame(frameIndex, valueMap["time"], valueMap[field] or false)
- frameIndex = frameIndex + 1
- end
- table.insert(timelines, timeline)
- duration = math.max(duration, timeline:getDuration())
- else
- error("Invalid timeline type for a bone: " .. timelineName .. " (" .. boneName .. ")")
- end
- end
- end
- end
- local ik = map["ik"]
- if ik then
- for ikConstraintName,values in pairs(ik) do
- local ikConstraint = skeletonData:findIkConstraint(ikConstraintName)
- local timeline = Animation.IkConstraintTimeline.new()
- for i,other in pairs(skeletonData.ikConstraints) do
- if other == ikConstraint then
- timeline.ikConstraintIndex = i
- break
- end
- end
- local frameIndex = 0
- for i,valueMap in ipairs(values) do
- local mix = 1
- if valueMap["mix"] ~= nil then mix = valueMap["mix"] end
- local bendPositive = 1
- if valueMap["bendPositive"] == false then bendPositive = -1 end
- timeline:setFrame(frameIndex, valueMap["time"], mix, bendPositive)
- readCurve(timeline, frameIndex, valueMap)
- frameIndex = frameIndex + 1
- end
- table.insert(timelines, timeline)
- duration = math.max(duration, timeline:getDuration())
- end
- end
- 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 isMesh = attachment.type == AttachmentType.mesh
- local vertexCount
- if isMesh then
- vertexCount = attachment.vertices.length
- else
- vertexCount = attachment.weights.length / 3 * 2
- end
- local frameIndex = 0
- for i,valueMap in ipairs(values) do
- 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())
- end
- end
- end
- end
- local drawOrderValues = map["drawOrder"]
- if not drawOrderValues then drawOrderValues = map["draworder"] end
- if drawOrderValues then
- local timeline = Animation.DrawOrderTimeline.new(#drawOrderValues)
- local slotCount = #skeletonData.slots
- local frameIndex = 0
- for i,drawOrderMap in ipairs(drawOrderValues) do
- local drawOrder = nil
- local offsets = drawOrderMap["offsets"]
- if offsets then
- drawOrder = {}
- local unchanged = {}
- local originalIndex = 1
- local unchangedIndex = 1
- for ii,offsetMap in ipairs(offsets) do
- local slotIndex = skeletonData:findSlotIndex(offsetMap["slot"])
- if slotIndex == -1 then error("Slot not found: " .. offsetMap["slot"]) end
- -- Collect unchanged items.
- while originalIndex ~= slotIndex do
- unchanged[unchangedIndex] = originalIndex
- unchangedIndex = unchangedIndex + 1
- originalIndex = originalIndex + 1
- end
- -- Set changed items.
- drawOrder[originalIndex + offsetMap["offset"]] = originalIndex
- originalIndex = originalIndex + 1
- end
- -- Collect remaining unchanged items.
- while originalIndex <= slotCount do
- unchanged[unchangedIndex] = originalIndex
- unchangedIndex = unchangedIndex + 1
- originalIndex = originalIndex + 1
- end
- -- Fill in unchanged items.
- for ii = slotCount, 1, -1 do
- if not drawOrder[ii] then
- unchangedIndex = unchangedIndex - 1
- drawOrder[ii] = unchanged[unchangedIndex]
- end
- end
- end
- timeline:setFrame(frameIndex, drawOrderMap["time"], drawOrder)
- frameIndex = frameIndex + 1
- end
- table.insert(timelines, timeline)
- 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
- readCurve = function (timeline, frameIndex, valueMap)
- local curve = valueMap["curve"]
- if not curve then
- timeline:setLinear(frameIndex)
- elseif curve == "stepped" then
- timeline:setStepped(frameIndex)
- else
- timeline:setCurve(frameIndex, curve[1], curve[2], curve[3], curve[4])
- end
- end
- getArray = function (map, name, scale)
- local list = map[name]
- local values = {}
- if scale == 1 then
- for i = 1, #list do
- values[i] = list[i]
- end
- else
- for i = 1, #list do
- values[i] = list[i] * scale
- end
- end
- return values
- end
- return self
- end
- return SkeletonJson
|