diff --git a/spine-corona/main.lua b/spine-corona/main.lua index a31136587..aa36a9219 100644 --- a/spine-corona/main.lua +++ b/spine-corona/main.lua @@ -77,12 +77,12 @@ end -- table.insert(skeletons, loadSkeleton("test.atlas", "test.json", 240, 300, 0.4, "animation")) table.insert(skeletons, loadSkeleton("coin.atlas", "coin.json", 240, 300, 0.4, "rotate")) ---[[table.insert(skeletons, loadSkeleton("spineboy.atlas", "spineboy.json", 240, 300, 0.4, "walk")) +table.insert(skeletons, loadSkeleton("spineboy.atlas", "spineboy.json", 240, 300, 0.4, "walk")) table.insert(skeletons, loadSkeleton("raptor.atlas", "raptor.json", 200, 300, 0.25, "walk")) table.insert(skeletons, loadSkeleton("goblins.atlas", "goblins-mesh.json", 240, 300, 0.8, "walk", "goblin")) table.insert(skeletons, loadSkeleton("stretchyman.atlas", "stretchyman.json", 40, 300, 0.5, "sneak")) table.insert(skeletons, loadSkeleton("tank.atlas", "tank.json", 400, 300, 0.2, "drive")) -table.insert(skeletons, loadSkeleton("vine.atlas", "vine.json", 240, 300, 0.3, "animation"))]]-- +table.insert(skeletons, loadSkeleton("vine.atlas", "vine.json", 240, 300, 0.3, "animation")) local triangulator = spine.Triangulator.new() local polygon = { 411, 219, 199, 230, 161, 362, 534, 407, 346, 305, 596, 265 } @@ -90,6 +90,12 @@ local indices = triangulator:triangulate(polygon) print(indices) print(triangulator:decompose(polygon, indices)) +local skeletonClipping = spine.SkeletonClipping.new() +local polygon2 = {0, 0, 100, 0, 100, 100, 0, 100 } +skeletonClipping:makeClockwise(polygon2) +print(polygon2) + + local bounds = spine.SkeletonBounds.new() skeletons[1].skeleton:updateWorldTransform() bounds:update(skeletons[1].skeleton, true) @@ -110,7 +116,7 @@ Runtime:addEventListener("enterFrame", function (event) state = skeletons[activeSkeleton].state state:update(delta) - -- state:apply(skeleton) + state:apply(skeleton) skeleton:updateWorldTransform() -- uncomment if you want to know how many batches a skeleton renders to @@ -120,7 +126,7 @@ end) Runtime:addEventListener("key", function(event) if activeSkeleton == 2 and event.phase == "down" then state = skeletons[activeSkeleton].state - state:setAnimationByName(0, "Jump", false) + state:setAnimationByName(0, "jump", false) state:addAnimationByName(0, "walk", true, 0) end return false diff --git a/spine-corona/spine-corona/spine.lua b/spine-corona/spine-corona/spine.lua index 8b614f291..95571d0ff 100644 --- a/spine-corona/spine-corona/spine.lua +++ b/spine-corona/spine-corona/spine.lua @@ -92,6 +92,7 @@ spine.Skeleton.new = function(skeletonData, group) self.batches = 0 self.tempColor = spine.Color.newWith(1, 1, 1, 1) self.tempColor2 = spine.Color.newWith(-1, 1, 1, 1) + self.clipper = spine.SkeletonClipping.new() return self end @@ -140,25 +141,49 @@ function spine.Skeleton:updateWorldTransform() local lastTexture = nil local blendMode = nil local lastBlendMode = nil + local renderable = { + vertices = nil, + uvs = nil + } + for i,slot in ipairs(drawOrder) do local attachment = slot.attachment local vertices = nil + local uvs = nil local numVertices = 0 local indices = nil if attachment then if attachment.type == spine.AttachmentType.region then numVertices = 4 - vertices = self:computeRegionVertices(slot, attachment, premultipliedAlpha, color) + vertices = worldVertices + attachment:computeWorldVertices(slot.bone, vertices, 0, 2) + uvs = attachment.uvs indices = QUAD_TRIANGLES texture = attachment.region.renderObject.texture blendMode = toCoronaBlendMode(slot.data.blendMode) elseif attachment.type == spine.AttachmentType.mesh then numVertices = attachment.worldVerticesLength / 2 - vertices = self:computeMeshVertices(slot, attachment, premultipliedAlpha, color) + vertices = worldVertices + attachment:computeWorldVertices(slot, 0, attachment.worldVerticesLength, vertices, 0, 2) + uvs = attachment.uvs indices = attachment.triangles texture = attachment.region.renderObject.texture blendMode = toCoronaBlendMode(slot.data.blendMode) + elseif attachment.type == spine.AttachmentType.clipping then + self.clipper:clipStart(slot, attachment) end + + local skeleton = slot.bone.skeleton + local skeletonColor = skeleton.color + local slotColor = slot.color + local attachmentColor = attachment.color + local alpha = skeletonColor.a * slotColor.a * attachmentColor.a + local multiplier = alpha + if premultipliedAlpha then multiplier = 1 end + color:set(skeletonColor.r * slotColor.r * attachmentColor.r * multiplier, + skeletonColor.g * slotColor.g * attachmentColor.g * multiplier, + skeletonColor.b * slotColor.b * attachmentColor.b * multiplier, + alpha) if texture and vertices and indices then if not lastTexture then lastTexture = texture end @@ -174,80 +199,27 @@ function spine.Skeleton:updateWorldTransform() groupUvs = {} groupIndices = {} end + + if self.clipper:isClipping() then + self.clipper:clipTriangles(vertices, uvs, indices, #indices) + vertices = self.clipper.clippedVertices + numVertices = #vertices / 2 + uvs = self.clipper.clippedUVs + indices = self.clipper.clippedTriangles + end - self:batch(vertices, numVertices, indices, groupVertices, groupUvs, groupIndices) + self:batch(vertices, uvs, numVertices, indices, groupVertices, groupUvs, groupIndices) end + + self.clipper:clipEnd(slot) end end if #groupVertices > 0 then self:flush(groupVertices, groupUvs, groupIndices, texture, color, blendMode, drawingGroup) end -end - -function spine.Skeleton:computeRegionVertices(slot, region, pma, color) - local skeleton = slot.bone.skeleton - local skeletonColor = skeleton.color - local slotColor = slot.color - local regionColor = region.color - local alpha = skeletonColor.a * slotColor.a * regionColor.a - local multiplier = alpha - if pma then multiplier = 1 end - color:set(skeletonColor.r * slotColor.r * regionColor.r * multiplier, - skeletonColor.g * slotColor.g * regionColor.g * multiplier, - skeletonColor.b * slotColor.b * regionColor.b * multiplier, - alpha) - - local vertices = worldVertices - region:computeWorldVertices(slot.bone, vertices, 0, 4) - - local uvs = region.uvs - - vertices[3] = uvs[1] - vertices[4] = uvs[2] - - vertices[7] = uvs[3] - vertices[8] = uvs[4] - - vertices[11] = uvs[5] - vertices[12] = uvs[6] - - vertices[15] = uvs[7] - vertices[16] = uvs[8] - - return vertices -end - -function spine.Skeleton:computeMeshVertices(slot, mesh, pma, color) - local skeleton = slot.bone.skeleton - local skeletonColor = skeleton.color - local slotColor = slot.color - local meshColor = mesh.color - local alpha = skeletonColor.a * slotColor.a * meshColor.a - local multiplier = alpha - if pma then multiplier = 1 end - color:set(skeletonColor.r * slotColor.r * meshColor.r * multiplier, - skeletonColor.g * slotColor.g * meshColor.g * multiplier, - skeletonColor.b * slotColor.b * meshColor.b * multiplier, - alpha) - - local numVertices = mesh.worldVerticesLength / 2 - local vertices = worldVertices - mesh:computeWorldVertices(slot, 0, mesh.worldVerticesLength, vertices, 0, 4) - local uvs = mesh.uvs - local i = 1 - local n = numVertices + 1 - local u = 1 - local v = 3 - while i < n do - vertices[v] = uvs[u] - vertices[v + 1] = uvs[u + 1] - i = i + 1 - u = u + 2 - v = v + 4 - end - return vertices + self.clipper:clipEnd2() end function spine.Skeleton:flush(groupVertices, groupUvs, groupIndices, texture, color, blendMode, drawingGroup) @@ -265,7 +237,7 @@ function spine.Skeleton:flush(groupVertices, groupUvs, groupIndices, texture, co self.batches = self.batches + 1 end -function spine.Skeleton:batch(vertices, numVertices, indices, groupVertices, groupUvs, groupIndices) +function spine.Skeleton:batch(vertices, uvs, numVertices, indices, groupVertices, groupUvs, groupIndices) local numIndices = #indices local i = 1 local indexStart = #groupIndices + 1 @@ -284,10 +256,10 @@ function spine.Skeleton:batch(vertices, numVertices, indices, groupVertices, gro while vertexStart < vertexEnd do groupVertices[vertexStart] = vertices[i] groupVertices[vertexStart+1] = vertices[i+1] - groupUvs[vertexStart] = vertices[i+2] - groupUvs[vertexStart+1] = vertices[i+3] + groupUvs[vertexStart] = uvs[i] + groupUvs[vertexStart+1] = uvs[i+1] vertexStart = vertexStart + 2 - i = i + 4 + i = i + 2 end end diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/Triangulator.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/Triangulator.java index 2d37136a8..330d81fb7 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/Triangulator.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/Triangulator.java @@ -289,5 +289,9 @@ class Triangulator { Array polys = triangulator.decompose(polygon, triangles); System.out.println(polys); + + FloatArray poly2 = new FloatArray(new float[] {0, 0, 100, 0, 100, 100, 0, 100 }); + SkeletonClipping.makeClockwise(poly2); + System.out.println(poly2); } } diff --git a/spine-lua/SkeletonClipping.lua b/spine-lua/SkeletonClipping.lua index c74931c3d..49d88f181 100644 --- a/spine-lua/SkeletonClipping.lua +++ b/spine-lua/SkeletonClipping.lua @@ -29,6 +29,7 @@ ------------------------------------------------------------------------------- local utils = require "spine-lua.utils" +local Triangulator = require "spine-lua.Triangulator" local setmetatable = setmetatable local math_min = math.min @@ -42,15 +43,306 @@ SkeletonClipping.__index = SkeletonClipping function SkeletonClipping.new () local self = { - convexPolygons = {}, - convexPolygonsIndices = {}, - indicesArray = {}, - isConcaveArray = {}, - triangles = {} + triangulator = Triangulator.new(), + clippingPolygon = {}, + clipOutput = {}, + clippedVertices = {}, + clippedUVs = {}, + clippedTriangles = {}, + clipAttachment = nil } setmetatable(self, SkeletonClipping) return self end -return Triangulator +function SkeletonClipping:clipStart(slot, clip) + if self.clipAttachment then return 0 end + self.clipAttachment = clip + + local n = clip.worldVerticesLength + self.clippingPolygon = {} + local vertices = self.clippingPolygon + clip:computeWorldVertices(slot, 0, n, vertices, 0, 2) + self:makeClockwise(self.clippingPolygon) + self.clippingPolygons = self.triangulator:decompose(self.clippingPolygon, self.triangulator:triangulate(self.clippingPolygon)) + for i,polygon in ipairs(self.clippingPolygons) do + self:makeClockwise(polygon) + table_insert(polygon, polygon[1]) + table_insert(polygon, polygon[2]) + end + return #self.clippingPolygons +end + +function SkeletonClipping:clipEnd(slot) + if self.clipAttachment and self.clipAttachment.endSlot == slot then self:clipEnd2() end +end + +function SkeletonClipping:clipEnd2() + if self.clipAttachment == nil then return end + self.clipAttachment = nil + self.clippingPolygons = nil + self.clippedVertices = {} + self.clippedUVs = {} + self.clippedTriangles = {} + self.clippingPolygon = {} +end + +function SkeletonClipping:isClipping() + return self.clipAttachment ~= nil +end + +function SkeletonClipping:clipTriangles(vertices, uvs, triangles, trianglesLength) + self.clippedVertices = {} + self.clippedUVs = {} + self.clippedTriangles = {} + local clippedVertices = self.clippedVertices + local clippedUVs = self.clippedUVs + local clippedTriangles = self.clippedTriangles + local polygons = self.clippingPolygons + local polygonsCount = #self.clippingPolygons + + local index = 1 + + local i = 1 + while i <= trianglesLength do + local vertexOffset = (triangles[i] - 1) * 2 + 1 + local x1 = vertices[vertexOffset] + local y1 = vertices[vertexOffset + 1] + local u1 = uvs[vertexOffset] + local v1 = uvs[vertexOffset + 1] + + vertexOffset = (triangles[i + 1] - 1) * 2 + 1 + local x2 = vertices[vertexOffset] + local y2 = vertices[vertexOffset + 1] + local u2 = uvs[vertexOffset] + local v2 = uvs[vertexOffset + 1] + + vertexOffset = (triangles[i + 2] - 1) * 2 + 1; + local x3 = vertices[vertexOffset] + local y3 = vertices[vertexOffset + 1] + local u3 = uvs[vertexOffset] + local v3 = uvs[vertexOffset + 1] + + local p = 1 + while p <= polygonsCount do + local s = #clippedVertices + 1 + local clipOutput = {} + if (self:clip(x1, y1, x2, y2, x3, y3, polygons[p], clipOutput)) then + local clipOutputLength = #clipOutput + if (clipOutputLength > 0) then + local d0 = y2 - y3 + local d1 = x3 - x2 + local d2 = x1 - x3 + local d4 = y3 - y1 + local d = 1 / (d0 * d2 + d1 * (y1 - y3)); + + local clipOutputCount = clipOutputLength / 2 + local clipOutputItems = clipOutput + local clippedVerticesItems = clippedVertices + local clippedUVsItems = clippedUVs + local ii = 1 + while ii <= clipOutputLength do + local x = clipOutputItems[ii] + local y = clipOutputItems[ii + 1] + clippedVerticesItems[s] = x + clippedVerticesItems[s + 1] = y + local c0 = x - x3 + local c1 = y - y3 + local a = (d0 * c0 + d1 * c1) * d + local b = (d4 * c0 + d2 * c1) * d + local c = 1 - a - b + clippedUVsItems[s] = u1 * a + u2 * b + u3 * c + clippedUVsItems[s + 1] = v1 * a + v2 * b + v3 * c + s = s + 2 + ii = ii + 2 + end + + s = #clippedTriangles + 1 + local clippedTrianglesItems = clippedTriangles + clipOutputCount = clipOutputCount - 1 + ii = 1 + while ii < clipOutputCount do + clippedTrianglesItems[s] = index + clippedTrianglesItems[s + 1] = index + ii + clippedTrianglesItems[s + 2] = index + ii + 1 + s = s + 3 + ii = ii + 1 + end + index = index + clipOutputCount + 1 + end + else + local clippedVerticesItems = clippedVertices + local clippedUVsItems = clippedUVs + clippedVerticesItems[s] = x1 + clippedVerticesItems[s + 1] = y1 + clippedVerticesItems[s + 2] = x2 + clippedVerticesItems[s + 3] = y2 + clippedVerticesItems[s + 4] = x3 + clippedVerticesItems[s + 5] = y3 + + clippedUVsItems[s] = u1 + clippedUVsItems[s + 1] = v1 + clippedUVsItems[s + 2] = u2 + clippedUVsItems[s + 3] = v2 + clippedUVsItems[s + 4] = u3 + clippedUVsItems[s + 5] = v3 + + s = #clippedTriangles + 1 + local clippedTrianglesItems = clippedTriangles + clippedTrianglesItems[s] = index + clippedTrianglesItems[s + 1] = index + 1 + clippedTrianglesItems[s + 2] = index + 2 + index = index + 3; + break + end + p = p + 1 + end + i = i + 3 + end +end + +function SkeletonClipping:clip(x1, y1, x2, y2, x3, y3, clippingArea, output) + local originalOutput = output + local clipped = false + local scratch = {} + + -- Avoid copy at the end. + local input = nil + if #clippingArea % 4 >= 2 then + input = output + output = scratch + else + input = scratch + end + + table_insert(input, x1) + table_insert(input, y1) + table_insert(input, x2) + table_insert(input, y2) + table_insert(input, x3) + table_insert(input, y3) + table_insert(input, x1) + table_insert(input, y1) + + local clippingVertices = clippingArea + local clippingVerticesLast = #clippingArea - 4 + 1 + local i = 1 + while true do + local edgeX = clippingVertices[i] + local edgeY = clippingVertices[i + 1] + local edgeX2 = clippingVertices[i + 2] + local edgeY2 = clippingVertices[i + 3] + local deltaX = edgeX - edgeX2 + local deltaY = edgeY - edgeY2 + + local inputVertices = input + local inputVerticesLength = #input - 2 + local outputStart = #output + local ii = 1 + while ii <= inputVerticesLength do + local inputX = inputVertices[ii] + local inputY = inputVertices[ii + 1] + local inputX2 = inputVertices[ii + 2] + local inputY2 = inputVertices[ii + 3] + local side2 = deltaX * (inputY2 - edgeY2) - deltaY * (inputX2 - edgeX2) > 0 + local continue = false; + if deltaX * (inputY - edgeY2) - deltaY * (inputX - edgeX2) > 0 then + if side2 then -- v1 inside, v2 inside + table_insert(output, inputX2) + table_insert(output, inputY2) + continue = true + else + -- v1 inside, v2 outside + local c0 = inputY2 - inputY + local c2 = inputX2 - inputX + local ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / (c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY)) + table_insert(output, edgeX + (edgeX2 - edgeX) * ua) + table_insert(output, edgeY + (edgeY2 - edgeY) * ua) + end + elseif side2 then -- v1 outside, v2 inside + local c0 = inputY2 - inputY + local c2 = inputX2 - inputX + local ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / (c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY)) + table_insert(output, edgeX + (edgeX2 - edgeX) * ua) + table_insert(output, edgeY + (edgeY2 - edgeY) * ua) + table_insert(output, inputX2) + table_insert(output, inputY2) + end + if not continue then clipped = true end + ii = ii + 2 + end + + if outputStart == #output then -- All edges outside. + for i, v in ipairs(originalOutput) do + originalOutput[i] = nil + end + return true + end + + table_insert(output, output[1]) + table_insert(output, output[2]) + + if (i == clippingVerticesLast) then break end + local temp = output + output = input + for i, v in ipairs(output) do + output[i] = nil + end + input = temp + i = i + 2 + end + + if originalOutput ~= output then + for i, v in ipairs(originalOutput) do + originalOutput[i] = nil + end + i = 1 + local n = #output - 2 + while i <= n do + originalOutput[i] = output[i] + i = i + 1 + end + else + utils.setArraySize(originalOutput, #originalOutput - 2) + end + + return clipped +end + +function SkeletonClipping:makeClockwise(polygon) + local vertices = polygon + local verticesLength = #polygon + local area = vertices[verticesLength - 2 + 1] * vertices[1 + 1] - vertices[0 + 1] * vertices[verticesLength - 1 + 1] + local p1x + local p1y + local p2x + local p2y + local i = 1 + local n = verticesLength - 3 + 1 + while i <= n do + p1x = vertices[i] + p1y = vertices[i + 1] + p2x = vertices[i + 2] + p2y = vertices[i + 3] + area = area + p1x * p2y - p2x * p1y + i = i + 2 + end + if (area < 0) then return end + + i = 1 + local lastX = verticesLength - 2 + 1 + n = verticesLength / 2 + while i <= n do + local x = vertices[i] + local y = vertices[i + 1] + local other = lastX - i + 1 + vertices[i] = vertices[other] + vertices[i + 1] = vertices[other + 1] + vertices[other] = x + vertices[other + 1] = y + i = i + 2 + end +end + +return SkeletonClipping diff --git a/spine-lua/attachments/RegionAttachment.lua b/spine-lua/attachments/RegionAttachment.lua index 02954d1dd..10e63065b 100644 --- a/spine-lua/attachments/RegionAttachment.lua +++ b/spine-lua/attachments/RegionAttachment.lua @@ -187,6 +187,15 @@ end function RegionAttachment:setRegion (region) local uvs = self.uvs if region.rotate then + uvs[5] = region.u + uvs[6] = region.v2 + uvs[7] = region.u + uvs[8] = region.v + uvs[1] = region.u2 + uvs[2] = region.v + uvs[3] = region.u2 + uvs[4] = region.v2 + else uvs[3] = region.u uvs[4] = region.v2 uvs[5] = region.u @@ -195,15 +204,6 @@ function RegionAttachment:setRegion (region) uvs[8] = region.v uvs[1] = region.u2 uvs[2] = region.v2 - else - uvs[1] = region.u - uvs[2] = region.v2 - uvs[3] = region.u - uvs[4] = region.v - uvs[5] = region.u2 - uvs[6] = region.v - uvs[7] = region.u2 - uvs[8] = region.v2 end end @@ -219,26 +219,26 @@ function RegionAttachment:computeWorldVertices (bone, worldVertices, offset, str local offsetX = 0 local offsetY = 0 - offsetX = vertexOffset[OX1] - offsetY = vertexOffset[OY1] + offsetX = vertexOffset[7] + offsetY = vertexOffset[8] worldVertices[offset] = offsetX * a + offsetY * b + x -- br worldVertices[offset + 1] = offsetX * c + offsetY * d + y offset = offset + stride - offsetX = vertexOffset[OX2] - offsetY = vertexOffset[OY2] + offsetX = vertexOffset[1] + offsetY = vertexOffset[2] worldVertices[offset] = offsetX * a + offsetY * b + x -- bl worldVertices[offset + 1] = offsetX * c + offsetY * d + y offset = offset + stride - offsetX = vertexOffset[OX3] - offsetY = vertexOffset[OY3] + offsetX = vertexOffset[3] + offsetY = vertexOffset[4] worldVertices[offset] = offsetX * a + offsetY * b + x -- ul worldVertices[offset + 1] = offsetX * c + offsetY * d + y offset = offset + stride - offsetX = vertexOffset[OX4] - offsetY = vertexOffset[OY4] + offsetX = vertexOffset[5] + offsetY = vertexOffset[6] worldVertices[offset] = offsetX * a + offsetY * b + x -- ur worldVertices[offset + 1] = offsetX * c + offsetY * d + y end diff --git a/spine-lua/utils.lua b/spine-lua/utils.lua index ba1e84111..3e72c1499 100644 --- a/spine-lua/utils.lua +++ b/spine-lua/utils.lua @@ -108,7 +108,12 @@ function utils.setArraySize (array, size) i = i + 1 end else - array[size + 1] = nil -- dirty trick to appease # without realloc + local originalSize = #array + local i = originalSize + while i > size do + array[i] = nil -- dirty trick to appease # without realloc + i = i - 1 + end end return array end