SkeletonClipping.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. -------------------------------------------------------------------------------
  2. -- Spine Runtimes License Agreement
  3. -- Last updated January 1, 2020. Replaces all prior versions.
  4. --
  5. -- Copyright (c) 2013-2020, 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. -- THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
  19. -- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20. -- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21. -- DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
  22. -- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  23. -- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
  24. -- BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
  25. -- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. -- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  27. -- THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. -------------------------------------------------------------------------------
  29. local utils = require "spine-lua.utils"
  30. local Triangulator = require "spine-lua.Triangulator"
  31. local setmetatable = setmetatable
  32. local math_min = math.min
  33. local math_max = math.max
  34. local math_abs = math.abs
  35. local ipairs = ipairs
  36. local table_insert = table.insert
  37. local table_remove = table.remove
  38. local SkeletonClipping = {}
  39. SkeletonClipping.__index = SkeletonClipping
  40. function SkeletonClipping.new ()
  41. local self = {
  42. triangulator = Triangulator.new(),
  43. clippingPolygon = {},
  44. clipOutput = {},
  45. clippedVertices = {},
  46. clippedUVs = {},
  47. clippedTriangles = {},
  48. clipAttachment = nil
  49. }
  50. setmetatable(self, SkeletonClipping)
  51. return self
  52. end
  53. function SkeletonClipping:clipStart(slot, clip)
  54. if self.clipAttachment then return 0 end
  55. self.clipAttachment = clip
  56. local n = clip.worldVerticesLength
  57. self.clippingPolygon = {}
  58. local vertices = self.clippingPolygon
  59. clip:computeWorldVertices(slot, 0, n, vertices, 0, 2)
  60. self:makeClockwise(self.clippingPolygon)
  61. self.clippingPolygons = self.triangulator:decompose(self.clippingPolygon, self.triangulator:triangulate(self.clippingPolygon))
  62. for _,polygon in ipairs(self.clippingPolygons) do
  63. self:makeClockwise(polygon)
  64. table_insert(polygon, polygon[1])
  65. table_insert(polygon, polygon[2])
  66. end
  67. return #self.clippingPolygons
  68. end
  69. function SkeletonClipping:clipEnd(slot)
  70. if self.clipAttachment and self.clipAttachment.endSlot == slot.data then self:clipEnd2() end
  71. end
  72. function SkeletonClipping:clipEnd2()
  73. if self.clipAttachment == nil then return end
  74. self.clipAttachment = nil
  75. self.clippingPolygons = nil
  76. self.clippedVertices = {}
  77. self.clippedUVs = {}
  78. self.clippedTriangles = {}
  79. self.clippingPolygon = {}
  80. end
  81. function SkeletonClipping:isClipping()
  82. return self.clipAttachment ~= nil
  83. end
  84. function SkeletonClipping:clipTriangles(vertices, uvs, triangles, trianglesLength)
  85. self.clippedVertices = {}
  86. self.clippedUVs = {}
  87. self.clippedTriangles = {}
  88. local clippedVertices = self.clippedVertices
  89. local clippedUVs = self.clippedUVs
  90. local clippedTriangles = self.clippedTriangles
  91. local polygons = self.clippingPolygons
  92. local polygonsCount = #self.clippingPolygons
  93. local index = 1
  94. local i = 1
  95. while i <= trianglesLength do
  96. local vertexOffset = (triangles[i] - 1) * 2 + 1
  97. local x1 = vertices[vertexOffset]
  98. local y1 = vertices[vertexOffset + 1]
  99. local u1 = uvs[vertexOffset]
  100. local v1 = uvs[vertexOffset + 1]
  101. vertexOffset = (triangles[i + 1] - 1) * 2 + 1
  102. local x2 = vertices[vertexOffset]
  103. local y2 = vertices[vertexOffset + 1]
  104. local u2 = uvs[vertexOffset]
  105. local v2 = uvs[vertexOffset + 1]
  106. vertexOffset = (triangles[i + 2] - 1) * 2 + 1;
  107. local x3 = vertices[vertexOffset]
  108. local y3 = vertices[vertexOffset + 1]
  109. local u3 = uvs[vertexOffset]
  110. local v3 = uvs[vertexOffset + 1]
  111. local p = 1
  112. while p <= polygonsCount do
  113. local s = #clippedVertices + 1
  114. local clipOutput = {}
  115. if (self:clip(x1, y1, x2, y2, x3, y3, polygons[p], clipOutput)) then
  116. local clipOutputLength = #clipOutput
  117. if (clipOutputLength > 0) then
  118. local d0 = y2 - y3
  119. local d1 = x3 - x2
  120. local d2 = x1 - x3
  121. local d4 = y3 - y1
  122. local d = 1 / (d0 * d2 + d1 * (y1 - y3));
  123. local clipOutputCount = clipOutputLength / 2
  124. local clipOutputItems = clipOutput
  125. local clippedVerticesItems = clippedVertices
  126. local clippedUVsItems = clippedUVs
  127. local ii = 1
  128. while ii <= clipOutputLength do
  129. local x = clipOutputItems[ii]
  130. local y = clipOutputItems[ii + 1]
  131. clippedVerticesItems[s] = x
  132. clippedVerticesItems[s + 1] = y
  133. local c0 = x - x3
  134. local c1 = y - y3
  135. local a = (d0 * c0 + d1 * c1) * d
  136. local b = (d4 * c0 + d2 * c1) * d
  137. local c = 1 - a - b
  138. clippedUVsItems[s] = u1 * a + u2 * b + u3 * c
  139. clippedUVsItems[s + 1] = v1 * a + v2 * b + v3 * c
  140. s = s + 2
  141. ii = ii + 2
  142. end
  143. s = #clippedTriangles + 1
  144. local clippedTrianglesItems = clippedTriangles
  145. clipOutputCount = clipOutputCount - 1
  146. ii = 1
  147. while ii < clipOutputCount do
  148. clippedTrianglesItems[s] = index
  149. clippedTrianglesItems[s + 1] = index + ii
  150. clippedTrianglesItems[s + 2] = index + ii + 1
  151. s = s + 3
  152. ii = ii + 1
  153. end
  154. index = index + clipOutputCount + 1
  155. end
  156. else
  157. local clippedVerticesItems = clippedVertices
  158. local clippedUVsItems = clippedUVs
  159. clippedVerticesItems[s] = x1
  160. clippedVerticesItems[s + 1] = y1
  161. clippedVerticesItems[s + 2] = x2
  162. clippedVerticesItems[s + 3] = y2
  163. clippedVerticesItems[s + 4] = x3
  164. clippedVerticesItems[s + 5] = y3
  165. clippedUVsItems[s] = u1
  166. clippedUVsItems[s + 1] = v1
  167. clippedUVsItems[s + 2] = u2
  168. clippedUVsItems[s + 3] = v2
  169. clippedUVsItems[s + 4] = u3
  170. clippedUVsItems[s + 5] = v3
  171. s = #clippedTriangles + 1
  172. local clippedTrianglesItems = clippedTriangles
  173. clippedTrianglesItems[s] = index
  174. clippedTrianglesItems[s + 1] = index + 1
  175. clippedTrianglesItems[s + 2] = index + 2
  176. index = index + 3;
  177. break
  178. end
  179. p = p + 1
  180. end
  181. i = i + 3
  182. end
  183. end
  184. function SkeletonClipping:clip(x1, y1, x2, y2, x3, y3, clippingArea, output)
  185. local originalOutput = output
  186. local clipped = false
  187. local scratch = {}
  188. -- Avoid copy at the end.
  189. local input = nil
  190. if #clippingArea % 4 >= 2 then
  191. input = output
  192. output = scratch
  193. else
  194. input = scratch
  195. end
  196. table_insert(input, x1)
  197. table_insert(input, y1)
  198. table_insert(input, x2)
  199. table_insert(input, y2)
  200. table_insert(input, x3)
  201. table_insert(input, y3)
  202. table_insert(input, x1)
  203. table_insert(input, y1)
  204. local clippingVertices = clippingArea
  205. local clippingVerticesLast = #clippingArea - 4 + 1
  206. local i = 1
  207. while true do
  208. local edgeX = clippingVertices[i]
  209. local edgeY = clippingVertices[i + 1]
  210. local edgeX2 = clippingVertices[i + 2]
  211. local edgeY2 = clippingVertices[i + 3]
  212. local deltaX = edgeX - edgeX2
  213. local deltaY = edgeY - edgeY2
  214. local inputVertices = input
  215. local inputVerticesLength = #input - 2
  216. local outputStart = #output
  217. local ii = 1
  218. while ii <= inputVerticesLength do
  219. local inputX = inputVertices[ii]
  220. local inputY = inputVertices[ii + 1]
  221. local inputX2 = inputVertices[ii + 2]
  222. local inputY2 = inputVertices[ii + 3]
  223. local side2 = deltaX * (inputY2 - edgeY2) - deltaY * (inputX2 - edgeX2) > 0
  224. local continue = false;
  225. if deltaX * (inputY - edgeY2) - deltaY * (inputX - edgeX2) > 0 then
  226. if side2 then -- v1 inside, v2 inside
  227. table_insert(output, inputX2)
  228. table_insert(output, inputY2)
  229. continue = true
  230. else
  231. -- v1 inside, v2 outside
  232. local c0 = inputY2 - inputY
  233. local c2 = inputX2 - inputX
  234. local s = c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY)
  235. if math_abs(s) > 0.000001 then
  236. local ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / s
  237. table_insert(output, edgeX + (edgeX2 - edgeX) * ua)
  238. table_insert(output, edgeY + (edgeY2 - edgeY) * ua)
  239. else
  240. table_insert(output, edgeX)
  241. table_insert(output, edgeY)
  242. end
  243. end
  244. elseif side2 then -- v1 outside, v2 inside
  245. local c0 = inputY2 - inputY
  246. local c2 = inputX2 - inputX
  247. local s = c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY)
  248. if math_abs(s) > 0.000001 then
  249. local ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / s
  250. table_insert(output, edgeX + (edgeX2 - edgeX) * ua)
  251. table_insert(output, edgeY + (edgeY2 - edgeY) * ua)
  252. else
  253. table_insert(output, edgeX)
  254. table_insert(output, edgeY)
  255. end
  256. table_insert(output, inputX2)
  257. table_insert(output, inputY2)
  258. end
  259. if not continue then clipped = true end
  260. ii = ii + 2
  261. end
  262. if outputStart == #output then -- All edges outside.
  263. for i, _ in ipairs(originalOutput) do
  264. originalOutput[i] = nil
  265. end
  266. return true
  267. end
  268. table_insert(output, output[1])
  269. table_insert(output, output[2])
  270. if (i == clippingVerticesLast) then break end
  271. local temp = output
  272. output = input
  273. for i, _ in ipairs(output) do
  274. output[i] = nil
  275. end
  276. input = temp
  277. i = i + 2
  278. end
  279. if originalOutput ~= output then
  280. for i, _ in ipairs(originalOutput) do
  281. originalOutput[i] = nil
  282. end
  283. i = 1
  284. local n = #output - 2
  285. while i <= n do
  286. originalOutput[i] = output[i]
  287. i = i + 1
  288. end
  289. else
  290. utils.setArraySize(originalOutput, #originalOutput - 2)
  291. end
  292. return clipped
  293. end
  294. function SkeletonClipping:makeClockwise(polygon)
  295. local vertices = polygon
  296. local verticesLength = #polygon
  297. local area = vertices[verticesLength - 2 + 1] * vertices[1 + 1] - vertices[0 + 1] * vertices[verticesLength - 1 + 1]
  298. local p1x
  299. local p1y
  300. local p2x
  301. local p2y
  302. local i = 1
  303. local n = verticesLength - 3 + 1
  304. while i <= n do
  305. p1x = vertices[i]
  306. p1y = vertices[i + 1]
  307. p2x = vertices[i + 2]
  308. p2y = vertices[i + 3]
  309. area = area + p1x * p2y - p2x * p1y
  310. i = i + 2
  311. end
  312. if (area < 0) then return end
  313. i = 1
  314. local lastX = verticesLength - 2 + 1
  315. n = verticesLength / 2
  316. while i <= n do
  317. local x = vertices[i]
  318. local y = vertices[i + 1]
  319. local other = lastX - i + 1
  320. vertices[i] = vertices[other]
  321. vertices[i + 1] = vertices[other + 1]
  322. vertices[other] = x
  323. vertices[other + 1] = y
  324. i = i + 2
  325. end
  326. end
  327. return SkeletonClipping