Skeleton.lua 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  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. scaleX = 1, scaleY = 1,
  62. x = 0, y = 0
  63. }
  64. setmetatable(self, Skeleton)
  65. for _,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 _,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 _, ikConstraintData in ipairs(data.ikConstraints) do
  84. table_insert(self.ikConstraints, IkConstraint.new(ikConstraintData, self))
  85. end
  86. for _, transformConstraintData in ipairs(data.transformConstraints) do
  87. table_insert(self.transformConstraints, TransformConstraint.new(transformConstraintData, self))
  88. end
  89. for _, 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 _, 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 _, 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 _, 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(self.skin, slotIndex, slotBone) end
  179. if self.data.defaultSkin and not (self.data.defaultSkin == self.skin) then
  180. self:sortPathConstraintAttachment(self.data.defaultSkin, slotIndex, slotBone)
  181. end
  182. for _,skin in ipairs(self.data.skins) do
  183. self:sortPathConstraintAttachment(skin, slotIndex, slotBone)
  184. end
  185. local attachment = slot.attachment
  186. if attachment and attachment.type == AttachmentType.path then self:sortPathConstraintAttachmentWith(attachment, slotBone) end
  187. local constrained = constraint.bones
  188. for _,bone in ipairs(constrained) do
  189. self:sortBone(bone)
  190. end
  191. table_insert(self._updateCache, constraint)
  192. for _,bone in ipairs(constrained) do
  193. self:sortReset(bone.children)
  194. end
  195. for _,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 _,bone in ipairs(constrained) do
  204. local child = constrained[#constrained]
  205. local contains = false
  206. self:sortBone(child.parent)
  207. for _,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 _,bone in ipairs(constrained) do
  217. self:sortBone(bone)
  218. end
  219. end
  220. table_insert(self._updateCache, constraint)
  221. for _,bone in ipairs(constrained) do
  222. self:sortReset(bone.children)
  223. end
  224. for _,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 _,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 _, 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 _,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 _, 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 _,bone in ipairs(self.bones) do
  292. bone:setToSetupPose()
  293. end
  294. for _,ikConstraint in ipairs(self.ikConstraints) do
  295. ikConstraint.mix = ikConstraint.data.mix
  296. ikConstraint.bendDirection = ikConstraint.data.bendDirection
  297. ikConstraint.compress = ikConstraint.data.compress
  298. ikConstraint.stretch = ikConstraint.data.stretch
  299. end
  300. local transformConstraints = self.transformConstraints
  301. for _, constraint in ipairs(transformConstraints) do
  302. local data = constraint.data
  303. constraint.rotateMix = data.rotateMix
  304. constraint.translateMix = data.translateMix
  305. constraint.scaleMix = data.scaleMix
  306. constraint.shearMix = data.shearMix
  307. end
  308. local pathConstraints = self.pathConstraints
  309. for _, constraint in ipairs(pathConstraints) do
  310. local data = constraint.data
  311. constraint.position = data.position
  312. constraint.spacing = data.spacing
  313. constraint.rotateMix = data.rotateMix
  314. constraint.translateMix = data.translateMix
  315. end
  316. end
  317. function Skeleton:setSlotsToSetupPose ()
  318. for i,slot in ipairs(self.slots) do
  319. self.drawOrder[i] = slot
  320. slot:setToSetupPose()
  321. end
  322. end
  323. function Skeleton:getRootBone ()
  324. return self.bones[1]
  325. end
  326. function Skeleton:findBone (boneName)
  327. if not boneName then error("boneName cannot be nil.", 2) end
  328. for _,bone in ipairs(self.bones) do
  329. if bone.data.name == boneName then return bone end
  330. end
  331. return nil
  332. end
  333. function Skeleton:findBoneIndex(boneName)
  334. if not boneName then error("boneName cannot be nil.", 2) end
  335. for i,bone in ipairs(self.bones) do
  336. if bone.data.name == boneName then return i end
  337. end
  338. return -1
  339. end
  340. function Skeleton:findSlot (slotName)
  341. if not slotName then error("slotName cannot be nil.", 2) end
  342. return self.slotsByName[slotName]
  343. end
  344. function Skeleton:findSlotIndex(slotName)
  345. if not slotName then error("slotName cannot be nil.", 2) end
  346. for i, slot in ipairs(self.slots) do
  347. if slot.data.name == slotName then return i end
  348. end
  349. return -1
  350. end
  351. -- Sets the skin used to look up attachments before looking in the {@link SkeletonData#getDefaultSkin() default skin}.
  352. -- Attachments from the new skin are attached if the corresponding attachment from the old skin was attached. If there was
  353. -- no old skin, each slot's setup mode attachment is attached from the new skin.
  354. function Skeleton:setSkin (skinName)
  355. local skin = self.data:findSkin(skinName)
  356. if not skin then error("Skin not found: " .. skinName, 2) end
  357. self:setSkinByReference(skin)
  358. end
  359. function Skeleton:setSkinByReference(newSkin)
  360. if newSkin then
  361. if self.skin then
  362. newSkin:attachAll(self, self.skin)
  363. else
  364. local slots = self.slots
  365. for i, slot in ipairs(slots) do
  366. local name = slot.data.attachmentName
  367. if name then
  368. local attachment = newSkin:getAttachment(i, name)
  369. if attachment then
  370. slot:setAttachment(attachment)
  371. end
  372. end
  373. end
  374. end
  375. end
  376. self.skin = newSkin
  377. end
  378. function Skeleton:getAttachment (slotName, attachmentName)
  379. return self:getAttachmentByIndex(self.data.slotNameIndices[slotName], attachmentName)
  380. end
  381. function Skeleton:getAttachmentByIndex (slotIndex, attachmentName)
  382. if self.skin then
  383. local attachment = self.skin:getAttachment(slotIndex, attachmentName)
  384. if attachment then return attachment end
  385. end
  386. if self.data.defaultSkin then
  387. return self.data.defaultSkin:getAttachment(slotIndex, attachmentName)
  388. end
  389. return nil
  390. end
  391. function Skeleton:setAttachment (slotName, attachmentName)
  392. if not slotName then error("slotName cannot be nil.", 2) end
  393. for i,slot in ipairs(self.slots) do
  394. if slot.data.name == slotName then
  395. local attachment = nil
  396. if attachmentName then
  397. attachment = self:getAttachmentByIndex(i, attachmentName)
  398. if not attachment then error("Attachment not found: " .. attachmentName .. ", for slot: " .. slotName, 2) end
  399. end
  400. slot:setAttachment(attachment)
  401. return
  402. end
  403. end
  404. error("Slot not found: " .. slotName, 2)
  405. end
  406. function Skeleton:findIkConstraint(constraintName)
  407. if not constraintName then error("constraintName cannot be null.", 2) end
  408. local ikConstraints = self.ikConstraints
  409. for _, ikConstraint in ipairs(ikConstraints) do
  410. if ikConstraint.data.name == constraintName then return ikConstraint end
  411. end
  412. return nil
  413. end
  414. function Skeleton:findTransformConstraint(constraintName)
  415. if not constraintName then error("constraintName cannot be null.", 2) end
  416. local transformConstraints = self.transformConstraints
  417. for _, transformConstraint in ipairs(transformConstraints) do
  418. if transformConstraint.data.name == constraintName then return transformConstraint end
  419. end
  420. return nil
  421. end
  422. function Skeleton:findPathConstraint(constraintName)
  423. if not constraintName then error("constraintName cannot be null.", 2) end
  424. local pathConstraints = self.pathConstraints
  425. for _, pathConstraint in ipairs(pathConstraints) do
  426. if pathConstraint.data.name == constraintName then return pathConstraint end
  427. end
  428. return nil
  429. end
  430. function Skeleton:getBounds(offset, size)
  431. if not offset then error("offset cannot be null.", 2) end
  432. if not size then error("size cannot be null.", 2) end
  433. local drawOrder = self.drawOrder;
  434. local minX = 99999999
  435. local minY = 99999999
  436. local maxX = -99999999
  437. local maxY = -99999999
  438. for _, slot in ipairs(drawOrder) do
  439. local vertices = {}
  440. local attachment = slot.attachment
  441. if attachment then
  442. if attachment.type == AttachmentType.region then
  443. attachment:computeWorldVertices(slot.bone, vertices, 0, 2)
  444. elseif attachment.type == AttachmentType.mesh then
  445. attachment:computeWorldVertices(slot, 0, attachment.worldVerticesLength, vertices, 0, 2)
  446. end
  447. end
  448. if #vertices > 0 then
  449. local nn = #vertices
  450. local ii = 1
  451. while ii <= nn do
  452. local x = vertices[ii]
  453. local y = vertices[ii + 1]
  454. minX = math_min(minX, x)
  455. minY = math_min(minY, y)
  456. maxX = math_max(maxX, x)
  457. maxY = math_max(maxY, y)
  458. ii = ii + 2
  459. end
  460. end
  461. end
  462. offset[1] = minX
  463. offset[2] = minY
  464. size[1] = maxX - minX
  465. size[2] = maxY - minY
  466. end
  467. function Skeleton:update (delta)
  468. self.time = self.time + delta
  469. end
  470. function Skeleton:setColor (r, g, b, a)
  471. self.color.r = r
  472. self.color.g = g
  473. self.color.b = b
  474. self.color.a = a
  475. end
  476. return Skeleton