From aaa0c1f25d8e76f09965ccd0c896b812d2fac63f Mon Sep 17 00:00:00 2001 From: Steven Johnson Date: Fri, 19 Feb 2016 19:18:12 -0600 Subject: [PATCH 01/11] Added requires in attachment loader Minor fixes for off-by-one indexing and loop counts for skinned meshes Some fixes for names or counts, again for skinned meshes --- spine-lua/AttachmentLoader.lua | 2 ++ spine-lua/SkeletonJson.lua | 10 ++++++---- spine-lua/SkinnedMeshAttachment.lua | 12 ++++++------ 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/spine-lua/AttachmentLoader.lua b/spine-lua/AttachmentLoader.lua index 3c93e5210..bb8f6ac9e 100644 --- a/spine-lua/AttachmentLoader.lua +++ b/spine-lua/AttachmentLoader.lua @@ -32,6 +32,8 @@ local AttachmentType = require "spine-lua.AttachmentType" local RegionAttachment = require "spine-lua.RegionAttachment" local BoundingBoxAttachment = require "spine-lua.BoundingBoxAttachment" +local MeshAttachment = require "spine-lua.MeshAttachment" +local SkinningMeshAttachment = require "spine-lua.SkinnedMeshAttachment" local AttachmentLoader = {} function AttachmentLoader.new () diff --git a/spine-lua/SkeletonJson.lua b/spine-lua/SkeletonJson.lua index f747197c4..7c8a315df 100755 --- a/spine-lua/SkeletonJson.lua +++ b/spine-lua/SkeletonJson.lua @@ -266,19 +266,21 @@ function SkeletonJson.new (attachmentLoader) return mesh elseif type == AttachmentType.skinnedmesh then - local mesh = self.attachmentLoader.newSkinnedMeshAttachment(skin, name, path) + local mesh = self.attachmentLoader.newSkinningMeshAttachment(skin, name, path) if not mesh then return null end mesh.path = path local uvs = getArray(map, "uvs", 1) - vertices = getArray(map, "vertices", 1) + local vertices = getArray(map, "vertices", 1) local weights = {} local bones = {} - for i = 1, vertices do + local i, n = 1, #vertices + while i < n do local boneCount = vertices[i] i = i + 1 table.insert(bones, boneCount) - for ii = 1, i + boneCount * 4 do + local nn = i + boneCount * 4 + while i < nn do table.insert(bones, vertices[i]) table.insert(weights, vertices[i + 1] * scale) table.insert(weights, vertices[i + 2] * scale) diff --git a/spine-lua/SkinnedMeshAttachment.lua b/spine-lua/SkinnedMeshAttachment.lua index 607a913e6..2281ce673 100644 --- a/spine-lua/SkinnedMeshAttachment.lua +++ b/spine-lua/SkinnedMeshAttachment.lua @@ -37,7 +37,7 @@ function SkinnedMeshAttachment.new (name) local self = { name = name, - type = AttachmentType.mesh, + type = AttachmentType.skinnedmesh, bones = nil, weights = nil, uvs = nil, @@ -75,15 +75,15 @@ function SkinnedMeshAttachment.new (name) end function self:computeWorldVertices (x, y, slot, worldVertices) - local skeletonBones = slot.skeleton.bones + local skeletonBones = slot.bone.skeleton.bones local weights = self.weights local bones = self.bones - local w, v, b, f = 0, 0, 0, 0 - local n = bones.length + local w, v, b, f = 1, 1, 1, 1 + local n = #bones local wx, wy, bone, vx, vy, weight - if #slot.attachmentVertices == 0 then - while v < n do + if slot.attachmentVerticesCount == 0 then + while v <= n do wx = 0 wy = 0 local nn = bones[v] + v From 678c6b1e14c175b588da23b37f5ceda2d4e0b57c Mon Sep 17 00:00:00 2001 From: Steven Johnson Date: Sat, 20 Feb 2016 22:45:59 -0600 Subject: [PATCH 02/11] Off-by-one error in skinned mesh attachment --- spine-lua/SkinnedMeshAttachment.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spine-lua/SkinnedMeshAttachment.lua b/spine-lua/SkinnedMeshAttachment.lua index 2281ce673..4f57cd63d 100644 --- a/spine-lua/SkinnedMeshAttachment.lua +++ b/spine-lua/SkinnedMeshAttachment.lua @@ -89,7 +89,7 @@ function SkinnedMeshAttachment.new (name) local nn = bones[v] + v v = v + 1 while v <= nn do - bone = skeletonBones[bones[v]] + bone = skeletonBones[bones[v] + 1] vx = weights[b] vy = weights[b + 1] weight = weights[b + 2] From 48f8ea1707fb566eeeda111e768ef887e834fcc3 Mon Sep 17 00:00:00 2001 From: Steven Johnson Date: Tue, 23 Feb 2016 16:30:35 -0600 Subject: [PATCH 03/11] FFD fixes Mesh attachment Animation fixes (still seem to be timing issues looping frames) Fix for loading default skin Zeroing out vertices when loading Miscellaneous indexing and name issues --- spine-lua/Animation.lua | 21 ++++++++++----------- spine-lua/MeshAttachment.lua | 9 +++++---- spine-lua/SkeletonJson.lua | 23 +++++++++++++---------- spine-lua/SkinnedMeshAttachment.lua | 8 ++++---- 4 files changed, 32 insertions(+), 29 deletions(-) diff --git a/spine-lua/Animation.lua b/spine-lua/Animation.lua index c4afe999f..d236a8244 100644 --- a/spine-lua/Animation.lua +++ b/spine-lua/Animation.lua @@ -604,7 +604,7 @@ function Animation.FfdTimeline.new () function self:apply (skeleton, lastTime, time, firedEvents, alpha) local slot = skeleton.slots[self.slotIndex] - if slot.attachment ~= attachment then return end + if slot.attachment ~= self.attachment then return end local frames = self.frames if time < frames[0] then return end -- Time is before first frame. @@ -612,7 +612,7 @@ function Animation.FfdTimeline.new () local frameVertices = self.frameVertices local vertexCount = #frameVertices[0] local vertices = slot.attachmentVertices - if #vertices < vertexCount then + if not vertices or #vertices < vertexCount then vertices = {} slot.attachmentVertices = vertices end @@ -620,16 +620,15 @@ function Animation.FfdTimeline.new () alpha = 1 -- Don't mix from uninitialized slot vertices. end slot.attachmentVerticesCount = vertexCount - - if time >= frames[#frames] then -- Time is after last frame. - local lastVertices = frameVertices[#frames] + if time >= frames[#frames - 1] then -- Time is after last frame. + local lastVertices = frameVertices[#frames - 1] if alpha < 1 then - for i = 0, vertexCount do + for i = 1, vertexCount do local vertex = vertices[i] vertices[i] = vertex + (lastVertices[i] - vertex) * alpha end else - for i = 0, vertexCount do + for i = 1, vertexCount do vertices[i] = lastVertices[i] end end @@ -647,13 +646,13 @@ function Animation.FfdTimeline.new () local nextVertices = frameVertices[frameIndex] if alpha < 1 then - for i = 0, vertexCount do + for i = 1, vertexCount do local prev = prevVertices[i] - local vertices = vertices[i] - vertices[i] = vertices + (prev + (nextVertices[i] - prev) * percent - vertices) * alpha + local vertex = vertices[i] + vertices[i] = vertex + (prev + (nextVertices[i] - prev) * percent - vertex) * alpha end else - for i = 0, vertexCount do + for i = 1, vertexCount do local prev = prevVertices[i] vertices[i] = prev + (nextVertices[i] - prev) * percent end diff --git a/spine-lua/MeshAttachment.lua b/spine-lua/MeshAttachment.lua index 9f6fa2086..ef22e8246 100644 --- a/spine-lua/MeshAttachment.lua +++ b/spine-lua/MeshAttachment.lua @@ -34,7 +34,7 @@ local AttachmentType = require "spine-lua.AttachmentType" local MeshAttachment = {} function MeshAttachment.new (name) if not name then error("name cannot be nil", 2) end - + local self = { name = name, type = AttachmentType.mesh, @@ -46,7 +46,7 @@ function MeshAttachment.new (name) r = 1, g = 1, b = 1, a = 1, path = nil, rendererObject = nil, - regionU = 0, regionV = 0, regionU2 = 0, regionV2 = 0, regionRotate = false, + regionU = 0, regionV = 0, regionU2 = 1, regionV2 = 1, regionRotate = false, regionOffsetX = 0, regionOffsetY = 0, regionWidth = 0, regionHeight = 0, regionOriginalWidth = 0, regionOriginalHeight = 0, @@ -75,12 +75,13 @@ function MeshAttachment.new (name) function self:computeWorldVertices (x, y, slot, worldVertices) local bone = slot.bone +x,y=slot.bone.skeleton.x,slot.bone.skeleton.y x = x + bone.worldX y = y + bone.worldY local m00, m01, m10, m11 = bone.m00, bone.m01, bone.m10, bone.m11 local vertices = self.vertices - local verticesCount = vertices.length - if #slot.attachmentVertices == verticesCount then vertices = slot.attachmentVertices end + local verticesCount = #vertices + if slot.attachmentVertices and #slot.attachmentVertices == verticesCount then vertices = slot.attachmentVertices end for i = 1, verticesCount, 2 do local vx = vertices[i] local vy = vertices[i + 1] diff --git a/spine-lua/SkeletonJson.lua b/spine-lua/SkeletonJson.lua index 7c8a315df..64dcf5263 100755 --- a/spine-lua/SkeletonJson.lua +++ b/spine-lua/SkeletonJson.lua @@ -178,9 +178,8 @@ function SkeletonJson.new (attachmentLoader) end if skin.name == "default" then skeletonData.defaultSkin = skin - else - table.insert(skeletonData.skins, skin) end + table.insert(skeletonData.skins, skin) end end @@ -470,22 +469,21 @@ function SkeletonJson.new (attachmentLoader) local ffd = map["ffd"] if ffd then for skinName,slotMap in pairs(ffd) do - local skin = skeletonData.findSkin(skinName) + local skin = skeletonData:findSkin(skinName) for slotName,meshMap in pairs(slotMap) do - local slotIndex = skeletonData.findSlotIndex(slotName) + local slotIndex = skeletonData:findSlotIndex(slotName) for meshName,values in pairs(meshMap) do local timeline = Animation.FfdTimeline.new() local attachment = skin:getAttachment(slotIndex, meshName) if not attachment then error("FFD attachment not found: " .. meshName) end timeline.slotIndex = slotIndex timeline.attachment = attachment - local isMesh = attachment.type == AttachmentType.mesh local vertexCount if isMesh then - vertexCount = attachment.vertices.length + vertexCount = #attachment.vertices else - vertexCount = attachment.weights.length / 3 * 2 + vertexCount = #attachment.weights / 3 * 2 end local frameIndex = 0 @@ -496,12 +494,18 @@ function SkeletonJson.new (attachmentLoader) vertices = attachment.vertices else vertices = {} - vertices.length = vertexCount + for i = 1, vertexCount do + vertices[i] = 0 + end end else local verticesValue = valueMap["vertices"] - local vertices = {} + local scale = self.scale + vertices = {} local start = valueMap["offset"] or 0 + for ii = 1, start do + vertices[ii] = 0 + end if scale == 1 then for ii = 1, #verticesValue do vertices[ii + start] = verticesValue[ii] @@ -520,7 +524,6 @@ function SkeletonJson.new (attachmentLoader) vertices[vertexCount] = 0 end end - timeline:setFrame(frameIndex, valueMap["time"], vertices) readCurve(timeline, frameIndex, valueMap) frameIndex = frameIndex + 1 diff --git a/spine-lua/SkinnedMeshAttachment.lua b/spine-lua/SkinnedMeshAttachment.lua index 4f57cd63d..e7832aa04 100644 --- a/spine-lua/SkinnedMeshAttachment.lua +++ b/spine-lua/SkinnedMeshAttachment.lua @@ -47,7 +47,7 @@ function SkinnedMeshAttachment.new (name) r = 1, g = 1, b = 1, a = 1, path = nil, rendererObject = nil, - regionU = 0, regionV = 0, regionU2 = 0, regionV2 = 0, regionRotate = false, + regionU = 0, regionV = 0, regionU2 = 1, regionV2 = 1, regionRotate = false, regionOffsetX = 0, regionOffsetY = 0, regionWidth = 0, regionHeight = 0, regionOriginalWidth = 0, regionOriginalHeight = 0, @@ -76,9 +76,9 @@ function SkinnedMeshAttachment.new (name) function self:computeWorldVertices (x, y, slot, worldVertices) local skeletonBones = slot.bone.skeleton.bones +x,y=slot.bone.skeleton.x,slot.bone.skeleton.y local weights = self.weights local bones = self.bones - local w, v, b, f = 1, 1, 1, 1 local n = #bones local wx, wy, bone, vx, vy, weight @@ -104,13 +104,13 @@ function SkinnedMeshAttachment.new (name) end else local ffd = slot.attachmentVertices - while v < n do + while v <= n do wx = 0 wy = 0 local nn = bones[v] + v v = v + 1 while v <= nn do - bone = skeletonBones[bones[v]] + bone = skeletonBones[bones[v] + 1] vx = weights[b] + ffd[f] vy = weights[b + 1] + ffd[f + 1] weight = weights[b + 2] From e1b709c1eb8ef6c70e03d2b5a20cbe34487be5ad Mon Sep 17 00:00:00 2001 From: pharan Date: Fri, 4 Mar 2016 02:20:30 +0800 Subject: [PATCH 04/11] Submeshed MeshGenerator --- .../Mesh Generation/Submeshed.meta | 9 + .../Submeshed/ArraysSubmeshedMeshGenerator.cs | 500 ++++++++++++++++++ .../ArraysSubmeshedMeshGenerator.cs.meta | 12 + .../Submeshed/ISubmeshedMeshGenerator.cs | 69 +++ .../Submeshed/ISubmeshedMeshGenerator.cs.meta | 12 + 5 files changed, 602 insertions(+) create mode 100644 spine-unity/Assets/spine-unity/Mesh Generation/Submeshed.meta create mode 100644 spine-unity/Assets/spine-unity/Mesh Generation/Submeshed/ArraysSubmeshedMeshGenerator.cs create mode 100644 spine-unity/Assets/spine-unity/Mesh Generation/Submeshed/ArraysSubmeshedMeshGenerator.cs.meta create mode 100644 spine-unity/Assets/spine-unity/Mesh Generation/Submeshed/ISubmeshedMeshGenerator.cs create mode 100644 spine-unity/Assets/spine-unity/Mesh Generation/Submeshed/ISubmeshedMeshGenerator.cs.meta diff --git a/spine-unity/Assets/spine-unity/Mesh Generation/Submeshed.meta b/spine-unity/Assets/spine-unity/Mesh Generation/Submeshed.meta new file mode 100644 index 000000000..a6dc6017c --- /dev/null +++ b/spine-unity/Assets/spine-unity/Mesh Generation/Submeshed.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 93a799664eb27fd4183aed06719b306c +folderAsset: yes +timeCreated: 1455486322 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/spine-unity/Assets/spine-unity/Mesh Generation/Submeshed/ArraysSubmeshedMeshGenerator.cs b/spine-unity/Assets/spine-unity/Mesh Generation/Submeshed/ArraysSubmeshedMeshGenerator.cs new file mode 100644 index 000000000..9ab49e702 --- /dev/null +++ b/spine-unity/Assets/spine-unity/Mesh Generation/Submeshed/ArraysSubmeshedMeshGenerator.cs @@ -0,0 +1,500 @@ +using UnityEngine; +using System.Collections; +using System.Collections.Generic; + +using Spine; +using Spine.Unity; + +public class ArraysSubmeshedMeshGenerator : Spine.Unity.ISubmeshedMeshGenerator { + + readonly List separators = new List(); + public List Separators { get { return this.separators; } } + + public bool generateNormals; + public bool generateTangents; + + public float zSpacing = 0f; + + public SubmeshedMeshInstructions GenerateInstructions (Skeleton skeleton) { + if (skeleton == null) throw new System.ArgumentNullException("skeleton"); + + // Count vertices and submesh triangles. + int runningVertexCount = 0; + + int submeshTriangleCount = 0; + int submeshFirstVertex = 0; + int submeshVertexCount = 0; + int submeshStartSlotIndex = 0; + Material lastMaterial = null; + + var drawOrder = skeleton.drawOrder; + var drawOrderItems = drawOrder.Items; + int drawOrderCount = drawOrder.Count; + int separatorCount = separators.Count; + + var instructionList = this.currentInstructions.submeshInstructions; + instructionList.Clear(); + + currentInstructions.attachmentList.Clear(); + + for (int i = 0; i < drawOrderCount; i++) { + var slot = drawOrderItems[i]; + var attachment = slot.attachment; + + object rendererObject; // An AtlasRegion in plain Spine-Unity. eventual source of Material object. + int attachmentVertexCount, attachmentTriangleCount; + + var regionAttachment = attachment as RegionAttachment; + if (regionAttachment != null) { + rendererObject = regionAttachment.RendererObject; + attachmentVertexCount = 4; + attachmentTriangleCount = 6; + } else { + var meshAttachment = attachment as MeshAttachment; + if (meshAttachment != null) { + rendererObject = meshAttachment.RendererObject; + attachmentVertexCount = meshAttachment.vertices.Length >> 1; + attachmentTriangleCount = meshAttachment.triangles.Length; + } else { + var skinnedMeshAttachment = attachment as WeightedMeshAttachment; + if (skinnedMeshAttachment != null) { + rendererObject = skinnedMeshAttachment.RendererObject; + attachmentVertexCount = skinnedMeshAttachment.uvs.Length >> 1; + attachmentTriangleCount = skinnedMeshAttachment.triangles.Length; + } else + continue; + } + } + + var attachmentMaterial = (Material)((AtlasRegion)rendererObject).page.rendererObject; + + // Populate submesh when material changes. (or when forced to separate by a submeshSeparator) + if (( runningVertexCount > 0 && lastMaterial.GetInstanceID() != attachmentMaterial.GetInstanceID() ) || + //if (( lastMaterial != null && lastMaterial.GetInstanceID() != attachmentMaterial.GetInstanceID() ) || + ( separatorCount > 0 && separators.Contains(slot) )) { + + instructionList.Add( + new SubmeshInstructions { + skeleton = skeleton, + material = lastMaterial, + triangleCount = submeshTriangleCount, + vertexCount = submeshVertexCount, + startSlot = submeshStartSlotIndex, + endSlot = i, + firstVertexIndex = submeshFirstVertex + } + ); + + // Prepare for next submesh + submeshTriangleCount = 0; + submeshVertexCount = 0; + submeshFirstVertex = runningVertexCount; + submeshStartSlotIndex = i; + } + lastMaterial = attachmentMaterial; + + submeshTriangleCount += attachmentTriangleCount; + submeshVertexCount += attachmentVertexCount; + runningVertexCount += attachmentVertexCount; + + currentInstructions.attachmentList.Add(attachment); + } + + instructionList.Add( + new SubmeshInstructions { + skeleton = skeleton, + material = lastMaterial, + triangleCount = submeshTriangleCount, + vertexCount = submeshVertexCount, + startSlot = submeshStartSlotIndex, + endSlot = drawOrderCount, + firstVertexIndex = submeshFirstVertex + } + ); + + currentInstructions.vertexCount = runningVertexCount; + return currentInstructions; + } + + public SubmeshedMesh GenerateMesh (SubmeshedMeshInstructions meshInstructions) { + var smartMesh = doubleBufferedSmartMesh.GetNextMesh(); + var mesh = smartMesh.mesh; + + int submeshCount = meshInstructions.submeshInstructions.Count; + + var instructionList = meshInstructions.submeshInstructions; + float zSpacing = this.zSpacing; + float[] attVertBuffer = this.attachmentVertexBuffer; + Vector2[] uvs = this.meshUVs; + Color32[] colors32 = this.meshColors32; + Color32 color; + + // Ensure correct buffer sizes. + Vector3[] vertices = this.meshVertices; + + bool newVertices = vertices == null || meshInstructions.vertexCount > vertices.Length; + int instructionVertexCount = meshInstructions.vertexCount; + if (newVertices) { + this.meshVertices = vertices = new Vector3[instructionVertexCount]; + this.meshColors32 = colors32 = new Color32[instructionVertexCount]; + this.meshUVs = uvs = new Vector2[instructionVertexCount]; + } else { + var zero = Vector3.zero; + for (int i = instructionVertexCount, n = this.meshVertices.Length; i < n; i++) + vertices[i] = zero; + } + + bool newSubmeshBuffers = submeshBuffers.Count < submeshCount; + if (newSubmeshBuffers) { + submeshBuffers.GrowIfNeeded(submeshCount); + for (int i = submeshBuffers.Count; submeshBuffers.Count < submeshCount; i++) { + submeshBuffers.Add(new SubmeshTriangleBuffer(instructionList.Items[i].triangleCount)); + //submeshBuffers.Items[i] = new SubmeshTriangleBuffer(tc); + //submeshBuffers.Count = i; + } + } + + Vector3 meshBoundsMin; + Vector3 meshBoundsMax; + + int attachmentCount = meshInstructions.attachmentList.Count; + + // Initial values for manual Mesh Bounds calculation + if (meshInstructions.attachmentList.Count <= 0) { + meshBoundsMin = new Vector3(0, 0, 0); + meshBoundsMax = new Vector3(0, 0, 0); + } else { + meshBoundsMin.x = int.MaxValue; + meshBoundsMin.y = int.MaxValue; + meshBoundsMax.x = int.MinValue; + meshBoundsMax.y = int.MinValue; + + if (zSpacing > 0f) { + meshBoundsMin.z = 0f; + meshBoundsMax.z = zSpacing * (attachmentCount - 1); + } else { + meshBoundsMin.z = zSpacing * (attachmentCount - 1); + meshBoundsMax.z = 0f; + } + } + + bool structureDoesntMatch = newVertices || newSubmeshBuffers || smartMesh.StructureDoesntMatch(meshInstructions); + + if (structureDoesntMatch) { + mesh.Clear(); + + if (submeshCount == sharedMaterials.Length) + meshInstructions.FillMaterialArray(this.sharedMaterials); + else + this.sharedMaterials = meshInstructions.GetNewMaterialArray(); + } + + int vertexIndex = 0; + + // For each submesh, add vertex data from attachments. + for (int submeshIndex = 0; submeshIndex < submeshCount; submeshIndex++) { + var currentSubmeshInstruction = instructionList.Items[submeshIndex]; + var skeleton = currentSubmeshInstruction.skeleton; + var skeletonDrawOrderItems = skeleton.DrawOrder.Items; + float a = skeleton.a * 255, r = skeleton.r, g = skeleton.g, b = skeleton.b; + + for (int slotIndex = currentSubmeshInstruction.startSlot, endSlot = currentSubmeshInstruction.endSlot; slotIndex < endSlot; slotIndex++) { + var slot = skeletonDrawOrderItems[slotIndex]; + var attachment = slot.attachment; + float z = slotIndex * zSpacing; + + var regionAttachment = attachment as RegionAttachment; + if (regionAttachment != null) { + regionAttachment.ComputeWorldVertices(slot.bone, attVertBuffer); + + float x1 = attVertBuffer[RegionAttachment.X1], y1 = attVertBuffer[RegionAttachment.Y1]; + float x2 = attVertBuffer[RegionAttachment.X2], y2 = attVertBuffer[RegionAttachment.Y2]; + float x3 = attVertBuffer[RegionAttachment.X3], y3 = attVertBuffer[RegionAttachment.Y3]; + float x4 = attVertBuffer[RegionAttachment.X4], y4 = attVertBuffer[RegionAttachment.Y4]; + vertices[vertexIndex].x = x1; vertices[vertexIndex].y = y1; vertices[vertexIndex].z = z; + vertices[vertexIndex + 1].x = x4; vertices[vertexIndex + 1].y = y4; vertices[vertexIndex + 1].z = z; + vertices[vertexIndex + 2].x = x2; vertices[vertexIndex + 2].y = y2; vertices[vertexIndex + 2].z = z; + vertices[vertexIndex + 3].x = x3; vertices[vertexIndex + 3].y = y3; vertices[vertexIndex + 3].z = z; + + color.a = (byte)(a * slot.a * regionAttachment.a); + color.r = (byte)(r * slot.r * regionAttachment.r * color.a); + color.g = (byte)(g * slot.g * regionAttachment.g * color.a); + color.b = (byte)(b * slot.b * regionAttachment.b * color.a); + if (slot.data.blendMode == BlendMode.additive) color.a = 0; + colors32[vertexIndex] = color; colors32[vertexIndex + 1] = color; colors32[vertexIndex + 2] = color; colors32[vertexIndex + 3] = color; + + float[] regionUVs = regionAttachment.uvs; + uvs[vertexIndex].x = regionUVs[RegionAttachment.X1]; uvs[vertexIndex].y = regionUVs[RegionAttachment.Y1]; + uvs[vertexIndex + 1].x = regionUVs[RegionAttachment.X4]; uvs[vertexIndex + 1].y = regionUVs[RegionAttachment.Y4]; + uvs[vertexIndex + 2].x = regionUVs[RegionAttachment.X2]; uvs[vertexIndex + 2].y = regionUVs[RegionAttachment.Y2]; + uvs[vertexIndex + 3].x = regionUVs[RegionAttachment.X3]; uvs[vertexIndex + 3].y = regionUVs[RegionAttachment.Y3]; + + // Calculate min/max X + if (x1 < meshBoundsMin.x) meshBoundsMin.x = x1; + else if (x1 > meshBoundsMax.x) meshBoundsMax.x = x1; + if (x2 < meshBoundsMin.x) meshBoundsMin.x = x2; + else if (x2 > meshBoundsMax.x) meshBoundsMax.x = x2; + if (x3 < meshBoundsMin.x) meshBoundsMin.x = x3; + else if (x3 > meshBoundsMax.x) meshBoundsMax.x = x3; + if (x4 < meshBoundsMin.x) meshBoundsMin.x = x4; + else if (x4 > meshBoundsMax.x) meshBoundsMax.x = x4; + + // Calculate min/max Y + if (y1 < meshBoundsMin.y) meshBoundsMin.y = y1; + else if (y1 > meshBoundsMax.y) meshBoundsMax.y = y1; + if (y2 < meshBoundsMin.y) meshBoundsMin.y = y2; + else if (y2 > meshBoundsMax.y) meshBoundsMax.y = y2; + if (y3 < meshBoundsMin.y) meshBoundsMin.y = y3; + else if (y3 > meshBoundsMax.y) meshBoundsMax.y = y3; + if (y4 < meshBoundsMin.y) meshBoundsMin.y = y4; + else if (y4 > meshBoundsMax.y) meshBoundsMax.y = y4; + + vertexIndex += 4; + } else { + var meshAttachment = attachment as MeshAttachment; + if (meshAttachment != null) { + int meshVertexCount = meshAttachment.vertices.Length; + if (attVertBuffer.Length < meshVertexCount) this.attachmentVertexBuffer = attVertBuffer = new float[meshVertexCount]; + meshAttachment.ComputeWorldVertices(slot, attVertBuffer); + + color.a = (byte)(a * slot.a * meshAttachment.a); + color.r = (byte)(r * slot.r * meshAttachment.r * color.a); + color.g = (byte)(g * slot.g * meshAttachment.g * color.a); + color.b = (byte)(b * slot.b * meshAttachment.b * color.a); + if (slot.data.blendMode == BlendMode.additive) color.a = 0; + + float[] attachmentUVs = meshAttachment.uvs; + for (int iii = 0; iii < meshVertexCount; iii += 2) { + float x = attVertBuffer[iii], y = attVertBuffer[iii + 1]; + vertices[vertexIndex].x = x; vertices[vertexIndex].y = y; vertices[vertexIndex].z = z; + colors32[vertexIndex] = color; uvs[vertexIndex].x = attachmentUVs[iii]; uvs[vertexIndex].y = attachmentUVs[iii + 1]; + + if (x < meshBoundsMin.x) meshBoundsMin.x = x; + else if (x > meshBoundsMax.x) meshBoundsMax.x = x; + + if (y < meshBoundsMin.y) meshBoundsMin.y = y; + else if (y > meshBoundsMax.y) meshBoundsMax.y = y; + + vertexIndex++; + } + } else { + var weightedMeshAttachment = attachment as WeightedMeshAttachment; + if (weightedMeshAttachment != null) { + int meshVertexCount = weightedMeshAttachment.uvs.Length; + if (attVertBuffer.Length < meshVertexCount) this.attachmentVertexBuffer = attVertBuffer = new float[meshVertexCount]; + weightedMeshAttachment.ComputeWorldVertices(slot, attVertBuffer); + + color.a = (byte)(a * slot.a * weightedMeshAttachment.a); + color.r = (byte)(r * slot.r * weightedMeshAttachment.r * color.a); + color.g = (byte)(g * slot.g * weightedMeshAttachment.g * color.a); + color.b = (byte)(b * slot.b * weightedMeshAttachment.b * color.a); + if (slot.data.blendMode == BlendMode.additive) color.a = 0; + + float[] attachmentUVs = weightedMeshAttachment.uvs; + for (int iii = 0; iii < meshVertexCount; iii += 2) { + float x = attVertBuffer[iii], y = attVertBuffer[iii + 1]; + vertices[vertexIndex].x = x; vertices[vertexIndex].y = y; vertices[vertexIndex].z = z; + colors32[vertexIndex] = color; + uvs[vertexIndex].x = attachmentUVs[iii]; uvs[vertexIndex].y = attachmentUVs[iii + 1]; + + if (x < meshBoundsMin.x) meshBoundsMin.x = x; + else if (x > meshBoundsMax.x) meshBoundsMax.x = x; + if (y < meshBoundsMin.y) meshBoundsMin.y = y; + else if (y > meshBoundsMax.y) meshBoundsMax.y = y; + + vertexIndex++; + } + } + } + } + } + + // Push triangles in this submesh + if (structureDoesntMatch) { + smartMesh.mesh.Clear(); // rebuild triangle array. + + var currentSubmesh = submeshBuffers.Items[submeshIndex]; + bool isLastSubmesh = (submeshIndex == submeshCount - 1); + + int triangleCount = currentSubmesh.triangleCount = currentSubmeshInstruction.triangleCount; + int trianglesCapacity = currentSubmesh.triangles.Length; + + int[] triangles = currentSubmesh.triangles; + if (isLastSubmesh) { + if (trianglesCapacity > triangleCount) { + for (int i = triangleCount; i < trianglesCapacity; i++) + triangles[i] = 0; + } + } else if (trianglesCapacity != triangleCount) { + triangles = currentSubmesh.triangles = new int[triangleCount]; + currentSubmesh.triangleCount = 0; + } + + // Iterate through submesh slots and store the triangles. + int triangleIndex = 0; + int afv = currentSubmeshInstruction.firstVertexIndex; // attachment first vertex + + for (int i = currentSubmeshInstruction.startSlot, n = currentSubmeshInstruction.endSlot; i < n; i++) { + var attachment = skeletonDrawOrderItems[i].attachment; + + if (attachment is RegionAttachment) { + triangles[triangleIndex] = afv; triangles[triangleIndex + 1] = afv + 2; triangles[triangleIndex + 2] = afv + 1; + triangles[triangleIndex + 3] = afv + 2; triangles[triangleIndex + 4] = afv + 3; triangles[triangleIndex + 5] = afv + 1; + + triangleIndex += 6; + afv += 4; + } else { + int[] attachmentTriangles; + int attachmentVertexCount; + var meshAttachment = attachment as MeshAttachment; + if (meshAttachment != null) { + attachmentVertexCount = meshAttachment.vertices.Length >> 1; // length/2 + attachmentTriangles = meshAttachment.triangles; + } else { + var weightedMeshAttachment = attachment as WeightedMeshAttachment; + if (weightedMeshAttachment != null) { + attachmentVertexCount = weightedMeshAttachment.uvs.Length >> 1; // length/2 + attachmentTriangles = weightedMeshAttachment.triangles; + } else + continue; + } + + for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii++, triangleIndex++) + triangles[triangleIndex] = afv + attachmentTriangles[ii]; + + afv += attachmentVertexCount; + } + } // Done adding current submesh triangles + } + } + + + Vector3 meshBoundsExtents = (meshBoundsMax - meshBoundsMin); + Vector3 meshCenter = meshBoundsMin + meshBoundsExtents * 0.5f; + + smartMesh.Set(this.meshVertices, this.meshUVs, this.meshColors32, meshInstructions); + mesh.bounds = new Bounds(meshCenter, meshBoundsExtents); + + if (structureDoesntMatch) { + + if (generateNormals) { + int vertexCount = meshInstructions.vertexCount; + Vector3[] normals = new Vector3[vertexCount]; + Vector3 normal = new Vector3(0, 0, -1); + for (int i = 0; i < vertexCount; i++) + normals[i] = normal; + mesh.normals = normals; + + if (generateTangents) { + Vector4[] tangents = new Vector4[vertexCount]; + Vector4 tangent = new Vector4(1, 0, 0, -1); + for (int i = 0; i < vertexCount; i++) + tangents[i] = tangent; + mesh.tangents = tangents; + } + } + + // push new triangles if doesn't match. + mesh.subMeshCount = submeshCount; + for (int i = 0; i < submeshCount; i++) + mesh.SetTriangles(submeshBuffers.Items[i].triangles, i); + } + + + return new SubmeshedMesh(smartMesh.mesh, sharedMaterials); + } + + #region Internals + readonly DoubleBufferedSmartMesh doubleBufferedSmartMesh = new DoubleBufferedSmartMesh(); + readonly SubmeshedMeshInstructions currentInstructions = new SubmeshedMeshInstructions(); + + float[] attachmentVertexBuffer = new float[8]; + Vector3[] meshVertices; + Color32[] meshColors32; + Vector2[] meshUVs; + Material[] sharedMaterials = new Material[0]; + readonly ExposedList submeshBuffers = new ExposedList(); + #endregion + + #region Types + class SubmeshTriangleBuffer { + public int[] triangles; + public int triangleCount; + + public SubmeshTriangleBuffer (int triangleCount) { + triangles = new int[triangleCount]; + this.triangleCount = triangleCount; + } + } + + class DoubleBufferedSmartMesh { + readonly SmartMesh mesh1 = new SmartMesh(); + readonly SmartMesh mesh2 = new SmartMesh(); + bool usingMesh1; + + public SmartMesh GetNextMesh () { + usingMesh1 = !usingMesh1; + return usingMesh1 ? mesh1 : mesh2; + } + } + + // A SmartMesh is a Mesh (with submeshes) that knows what attachments and instructions were used to generate it. + class SmartMesh { + public readonly Mesh mesh = SpineMesh.NewMesh(); + readonly ExposedList attachmentsUsed = new ExposedList(); + readonly ExposedList instructionsUsed = new ExposedList(); + + public void Set (Vector3[] verts, Vector2[] uvs, Color32[] colors, SubmeshedMeshInstructions instructions) { + mesh.vertices = verts; + mesh.uv = uvs; + mesh.colors32 = colors; + + attachmentsUsed.Clear(); + attachmentsUsed.GrowIfNeeded(instructions.attachmentList.Capacity); + attachmentsUsed.Count = instructions.attachmentList.Count; + instructions.attachmentList.CopyTo(attachmentsUsed.Items); + + instructionsUsed.Clear(); + instructionsUsed.GrowIfNeeded(instructions.submeshInstructions.Capacity); + instructionsUsed.Count = instructions.submeshInstructions.Count; + instructions.submeshInstructions.CopyTo(instructionsUsed.Items); + } + + public bool StructureDoesntMatch (SubmeshedMeshInstructions instructions) { + // Check count inequality. + if (instructions.attachmentList.Count != this.attachmentsUsed.Count) return true; + if (instructions.submeshInstructions.Count != this.instructionsUsed.Count) return true; + + //Debug.Log("broadphase matched"); + + // Check each attachment. + var attachmentsPassed = instructions.attachmentList.Items; + var myAttachments = this.attachmentsUsed.Items; + for (int i = 0, n = attachmentsUsed.Count; i < n; i++) + if (attachmentsPassed[i] != myAttachments[i]) return true; + + //Debug.Log("attachments matched"); + + // Check each submesh for equal arrangement. + var instructionListItems = instructions.submeshInstructions.Items; + var myInstructions = this.instructionsUsed.Items; + for (int i = 0, n = this.instructionsUsed.Count; i < n; i++) { + var lhs = instructionListItems[i]; + var rhs = myInstructions[i]; + if ( + lhs.skeleton != rhs.skeleton || + lhs.material.GetInstanceID() != rhs.material.GetInstanceID() || + lhs.startSlot != rhs.startSlot || + lhs.endSlot != rhs.endSlot || + lhs.triangleCount != rhs.triangleCount || + lhs.vertexCount != rhs.vertexCount || + lhs.firstVertexIndex != rhs.firstVertexIndex + ) return true; + } + + //Debug.Log("structure matched"); + return false; + } + } + #endregion +} diff --git a/spine-unity/Assets/spine-unity/Mesh Generation/Submeshed/ArraysSubmeshedMeshGenerator.cs.meta b/spine-unity/Assets/spine-unity/Mesh Generation/Submeshed/ArraysSubmeshedMeshGenerator.cs.meta new file mode 100644 index 000000000..81ce28428 --- /dev/null +++ b/spine-unity/Assets/spine-unity/Mesh Generation/Submeshed/ArraysSubmeshedMeshGenerator.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 412898b02d5caaf489b3ffb29e4ae1c0 +timeCreated: 1455407003 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/spine-unity/Assets/spine-unity/Mesh Generation/Submeshed/ISubmeshedMeshGenerator.cs b/spine-unity/Assets/spine-unity/Mesh Generation/Submeshed/ISubmeshedMeshGenerator.cs new file mode 100644 index 000000000..82030c6e2 --- /dev/null +++ b/spine-unity/Assets/spine-unity/Mesh Generation/Submeshed/ISubmeshedMeshGenerator.cs @@ -0,0 +1,69 @@ +using UnityEngine; +using System.Collections.Generic; + +namespace Spine.Unity { + public interface ISubmeshedMeshGenerator { + /// Generates instructions for how to generate the submeshed mesh based on the given state of the + /// skeleton. The returned instructions can be used to generate a whole submeshed mesh or individual submeshes. + SubmeshedMeshInstructions GenerateInstructions (Skeleton skeleton); + + /// Returns a SubmeshedMesh (a mesh and a material array coupled in a struct). + /// Call GenerateInstructions to get the SubmeshedMeshInstructions to pass into this. + SubmeshedMesh GenerateMesh (SubmeshedMeshInstructions wholeMeshInstruction); + + /// A list of slots that mark the end of a submesh. The slot after it will be the start of a new submesh. + List Separators { get; } + } + + public interface ISingleSubmeshGenerator { + void FillMesh (SubmeshInstructions instructions, Mesh meshToFill); + } + + /// A Submeshed mesh is a return type so the mesh with + /// multiple submeshes can be coupled with a material array to render its submeshes. + public struct SubmeshedMesh { + public readonly Mesh mesh; + public readonly Material[] materials; + public SubmeshedMesh (Mesh mesh, Material[] materials) { + this.mesh = mesh; + this.materials = materials; + } + } + + /// Primarily a collection of Submesh Instructions. This constitutes instructions for how to construct a mesh containing submeshes. + public class SubmeshedMeshInstructions { + public readonly ExposedList submeshInstructions = new ExposedList(); + public readonly ExposedList attachmentList = new ExposedList(); + public int vertexCount = -1; + + /// Allocates a new material array to render this mesh and its constituent submeshes. + public Material[] GetNewMaterialArray () { + var materials = new Material[submeshInstructions.Count]; + FillMaterialArray(materials); + return materials; + } + + /// Fills a given array with the materials needed to render this submeshed mesh. + public void FillMaterialArray (Material[] materialArray) { + var instructionsItems = submeshInstructions.Items; + for (int i = 0, n = materialArray.Length; i < n; i++) + materialArray[i] = instructionsItems[i].material; + } + } + + /// Instructions for how to generate a mesh or submesh out of a range of slots in a given skeleton. + public struct SubmeshInstructions { + public Skeleton skeleton; + public int startSlot; + public int endSlot; + + // Cached values because they are determined in the process of generating instructions, + // but could otherwise be pulled from accessing attachments, checking materials and counting tris and verts. + public Material material; + public int triangleCount; + public int vertexCount; + + // Vertex index offset. Used by submesh generation if part of a bigger mesh. + public int firstVertexIndex; + } +} \ No newline at end of file diff --git a/spine-unity/Assets/spine-unity/Mesh Generation/Submeshed/ISubmeshedMeshGenerator.cs.meta b/spine-unity/Assets/spine-unity/Mesh Generation/Submeshed/ISubmeshedMeshGenerator.cs.meta new file mode 100644 index 000000000..b7a7f946d --- /dev/null +++ b/spine-unity/Assets/spine-unity/Mesh Generation/Submeshed/ISubmeshedMeshGenerator.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: c518552ea622e98418ce673c5febc468 +timeCreated: 1455406760 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 19ff671addc7491f124c59fa3d8cf220ba957e3d Mon Sep 17 00:00:00 2001 From: pharan Date: Fri, 4 Mar 2016 03:05:11 +0800 Subject: [PATCH 05/11] SkeletonRenderer optional custom material per slot. --- .../Assets/spine-unity/SkeletonRenderer.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs index 5b072d5f8..2b96fa816 100644 --- a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs +++ b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs @@ -54,6 +54,10 @@ public class SkeletonRenderer : MonoBehaviour { // Submesh Separation [SpineSlot] public string[] submeshSeparators = new string[0]; [HideInInspector] public List submeshSeparatorSlots = new List(); + + // Custom Slot Material + [System.NonSerialized] private readonly Dictionary customSlotMaterials = new Dictionary(); + public Dictionary CustomSlotMaterials { get { return customSlotMaterials; } } #endregion [System.NonSerialized] public bool valid; @@ -207,6 +211,8 @@ public class SkeletonRenderer : MonoBehaviour { drawOrder.Count != storedAttachments.Count || // Number of slots changed (when does this happen?) immutableTriangles != storedState.immutableTriangles; // Immutable Triangles flag changed. + bool isCustomMaterialsPopulated = customSlotMaterials.Count > 0; + for (int i = 0; i < drawOrderCount; i++) { Slot slot = drawOrderItems[i]; Bone bone = slot.bone; @@ -252,13 +258,22 @@ public class SkeletonRenderer : MonoBehaviour { } #if !SPINE_TK2D - Material material = (Material)((AtlasRegion)rendererObject).page.rendererObject; + // Material material = (Material)((AtlasRegion)rendererObject).page.rendererObject; // For no customSlotMaterials + + Material material; + if (isCustomMaterialsPopulated) { + if (!customSlotMaterials.TryGetValue(slot, out material)) { + material = (Material)((AtlasRegion)rendererObject).page.rendererObject; + } + } else { + material = (Material)((AtlasRegion)rendererObject).page.rendererObject; + } #else Material material = (rendererObject.GetType() == typeof(Material)) ? (Material)rendererObject : (Material)((AtlasRegion)rendererObject).page.rendererObject; #endif // Populate submesh when material changes. (or when forced to separate by a submeshSeparator) - if ((lastMaterial != null && lastMaterial.GetInstanceID() != material.GetInstanceID()) || + if ((vertexCount > 0 && lastMaterial.GetInstanceID() != material.GetInstanceID()) || (submeshSeparatorSlotsCount > 0 && submeshSeparatorSlots.Contains(slot))) { workingSubmeshArguments.Add( From 4d5295055d0c3691aff56542841ef338b503a030 Mon Sep 17 00:00:00 2001 From: John Date: Fri, 4 Mar 2016 21:23:46 +0800 Subject: [PATCH 06/11] Default for manually-created SkeletonDataAsset. --- spine-unity/Assets/spine-unity/Asset Types/SkeletonDataAsset.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spine-unity/Assets/spine-unity/Asset Types/SkeletonDataAsset.cs b/spine-unity/Assets/spine-unity/Asset Types/SkeletonDataAsset.cs index 72fb9d38a..3912a3461 100644 --- a/spine-unity/Assets/spine-unity/Asset Types/SkeletonDataAsset.cs +++ b/spine-unity/Assets/spine-unity/Asset Types/SkeletonDataAsset.cs @@ -41,7 +41,7 @@ public class SkeletonDataAsset : ScriptableObject { public tk2dSpriteCollectionData spriteCollection; #endif public TextAsset skeletonJSON; - public float scale = 1; + public float scale = 0.01f; public String[] fromAnimation; public String[] toAnimation; public float[] duration; From 033a1eeb2c3f3eb9a98b51cecd9b9e9f7ada5108 Mon Sep 17 00:00:00 2001 From: pharan Date: Fri, 4 Mar 2016 23:03:33 +0800 Subject: [PATCH 07/11] Updated Readme.md --- spine-cocos2dx/3/README.md | 54 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/spine-cocos2dx/3/README.md b/spine-cocos2dx/3/README.md index 31f2d056d..c49b1601f 100644 --- a/spine-cocos2dx/3/README.md +++ b/spine-cocos2dx/3/README.md @@ -17,6 +17,60 @@ The Spine Runtimes are developed with the intent to be used with data exported f Alternatively, the contents of the `spine-c/src`, `spine-c/include` and `spine-cocos2dx/3/src` directories can be copied into your project. Be sure your header search path will find the contents of the `spine-c/include` and `spine-cocos2dx/3.1/src` directories. Note that the includes use `spine/Xxx.h`, so the `spine` directory cannot be omitted when copying the files. +## Setup for Cocos Studio Users (cocos2d-x-3.1) +1. Download the Spine Runtimes source using [git](https://help.github.com/articles/set-up-git) or by downloading it [as a zip](https://github.com/EsotericSoftware/spine-runtimes/archive/master.zip). + - Extract them somewhere you can find them. You will only need `spine-c` and `spine-cocos2dx`. +1. Download and install Cocos: http://www.cocos2d-x.org/download + - (2016 March 4) Downloading and installing Cocos Studio and cocos2d-x-3.1 includes a Spine runtime in their source files. For instance, if the default install folder is `C:\Cocos\`, you will find an existing spine-c and spine-cocos2dx runtime inside `C:\Cocos\Cocos2d-x\cocos2d-x-3.10\cocos\editor-support\spine`. + - Replace the contents of that folder with the files from the following Spine runtime folders: + - `spine-c/src/spine` + - `spine-c/include/spine` + - `spine-cocos2dx/3/src/spine` + - Since this is the shared version of the runtime Cocos Studio uses for all projects created through it, updating the Spine runtime here also updates the Spine runtime of all those projects. + +### Examples +1. The `spine-cocos2dx/3/` runtime folder contains a folder named `example`. + - Copy the files in `spine-cocos2dx/3/example/Classes` folder (except `AppDelegate.cpp`, `AppDelegate.h` and `AppMacros.h` into the `Classes` folder of your Cocos Studio project.) +1. Add these files into your C++ Solution. (through your IDE, Visual Studio or Eclipse, etc...) +2. If this is a newly created project, edit the `AppDelegate.cpp`. For this example, we will use RaptorExample. + 1. Add `#include "RaptorExample.h"` to the includes near the top of the file. + 2. Look for the `bool AppDelegate::applicationDidFinishLaunching()` method and replace it with this. Notice the line where it defines the scene `auto scene = RaptorExample::scene()`: + +```cpp +bool AppDelegate::applicationDidFinishLaunching() { + // initialize director + auto director = Director::getInstance(); + auto glview = director->getOpenGLView(); + if(!glview) { + glview = GLViewImpl::createWithRect("Spine Test", Rect(0, 0, 960, 640)); + director->setOpenGLView(glview); + } + + director->getOpenGLView()->setDesignResolutionSize(960, 640, ResolutionPolicy::SHOW_ALL); + + // turn on display FPS + director->setDisplayStats(true); + + // set FPS. the default value is 1.0/60 if you don't call this + director->setAnimationInterval(1.0f / 60); + + FileUtils::getInstance()->addSearchPath("res"); + + // create a scene. it's an autorelease object + auto scene = RaptorExample::scene(); + + // run + director->runWithScene(scene); + + return true; +} +``` + +You can do the same with `GoblinsExample` and `SpineboyExample`. + + + + ## Notes - Images are premultiplied by cocos2d-x, so the Spine atlas images should *not* use premultiplied alpha. From 524aa4fb2d16ba0557c51045ec9a1b3d1c710720 Mon Sep 17 00:00:00 2001 From: pharan Date: Fri, 4 Mar 2016 23:11:48 +0800 Subject: [PATCH 08/11] Updated Readme.md --- spine-cocos2dx/3/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/spine-cocos2dx/3/README.md b/spine-cocos2dx/3/README.md index c49b1601f..0896eb2ad 100644 --- a/spine-cocos2dx/3/README.md +++ b/spine-cocos2dx/3/README.md @@ -29,6 +29,7 @@ Alternatively, the contents of the `spine-c/src`, `spine-c/include` and `spine-c - Since this is the shared version of the runtime Cocos Studio uses for all projects created through it, updating the Spine runtime here also updates the Spine runtime of all those projects. ### Examples +1. Create a **new C++ Cocos Studio project**. Make sure you can find that folder. 1. The `spine-cocos2dx/3/` runtime folder contains a folder named `example`. - Copy the files in `spine-cocos2dx/3/example/Classes` folder (except `AppDelegate.cpp`, `AppDelegate.h` and `AppMacros.h` into the `Classes` folder of your Cocos Studio project.) 1. Add these files into your C++ Solution. (through your IDE, Visual Studio or Eclipse, etc...) From a3311a8d6b17eebed5cb3dab7050e354bed82b50 Mon Sep 17 00:00:00 2001 From: pharan Date: Sat, 5 Mar 2016 03:09:22 +0800 Subject: [PATCH 09/11] Updated Readme.md --- spine-cocos2dx/3/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spine-cocos2dx/3/README.md b/spine-cocos2dx/3/README.md index 0896eb2ad..a7b3485c0 100644 --- a/spine-cocos2dx/3/README.md +++ b/spine-cocos2dx/3/README.md @@ -17,11 +17,11 @@ The Spine Runtimes are developed with the intent to be used with data exported f Alternatively, the contents of the `spine-c/src`, `spine-c/include` and `spine-cocos2dx/3/src` directories can be copied into your project. Be sure your header search path will find the contents of the `spine-c/include` and `spine-cocos2dx/3.1/src` directories. Note that the includes use `spine/Xxx.h`, so the `spine` directory cannot be omitted when copying the files. -## Setup for Cocos Studio Users (cocos2d-x-3.1) +## Setup for Cocos Studio Users (cocos2d-x-3.10) 1. Download the Spine Runtimes source using [git](https://help.github.com/articles/set-up-git) or by downloading it [as a zip](https://github.com/EsotericSoftware/spine-runtimes/archive/master.zip). - Extract them somewhere you can find them. You will only need `spine-c` and `spine-cocos2dx`. 1. Download and install Cocos: http://www.cocos2d-x.org/download - - (2016 March 4) Downloading and installing Cocos Studio and cocos2d-x-3.1 includes a Spine runtime in their source files. For instance, if the default install folder is `C:\Cocos\`, you will find an existing spine-c and spine-cocos2dx runtime inside `C:\Cocos\Cocos2d-x\cocos2d-x-3.10\cocos\editor-support\spine`. + - (2016 March 4) Downloading and installing Cocos Studio and cocos2d-x-3.10 includes a Spine runtime in their source files. For instance, if the default install folder is `C:\Cocos\`, you will find an existing spine-c and spine-cocos2dx runtime inside `C:\Cocos\Cocos2d-x\cocos2d-x-3.10\cocos\editor-support\spine`. - Replace the contents of that folder with the files from the following Spine runtime folders: - `spine-c/src/spine` - `spine-c/include/spine` From fa66c7a15a92f2b981e9dfe9608ddb35d578341e Mon Sep 17 00:00:00 2001 From: pharan Date: Sat, 5 Mar 2016 03:51:06 +0800 Subject: [PATCH 10/11] Useful SkeletonGraphic editor messages. --- .../Editor/SkeletonGraphicInspector.cs | 68 ++++++++----------- .../SkeletonGraphic/SkeletonGraphic.cs | 4 ++ 2 files changed, 31 insertions(+), 41 deletions(-) diff --git a/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs b/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs index f9c9ab1d5..6b2b8c9b5 100644 --- a/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs +++ b/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs @@ -13,7 +13,7 @@ public class SkeletonGraphicInspector : Editor { SerializedProperty material_, color_; SerializedProperty skeletonDataAsset_, initialSkinName_; SerializedProperty startingAnimation_, startingLoop_, timeScale_, freeze_; - #if !PREUNITY_5_2 +#if !PREUNITY_5_2 SerializedProperty raycastTarget_; SkeletonGraphic thisSkeletonGraphic; @@ -38,17 +38,12 @@ public class SkeletonGraphicInspector : Editor { freeze_ = so.FindProperty("freeze"); } - public override void OnInspectorGUI () { - - var s = thisSkeletonGraphic; - s.skeletonDataAsset = SkeletonGraphicInspector.ObjectField(skeletonDataAsset_); - s.material = SkeletonGraphicInspector.ObjectField(material_); - EditorGUI.BeginChangeCheck(); - thisSkeletonGraphic.color = EditorGUILayout.ColorField(color_.displayName, color_.colorValue); - if (EditorGUI.EndChangeCheck()) - SkeletonGraphicInspector.ForceUpdateHack(thisSkeletonGraphic.transform); + + EditorGUILayout.PropertyField(skeletonDataAsset_); + EditorGUILayout.PropertyField(material_); + EditorGUILayout.PropertyField(color_); if (thisSkeletonGraphic.skeletonDataAsset == null) { EditorGUILayout.HelpBox("You need to assign a SkeletonDataAsset first.", MessageType.Info); @@ -62,49 +57,34 @@ public class SkeletonGraphicInspector : Editor { EditorGUILayout.Space(); EditorGUILayout.LabelField("Animation", EditorStyles.boldLabel); EditorGUILayout.PropertyField(startingAnimation_); - s.startingLoop = SkeletonGraphicInspector.BoolField(startingLoop_); - s.timeScale = EditorGUILayout.FloatField(timeScale_.displayName, timeScale_.floatValue); + EditorGUILayout.PropertyField(startingLoop_); + EditorGUILayout.PropertyField(timeScale_); EditorGUILayout.Space(); - s.freeze = SkeletonGraphicInspector.BoolField(freeze_); + EditorGUILayout.PropertyField(freeze_); EditorGUILayout.Space(); EditorGUILayout.LabelField("UI", EditorStyles.boldLabel); - s.raycastTarget = SkeletonGraphicInspector.BoolField(raycastTarget_); + EditorGUILayout.PropertyField(raycastTarget_); - } + bool wasChanged = EditorGUI.EndChangeCheck(); - #region HAX - Thanks, Unity - // colors weren't updating in realtime in the custom inspector. - // Why the hell do I have to do this?? - /// Use this when scene repaint and proper explicit update methods don't work. - public static void ForceUpdateHack (Transform t) { - var origValue = t.localScale; - t.localScale = new Vector3(11f, 22f, 33f); - t.localScale = origValue; + if (wasChanged) { + serializedObject.ApplyModifiedProperties(); + } } - // Hack for Unity 5.3 problem with PropertyField - public static T ObjectField (SerializedProperty property) where T : UnityEngine.Object { - return (T)EditorGUILayout.ObjectField(property.displayName, property.objectReferenceValue, typeof(T), false); - } - - public static bool BoolField (SerializedProperty property) { - return EditorGUILayout.Toggle(property.displayName, property.boolValue); - } - #endregion - #region Menus - [MenuItem ("CONTEXT/SkeletonGraphic/Match RectTransform with Mesh Bounds")] + [MenuItem("CONTEXT/SkeletonGraphic/Match RectTransform with Mesh Bounds")] static void MatchRectTransformWithBounds (MenuCommand command) { var skeletonGraphic = (SkeletonGraphic)command.context; - var mesh = skeletonGraphic.SpineMeshGenerator.LastGeneratedMesh; + var mesh = skeletonGraphic.SpineMeshGenerator.LastGeneratedMesh; var bounds = mesh.bounds; var size = bounds.size; var center = bounds.center; var p = new Vector2( - 0.5f - (center.x / size.x), - 0.5f - (center.y / size.y) - ); + 0.5f - (center.x / size.x), + 0.5f - (center.y / size.y) + ); skeletonGraphic.rectTransform.sizeDelta = size; skeletonGraphic.rectTransform.pivot = p; @@ -112,8 +92,12 @@ public class SkeletonGraphicInspector : Editor { public static Material DefaultSkeletonGraphicMaterial { get { - var guids = AssetDatabase.FindAssets("SkeletonGraphicDefault t:material"); if (guids.Length <= 0) return null; - var firstAssetPath = AssetDatabase.GUIDToAssetPath(guids[0]); if (string.IsNullOrEmpty(firstAssetPath)) return null; + var guids = AssetDatabase.FindAssets("SkeletonGraphicDefault t:material"); + if (guids.Length <= 0) + return null; + var firstAssetPath = AssetDatabase.GUIDToAssetPath(guids[0]); + if (string.IsNullOrEmpty(firstAssetPath)) + return null; var firstMaterial = AssetDatabase.LoadAssetAtPath(firstAssetPath); return firstMaterial; } @@ -204,6 +188,8 @@ public class SkeletonGraphicInspector : Editor { graphic.material = SkeletonGraphicInspector.DefaultSkeletonGraphicMaterial; return go; } + #endregion - #endif + +#endif } diff --git a/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs b/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs index 32cf70734..778495f65 100644 --- a/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs +++ b/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs @@ -89,6 +89,10 @@ public class SkeletonGraphic : MaskableGraphic { protected override void Reset () { base.Reset(); + if (canvas == null) { + Debug.LogWarningFormat("SkeletonGraphic requires a Canvas to be visible. Move this GameObject ({0}) in the Hierarchy so it becomes a child of a Canvas.", gameObject.name); + } + if (material == null || material.shader != Shader.Find("Spine/SkeletonGraphic (Premultiply Alpha)")) { Debug.LogWarning("SkeletonGraphic works best with the SkeletonGraphic material."); } From 7b49e63213ddf8db993a2be64c52754ceb1c2d82 Mon Sep 17 00:00:00 2001 From: pharan Date: Sat, 5 Mar 2016 04:56:01 +0800 Subject: [PATCH 11/11] Clarify SkeletonRendererInspector label and tooltip. --- .../Assets/spine-unity/Editor/SkeletonAnimationInspector.cs | 4 ++-- .../Assets/spine-unity/Editor/SkeletonAnimatorInspector.cs | 4 ++-- .../Assets/spine-unity/Editor/SkeletonRendererInspector.cs | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs b/spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs index b2723b504..90b935b36 100644 --- a/spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs +++ b/spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs @@ -50,8 +50,8 @@ public class SkeletonAnimationInspector : SkeletonRendererInspector { m_isPrefab = true; } - protected override void gui () { - base.gui(); + protected override void DrawInspectorGUI () { + base.DrawInspectorGUI(); SkeletonAnimation component = (SkeletonAnimation)target; if (!component.valid) diff --git a/spine-unity/Assets/spine-unity/Editor/SkeletonAnimatorInspector.cs b/spine-unity/Assets/spine-unity/Editor/SkeletonAnimatorInspector.cs index 9c4bdec98..bf6df6729 100644 --- a/spine-unity/Assets/spine-unity/Editor/SkeletonAnimatorInspector.cs +++ b/spine-unity/Assets/spine-unity/Editor/SkeletonAnimatorInspector.cs @@ -21,8 +21,8 @@ public class SkeletonAnimatorInspector : SkeletonRendererInspector { isPrefab = true; } - protected override void gui () { - base.gui(); + protected override void DrawInspectorGUI () { + base.DrawInspectorGUI(); EditorGUILayout.PropertyField(layerMixModes, true); diff --git a/spine-unity/Assets/spine-unity/Editor/SkeletonRendererInspector.cs b/spine-unity/Assets/spine-unity/Editor/SkeletonRendererInspector.cs index 3fd26bbd7..f2e46c9bd 100644 --- a/spine-unity/Assets/spine-unity/Editor/SkeletonRendererInspector.cs +++ b/spine-unity/Assets/spine-unity/Editor/SkeletonRendererInspector.cs @@ -63,7 +63,7 @@ public class SkeletonRendererInspector : Editor { sortingLayerIDProperty = rendererSerializedObject.FindProperty("m_SortingLayerID"); } - protected virtual void gui () { + protected virtual void DrawInspectorGUI () { SkeletonRenderer component = (SkeletonRenderer)target; EditorGUILayout.BeginHorizontal(); EditorGUILayout.PropertyField(skeletonDataAsset); @@ -134,7 +134,7 @@ public class SkeletonRendererInspector : Editor { EditorGUILayout.PropertyField(submeshSeparators, true); EditorGUILayout.Space(); EditorGUILayout.PropertyField(meshes, - new GUIContent("Render Meshes", "Disable to optimize rendering for skeletons that don't use meshes")); + new GUIContent("Render Mesh Attachments", "Disable to optimize rendering for skeletons that don't use Mesh Attachments")); EditorGUILayout.PropertyField(immutableTriangles, new GUIContent("Immutable Triangles", "Enable to optimize rendering for skeletons that never change attachment visbility")); EditorGUILayout.Space(); @@ -154,7 +154,7 @@ public class SkeletonRendererInspector : Editor { override public void OnInspectorGUI () { serializedObject.Update(); - gui(); + DrawInspectorGUI(); if (serializedObject.ApplyModifiedProperties() || (Event.current.type == EventType.ValidateCommand && Event.current.commandName == "UndoRedoPerformed") ) {