Skeleton.lua 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541
  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 Bone = require "spine-lua.Bone"
  30. local Slot = require "spine-lua.Slot"
  31. local IkConstraint = require "spine-lua.IkConstraint"
  32. local PathConstraint = require "spine-lua.PathConstraint"
  33. local TransformConstraint = require "spine-lua.TransformConstraint"
  34. local AttachmentLoader = require "spine-lua.AttachmentLoader"
  35. local AttachmentType = require "spine-lua.attachments.AttachmentType"
  36. local Color = require "spine-lua.Color"
  37. local setmetatable = setmetatable
  38. local ipairs = ipairs
  39. local table_insert = table.insert
  40. local math_min = math.min
  41. local math_max = math.max
  42. local Skeleton = {}
  43. Skeleton.__index = Skeleton
  44. function Skeleton.new (data)
  45. if not data then error("data cannot be nil", 2) end
  46. local self = {
  47. data = data,
  48. bones = {},
  49. slots = {},
  50. slotsByName = {},
  51. drawOrder = {},
  52. ikConstraints = {},
  53. transformConstraints = {},
  54. pathConstraints = {},
  55. _updateCache = {},
  56. updateCacheReset = {},
  57. skin = nil,
  58. color = Color.newWith(1, 1, 1, 1),
  59. time = 0,
  60. scaleX = 1, scaleY = 1,
  61. x = 0, y = 0
  62. }
  63. setmetatable(self, Skeleton)
  64. for _,boneData in ipairs(data.bones) do
  65. local bone = nil
  66. if boneData.parent == nil then
  67. bone = Bone.new(boneData, self, nil)
  68. else
  69. local parent = self.bones[boneData.parent.index]
  70. bone = Bone.new(boneData, self, parent)
  71. table_insert(parent.children, bone)
  72. end
  73. table_insert(self.bones, bone)
  74. end
  75. for _,slotData in ipairs(data.slots) do
  76. local bone = self.bones[slotData.boneData.index]
  77. local slot = Slot.new(slotData, bone)
  78. table_insert(self.slots, slot)
  79. self.slotsByName[slot.data.name] = slot
  80. table_insert(self.drawOrder, slot)
  81. end
  82. for _, ikConstraintData in ipairs(data.ikConstraints) do
  83. table_insert(self.ikConstraints, IkConstraint.new(ikConstraintData, self))
  84. end
  85. for _, transformConstraintData in ipairs(data.transformConstraints) do
  86. table_insert(self.transformConstraints, TransformConstraint.new(transformConstraintData, self))
  87. end
  88. for _, pathConstraintData in ipairs(data.pathConstraints) do
  89. table_insert(self.pathConstraints, PathConstraint.new(pathConstraintData, self))
  90. end
  91. self:updateCache()
  92. return self
  93. end
  94. -- Caches information about bones and IK constraints. Must be called if bones or IK constraints are added or removed.
  95. function Skeleton:updateCache ()
  96. local updateCache = {}
  97. self._updateCache = updateCache
  98. self.updateCacheReset = {}
  99. local bones = self.bones
  100. for _, bone in ipairs(bones) do
  101. bone.sorted = false
  102. end
  103. local ikConstraints = self.ikConstraints
  104. local transformConstraints = self.transformConstraints
  105. local pathConstraints = self.pathConstraints
  106. local ikCount = #ikConstraints
  107. local transformCount = #transformConstraints
  108. local pathCount = #pathConstraints
  109. local constraintCount = ikCount + transformCount + pathCount
  110. local i = 0
  111. while i < constraintCount do
  112. local found = false
  113. local ii = 1
  114. while ii <= ikCount do
  115. local constraint = ikConstraints[ii]
  116. if constraint.data.order == i then
  117. self:sortIkConstraint(constraint)
  118. found = true
  119. break
  120. end
  121. ii = ii + 1
  122. end
  123. if not found then
  124. ii = 1
  125. while ii <= transformCount do
  126. local constraint = transformConstraints[ii]
  127. if constraint.data.order == i then
  128. self:sortTransformConstraint(constraint)
  129. found = true
  130. break
  131. end
  132. ii = ii + 1
  133. end
  134. end
  135. if not found then
  136. ii = 1
  137. while ii <= pathCount do
  138. local constraint = pathConstraints[ii]
  139. if constraint.data.order == i then
  140. self:sortPathConstraint(constraint)
  141. break
  142. end
  143. ii = ii + 1
  144. end
  145. end
  146. i = i + 1
  147. end
  148. for _, bone in ipairs(self.bones) do
  149. self:sortBone(bone)
  150. end
  151. end
  152. function Skeleton:sortIkConstraint (constraint)
  153. local target = constraint.target
  154. self:sortBone(target)
  155. local constrained = constraint.bones
  156. local parent = constrained[1]
  157. self:sortBone(parent)
  158. if #constrained > 1 then
  159. local child = constrained[#constrained]
  160. local contains = false
  161. for _, updatable in ipairs(self._updateCache) do
  162. if updatable == child then
  163. contains = true
  164. break
  165. end
  166. end
  167. if not contains then table_insert(self.updateCacheReset, child) end
  168. end
  169. table_insert(self._updateCache, constraint)
  170. self:sortReset(parent.children)
  171. constrained[#constrained].sorted = true
  172. end
  173. function Skeleton:sortPathConstraint(constraint)
  174. local slot = constraint.target
  175. local slotIndex = slot.data.index
  176. local slotBone = slot.bone
  177. if self.skin then self:sortPathConstraintAttachment(self.skin, slotIndex, slotBone) end
  178. if self.data.defaultSkin and not (self.data.defaultSkin == self.skin) then
  179. self:sortPathConstraintAttachment(self.data.defaultSkin, slotIndex, slotBone)
  180. end
  181. for _,skin in ipairs(self.data.skins) do
  182. self:sortPathConstraintAttachment(skin, slotIndex, slotBone)
  183. end
  184. local attachment = slot.attachment
  185. if attachment and attachment.type == AttachmentType.path then self:sortPathConstraintAttachmentWith(attachment, slotBone) end
  186. local constrained = constraint.bones
  187. for _,bone in ipairs(constrained) do
  188. self:sortBone(bone)
  189. end
  190. table_insert(self._updateCache, constraint)
  191. for _,bone in ipairs(constrained) do
  192. self:sortReset(bone.children)
  193. end
  194. for _,bone in ipairs(constrained) do
  195. bone.sorted = true
  196. end
  197. end
  198. function Skeleton:sortTransformConstraint(constraint)
  199. self:sortBone(constraint.target)
  200. local constrained = constraint.bones
  201. if constraint.data.local_ then
  202. for _,bone in ipairs(constrained) do
  203. local child = constrained[#constrained]
  204. local contains = false
  205. self:sortBone(child.parent)
  206. for _,updatable in ipairs(self._updateCache) do
  207. if updatable == child then
  208. contains = true
  209. break
  210. end
  211. end
  212. if not contains then table_insert(self.updateCacheReset, child) end
  213. end
  214. else
  215. for _,bone in ipairs(constrained) do
  216. self:sortBone(bone)
  217. end
  218. end
  219. table_insert(self._updateCache, constraint)
  220. for _,bone in ipairs(constrained) do
  221. self:sortReset(bone.children)
  222. end
  223. for _,bone in ipairs(constrained) do
  224. bone.sorted = true
  225. end
  226. end
  227. function Skeleton:sortPathConstraintAttachment(skin, slotIndex, slotBone)
  228. local attachments = skin.attachments[slotIndex]
  229. if not attachments then return end
  230. for _,attachment in pairs(attachments) do
  231. self:sortPathConstraintAttachmentWith(attachment, slotBone)
  232. end
  233. end
  234. function Skeleton:sortPathConstraintAttachmentWith(attachment, slotBone)
  235. if attachment.type ~= AttachmentType.path then return end
  236. local pathBones = attachment.bones
  237. if not pathBones then
  238. self:sortBone(slotBone)
  239. else
  240. local bones = self.bones
  241. local i = 0
  242. local n = #pathBones
  243. while i < n do
  244. local boneCount = pathBones[i + 1]
  245. i = i + 1
  246. local nn = i + boneCount
  247. while i < nn do
  248. self:sortBone(bones[pathBones[i + 1]])
  249. i = i + 1
  250. end
  251. end
  252. end
  253. end
  254. function Skeleton:sortBone(bone)
  255. if bone.sorted then return end
  256. local parent = bone.parent
  257. if parent then self:sortBone(parent) end
  258. bone.sorted = true
  259. table_insert(self._updateCache, bone)
  260. end
  261. function Skeleton:sortReset(bones)
  262. for _, bone in ipairs(bones) do
  263. if bone.sorted then self:sortReset(bone.children) end
  264. bone.sorted = false
  265. end
  266. end
  267. -- Updates the world transform for each bone and applies IK constraints.
  268. function Skeleton:updateWorldTransform ()
  269. local updateCacheReset = self.updateCacheReset
  270. for _,bone in ipairs(updateCacheReset) do
  271. bone.ax = bone.x
  272. bone.ay = bone.y
  273. bone.arotation = bone.rotation
  274. bone.ascaleX = bone.scaleX
  275. bone.ascaleY = bone.scaleY
  276. bone.ashearX = bone.shearX
  277. bone.ashearY = bone.shearY
  278. bone.appliedValid = true
  279. end
  280. local updateCache = self._updateCache
  281. for _, updatable in ipairs(updateCache) do
  282. updatable:update()
  283. end
  284. end
  285. function Skeleton:setToSetupPose ()
  286. self:setBonesToSetupPose()
  287. self:setSlotsToSetupPose()
  288. end
  289. function Skeleton:setBonesToSetupPose ()
  290. for _,bone in ipairs(self.bones) do
  291. bone:setToSetupPose()
  292. end
  293. for _,ikConstraint in ipairs(self.ikConstraints) do
  294. ikConstraint.mix = ikConstraint.data.mix
  295. ikConstraint.bendDirection = ikConstraint.data.bendDirection
  296. ikConstraint.compress = ikConstraint.data.compress
  297. ikConstraint.stretch = ikConstraint.data.stretch
  298. end
  299. local transformConstraints = self.transformConstraints
  300. for _, constraint in ipairs(transformConstraints) do
  301. local data = constraint.data
  302. constraint.rotateMix = data.rotateMix
  303. constraint.translateMix = data.translateMix
  304. constraint.scaleMix = data.scaleMix
  305. constraint.shearMix = data.shearMix
  306. end
  307. local pathConstraints = self.pathConstraints
  308. for _, constraint in ipairs(pathConstraints) do
  309. local data = constraint.data
  310. constraint.position = data.position
  311. constraint.spacing = data.spacing
  312. constraint.rotateMix = data.rotateMix
  313. constraint.translateMix = data.translateMix
  314. end
  315. end
  316. function Skeleton:setSlotsToSetupPose ()
  317. for i,slot in ipairs(self.slots) do
  318. self.drawOrder[i] = slot
  319. slot:setToSetupPose()
  320. end
  321. end
  322. function Skeleton:getRootBone ()
  323. return self.bones[1]
  324. end
  325. function Skeleton:findBone (boneName)
  326. if not boneName then error("boneName cannot be nil.", 2) end
  327. for _,bone in ipairs(self.bones) do
  328. if bone.data.name == boneName then return bone end
  329. end
  330. return nil
  331. end
  332. function Skeleton:findBoneIndex(boneName)
  333. if not boneName then error("boneName cannot be nil.", 2) end
  334. for i,bone in ipairs(self.bones) do
  335. if bone.data.name == boneName then return i end
  336. end
  337. return -1
  338. end
  339. function Skeleton:findSlot (slotName)
  340. if not slotName then error("slotName cannot be nil.", 2) end
  341. return self.slotsByName[slotName]
  342. end
  343. function Skeleton:findSlotIndex(slotName)
  344. if not slotName then error("slotName cannot be nil.", 2) end
  345. for i, slot in ipairs(self.slots) do
  346. if slot.data.name == slotName then return i end
  347. end
  348. return -1
  349. end
  350. -- Sets the skin used to look up attachments before looking in the {@link SkeletonData#getDefaultSkin() default skin}.
  351. -- Attachments from the new skin are attached if the corresponding attachment from the old skin was attached. If there was
  352. -- no old skin, each slot's setup mode attachment is attached from the new skin.
  353. function Skeleton:setSkin (skinName)
  354. local skin = self.data:findSkin(skinName)
  355. if not skin then error("Skin not found: " .. skinName, 2) end
  356. self:setSkinByReference(skin)
  357. end
  358. function Skeleton:setSkinByReference(newSkin)
  359. if newSkin then
  360. if self.skin then
  361. newSkin:attachAll(self, self.skin)
  362. else
  363. local slots = self.slots
  364. for i, slot in ipairs(slots) do
  365. local name = slot.data.attachmentName
  366. if name then
  367. local attachment = newSkin:getAttachment(i, name)
  368. if attachment then
  369. slot:setAttachment(attachment)
  370. end
  371. end
  372. end
  373. end
  374. end
  375. self.skin = newSkin
  376. end
  377. function Skeleton:getAttachment (slotName, attachmentName)
  378. return self:getAttachmentByIndex(self.data.slotNameIndices[slotName], attachmentName)
  379. end
  380. function Skeleton:getAttachmentByIndex (slotIndex, attachmentName)
  381. if self.skin then
  382. local attachment = self.skin:getAttachment(slotIndex, attachmentName)
  383. if attachment then return attachment end
  384. end
  385. if self.data.defaultSkin then
  386. return self.data.defaultSkin:getAttachment(slotIndex, attachmentName)
  387. end
  388. return nil
  389. end
  390. function Skeleton:setAttachment (slotName, attachmentName)
  391. if not slotName then error("slotName cannot be nil.", 2) end
  392. for i,slot in ipairs(self.slots) do
  393. if slot.data.name == slotName then
  394. local attachment = nil
  395. if attachmentName then
  396. attachment = self:getAttachmentByIndex(i, attachmentName)
  397. if not attachment then error("Attachment not found: " .. attachmentName .. ", for slot: " .. slotName, 2) end
  398. end
  399. slot:setAttachment(attachment)
  400. return
  401. end
  402. end
  403. error("Slot not found: " .. slotName, 2)
  404. end
  405. function Skeleton:findIkConstraint(constraintName)
  406. if not constraintName then error("constraintName cannot be null.", 2) end
  407. local ikConstraints = self.ikConstraints
  408. for _, ikConstraint in ipairs(ikConstraints) do
  409. if ikConstraint.data.name == constraintName then return ikConstraint end
  410. end
  411. return nil
  412. end
  413. function Skeleton:findTransformConstraint(constraintName)
  414. if not constraintName then error("constraintName cannot be null.", 2) end
  415. local transformConstraints = self.transformConstraints
  416. for _, transformConstraint in ipairs(transformConstraints) do
  417. if transformConstraint.data.name == constraintName then return transformConstraint end
  418. end
  419. return nil
  420. end
  421. function Skeleton:findPathConstraint(constraintName)
  422. if not constraintName then error("constraintName cannot be null.", 2) end
  423. local pathConstraints = self.pathConstraints
  424. for _, pathConstraint in ipairs(pathConstraints) do
  425. if pathConstraint.data.name == constraintName then return pathConstraint end
  426. end
  427. return nil
  428. end
  429. function Skeleton:getBounds(offset, size)
  430. if not offset then error("offset cannot be null.", 2) end
  431. if not size then error("size cannot be null.", 2) end
  432. local drawOrder = self.drawOrder;
  433. local minX = 99999999
  434. local minY = 99999999
  435. local maxX = -99999999
  436. local maxY = -99999999
  437. for _, slot in ipairs(drawOrder) do
  438. local vertices = {}
  439. local attachment = slot.attachment
  440. if attachment then
  441. if attachment.type == AttachmentType.region then
  442. attachment:computeWorldVertices(slot.bone, vertices, 0, 2)
  443. elseif attachment.type == AttachmentType.mesh then
  444. attachment:computeWorldVertices(slot, 0, attachment.worldVerticesLength, vertices, 0, 2)
  445. end
  446. end
  447. if #vertices > 0 then
  448. local nn = #vertices
  449. local ii = 1
  450. while ii <= nn do
  451. local x = vertices[ii]
  452. local y = vertices[ii + 1]
  453. minX = math_min(minX, x)
  454. minY = math_min(minY, y)
  455. maxX = math_max(maxX, x)
  456. maxY = math_max(maxY, y)
  457. ii = ii + 2
  458. end
  459. end
  460. end
  461. offset[1] = minX
  462. offset[2] = minY
  463. size[1] = maxX - minX
  464. size[2] = maxY - minY
  465. end
  466. function Skeleton:update (delta)
  467. self.time = self.time + delta
  468. end
  469. function Skeleton:setColor (r, g, b, a)
  470. self.color.r = r
  471. self.color.g = g
  472. self.color.b = b
  473. self.color.a = a
  474. end
  475. return Skeleton