Skeleton.lua 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  1. -------------------------------------------------------------------------------
  2. -- Spine Runtimes Software License v2.5
  3. --
  4. -- Copyright (c) 2013-2016, Esoteric Software
  5. -- All rights reserved.
  6. --
  7. -- You are granted a perpetual, non-exclusive, non-sublicensable, and
  8. -- non-transferable license to use, install, execute, and perform the Spine
  9. -- Runtimes software and derivative works solely for personal or internal
  10. -- use. Without the written permission of Esoteric Software (see Section 2 of
  11. -- the Spine Software License Agreement), you may not (a) modify, translate,
  12. -- adapt, or develop new applications using the Spine Runtimes or otherwise
  13. -- create derivative works or improvements of the Spine Runtimes or (b) remove,
  14. -- delete, alter, or obscure any trademarks or any copyright, trademark, patent,
  15. -- or other intellectual property or proprietary rights notices on or in the
  16. -- Software, including any copy thereof. Redistributions in binary or source
  17. -- form must include this license and terms.
  18. --
  19. -- THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
  20. -- IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  21. -- MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
  22. -- EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  23. -- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  24. -- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
  25. -- USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
  26. -- IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  27. -- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  28. -- POSSIBILITY OF SUCH DAMAGE.
  29. -------------------------------------------------------------------------------
  30. local Bone = require "spine-lua.Bone"
  31. local Slot = require "spine-lua.Slot"
  32. local IkConstraint = require "spine-lua.IkConstraint"
  33. local PathConstraint = require "spine-lua.PathConstraint"
  34. local TransformConstraint = require "spine-lua.TransformConstraint"
  35. local AttachmentLoader = require "spine-lua.AttachmentLoader"
  36. local AttachmentType = require "spine-lua.attachments.AttachmentType"
  37. local Color = require "spine-lua.Color"
  38. local setmetatable = setmetatable
  39. local ipairs = ipairs
  40. local table_insert = table.insert
  41. local math_min = math.min
  42. local math_max = math.max
  43. local Skeleton = {}
  44. Skeleton.__index = Skeleton
  45. function Skeleton.new (data)
  46. if not data then error("data cannot be nil", 2) end
  47. local self = {
  48. data = data,
  49. bones = {},
  50. slots = {},
  51. slotsByName = {},
  52. drawOrder = {},
  53. ikConstraints = {},
  54. transformConstraints = {},
  55. pathConstraints = {},
  56. _updateCache = {},
  57. updateCacheReset = {},
  58. skin = nil,
  59. color = Color.newWith(1, 1, 1, 1),
  60. time = 0,
  61. flipX = false, flipY = false,
  62. x = 0, y = 0
  63. }
  64. setmetatable(self, Skeleton)
  65. for i,boneData in ipairs(data.bones) do
  66. local bone = nil
  67. if boneData.parent == nil then
  68. bone = Bone.new(boneData, self, nil)
  69. else
  70. local parent = self.bones[boneData.parent.index]
  71. bone = Bone.new(boneData, self, parent)
  72. table_insert(parent.children, bone)
  73. end
  74. table_insert(self.bones, bone)
  75. end
  76. for i,slotData in ipairs(data.slots) do
  77. local bone = self.bones[slotData.boneData.index]
  78. local slot = Slot.new(slotData, bone)
  79. table_insert(self.slots, slot)
  80. self.slotsByName[slot.data.name] = slot
  81. table_insert(self.drawOrder, slot)
  82. end
  83. for i,ikConstraintData in ipairs(data.ikConstraints) do
  84. table_insert(self.ikConstraints, IkConstraint.new(ikConstraintData, self))
  85. end
  86. for i, transformConstraintData in ipairs(data.transformConstraints) do
  87. table_insert(self.transformConstraints, TransformConstraint.new(transformConstraintData, self))
  88. end
  89. for i, pathConstraintData in ipairs(data.pathConstraints) do
  90. table_insert(self.pathConstraints, PathConstraint.new(pathConstraintData, self))
  91. end
  92. self:updateCache()
  93. return self
  94. end
  95. -- Caches information about bones and IK constraints. Must be called if bones or IK constraints are added or removed.
  96. function Skeleton:updateCache ()
  97. local updateCache = {}
  98. self._updateCache = updateCache
  99. self.updateCacheReset = {}
  100. local bones = self.bones
  101. for i, bone in ipairs(bones) do
  102. bone.sorted = false
  103. end
  104. local ikConstraints = self.ikConstraints
  105. local transformConstraints = self.transformConstraints
  106. local pathConstraints = self.pathConstraints
  107. local ikCount = #ikConstraints
  108. local transformCount = #transformConstraints
  109. local pathCount = #pathConstraints
  110. local constraintCount = ikCount + transformCount + pathCount
  111. local i = 0
  112. while i < constraintCount do
  113. local found = false
  114. local ii = 1
  115. while ii <= ikCount do
  116. local constraint = ikConstraints[ii]
  117. if constraint.data.order == i then
  118. self:sortIkConstraint(constraint)
  119. found = true
  120. break
  121. end
  122. ii = ii + 1
  123. end
  124. if not found then
  125. ii = 1
  126. while ii <= transformCount do
  127. local constraint = transformConstraints[ii]
  128. if constraint.data.order == i then
  129. self:sortTransformConstraint(constraint)
  130. found = true
  131. break
  132. end
  133. ii = ii + 1
  134. end
  135. end
  136. if not found then
  137. ii = 1
  138. while ii <= pathCount do
  139. local constraint = pathConstraints[ii]
  140. if constraint.data.order == i then
  141. self:sortPathConstraint(constraint)
  142. break
  143. end
  144. ii = ii + 1
  145. end
  146. end
  147. i = i + 1
  148. end
  149. for i, bone in ipairs(self.bones) do
  150. self:sortBone(bone)
  151. end
  152. end
  153. function Skeleton:sortIkConstraint (constraint)
  154. local target = constraint.target
  155. self:sortBone(target)
  156. local constrained = constraint.bones
  157. local parent = constrained[1]
  158. self:sortBone(parent)
  159. if #constrained > 1 then
  160. local child = constrained[#constrained]
  161. local contains = false
  162. for i,updatable in ipairs(self._updateCache) do
  163. if updatable == child then
  164. contains = true
  165. break
  166. end
  167. end
  168. if not contains then table_insert(self.updateCacheReset, child) end
  169. end
  170. table_insert(self._updateCache, constraint)
  171. self:sortReset(parent.children)
  172. constrained[#constrained].sorted = true
  173. end
  174. function Skeleton:sortPathConstraint(constraint)
  175. local slot = constraint.target
  176. local slotIndex = slot.data.index
  177. local slotBone = slot.bone
  178. if self.skin then self:sortPathConstraintAttachment(skin, slotIndex, slotBone) end
  179. if self.data.defaultSkin and not (self.data.defaultSkin == skin) then
  180. self:sortPathConstraintAttachment(self.data.defaultSkin, slotIndex, slotBone)
  181. end
  182. for i,skin in ipairs(self.data.skins) do
  183. self:sortPathConstraintAttachment(skin, slotIndex, slotBone)
  184. end
  185. local attachment = slot.attachment
  186. if attachment.type == AttachmentType.path then self:sortPathConstraintAttachmentWith(attachment, slotBone) end
  187. local constrained = constraint.bones
  188. for i,bone in ipairs(constrained) do
  189. self:sortBone(bone)
  190. end
  191. table_insert(self._updateCache, constraint)
  192. for i,bone in ipairs(constrained) do
  193. self:sortReset(bone.children)
  194. end
  195. for i,bone in ipairs(constrained) do
  196. bone.sorted = true
  197. end
  198. end
  199. function Skeleton:sortTransformConstraint(constraint)
  200. self:sortBone(constraint.target)
  201. local constrained = constraint.bones
  202. if constraint.data.local_ then
  203. for i,bone in ipairs(constrained) do
  204. local child = constrained[#constrained]
  205. local contains = false
  206. sortBone(child.parent)
  207. for i,updatable in ipairs(self._updateCache) do
  208. if updatable == child then
  209. contains = true
  210. break
  211. end
  212. end
  213. if not contains then table_insert(self.updateCacheReset, child) end
  214. end
  215. else
  216. for i,bone in ipairs(constrained) do
  217. self:sortBone(bone)
  218. end
  219. end
  220. table_insert(self._updateCache, constraint)
  221. for i,bone in ipairs(constrained) do
  222. self:sortReset(bone.children)
  223. end
  224. for i,bone in ipairs(constrained) do
  225. bone.sorted = true
  226. end
  227. end
  228. function Skeleton:sortPathConstraintAttachment(skin, slotIndex, slotBone)
  229. local attachments = skin.attachments[slotIndex]
  230. if not attachments then return end
  231. for key,attachment in pairs(attachments) do
  232. self:sortPathConstraintAttachmentWith(attachment, slotBone)
  233. end
  234. end
  235. function Skeleton:sortPathConstraintAttachmentWith(attachment, slotBone)
  236. if attachment.type ~= AttachmentType.path then return end
  237. local pathBones = attachment.bones
  238. if not pathBones then
  239. self:sortBone(slotBone)
  240. else
  241. local bones = self.bones
  242. local i = 0
  243. local n = #pathBones
  244. while i < n do
  245. local boneCount = pathBones[i + 1]
  246. i = i + 1
  247. local nn = i + boneCount
  248. while i < nn do
  249. self:sortBone(bones[pathBones[i + 1]])
  250. i = i + 1
  251. end
  252. end
  253. end
  254. end
  255. function Skeleton:sortBone(bone)
  256. if bone.sorted then return end
  257. local parent = bone.parent
  258. if parent then self:sortBone(parent) end
  259. bone.sorted = true
  260. table_insert(self._updateCache, bone)
  261. end
  262. function Skeleton:sortReset(bones)
  263. for i, bone in ipairs(bones) do
  264. if bone.sorted then self:sortReset(bone.children) end
  265. bone.sorted = false
  266. end
  267. end
  268. -- Updates the world transform for each bone and applies IK constraints.
  269. function Skeleton:updateWorldTransform ()
  270. local updateCacheReset = self.updateCacheReset
  271. for i,bone in ipairs(updateCacheReset) do
  272. bone.ax = bone.x
  273. bone.ay = bone.y
  274. bone.arotation = bone.rotation
  275. bone.ascaleX = bone.scaleX
  276. bone.ascaleY = bone.scaleY
  277. bone.ashearX = bone.shearX
  278. bone.ashearY = bone.shearY
  279. bone.appliedValid = true
  280. end
  281. local updateCache = self._updateCache
  282. for i, updatable in ipairs(updateCache) do
  283. updatable:update()
  284. end
  285. end
  286. function Skeleton:setToSetupPose ()
  287. self:setBonesToSetupPose()
  288. self:setSlotsToSetupPose()
  289. end
  290. function Skeleton:setBonesToSetupPose ()
  291. for i,bone in ipairs(self.bones) do
  292. bone:setToSetupPose()
  293. end
  294. for i,ikConstraint in ipairs(self.ikConstraints) do
  295. ikConstraint.bendDirection = ikConstraint.data.bendDirection
  296. ikConstraint.mix = ikConstraint.data.mix
  297. end
  298. local transformConstraints = self.transformConstraints
  299. for i, constraint in ipairs(transformConstraints) do
  300. local data = constraint.data
  301. constraint.rotateMix = data.rotateMix
  302. constraint.translateMix = data.translateMix
  303. constraint.scaleMix = data.scaleMix
  304. constraint.shearMix = data.shearMix
  305. end
  306. local pathConstraints = self.pathConstraints
  307. for i, constraint in ipairs(pathConstraints) do
  308. local data = constraint.data
  309. constraint.position = data.position
  310. constraint.spacing = data.spacing
  311. constraint.rotateMix = data.rotateMix
  312. constraint.translateMix = data.translateMix
  313. end
  314. end
  315. function Skeleton:setSlotsToSetupPose ()
  316. for i,slot in ipairs(self.slots) do
  317. self.drawOrder[i] = slot
  318. slot:setToSetupPose()
  319. end
  320. end
  321. function Skeleton:getRootBone ()
  322. return self.bones[1]
  323. end
  324. function Skeleton:findBone (boneName)
  325. if not boneName then error("boneName cannot be nil.", 2) end
  326. for i,bone in ipairs(self.bones) do
  327. if bone.data.name == boneName then return bone end
  328. end
  329. return nil
  330. end
  331. function Skeleton:findBoneIndex(boneName)
  332. if not boneName then error("boneName cannot be nil.", 2) end
  333. for i,bone in ipairs(self.bones) do
  334. if bone.data.name == boneName then return i end
  335. end
  336. return -1
  337. end
  338. function Skeleton:findSlot (slotName)
  339. if not slotName then error("slotName cannot be nil.", 2) end
  340. return self.slotsByName[slotName]
  341. end
  342. function Skeleton:findSlotIndex(slotName)
  343. if not slotName then error("slotName cannot be nil.", 2) end
  344. for i, slot in ipairs(self.slots) do
  345. if slot.data.name == slotName then return i end
  346. end
  347. return -1
  348. end
  349. -- Sets the skin used to look up attachments before looking in the {@link SkeletonData#getDefaultSkin() default skin}.
  350. -- Attachments from the new skin are attached if the corresponding attachment from the old skin was attached. If there was
  351. -- no old skin, each slot's setup mode attachment is attached from the new skin.
  352. function Skeleton:setSkin (skinName)
  353. local skin = self.data:findSkin(skinName)
  354. if not skin then error("Skin not found: " .. skinName, 2) end
  355. self:setSkinByReference(skin)
  356. end
  357. function Skeleton:setSkinByReference(newSkin)
  358. if newSkin then
  359. if self.skin then
  360. newSkin:attachAll(self, self.skin)
  361. else
  362. local slots = self.slots
  363. for i, slot in ipairs(slots) do
  364. local name = slot.data.attachmentName
  365. if name then
  366. local attachment = newSkin:getAttachment(i, name)
  367. if attachment then
  368. slot:setAttachment(attachment)
  369. end
  370. end
  371. end
  372. end
  373. end
  374. self.skin = newSkin
  375. end
  376. function Skeleton:getAttachment (slotName, attachmentName)
  377. return self:getAttachmentByIndex(self.data.slotNameIndices[slotName], attachmentName)
  378. end
  379. function Skeleton:getAttachmentByIndex (slotIndex, attachmentName)
  380. if self.skin then
  381. local attachment = self.skin:getAttachment(slotIndex, attachmentName)
  382. if attachment then return attachment end
  383. end
  384. if self.data.defaultSkin then
  385. return self.data.defaultSkin:getAttachment(slotIndex, attachmentName)
  386. end
  387. return nil
  388. end
  389. function Skeleton:setAttachment (slotName, attachmentName)
  390. if not slotName then error("slotName cannot be nil.", 2) end
  391. for i,slot in ipairs(self.slots) do
  392. if slot.data.name == slotName then
  393. local attachment = nil
  394. if attachmentName then
  395. attachment = self:getAttachmentByIndex(i, attachmentName)
  396. if not attachment then error("Attachment not found: " .. attachmentName .. ", for slot: " .. slotName, 2) end
  397. end
  398. slot:setAttachment(attachment)
  399. return
  400. end
  401. end
  402. error("Slot not found: " .. slotName, 2)
  403. end
  404. function Skeleton:findIkConstraint(constraintName)
  405. if not constraintName then error("constraintName cannot be null.", 2) end
  406. local ikConstaints = self.ikConstraints
  407. for i, ikConstraint in ipairs(ikConstraints) do
  408. if ikConstraint.data.name == constraintName then return ikConstraint end
  409. end
  410. return nil
  411. end
  412. function Skeleton:findTransformConstraint(constraintName)
  413. if not constraintName then error("constraintName cannot be null.", 2) end
  414. local transformConstraints = self.transformConstraints
  415. for i, transformConstraint in ipairs(transformConstraints) do
  416. if transformConstraint.data.name == constraintName then return transformConstraint end
  417. end
  418. return nil
  419. end
  420. function Skeleton:findPathConstraint(constraintName)
  421. if not constraintName then error("constraintName cannot be null.", 2) end
  422. local pathConstraints = self.pathConstraints
  423. for i, pathConstraint in ipairs(pathConstraints) do
  424. if pathConstraint.data.name == constraintName then return pathConstraint end
  425. end
  426. return nil
  427. end
  428. function Skeleton:getBounds(offset, size)
  429. if not offset then error("offset cannot be null.", 2) end
  430. if not size then error("size cannot be null.", 2) end
  431. local drawOrder = self.drawOrder;
  432. local minX = 99999999
  433. local minY = 99999999
  434. local maxX = -99999999
  435. local maxY = -99999999
  436. for i, slot in ipairs(drawOrder) do
  437. local vertices = {}
  438. local attachment = slot.attachment
  439. if attachment then
  440. if attachment.type == AttachmentType.region then
  441. attachment:computeWorldVertices(slot.bone, vertices, 0, 2)
  442. elseif attachment.type == AttachmentType.mesh then
  443. attachment:computeWorldVertices(slot, 0, attachment.worldVerticesLength, vertices, 0, 2)
  444. end
  445. end
  446. if #vertices > 0 then
  447. local nn = #vertices
  448. local ii = 1
  449. while ii <= nn do
  450. local x = vertices[ii]
  451. local y = vertices[ii + 1]
  452. minX = math_min(minX, x)
  453. minY = math_min(minY, y)
  454. maxX = math_max(maxX, x)
  455. maxY = math_max(maxY, y)
  456. ii = ii + 2
  457. end
  458. end
  459. end
  460. offset[1] = minX
  461. offset[2] = minY
  462. size[1] = maxX - minX
  463. size[2] = maxY - minY
  464. end
  465. function Skeleton:update (delta)
  466. self.time = self.time + delta
  467. end
  468. function Skeleton:setColor (r, g, b, a)
  469. self.color.r = r
  470. self.color.g = g
  471. self.color.b = b
  472. self.color.a = a
  473. end
  474. return Skeleton