SkeletonJson.lua 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878
  1. -------------------------------------------------------------------------------
  2. -- Spine Runtimes License Agreement
  3. -- Last updated May 1, 2019. Replaces all prior versions.
  4. --
  5. -- Copyright (c) 2013-2019, Esoteric Software LLC
  6. --
  7. -- Integration of the Spine Runtimes into software or otherwise creating
  8. -- derivative works of the Spine Runtimes is permitted under the terms and
  9. -- conditions of Section 2 of the Spine Editor License Agreement:
  10. -- http://esotericsoftware.com/spine-editor-license
  11. --
  12. -- Otherwise, it is permitted to integrate the Spine Runtimes into software
  13. -- or otherwise create derivative works of the Spine Runtimes (collectively,
  14. -- "Products"), provided that each user of the Products must obtain their own
  15. -- Spine Editor license and redistribution of the Products in any form must
  16. -- include this license and copyright notice.
  17. --
  18. -- THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS
  19. -- OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  20. -- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
  21. -- NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT,
  22. -- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  23. -- BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS
  24. -- INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY
  25. -- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  26. -- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
  27. -- EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. -------------------------------------------------------------------------------
  29. local table_insert = table.insert
  30. local SkeletonData = require "spine-lua.SkeletonData"
  31. local BoneData = require "spine-lua.BoneData"
  32. local SlotData = require "spine-lua.SlotData"
  33. local Skin = require "spine-lua.Skin"
  34. local AttachmentLoader = require "spine-lua.AttachmentLoader"
  35. local Animation = require "spine-lua.Animation"
  36. local IkConstraintData = require "spine-lua.IkConstraintData"
  37. local IkConstraint = require "spine-lua.IkConstraint"
  38. local PathConstraintData = require "spine-lua.PathConstraintData"
  39. local PathConstraint = require "spine-lua.PathConstraint"
  40. local TransformConstraintData = require "spine-lua.TransformConstraintData"
  41. local TransformConstraint = require "spine-lua.TransformConstraint"
  42. local EventData = require "spine-lua.EventData"
  43. local Event = require "spine-lua.Event"
  44. local AttachmentType = require "spine-lua.attachments.AttachmentType"
  45. local BlendMode = require "spine-lua.BlendMode"
  46. local TransformMode = require "spine-lua.TransformMode"
  47. local utils = require "spine-lua.utils"
  48. local Color = require "spine-lua.Color"
  49. local SkeletonJson = {}
  50. function SkeletonJson.new (attachmentLoader)
  51. if not attachmentLoader then attachmentLoader = AttachmentLoader.new() end
  52. local self = {
  53. attachmentLoader = attachmentLoader,
  54. scale = 1,
  55. linkedMeshes = {}
  56. }
  57. function self:readSkeletonDataFile (fileName, base)
  58. return self:readSkeletonData(utils.readFile(fileName, base))
  59. end
  60. local readAttachment
  61. local readAnimation
  62. local readCurve
  63. local getArray
  64. local getValue = function (map, name, default)
  65. local value = map[name]
  66. if value == nil then return default else return value end
  67. end
  68. function self:readSkeletonData (jsonText)
  69. local scale = self.scale
  70. local skeletonData = SkeletonData.new(self.attachmentLoader)
  71. local root = utils.readJSON(jsonText)
  72. if not root then error("Invalid JSON: " .. jsonText, 2) end
  73. -- Skeleton.
  74. local skeletonMap = root["skeleton"]
  75. if skeletonMap then
  76. skeletonData.hash = skeletonMap["hash"]
  77. skeletonData.version = skeletonMap["spine"]
  78. skeletonData.width = skeletonMap["width"]
  79. skeletonData.height = skeletonMap["height"]
  80. skeletonData.fps = skeletonMap["fps"]
  81. skeletonData.imagesPath = skeletonMap["images"]
  82. end
  83. -- Bones.
  84. for i,boneMap in ipairs(root["bones"]) do
  85. local boneName = boneMap["name"]
  86. local parent = nil
  87. local parentName = boneMap["parent"]
  88. if parentName then
  89. parent = skeletonData:findBone(parentName)
  90. if not parent then error("Parent bone not found: " .. parentName) end
  91. end
  92. local data = BoneData.new(i, boneName, parent)
  93. data.length = getValue(boneMap, "length", 0) * scale;
  94. data.x = getValue(boneMap, "x", 0) * scale;
  95. data.y = getValue(boneMap, "y", 0) * scale;
  96. data.rotation = getValue(boneMap, "rotation", 0);
  97. data.scaleX = getValue(boneMap, "scaleX", 1);
  98. data.scaleY = getValue(boneMap, "scaleY", 1);
  99. data.shearX = getValue(boneMap, "shearX", 0);
  100. data.shearY = getValue(boneMap, "shearY", 0);
  101. data.transformMode = TransformMode[getValue(boneMap, "transform", "normal")]
  102. table_insert(skeletonData.bones, data)
  103. end
  104. -- Slots.
  105. if root["slots"] then
  106. for i,slotMap in ipairs(root["slots"]) do
  107. local slotName = slotMap["name"]
  108. local boneName = slotMap["bone"]
  109. local boneData = skeletonData:findBone(boneName)
  110. if not boneData then error("Slot bone not found: " .. boneName) end
  111. local data = SlotData.new(i, slotName, boneData)
  112. local color = slotMap["color"]
  113. if color then
  114. data.color:set(tonumber(color:sub(1, 2), 16) / 255,
  115. tonumber(color:sub(3, 4), 16) / 255,
  116. tonumber(color:sub(5, 6), 16) / 255,
  117. tonumber(color:sub(7, 8), 16) / 255)
  118. end
  119. local dark = slotMap["dark"]
  120. if dark then
  121. data.darkColor = Color.newWith(1, 1, 1, 1)
  122. data.darkColor:set(tonumber(dark:sub(1, 2), 16) / 255,
  123. tonumber(dark:sub(3, 4), 16) / 255,
  124. tonumber(dark:sub(5, 6), 16) / 255,
  125. 0)
  126. end
  127. data.attachmentName = getValue(slotMap, "attachment", nil)
  128. data.blendMode = BlendMode[getValue(slotMap, "blend", "normal")]
  129. table_insert(skeletonData.slots, data)
  130. skeletonData.slotNameIndices[data.name] = #skeletonData.slots
  131. end
  132. end
  133. -- IK constraints.
  134. if root["ik"] then
  135. for _,constraintMap in ipairs(root["ik"]) do
  136. local data = IkConstraintData.new(constraintMap["name"])
  137. data.order = getValue(constraintMap, "order", 0)
  138. for _,boneName in ipairs(constraintMap["bones"]) do
  139. local bone = skeletonData:findBone(boneName)
  140. if not bone then error("IK bone not found: " .. boneName) end
  141. table_insert(data.bones, bone)
  142. end
  143. local targetName = constraintMap["target"]
  144. data.target = skeletonData:findBone(targetName)
  145. if not data.target then error("Target bone not found: " .. targetName) end
  146. data.mix = getValue(constraintMap, "mix", 1)
  147. if constraintMap["bendPositive"] == nil or constraintMap["bendPositive"] == true then
  148. data.bendDirection = 1
  149. else
  150. data.bendDirection = -1
  151. end
  152. if constraintMap["compress"] == nil or constraintMap["compress"] == false then data.compress = false else data.compress = true end
  153. if constraintMap["stretch"] == nil or constraintMap["stretch"] == false then data.stretch = false else data.stretch = true end
  154. if constraintMap["uniform"] == nil or constraintMap["uniform"] == false then data.uniform = false else data.uniform = true end
  155. table_insert(skeletonData.ikConstraints, data)
  156. end
  157. end
  158. -- Transform constraints
  159. if root["transform"] then
  160. for _,constraintMap in ipairs(root["transform"]) do
  161. local data = TransformConstraintData.new(constraintMap.name)
  162. data.order = getValue(constraintMap, "order", 0)
  163. for _,boneName in ipairs(constraintMap.bones) do
  164. local bone = skeletonData:findBone(boneName)
  165. if not bone then error("Transform constraint bone not found: " .. boneName, 2) end
  166. table_insert(data.bones, bone)
  167. end
  168. local targetName = constraintMap.target
  169. data.target = skeletonData:findBone(targetName)
  170. if not data.target then error("Transform constraint target bone not found: " .. (targetName or "none"), 2) end
  171. data.offsetRotation = getValue(constraintMap, "rotation", 0);
  172. data.offsetX = getValue(constraintMap, "x", 0) * scale;
  173. data.offsetY = getValue(constraintMap, "y", 0) * scale;
  174. data.offsetScaleX = getValue(constraintMap, "scaleX", 0);
  175. data.offsetScaleY = getValue(constraintMap, "scaleY", 0);
  176. data.offsetShearY = getValue(constraintMap, "shearY", 0);
  177. data.rotateMix = getValue(constraintMap, "rotateMix", 1);
  178. data.translateMix = getValue(constraintMap, "translateMix", 1);
  179. data.scaleMix = getValue(constraintMap, "scaleMix", 1);
  180. data.shearMix = getValue(constraintMap, "shearMix", 1);
  181. table_insert(skeletonData.transformConstraints, data)
  182. end
  183. end
  184. -- Path constraints
  185. if root["path"] then
  186. for _,constraintMap in ipairs(root.path) do
  187. local data = PathConstraintData.new(constraintMap.name);
  188. data.order = getValue(constraintMap, "order", 0)
  189. for _,boneName in ipairs(constraintMap.bones) do
  190. local bone = skeletonData:findBone(boneName)
  191. if not bone then error("Path constraint bone not found: " .. boneName, 2) end
  192. table_insert(data.bones, bone)
  193. end
  194. local targetName = constraintMap.target;
  195. data.target = skeletonData:findSlot(targetName)
  196. if data.target == nil then error("Path target slot not found: " .. targetName, 2) end
  197. data.positionMode = PathConstraintData.PositionMode[getValue(constraintMap, "positionMode", "percent"):lower()]
  198. data.spacingMode = PathConstraintData.SpacingMode[getValue(constraintMap, "spacingMode", "length"):lower()]
  199. data.rotateMode = PathConstraintData.RotateMode[getValue(constraintMap, "rotateMode", "tangent"):lower()]
  200. data.offsetRotation = getValue(constraintMap, "rotation", 0);
  201. data.position = getValue(constraintMap, "position", 0);
  202. if data.positionMode == PathConstraintData.PositionMode.fixed then data.position = data.position * scale end
  203. data.spacing = getValue(constraintMap, "spacing", 0);
  204. if data.spacingMode == PathConstraintData.SpacingMode.length or data.spacingMode == PathConstraintData.SpacingMode.fixed then data.spacing = data.spacing * scale end
  205. data.rotateMix = getValue(constraintMap, "rotateMix", 1);
  206. data.translateMix = getValue(constraintMap, "translateMix", 1);
  207. table_insert(skeletonData.pathConstraints, data)
  208. end
  209. end
  210. -- Skins.
  211. if root["skins"] then
  212. for skinName,skinMap in pairs(root["skins"]) do
  213. local skin = Skin.new(skinName)
  214. for slotName,slotMap in pairs(skinMap) do
  215. local slotIndex = skeletonData.slotNameIndices[slotName]
  216. for attachmentName,attachmentMap in pairs(slotMap) do
  217. local attachment = readAttachment(attachmentMap, skin, slotIndex, attachmentName, skeletonData)
  218. if attachment then
  219. skin:addAttachment(slotIndex, attachmentName, attachment)
  220. end
  221. end
  222. end
  223. table_insert(skeletonData.skins, skin)
  224. if skin.name == "default" then skeletonData.defaultSkin = skin end
  225. end
  226. end
  227. -- Linked meshes
  228. for _, linkedMesh in ipairs(self.linkedMeshes) do
  229. local skin = skeletonData.defaultSkin
  230. if linkedMesh.skin then skin = skeletonData:findSkin(linkedMesh.skin) end
  231. if not skin then error("Skin not found: " .. linkedMesh.skin) end
  232. local parent = skin:getAttachment(linkedMesh.slotIndex, linkedMesh.parent)
  233. if not parent then error("Parent mesh not found: " + linkedMesh.parent) end
  234. linkedMesh.mesh:setParentMesh(parent)
  235. linkedMesh.mesh:updateUVs()
  236. end
  237. self.linkedMeshes = {}
  238. -- Events.
  239. if root["events"] then
  240. for eventName,eventMap in pairs(root["events"]) do
  241. local data = EventData.new(eventName)
  242. data.intValue = getValue(eventMap, "int", 0)
  243. data.floatValue = getValue(eventMap, "float", 0)
  244. data.stringValue = getValue(eventMap, "string", "")
  245. data.audioPath = getValue(eventMap, "audio", nil)
  246. if data.audioPath ~= nil then
  247. data.volume = getValue(eventMap, "volume", 1)
  248. data.balance = getValue(eventMap, "balance", 0)
  249. end
  250. table_insert(skeletonData.events, data)
  251. end
  252. end
  253. -- Animations.
  254. if root["animations"] then
  255. for animationName,animationMap in pairs(root["animations"]) do
  256. readAnimation(animationMap, animationName, skeletonData)
  257. end
  258. end
  259. return skeletonData
  260. end
  261. readAttachment = function (map, skin, slotIndex, name, skeletonData)
  262. local scale = self.scale
  263. name = getValue(map, "name", name)
  264. local type = AttachmentType[getValue(map, "type", "region")]
  265. local path = getValue(map, "path", name)
  266. if type == AttachmentType.region then
  267. local region = attachmentLoader:newRegionAttachment(skin, name, path)
  268. if not region then return nil end
  269. region.path = path
  270. region.x = getValue(map, "x", 0) * scale
  271. region.y = getValue(map, "y", 0) * scale
  272. region.scaleX = getValue(map, "scaleX", 1);
  273. region.scaleY = getValue(map, "scaleY", 1);
  274. region.rotation = getValue(map, "rotation", 0);
  275. region.width = map.width * scale;
  276. region.height = map.height * scale;
  277. local color = map["color"]
  278. if color then
  279. region.color:set(tonumber(color:sub(1, 2), 16) / 255,
  280. tonumber(color:sub(3, 4), 16) / 255,
  281. tonumber(color:sub(5, 6), 16) / 255,
  282. tonumber(color:sub(7, 8), 16) / 255)
  283. end
  284. region:updateOffset()
  285. return region
  286. elseif type == AttachmentType.boundingbox then
  287. local box = attachmentLoader:newBoundingBoxAttachment(skin, name)
  288. if not box then return nil end
  289. readVertices(map, box, map.vertexCount * 2)
  290. local color = map.color
  291. if color then
  292. box.color:set(tonumber(color:sub(1, 2), 16) / 255,
  293. tonumber(color:sub(3, 4), 16) / 255,
  294. tonumber(color:sub(5, 6), 16) / 255,
  295. tonumber(color:sub(7, 8), 16) / 255)
  296. end
  297. return box
  298. elseif type == AttachmentType.mesh or type == AttachmentType.linkedmesh then
  299. local mesh = attachmentLoader:newMeshAttachment(skin, name, path)
  300. if not mesh then return nil end
  301. mesh.path = path
  302. local color = map.color
  303. if color then
  304. mesh.color:set(tonumber(color:sub(1, 2), 16) / 255,
  305. tonumber(color:sub(3, 4), 16) / 255,
  306. tonumber(color:sub(5, 6), 16) / 255,
  307. tonumber(color:sub(7, 8), 16) / 255)
  308. end
  309. local parent = map.parent
  310. if parent then
  311. mesh.inheritDeform = getValue(map, "deform", true)
  312. table_insert(self.linkedMeshes, {
  313. mesh = mesh,
  314. skin = getValue(map, "skin", nil),
  315. slotIndex = slotIndex,
  316. parent = parent
  317. })
  318. return mesh
  319. end
  320. local uvs = getArray(map, "uvs", 1)
  321. readVertices(map, mesh, #uvs)
  322. mesh.triangles = getArray(map, "triangles", 1)
  323. -- adjust triangle indices by 1, vertices are one-indexed
  324. for i,v in ipairs(mesh.triangles) do
  325. mesh.triangles[i] = v + 1
  326. end
  327. mesh.regionUVs = uvs
  328. mesh:updateUVs()
  329. mesh.hullLength = getValue(map, "hull", 0) * 2
  330. return mesh
  331. elseif type == AttachmentType.path then
  332. local path = self.attachmentLoader:newPathAttachment(skin, name)
  333. if not path then return nil end
  334. path.closed = getValue(map, "closed", false)
  335. path.constantSpeed = getValue(map, "constantSpeed", true)
  336. local vertexCount = map.vertexCount
  337. readVertices(map, path, vertexCount * 2)
  338. local lengths = utils.newNumberArray(vertexCount / 3, 0)
  339. for i,v in ipairs(map.lengths) do
  340. lengths[i] = v * scale
  341. end
  342. path.lengths = lengths
  343. local color = map.color
  344. if color then
  345. path.color:set(tonumber(color:sub(1, 2), 16) / 255,
  346. tonumber(color:sub(3, 4), 16) / 255,
  347. tonumber(color:sub(5, 6), 16) / 255,
  348. tonumber(color:sub(7, 8), 16) / 255)
  349. end
  350. return path;
  351. elseif type == AttachmentType.point then
  352. local point = self.attachmentLoader:newPointAttachment(skin, name)
  353. if not point then return nil end
  354. point.x = getValue(map, "x", 0) * scale
  355. point.y = getValue(map, "y", 0) * scale
  356. point.rotation = getValue(map, "rotation", 0)
  357. local color = map.color
  358. if color then
  359. path.color:set(tonumber(color:sub(1, 2), 16) / 255,
  360. tonumber(color:sub(3, 4), 16) / 255,
  361. tonumber(color:sub(5, 6), 16) / 255,
  362. tonumber(color:sub(7, 8), 16) / 255)
  363. end
  364. return point
  365. elseif type == AttachmentType.clipping then
  366. local clip = attachmentLoader:newClippingAttachment(skin, name)
  367. if not clip then return nil end
  368. local _end = getValue(map, "end", nil)
  369. if _end then
  370. local slot = skeletonData:findSlot(_end)
  371. if not slot then error("Clipping end slot not found: " + _end) end
  372. clip.endSlot = slot
  373. end
  374. readVertices(map, clip, map.vertexCount * 2)
  375. local color = map.color
  376. if color then
  377. clip.color:set(tonumber(color:sub(1, 2), 16) / 255,
  378. tonumber(color:sub(3, 4), 16) / 255,
  379. tonumber(color:sub(5, 6), 16) / 255,
  380. tonumber(color:sub(7, 8), 16) / 255)
  381. end
  382. return clip
  383. end
  384. error("Unknown attachment type: " .. type .. " (" .. name .. ")")
  385. end
  386. readVertices = function (map, attachment, verticesLength)
  387. local scale = self.scale
  388. attachment.worldVerticesLength = verticesLength
  389. local vertices = getArray(map, "vertices", 1)
  390. if verticesLength == #vertices then
  391. if scale ~= 1 then
  392. local i = 0
  393. local n = #vertices
  394. while i < n do
  395. vertices[i + 1] = vertices[i + 1] * scale
  396. i = i + 1
  397. end
  398. end
  399. attachment.vertices = vertices
  400. return
  401. end
  402. local weights = {}
  403. local bones = {}
  404. local i = 0
  405. local n = #vertices
  406. while i < n do
  407. local boneCount = vertices[i + 1]
  408. i = i + 1
  409. table_insert(bones, boneCount)
  410. local nn = i + boneCount * 4
  411. while i < nn do
  412. table_insert(bones, vertices[i + 1] + 1) -- +1 because bones are one-indexed
  413. table_insert(weights, vertices[i + 2] * scale)
  414. table_insert(weights, vertices[i + 3] * scale)
  415. table_insert(weights, vertices[i + 4])
  416. i = i + 4
  417. end
  418. end
  419. attachment.bones = bones
  420. attachment.vertices = weights
  421. end
  422. readAnimation = function (map, name, skeletonData)
  423. local timelines = {}
  424. local duration = 0
  425. local scale = self.scale
  426. -- Slot timelines
  427. local slotsMap = map["slots"]
  428. if slotsMap then
  429. for slotName,timelineMap in pairs(slotsMap) do
  430. local slotIndex = skeletonData.slotNameIndices[slotName]
  431. for timelineName,values in pairs(timelineMap) do
  432. if timelineName == "color" then
  433. local timeline = Animation.ColorTimeline.new(#values)
  434. timeline.slotIndex = slotIndex
  435. local frameIndex = 0
  436. for _,valueMap in ipairs(values) do
  437. local color = valueMap["color"]
  438. timeline:setFrame(
  439. frameIndex, valueMap["time"],
  440. tonumber(color:sub(1, 2), 16) / 255,
  441. tonumber(color:sub(3, 4), 16) / 255,
  442. tonumber(color:sub(5, 6), 16) / 255,
  443. tonumber(color:sub(7, 8), 16) / 255
  444. )
  445. readCurve(valueMap, timeline, frameIndex)
  446. frameIndex = frameIndex + 1
  447. end
  448. table_insert(timelines, timeline)
  449. duration = math.max(duration, timeline.frames[(timeline:getFrameCount() - 1) * Animation.ColorTimeline.ENTRIES])
  450. elseif timelineName == "twoColor" then
  451. local timeline = Animation.TwoColorTimeline.new(#values)
  452. timeline.slotIndex = slotIndex
  453. local frameIndex = 0
  454. for _,valueMap in ipairs(values) do
  455. local light = valueMap["light"]
  456. local dark = valueMap["dark"]
  457. timeline:setFrame(
  458. frameIndex, valueMap["time"],
  459. tonumber(light:sub(1, 2), 16) / 255,
  460. tonumber(light:sub(3, 4), 16) / 255,
  461. tonumber(light:sub(5, 6), 16) / 255,
  462. tonumber(light:sub(7, 8), 16) / 255,
  463. tonumber(dark:sub(1, 2), 16) / 255,
  464. tonumber(dark:sub(3, 4), 16) / 255,
  465. tonumber(dark:sub(5, 6), 16) / 255
  466. )
  467. readCurve(valueMap, timeline, frameIndex)
  468. frameIndex = frameIndex + 1
  469. end
  470. table_insert(timelines, timeline)
  471. duration = math.max(duration, timeline.frames[(timeline:getFrameCount() - 1) * Animation.TwoColorTimeline.ENTRIES])
  472. elseif timelineName == "attachment" then
  473. local timeline = Animation.AttachmentTimeline.new(#values)
  474. timeline.slotIndex = slotIndex
  475. local frameIndex = 0
  476. for _,valueMap in ipairs(values) do
  477. local attachmentName = valueMap["name"]
  478. timeline:setFrame(frameIndex, valueMap["time"], attachmentName)
  479. frameIndex = frameIndex + 1
  480. end
  481. table_insert(timelines, timeline)
  482. duration = math.max(duration, timeline.frames[timeline:getFrameCount() - 1])
  483. else
  484. error("Invalid frame type for a slot: " .. timelineName .. " (" .. slotName .. ")")
  485. end
  486. end
  487. end
  488. end
  489. -- Bone timelines
  490. local bonesMap = map["bones"]
  491. if bonesMap then
  492. for boneName,timelineMap in pairs(bonesMap) do
  493. local boneIndex = skeletonData:findBoneIndex(boneName)
  494. if boneIndex == -1 then error("Bone not found: " .. boneName) end
  495. for timelineName,values in pairs(timelineMap) do
  496. if timelineName == "rotate" then
  497. local timeline = Animation.RotateTimeline.new(#values)
  498. timeline.boneIndex = boneIndex
  499. local frameIndex = 0
  500. for _,valueMap in ipairs(values) do
  501. timeline:setFrame(frameIndex, valueMap["time"], valueMap["angle"])
  502. readCurve(valueMap, timeline, frameIndex)
  503. frameIndex = frameIndex + 1
  504. end
  505. table_insert(timelines, timeline)
  506. duration = math.max(duration, timeline.frames[(timeline:getFrameCount() - 1) * Animation.RotateTimeline.ENTRIES])
  507. elseif timelineName == "translate" or timelineName == "scale" or timelineName == "shear" then
  508. local timeline
  509. local timelineScale = 1
  510. if timelineName == "scale" then
  511. timeline = Animation.ScaleTimeline.new(#values)
  512. elseif timelineName == "shear" then
  513. timeline = Animation.ShearTimeline.new(#values)
  514. else
  515. timeline = Animation.TranslateTimeline.new(#values)
  516. timelineScale = self.scale
  517. end
  518. timeline.boneIndex = boneIndex
  519. local frameIndex = 0
  520. for _,valueMap in ipairs(values) do
  521. local x = (valueMap["x"] or 0) * timelineScale
  522. local y = (valueMap["y"] or 0) * timelineScale
  523. timeline:setFrame(frameIndex, valueMap["time"], x, y)
  524. readCurve(valueMap, timeline, frameIndex)
  525. frameIndex = frameIndex + 1
  526. end
  527. table_insert(timelines, timeline)
  528. duration = math.max(duration, timeline.frames[(timeline:getFrameCount() - 1) * Animation.TranslateTimeline.ENTRIES])
  529. else
  530. error("Invalid timeline type for a bone: " .. timelineName .. " (" .. boneName .. ")")
  531. end
  532. end
  533. end
  534. end
  535. -- IK timelines.
  536. local ik = map["ik"]
  537. if ik then
  538. for ikConstraintName,values in pairs(ik) do
  539. local ikConstraint = skeletonData:findIkConstraint(ikConstraintName)
  540. local timeline = Animation.IkConstraintTimeline.new(#values)
  541. for i,other in pairs(skeletonData.ikConstraints) do
  542. if other == ikConstraint then
  543. timeline.ikConstraintIndex = i
  544. break
  545. end
  546. end
  547. local frameIndex = 0
  548. for _,valueMap in ipairs(values) do
  549. local mix = 1
  550. if valueMap["mix"] ~= nil then mix = valueMap["mix"] end
  551. local bendPositive = 1
  552. if valueMap["bendPositive"] == false then bendPositive = -1 end
  553. local stretch = false
  554. if valueMap["stretch"] ~= nil then stretch = valueMap["stretch"] end
  555. local compress = false
  556. if valueMap["compress"] ~= nil then compress = valueMap["compress"] end
  557. timeline:setFrame(frameIndex, valueMap["time"], mix, bendPositive, compress, stretch)
  558. readCurve(valueMap, timeline, frameIndex)
  559. frameIndex = frameIndex + 1
  560. end
  561. table_insert(timelines, timeline)
  562. duration = math.max(duration, timeline.frames[(timeline:getFrameCount() - 1) * Animation.IkConstraintTimeline.ENTRIES])
  563. end
  564. end
  565. -- Transform constraint timelines.
  566. local transform = map["transform"]
  567. if transform then
  568. for constraintName, values in pairs(transform) do
  569. local constraint = skeletonData:findTransformConstraint(constraintName)
  570. local timeline = Animation.TransformConstraintTimeline.new(#values)
  571. for i,other in pairs(skeletonData.transformConstraints) do
  572. if other == constraint then
  573. timeline.transformConstraintIndex = i
  574. break
  575. end
  576. end
  577. local frameIndex = 0
  578. for _,valueMap in ipairs(values) do
  579. timeline:setFrame(frameIndex, valueMap.time, getValue(valueMap, "rotateMix", 1), getValue(valueMap, "translateMix", 1), getValue(valueMap, "scaleMix", 1), getValue(valueMap, "shearMix", 1))
  580. readCurve(valueMap, timeline, frameIndex)
  581. frameIndex = frameIndex + 1
  582. end
  583. table_insert(timelines, timeline)
  584. duration = math.max(duration, timeline.frames[(timeline:getFrameCount() - 1) * Animation.TransformConstraintTimeline.ENTRIES])
  585. end
  586. end
  587. -- Path constraint timelines.
  588. if map.paths then
  589. for constraintName,constraintMap in pairs(map.paths) do
  590. local index = skeletonData:findPathConstraintIndex(constraintName)
  591. if index == -1 then error("Path constraint not found: " .. constraintName, 2) end
  592. local data = skeletonData.pathConstraints[index]
  593. for timelineName, timelineMap in pairs(constraintMap) do
  594. if timelineName == "position" or timelineName == "spacing" then
  595. local timeline = nil
  596. local timelineScale = 1
  597. if timelineName == "spacing" then
  598. timeline = Animation.PathConstraintSpacingTimeline.new(#timelineMap)
  599. if data.spacingMode == PathConstraintData.SpacingMode.length or data.spacingMode == PathConstraintData.SpacingMode.fixed then timelineScale = scale end
  600. else
  601. timeline = Animation.PathConstraintPositionTimeline.new(#timelineMap)
  602. if data.positionMode == PathConstraintData.PositionMode.fixed then timelineScale = scale end
  603. end
  604. timeline.pathConstraintIndex = index
  605. local frameIndex = 0
  606. for _,valueMap in ipairs(timelineMap) do
  607. timeline:setFrame(frameIndex, valueMap.time, getValue(valueMap, timelineName, 0) * timelineScale)
  608. readCurve(valueMap, timeline, frameIndex)
  609. frameIndex = frameIndex + 1
  610. end
  611. table_insert(timelines, timeline)
  612. duration = math.max(duration, timeline.frames[(timeline:getFrameCount() - 1) * Animation.PathConstraintPositionTimeline.ENTRIES])
  613. elseif timelineName == "mix" then
  614. local timeline = Animation.PathConstraintMixTimeline.new(#timelineMap)
  615. timeline.pathConstraintIndex = index
  616. local frameIndex = 0
  617. for _,valueMap in ipairs(timelineMap) do
  618. timeline:setFrame(frameIndex, valueMap.time, getValue(valueMap, "rotateMix", 1), getValue(valueMap, "translateMix", 1))
  619. readCurve(valueMap, timeline, frameIndex)
  620. frameIndex = frameIndex + 1
  621. end
  622. table_insert(timelines, timeline)
  623. duration = math.max(duration, timeline.frames[(timeline:getFrameCount() - 1) * Animation.PathConstraintMixTimeline.ENTRIES])
  624. end
  625. end
  626. end
  627. end
  628. -- Deform timelines.
  629. if map.deform then
  630. for deformName, deformMap in pairs(map.deform) do
  631. local skin = skeletonData:findSkin(deformName)
  632. if not skin then error("Skin not found: " .. deformName, 2) end
  633. for slotName,slotMap in pairs(deformMap) do
  634. local slotIndex = skeletonData:findSlotIndex(slotName)
  635. if slotIndex == -1 then error("Slot not found: " .. slotMap.name, 2) end
  636. for timelineName,timelineMap in pairs(slotMap) do
  637. local attachment = skin:getAttachment(slotIndex, timelineName)
  638. if not attachment then error("Deform attachment not found: " .. timelineMap.name, 2) end
  639. local weighted = attachment.bones ~= nil
  640. local vertices = attachment.vertices;
  641. local deformLength = #vertices
  642. if weighted then deformLength = math.floor(#vertices / 3) * 2 end
  643. local timeline = Animation.DeformTimeline.new(#timelineMap)
  644. timeline.slotIndex = slotIndex
  645. timeline.attachment = attachment
  646. local frameIndex = 0
  647. for _,valueMap in ipairs(timelineMap) do
  648. local deform = nil
  649. local verticesValue = getValue(valueMap, "vertices", nil)
  650. if verticesValue == nil then
  651. deform = vertices
  652. if weighted then deform = utils.newNumberArray(deformLength) end
  653. else
  654. deform = utils.newNumberArray(deformLength)
  655. local start = getValue(valueMap, "offset", 0) + 1
  656. utils.arrayCopy(verticesValue, 1, deform, start, #verticesValue)
  657. if scale ~= 1 then
  658. local i = start
  659. local n = i + #verticesValue
  660. while i < n do
  661. deform[i] = deform[i] * scale
  662. i = i + 1
  663. end
  664. end
  665. if not weighted then
  666. local i = 1
  667. local n = i + deformLength
  668. while i < n do
  669. deform[i] = deform[i] + vertices[i]
  670. i = i + 1
  671. end
  672. end
  673. end
  674. timeline:setFrame(frameIndex, valueMap.time, deform)
  675. readCurve(valueMap, timeline, frameIndex)
  676. frameIndex = frameIndex + 1
  677. end
  678. table_insert(timelines, timeline)
  679. duration = math.max(duration, timeline.frames[timeline:getFrameCount() - 1])
  680. end
  681. end
  682. end
  683. end
  684. -- Draworder timeline.
  685. local drawOrderValues = map["drawOrder"]
  686. if not drawOrderValues then drawOrderValues = map["draworder"] end
  687. if drawOrderValues then
  688. local timeline = Animation.DrawOrderTimeline.new(#drawOrderValues)
  689. local slotCount = #skeletonData.slots
  690. local frameIndex = 0
  691. for _,drawOrderMap in ipairs(drawOrderValues) do
  692. local drawOrder = nil
  693. local offsets = drawOrderMap["offsets"]
  694. if offsets then
  695. drawOrder = {}
  696. local unchanged = {}
  697. local originalIndex = 1
  698. local unchangedIndex = 1
  699. for _,offsetMap in ipairs(offsets) do
  700. local slotIndex = skeletonData:findSlotIndex(offsetMap["slot"])
  701. if slotIndex == -1 then error("Slot not found: " .. offsetMap["slot"]) end
  702. -- Collect unchanged items.
  703. while originalIndex ~= slotIndex do
  704. unchanged[unchangedIndex] = originalIndex
  705. unchangedIndex = unchangedIndex + 1
  706. originalIndex = originalIndex + 1
  707. end
  708. -- Set changed items.
  709. drawOrder[originalIndex + offsetMap["offset"]] = originalIndex
  710. originalIndex = originalIndex + 1
  711. end
  712. -- Collect remaining unchanged items.
  713. while originalIndex <= slotCount do
  714. unchanged[unchangedIndex] = originalIndex
  715. unchangedIndex = unchangedIndex + 1
  716. originalIndex = originalIndex + 1
  717. end
  718. -- Fill in unchanged items.
  719. for ii = slotCount, 1, -1 do
  720. if not drawOrder[ii] then
  721. unchangedIndex = unchangedIndex - 1
  722. drawOrder[ii] = unchanged[unchangedIndex]
  723. end
  724. end
  725. end
  726. timeline:setFrame(frameIndex, drawOrderMap["time"], drawOrder)
  727. frameIndex = frameIndex + 1
  728. end
  729. table_insert(timelines, timeline)
  730. duration = math.max(duration, timeline.frames[timeline:getFrameCount() - 1])
  731. end
  732. -- Event timeline.
  733. local events = map["events"]
  734. if events then
  735. local timeline = Animation.EventTimeline.new(#events)
  736. local frameIndex = 0
  737. for _,eventMap in ipairs(events) do
  738. local eventData = skeletonData:findEvent(eventMap["name"])
  739. if not eventData then error("Event not found: " .. eventMap["name"]) end
  740. local event = Event.new(eventMap["time"], eventData)
  741. if eventMap["int"] ~= nil then
  742. event.intValue = eventMap["int"]
  743. else
  744. event.intValue = eventData.intValue
  745. end
  746. if eventMap["float"] ~= nil then
  747. event.floatValue = eventMap["float"]
  748. else
  749. event.floatValue = eventData.floatValue
  750. end
  751. if eventMap["string"] ~= nil then
  752. event.stringValue = eventMap["string"]
  753. else
  754. event.stringValue = eventData.stringValue
  755. end
  756. if eventData.audioPath ~= nil then
  757. event.volume = getValue(eventMap, "volume", 1)
  758. event.balance = getValue(eventMap, "balance", 0)
  759. end
  760. timeline:setFrame(frameIndex, event)
  761. frameIndex = frameIndex + 1
  762. end
  763. table_insert(timelines, timeline)
  764. duration = math.max(duration, timeline.frames[timeline:getFrameCount() - 1])
  765. end
  766. table_insert(skeletonData.animations, Animation.new(name, timelines, duration))
  767. end
  768. readCurve = function (map, timeline, frameIndex)
  769. local curve = map["curve"]
  770. if not curve then return end
  771. if curve == "stepped" then
  772. timeline:setStepped(frameIndex)
  773. elseif #curve > 0 then
  774. timeline:setCurve(frameIndex, curve[1], curve[2], curve[3], curve[4])
  775. end
  776. end
  777. getArray = function (map, name, scale)
  778. local list = map[name]
  779. local values = {}
  780. if scale == 1 then
  781. for i = 1, #list do
  782. values[i] = list[i]
  783. end
  784. else
  785. for i = 1, #list do
  786. values[i] = list[i] * scale
  787. end
  788. end
  789. return values
  790. end
  791. return self
  792. end
  793. return SkeletonJson