[lua] Fixed SkeletonClipping. [corona] Refactored rendering, should be a bit faster as it avoids a bunch of copies. Fixed RegionAttachment to be in alignement with reference implementation

This commit is contained in:
badlogic 2017-05-04 13:17:54 +02:00
parent 1fafbd0cb7
commit 8123422f33
6 changed files with 378 additions and 99 deletions

View File

@ -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

View File

@ -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

View File

@ -289,5 +289,9 @@ class Triangulator {
Array<FloatArray> 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);
}
}

View File

@ -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

View File

@ -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

View File

@ -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