2021-12-03 13:41:19 +01:00

362 lines
12 KiB
Lua

-------------------------------------------------------------------------------
-- Spine Runtimes License Agreement
-- Last updated January 1, 2020. Replaces all prior versions.
--
-- Copyright (c) 2013-2020, Esoteric Software LLC
--
-- Integration of the Spine Runtimes into software or otherwise creating
-- derivative works of the Spine Runtimes is permitted under the terms and
-- conditions of Section 2 of the Spine Editor License Agreement:
-- http://esotericsoftware.com/spine-editor-license
--
-- Otherwise, it is permitted to integrate the Spine Runtimes into software
-- or otherwise create derivative works of the Spine Runtimes (collectively,
-- "Products"), provided that each user of the Products must obtain their own
-- Spine Editor license and redistribution of the Products in any form must
-- include this license and copyright notice.
--
-- THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
-- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-- DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
-- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
-- BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
-- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-- THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-------------------------------------------------------------------------------
local spine = {}
spine.utils = require "spine-lua.utils"
spine.SkeletonJson = require "spine-lua.SkeletonJson"
spine.SkeletonData = require "spine-lua.SkeletonData"
spine.BoneData = require "spine-lua.BoneData"
spine.SlotData = require "spine-lua.SlotData"
spine.IkConstraintData = require "spine-lua.IkConstraintData"
spine.Skin = require "spine-lua.Skin"
spine.Attachment = require "spine-lua.attachments.Attachment"
spine.BoundingBoxAttachment = require "spine-lua.attachments.BoundingBoxAttachment"
spine.RegionAttachment = require "spine-lua.attachments.RegionAttachment"
spine.MeshAttachment = require "spine-lua.attachments.MeshAttachment"
spine.VertexAttachment = require "spine-lua.attachments.VertexAttachment"
spine.PathAttachment = require "spine-lua.attachments.PathAttachment"
spine.PointAttachment = require "spine-lua.attachments.PointAttachment"
spine.ClippingAttachment = require "spine-lua.attachments.ClippingAttachment"
spine.Skeleton = require "spine-lua.Skeleton"
spine.Bone = require "spine-lua.Bone"
spine.Slot = require "spine-lua.Slot"
spine.IkConstraint = require "spine-lua.IkConstraint"
spine.AttachmentType = require "spine-lua.attachments.AttachmentType"
spine.AttachmentLoader = require "spine-lua.AttachmentLoader"
spine.Animation = require "spine-lua.Animation"
spine.AnimationStateData = require "spine-lua.AnimationStateData"
spine.AnimationState = require "spine-lua.AnimationState"
spine.EventData = require "spine-lua.EventData"
spine.Event = require "spine-lua.Event"
spine.SkeletonBounds = require "spine-lua.SkeletonBounds"
spine.BlendMode = require "spine-lua.BlendMode"
spine.TextureAtlas = require "spine-lua.TextureAtlas"
spine.TextureRegion = require "spine-lua.TextureRegion"
spine.TextureAtlasRegion = require "spine-lua.TextureAtlasRegion"
spine.AtlasAttachmentLoader = require "spine-lua.AtlasAttachmentLoader"
spine.Color = require "spine-lua.Color"
spine.Triangulator = require "spine-lua.Triangulator"
spine.SkeletonClipping = require "spine-lua.SkeletonClipping"
spine.JitterEffect = require "spine-lua.vertexeffects.JitterEffect"
spine.SwirlEffect = require "spine-lua.vertexeffects.SwirlEffect"
spine.Interpolation = require "spine-lua.Interpolation"
spine.utils.readFile = function (fileName, base)
if not base then base = system.ResourceDirectory end
local path = system.pathForFile(fileName, base)
local file = io.open(path, "r")
if not file then return nil end
local contents = file:read("*a")
io.close(file)
return contents
end
local json = require "json"
spine.utils.readJSON = function (text)
return json.decode(text)
end
local QUAD_TRIANGLES = { 1, 2, 3, 3, 4, 1 }
spine.Skeleton.new_super = spine.Skeleton.new
spine.Skeleton.updateWorldTransform_super = spine.Skeleton.updateWorldTransform
spine.Skeleton.new = function(skeletonData, group)
local self = spine.Skeleton.new_super(skeletonData)
self.group = group or display.newGroup()
self.drawingGroup = display.newGroup()
self.group:insert(self.drawingGroup)
self.slotData = {}
self.premultipliedAlpha = false
self.batches = 0
self.tempColor = spine.Color.newWith(1, 1, 1, 1)
self.tempColor2 = spine.Color.newWith(-1, 1, 1, 1)
self.tempVertex = {
x = 0,
y = 0,
u = 0,
v = 0,
light = spine.Color.newWith(1, 1, 1, 1),
dark = spine.Color.newWith(0, 0, 0, 0)
}
self.clipper = spine.SkeletonClipping.new()
return self
end
local function colorEquals(color1, color2)
return color1.r == color2.r and color1.g == color2.g and color1.b == color2.b and color1.a == color2.a
end
local function toCoronaBlendMode(blendMode)
if blendMode == spine.BlendMode.normal then
return "normal"
elseif blendMode == spine.BlendMode.additive then
return "add"
elseif blendMode == spine.BlendMode.multiply then
return "multiply"
elseif blendMode == spine.BlendMode.screen then
return "screen"
end
end
local worldVertices = spine.utils.newNumberArray(10000 * 8)
function spine.Skeleton:hideSlot(slot)
if not self.slotData[slot] then return end
self.slotData[slot].mesh.isVisible = false
end
function spine.Skeleton:createSlot(slot, params)
local mesh = display.newMesh(self.drawingGroup, 0, 0, {
mode = "indexed",
vertices = params.vertices,
uvs = params.uvs,
indices = params.indices
})
mesh.x, mesh.y = mesh.path:getVertexOffset()
mesh.blendMode = params.blendMode
mesh.fill = params.texture
local color = params.color
mesh:setFillColor(color.r, color.g, color.b, color.a)
self.slotData[slot] = {
mesh = mesh,
indices = params.indices,
texture = params.texture,
uvs = params.uvs,
}
end
function spine.Skeleton:updateSlot(slot, params)
if not self.slotData[slot] then
return self:createSlot(slot, params)
end
local slotData = self.slotData[slot]
local mesh = slotData.mesh
if #params.indices ~= #slotData.indices or #params.uvs ~= #slotData.uvs then
slotData.mesh:removeSelf()
self.slotData[slot] = nil
return self:createSlot(slot, params)
end
mesh.path:update({
vertices = params.vertices,
uvs = params.uvs,
indices = params.indices
})
mesh.isVisible = true
mesh.x, mesh.y = mesh.path:getVertexOffset()
mesh:toFront()
local color = params.color
mesh:setFillColor(color.r, color.g, color.b, color.a)
if slotData.texture ~= params.texture then
slotData.texture = params.texture
mesh.fill = params.texture
end
if mesh.blendMode ~= params.blendMode then
mesh.blendMode = params.blendMode
end
end
function spine.Skeleton:updateWorldTransform()
spine.Skeleton.updateWorldTransform_super(self)
local premultipliedAlpha = self.premultipliedAlpha
self.batches = 0
if (self.vertexEffect) then self.vertexEffect:beginEffect(self) end
local drawOrder = self.drawOrder
local groupVertices = {}
local groupIndices = {}
local groupUvs = {}
local color = self.tempColor
local lastColor = self.tempColor2
lastColor.r = -1
local texture = nil
local lastTexture = nil
local blendMode = nil
local lastBlendMode = nil
for k, v in pairs(self.slotData) do
self:hideSlot(k)
end
for i,slot in ipairs(drawOrder) do
local attachment = slot.attachment
local vertices = nil
local uvs = nil
local numVertices = 0
local indices = nil
if slot.bone.active then
local isClippingAttachment = false
if attachment then
if attachment.type == spine.AttachmentType.region then
numVertices = 4
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 = 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)
isClippingAttachment = true
end
if texture and vertices and indices then
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 = 1
if premultipliedAlpha then multiplier = alpha 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 not lastTexture then lastTexture = texture end
if lastColor.r == -1 then lastColor:setFrom(color) end
if not lastBlendMode then lastBlendMode = blendMode end
if (texture ~= lastTexture or not colorEquals(color, lastColor) or blendMode ~= lastBlendMode) then
local lastSlot = drawOrder[i-1]
self:updateSlot(lastSlot, {
texture = lastTexture,
color = lastColor,
blendMode = lastBlendMode,
vertices = groupVertices,
uvs = groupUvs,
indices = groupIndices
})
lastTexture = texture
lastColor:setFrom(color)
lastBlendMode = blendMode
groupVertices = {}
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, uvs, numVertices, indices, groupVertices, groupUvs, groupIndices)
end
if not isClippingAttachment then self.clipper:clipEnd(slot) end
end
else
self.clipper:clipEnd(slot)
end
end
if #groupVertices > 0 then
local slot = drawOrder[#drawOrder]
self:updateSlot(slot, {
texture = texture,
color = color,
blendMode = blendMode,
vertices = groupVertices,
uvs = groupUvs,
indices = groupIndices
})
end
self.clipper:clipEnd2()
if (self.vertexEffect) then self.vertexEffect:endEffect() end
end
function spine.Skeleton:batch(vertices, uvs, numVertices, indices, groupVertices, groupUvs, groupIndices)
local numIndices = #indices
local i = 1
local indexStart = #groupIndices + 1
local offset = #groupVertices / 2
local indexEnd = indexStart + numIndices
while indexStart < indexEnd do
groupIndices[indexStart] = indices[i] + offset
indexStart = indexStart + 1
i = i + 1
end
i = 1
local vertexStart = #groupVertices + 1
local vertexEnd = vertexStart + numVertices * 2
if (self.vertexEffect) then
local effect = self.vertexEffect
local vertex = self.tempVertex
while vertexStart < vertexEnd do
vertex.x = vertices[i]
vertex.y = vertices[i+1]
vertex.u = uvs[i]
vertex.v = uvs[i+1]
effect:transform(vertex);
groupVertices[vertexStart] = vertex.x
groupVertices[vertexStart+1] = vertex.y
groupUvs[vertexStart] = vertex.u
groupUvs[vertexStart+1] = vertex.v
vertexStart = vertexStart + 2
i = i + 2
end
else
while vertexStart < vertexEnd do
groupVertices[vertexStart] = vertices[i]
groupVertices[vertexStart+1] = vertices[i+1]
groupUvs[vertexStart] = uvs[i]
groupUvs[vertexStart+1] = uvs[i+1]
vertexStart = vertexStart + 2
i = i + 2
end
end
end
return spine