diff --git a/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs b/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs
index f81795313..4715d4732 100644
--- a/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs
+++ b/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs
@@ -919,6 +919,10 @@ namespace Spine.Unity.Editor {
public PointAttachment NewPointAttachment (Skin skin, string name) {
return new PointAttachment(name);
}
+
+ public ClippingAttachment NewClippingAttachment (Skin skin, string name) {
+ return new ClippingAttachment(name);
+ }
}
#endregion
diff --git a/spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysMeshGenerator.cs b/spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysMeshGenerator.cs
index d40973c7a..e69de29bb 100644
--- a/spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysMeshGenerator.cs
+++ b/spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysMeshGenerator.cs
@@ -1,488 +0,0 @@
-/******************************************************************************
- * Spine Runtimes Software License v2.5
- *
- * Copyright (c) 2013-2016, Esoteric Software
- * All rights reserved.
- *
- * You are granted a perpetual, non-exclusive, non-sublicensable, and
- * non-transferable license to use, install, execute, and perform the Spine
- * Runtimes software and derivative works solely for personal or internal
- * use. Without the written permission of Esoteric Software (see Section 2 of
- * the Spine Software License Agreement), you may not (a) modify, translate,
- * adapt, or develop new applications using the Spine Runtimes or otherwise
- * create derivative works or improvements of the Spine Runtimes or (b) remove,
- * delete, alter, or obscure any trademarks or any copyright, trademark, patent,
- * or other intellectual property or proprietary rights notices on or in the
- * Software, including any copy thereof. Redistributions in binary or source
- * form must include this license and terms.
- *
- * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "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 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 THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-#define SPINE_OPTIONAL_NORMALS
-using UnityEngine;
-
-namespace Spine.Unity.MeshGeneration {
- public class ArraysMeshGenerator {
- #region Settings
- public bool PremultiplyVertexColors { get; set; }
- protected bool addNormals;
- public bool AddNormals { get { return addNormals; } set { addNormals = value; } }
- protected bool addTangents;
- public bool AddTangents { get { return addTangents; } set { addTangents = value; } }
- protected bool addBlackTint;
- public bool AddBlackTint { get { return addBlackTint; } set { addBlackTint = value; } }
- #endregion
-
- protected float[] attachmentVertexBuffer = new float[8];
- protected Vector3[] meshVertices;
- protected Color32[] meshColors32;
- protected Vector2[] meshUVs;
-
- #if SPINE_OPTIONAL_NORMALS
- protected Vector3[] meshNormals;
- #endif
- protected Vector4[] meshTangents;
- protected Vector2[] tempTanBuffer;
-
- protected Vector2[] uv2, uv3; // Black tint
-
- public void TryAddNormalsTo (Mesh mesh, int targetVertexCount) {
- #if SPINE_OPTIONAL_NORMALS
- if (addNormals) {
- bool verticesWasResized = this.meshNormals == null || meshNormals.Length < targetVertexCount;
- if (verticesWasResized) {
- this.meshNormals = new Vector3[targetVertexCount];
- Vector3 fixedNormal = new Vector3(0, 0, -1f);
- Vector3[] normals = this.meshNormals;
- for (int i = 0; i < targetVertexCount; i++)
- normals[i] = fixedNormal;
- }
-
- mesh.normals = this.meshNormals;
- }
- #endif
- }
-
- /// Ensures the sizes of the passed array references. If they are not the correct size, a new array will be assigned to the references.
- /// true, if a resize occurred, false otherwise.
- public static bool EnsureSize (int targetVertexCount, ref Vector3[] vertices, ref Vector2[] uvs, ref Color32[] colors) {
- Vector3[] verts = vertices;
- bool verticesWasResized = verts == null || targetVertexCount > verts.Length;
- if (verticesWasResized) {
- // Not enough space, increase size.
- vertices = new Vector3[targetVertexCount];
- colors = new Color32[targetVertexCount];
- uvs = new Vector2[targetVertexCount];
- } else {
- // Too many vertices, zero the extra.
- Vector3 zero = Vector3.zero;
- for (int i = targetVertexCount, n = verts.Length; i < n; i++)
- verts[i] = zero;
- }
- return verticesWasResized;
- }
-
- public static bool EnsureSize (int targetVertexCount, ref Vector2[] buffer) {
- Vector2[] buff = buffer;
- bool verticesWasResized = (buffer == null || targetVertexCount > buffer.Length);
- if (verticesWasResized) {
- buffer = new Vector2[targetVertexCount];
- } else {
- Vector3 zero = Vector3.zero;
- for (int i = targetVertexCount, n = buff.Length; i < n; i++)
- buff[i] = zero;
- }
- return verticesWasResized;
- }
-
- public static bool EnsureTriangleBuffersSize (ExposedList submeshBuffers, int targetSubmeshCount, SubmeshInstruction[] instructionItems) {
- bool submeshBuffersWasResized = submeshBuffers.Count < targetSubmeshCount;
- if (submeshBuffersWasResized) {
- submeshBuffers.GrowIfNeeded(targetSubmeshCount - submeshBuffers.Count);
- for (int i = submeshBuffers.Count; submeshBuffers.Count < targetSubmeshCount; i++)
- submeshBuffers.Add(new SubmeshTriangleBuffer(instructionItems[i].triangleCount));
- }
- return submeshBuffersWasResized;
- }
-
- public static void FillBlackUVs (Skeleton skeleton, int startSlot, int endSlot, Vector2[] uv2, Vector2[] uv3, int vertexIndex, bool renderMeshes = true) {
- var skeletonDrawOrderItems = skeleton.DrawOrder.Items;
- Vector2 rg, b2;
- int vi = vertexIndex;
- b2.y = 1f;
-
- // drawOrder[endSlot] is excluded
- for (int slotIndex = startSlot; slotIndex < endSlot; slotIndex++) {
- var slot = skeletonDrawOrderItems[slotIndex];
- var attachment = slot.attachment;
-
- rg.x = slot.r2; //r
- rg.y = slot.g2; //g
- b2.x = slot.b2; //b
-
- var regionAttachment = attachment as RegionAttachment;
- if (regionAttachment != null) {
- uv2[vi] = rg; uv2[vi + 1] = rg; uv2[vi + 2] = rg; uv2[vi + 3] = rg;
- uv3[vi] = b2; uv3[vi + 1] = b2; uv3[vi + 2] = b2; uv3[vi + 3] = b2;
- vi += 4;
- } else if (renderMeshes) {
- var meshAttachment = attachment as MeshAttachment;
- if (meshAttachment != null) {
- int meshVertexCount = meshAttachment.worldVerticesLength;
- for (int iii = 0; iii < meshVertexCount; iii += 2) {
- uv2[vi] = rg;
- uv3[vi] = b2;
- vi++;
- }
- }
- }
- }
- }
-
- /// Fills Unity vertex data buffers with verts from the Spine Skeleton.
- /// Spine.Skeleton source of the drawOrder array
- /// Slot index of the first slot.
- /// The index bounding the slot list. [endSlot - 1] is the last slot to be added.
- /// Spacing along the z-axis between attachments.
- /// If set to true, vertex colors will be premultiplied. This will also enable additive.
- /// Vertex positions array.
- /// Vertex UV array.
- /// Vertex color array (Color32).
- /// A reference to the running vertex index. This is used when more than one submesh is to be added.
- /// A temporary vertex position buffer for attachment position values.
- /// Reference to the running calculated minimum bounds.
- /// Reference to the running calculated maximum bounds.
- /// Include MeshAttachments. If false, it will ignore MeshAttachments.
- public static void FillVerts (Skeleton skeleton, int startSlot, int endSlot, float zSpacing, bool pmaColors, Vector3[] verts, Vector2[] uvs, Color32[] colors, ref int vertexIndex, ref float[] tempVertBuffer, ref Vector3 boundsMin, ref Vector3 boundsMax, bool renderMeshes = true) {
- Color32 color;
- var skeletonDrawOrderItems = skeleton.DrawOrder.Items;
- float a = skeleton.a * 255, r = skeleton.r, g = skeleton.g, b = skeleton.b;
-
- int vi = vertexIndex;
- var tempVerts = tempVertBuffer;
- Vector3 bmin = boundsMin;
- Vector3 bmax = boundsMax;
-
- // drawOrder[endSlot] is excluded
- for (int slotIndex = startSlot; 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, tempVerts, 0);
-
- float x1 = tempVerts[RegionAttachment.BLX], y1 = tempVerts[RegionAttachment.BLY];
- float x2 = tempVerts[RegionAttachment.ULX], y2 = tempVerts[RegionAttachment.ULY];
- float x3 = tempVerts[RegionAttachment.URX], y3 = tempVerts[RegionAttachment.URY];
- float x4 = tempVerts[RegionAttachment.BRX], y4 = tempVerts[RegionAttachment.BRY];
- verts[vi].x = x1; verts[vi].y = y1; verts[vi].z = z;
- verts[vi + 1].x = x4; verts[vi + 1].y = y4; verts[vi + 1].z = z;
- verts[vi + 2].x = x2; verts[vi + 2].y = y2; verts[vi + 2].z = z;
- verts[vi + 3].x = x3; verts[vi + 3].y = y3; verts[vi + 3].z = z;
-
- if (pmaColors) {
- 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;
- } else {
- color.a = (byte)(a * slot.a * regionAttachment.a);
- color.r = (byte)(r * slot.r * regionAttachment.r * 255);
- color.g = (byte)(g * slot.g * regionAttachment.g * 255);
- color.b = (byte)(b * slot.b * regionAttachment.b * 255);
- }
-
- colors[vi] = color; colors[vi + 1] = color; colors[vi + 2] = color; colors[vi + 3] = color;
-
- float[] regionUVs = regionAttachment.uvs;
- uvs[vi].x = regionUVs[RegionAttachment.BLX]; uvs[vi].y = regionUVs[RegionAttachment.BLY];
- uvs[vi + 1].x = regionUVs[RegionAttachment.BRX]; uvs[vi + 1].y = regionUVs[RegionAttachment.BRY];
- uvs[vi + 2].x = regionUVs[RegionAttachment.ULX]; uvs[vi + 2].y = regionUVs[RegionAttachment.ULY];
- uvs[vi + 3].x = regionUVs[RegionAttachment.URX]; uvs[vi + 3].y = regionUVs[RegionAttachment.URY];
-
- if (x1 < bmin.x) bmin.x = x1; // Potential first attachment bounds initialization. Initial min should not block initial max. Same for Y below.
- if (x1 > bmax.x) bmax.x = x1;
- if (x2 < bmin.x) bmin.x = x2;
- else if (x2 > bmax.x) bmax.x = x2;
- if (x3 < bmin.x) bmin.x = x3;
- else if (x3 > bmax.x) bmax.x = x3;
- if (x4 < bmin.x) bmin.x = x4;
- else if (x4 > bmax.x) bmax.x = x4;
-
- if (y1 < bmin.y) bmin.y = y1;
- if (y1 > bmax.y) bmax.y = y1;
- if (y2 < bmin.y) bmin.y = y2;
- else if (y2 > bmax.y) bmax.y = y2;
- if (y3 < bmin.y) bmin.y = y3;
- else if (y3 > bmax.y) bmax.y = y3;
- if (y4 < bmin.y) bmin.y = y4;
- else if (y4 > bmax.y) bmax.y = y4;
-
- vi += 4;
- } else if (renderMeshes) {
- var meshAttachment = attachment as MeshAttachment;
- if (meshAttachment != null) {
- int meshVertexCount = meshAttachment.worldVerticesLength;
- if (tempVerts.Length < meshVertexCount) tempVerts = new float[meshVertexCount];
- meshAttachment.ComputeWorldVertices(slot, tempVerts);
-
- if (pmaColors) {
- 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;
- } else {
- color.a = (byte)(a * slot.a * meshAttachment.a);
- color.r = (byte)(r * slot.r * meshAttachment.r * 255);
- color.g = (byte)(g * slot.g * meshAttachment.g * 255);
- color.b = (byte)(b * slot.b * meshAttachment.b * 255);
- }
-
- float[] attachmentUVs = meshAttachment.uvs;
-
- // Potential first attachment bounds initialization. See conditions in RegionAttachment logic.
- if (vi == vertexIndex) {
- // Initial min should not block initial max.
- // vi == vertexIndex does not always mean the bounds are fresh. It could be a submesh. Do not nuke old values by omitting the check.
- // Should know that this is the first attachment in the submesh. slotIndex == startSlot could be an empty slot.
- float fx = tempVerts[0], fy = tempVerts[1];
- if (fx < bmin.x) bmin.x = fx;
- if (fx > bmax.x) bmax.x = fx;
- if (fy < bmin.y) bmin.y = fy;
- if (fy > bmax.y) bmax.y = fy;
- }
-
- for (int iii = 0; iii < meshVertexCount; iii += 2) {
- float x = tempVerts[iii], y = tempVerts[iii + 1];
- verts[vi].x = x; verts[vi].y = y; verts[vi].z = z;
- colors[vi] = color; uvs[vi].x = attachmentUVs[iii]; uvs[vi].y = attachmentUVs[iii + 1];
-
- if (x < bmin.x) bmin.x = x;
- else if (x > bmax.x) bmax.x = x;
-
- if (y < bmin.y) bmin.y = y;
- else if (y > bmax.y) bmax.y = y;
-
- vi++;
- }
- }
- }
- }
-
- // ref return values
- vertexIndex = vi;
- tempVertBuffer = tempVerts;
- boundsMin = bmin;
- boundsMax = bmax;
- }
-
-
- /// Fills a submesh triangle buffer array.
- /// Spine.Skeleton source of draw order slots.
- /// The target triangle count.
- /// First vertex of this submesh.
- /// Start slot.
- /// End slot.
- /// The triangle buffer array to be filled. This reference will be replaced in case the triangle values don't fit.
- /// If set to true, the triangle buffer is allowed to be larger than needed.
- public static void FillTriangles (ref int[] triangleBuffer, Skeleton skeleton, int triangleCount, int firstVertex, int startSlot, int endSlot, bool isLastSubmesh) {
- int trianglesCapacity = triangleBuffer.Length;
- int[] tris = triangleBuffer;
-
- if (isLastSubmesh) {
- if (trianglesCapacity > triangleCount) {
- for (int i = triangleCount; i < trianglesCapacity; i++)
- tris[i] = 0;
- } else if (trianglesCapacity < triangleCount) {
- triangleBuffer = tris = new int[triangleCount];
- }
- } else if (trianglesCapacity != triangleCount) {
- triangleBuffer = tris = new int[triangleCount];
- }
-
- var skeletonDrawOrderItems = skeleton.drawOrder.Items;
- for (int i = startSlot, n = endSlot, ti = 0, afv = firstVertex; i < n; i++) {
- var attachment = skeletonDrawOrderItems[i].attachment;
-
- // RegionAttachment
- if (attachment is RegionAttachment) {
- tris[ti] = afv;
- tris[ti + 1] = afv + 2;
- tris[ti + 2] = afv + 1;
- tris[ti + 3] = afv + 2;
- tris[ti + 4] = afv + 3;
- tris[ti + 5] = afv + 1;
- ti += 6;
- afv += 4;
- continue;
- }
-
- // MeshAttachment
- var meshAttachment = attachment as MeshAttachment;
- if (meshAttachment != null) {
- int[] attachmentTriangles = meshAttachment.triangles;
- for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii++, ti++)
- tris[ti] = afv + attachmentTriangles[ii];
-
- afv += meshAttachment.worldVerticesLength >> 1; // length/2;
- }
-
- }
- }
-
- public static void FillTrianglesQuads (ref int[] triangleBuffer, ref int storedTriangleCount, ref int storedFirstVertex, int instructionsFirstVertex, int instructionTriangleCount, bool isLastSubmesh) {
- int trianglesCapacity = triangleBuffer.Length;
-
- if (isLastSubmesh && trianglesCapacity > instructionTriangleCount) {
- for (int i = instructionTriangleCount; i < trianglesCapacity; i++)
- triangleBuffer[i] = 0;
- storedTriangleCount = instructionTriangleCount;
- } else if (trianglesCapacity != instructionTriangleCount) {
- triangleBuffer = new int[instructionTriangleCount];
- storedTriangleCount = 0;
- }
-
- // Use stored quad triangles if possible.
- int[] tris = triangleBuffer;
- if (storedFirstVertex != instructionsFirstVertex || storedTriangleCount < instructionTriangleCount) { //|| storedTriangleCount == 0
- storedTriangleCount = instructionTriangleCount;
- storedFirstVertex = instructionsFirstVertex;
- int afv = instructionsFirstVertex; // attachment first vertex
- for (int ti = 0; ti < instructionTriangleCount; ti += 6, afv += 4) {
- tris[ti] = afv;
- tris[ti + 1] = afv + 2;
- tris[ti + 2] = afv + 1;
- tris[ti + 3] = afv + 2;
- tris[ti + 4] = afv + 3;
- tris[ti + 5] = afv + 1;
- }
- }
- }
-
- /// Creates a UnityEngine.Bounds struct from minimum and maximum value vectors.
- public static Bounds ToBounds (Vector3 boundsMin, Vector3 boundsMax) {
- Vector3 size = (boundsMax - boundsMin);
- return new Bounds((boundsMin + (size * 0.5f)), size);
- }
-
- #region TangentSolver2D
- // Thanks to contributions from forum user ToddRivers
-
- /// Step 1 of solving tangents. Ensure you have buffers of the correct size.
- /// Eventual Vector4[] tangent buffer to assign to Mesh.tangents.
- /// Temporary Vector2 buffer for calculating directions.
- /// Number of vertices that require tangents (or the size of the vertex array)
- public static void SolveTangents2DEnsureSize (ref Vector4[] tangentBuffer, ref Vector2[] tempTanBuffer, int vertexCount) {
- if (tangentBuffer == null || tangentBuffer.Length < vertexCount)
- tangentBuffer = new Vector4[vertexCount];
-
- if (tempTanBuffer == null || tempTanBuffer.Length < vertexCount * 2)
- tempTanBuffer = new Vector2[vertexCount * 2]; // two arrays in one.
- }
-
- /// Step 2 of solving tangents. Fills (part of) a temporary tangent-solution buffer based on the vertices and uvs defined by a submesh's triangle buffer. Only needs to be called once for single-submesh meshes.
- /// A temporary Vector3[] for calculating tangents.
- /// The mesh's current vertex position buffer.
- /// The mesh's current triangles buffer.
- /// The mesh's current uvs buffer.
- /// Number of vertices that require tangents (or the size of the vertex array)
- /// The number of triangle indexes in the triangle array to be used.
- public static void SolveTangents2DTriangles (Vector2[] tempTanBuffer, int[] triangles, int triangleCount, Vector3[] vertices, Vector2[] uvs, int vertexCount) {
- Vector2 sdir;
- Vector2 tdir;
- for (int t = 0; t < triangleCount; t += 3) {
- int i1 = triangles[t + 0];
- int i2 = triangles[t + 1];
- int i3 = triangles[t + 2];
-
- Vector3 v1 = vertices[i1];
- Vector3 v2 = vertices[i2];
- Vector3 v3 = vertices[i3];
-
- Vector2 w1 = uvs[i1];
- Vector2 w2 = uvs[i2];
- Vector2 w3 = uvs[i3];
-
- float x1 = v2.x - v1.x;
- float x2 = v3.x - v1.x;
- float y1 = v2.y - v1.y;
- float y2 = v3.y - v1.y;
-
- float s1 = w2.x - w1.x;
- float s2 = w3.x - w1.x;
- float t1 = w2.y - w1.y;
- float t2 = w3.y - w1.y;
-
- float div = s1 * t2 - s2 * t1;
- float r = (div == 0f) ? 0f : 1f / div;
-
- sdir.x = (t2 * x1 - t1 * x2) * r;
- sdir.y = (t2 * y1 - t1 * y2) * r;
- tempTanBuffer[i1] = tempTanBuffer[i2] = tempTanBuffer[i3] = sdir;
-
- tdir.x = (s1 * x2 - s2 * x1) * r;
- tdir.y = (s1 * y2 - s2 * y1) * r;
- tempTanBuffer[vertexCount + i1] = tempTanBuffer[vertexCount + i2] = tempTanBuffer[vertexCount + i3] = tdir;
- }
- }
-
- /// Step 3 of solving tangents. Fills a Vector4[] tangents array according to values calculated in step 2.
- /// A Vector4[] that will eventually be used to set Mesh.tangents
- /// A temporary Vector3[] for calculating tangents.
- /// Number of vertices that require tangents (or the size of the vertex array)
- public static void SolveTangents2DBuffer (Vector4[] tangents, Vector2[] tempTanBuffer, int vertexCount) {
-
- Vector4 tangent;
- tangent.z = 0;
- for (int i = 0; i < vertexCount; ++i) {
- Vector2 t = tempTanBuffer[i];
-
- // t.Normalize() (aggressively inlined). Even better if offloaded to GPU via vertex shader.
- float magnitude = Mathf.Sqrt(t.x * t.x + t.y * t.y);
- if (magnitude > 1E-05) {
- float reciprocalMagnitude = 1f/magnitude;
- t.x *= reciprocalMagnitude;
- t.y *= reciprocalMagnitude;
- }
-
- Vector2 t2 = tempTanBuffer[vertexCount + i];
- tangent.x = t.x;
- tangent.y = t.y;
- //tangent.z = 0;
- tangent.w = (t.y * t2.x > t.x * t2.y) ? 1 : -1; // 2D direction calculation. Used for binormals.
- tangents[i] = tangent;
- }
-
- }
- #endregion
-
- #region SubmeshTriangleBuffer
- public class SubmeshTriangleBuffer {
- public int[] triangles;
- public int triangleCount; // for last/single submeshes with potentially zeroed triangles.
- public int firstVertex = -1; // for !renderMeshes.
-
- public SubmeshTriangleBuffer () { }
-
- public SubmeshTriangleBuffer (int triangleCount) {
- triangles = new int[triangleCount];
- }
- }
- #endregion
-
- }
-}
diff --git a/spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysSimpleMeshGenerator.cs b/spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysSimpleMeshGenerator.cs
index e4d9de211..e69de29bb 100644
--- a/spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysSimpleMeshGenerator.cs
+++ b/spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysSimpleMeshGenerator.cs
@@ -1,144 +0,0 @@
-/******************************************************************************
- * Spine Runtimes Software License v2.5
- *
- * Copyright (c) 2013-2016, Esoteric Software
- * All rights reserved.
- *
- * You are granted a perpetual, non-exclusive, non-sublicensable, and
- * non-transferable license to use, install, execute, and perform the Spine
- * Runtimes software and derivative works solely for personal or internal
- * use. Without the written permission of Esoteric Software (see Section 2 of
- * the Spine Software License Agreement), you may not (a) modify, translate,
- * adapt, or develop new applications using the Spine Runtimes or otherwise
- * create derivative works or improvements of the Spine Runtimes or (b) remove,
- * delete, alter, or obscure any trademarks or any copyright, trademark, patent,
- * or other intellectual property or proprietary rights notices on or in the
- * Software, including any copy thereof. Redistributions in binary or source
- * form must include this license and terms.
- *
- * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "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 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 THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-using UnityEngine;
-
-namespace Spine.Unity.MeshGeneration {
- public class ArraysSimpleMeshGenerator : ArraysMeshGenerator, ISimpleMeshGenerator {
- #region Settings
- protected float scale = 1f;
- public float Scale { get { return scale; } set { scale = value; } }
- public float ZSpacing { get; set; }
- #endregion
-
- protected Mesh lastGeneratedMesh;
- public Mesh LastGeneratedMesh { get { return lastGeneratedMesh; } }
-
- readonly DoubleBufferedMesh doubleBufferedMesh = new DoubleBufferedMesh();
- int[] triangles;
-
- public Mesh GenerateMesh (Skeleton skeleton) {
- int totalVertexCount = 0; // size of vertex arrays
- int totalTriangleCount = 0; // size of index array
-
- // STEP 1 : GenerateInstruction(). Count verts and tris to determine array sizes.
- var drawOrderItems = skeleton.drawOrder.Items;
- int drawOrderCount = skeleton.drawOrder.Count;
- for (int i = 0; i < drawOrderCount; i++) {
- Slot slot = drawOrderItems[i];
- Attachment attachment = slot.attachment;
- int attachmentVertexCount, attachmentTriangleCount;
- var regionAttachment = attachment as RegionAttachment;
- if (regionAttachment != null) {
- attachmentVertexCount = 4;
- attachmentTriangleCount = 6;
- } else {
- var meshAttachment = attachment as MeshAttachment;
- if (meshAttachment != null) {
- attachmentVertexCount = meshAttachment.worldVerticesLength >> 1;
- attachmentTriangleCount = meshAttachment.triangles.Length;
- } else {
- continue;
- }
- }
- totalTriangleCount += attachmentTriangleCount;
- totalVertexCount += attachmentVertexCount;
- }
-
- // STEP 2 : Ensure buffers are the correct size
- ArraysMeshGenerator.EnsureSize(totalVertexCount, ref this.meshVertices, ref this.meshUVs, ref this.meshColors32);
- if (addBlackTint) {
- ArraysMeshGenerator.EnsureSize(totalVertexCount, ref this.uv2);
- ArraysMeshGenerator.EnsureSize(totalVertexCount, ref this.uv3);
- }
-
- this.triangles = this.triangles ?? new int[totalTriangleCount];
-
- // STEP 3 : Update vertex buffer
- const float zFauxHalfThickness = 0.01f; // Somehow needs this thickness for bounds to work properly in some cases (eg, Unity UI clipping)
- Vector3 meshBoundsMin;
- Vector3 meshBoundsMax;
- if (totalVertexCount == 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;
- meshBoundsMin.z = -zFauxHalfThickness * scale;
- meshBoundsMax.z = zFauxHalfThickness * scale;
-
- int vertexIndex = 0;
- if (addBlackTint) ArraysMeshGenerator.FillBlackUVs(skeleton, 0, drawOrderCount, this.uv2, this.uv3, vertexIndex);
- ArraysMeshGenerator.FillVerts(skeleton, 0, drawOrderCount, this.ZSpacing, this.PremultiplyVertexColors, this.meshVertices, this.meshUVs, this.meshColors32, ref vertexIndex, ref this.attachmentVertexBuffer, ref meshBoundsMin, ref meshBoundsMax);
-
- // Apply scale to vertices
- meshBoundsMax.x *= scale; meshBoundsMax.y *= scale;
- meshBoundsMin.x *= scale; meshBoundsMin.y *= scale;
- var vertices = this.meshVertices;
- for (int i = 0; i < totalVertexCount; i++) {
- Vector3 p = vertices[i];
- p.x *= scale;
- p.y *= scale;
- vertices[i] = p;
- }
- }
-
- // Step 4 : Update Triangles buffer
- ArraysMeshGenerator.FillTriangles(ref this.triangles, skeleton, totalTriangleCount, 0, 0, drawOrderCount, true);
-
- // Step 5 : Update Mesh with buffers
- var mesh = doubleBufferedMesh.GetNextMesh();
- mesh.vertices = this.meshVertices;
- mesh.colors32 = meshColors32;
- mesh.uv = meshUVs;
- mesh.bounds = ArraysMeshGenerator.ToBounds(meshBoundsMin, meshBoundsMax);
- mesh.triangles = triangles;
- if (addBlackTint) {
- mesh.uv2 = this.uv2;
- mesh.uv3 = this.uv3;
- }
-
- TryAddNormalsTo(mesh, totalVertexCount);
-
- if (addTangents) {
- SolveTangents2DEnsureSize(ref this.meshTangents, ref this.tempTanBuffer, totalVertexCount);
- SolveTangents2DTriangles(this.tempTanBuffer, triangles, totalTriangleCount, meshVertices, meshUVs, totalVertexCount);
- SolveTangents2DBuffer(this.meshTangents, this.tempTanBuffer, totalVertexCount);
- }
-
- lastGeneratedMesh = mesh;
- return mesh;
- }
-
- }
-
-}
diff --git a/spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysSubmeshSetMeshGenerator.cs b/spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysSubmeshSetMeshGenerator.cs
index 90f60d8b5..e69de29bb 100644
--- a/spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysSubmeshSetMeshGenerator.cs
+++ b/spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysSubmeshSetMeshGenerator.cs
@@ -1,232 +0,0 @@
-/******************************************************************************
- * Spine Runtimes Software License v2.5
- *
- * Copyright (c) 2013-2016, Esoteric Software
- * All rights reserved.
- *
- * You are granted a perpetual, non-exclusive, non-sublicensable, and
- * non-transferable license to use, install, execute, and perform the Spine
- * Runtimes software and derivative works solely for personal or internal
- * use. Without the written permission of Esoteric Software (see Section 2 of
- * the Spine Software License Agreement), you may not (a) modify, translate,
- * adapt, or develop new applications using the Spine Runtimes or otherwise
- * create derivative works or improvements of the Spine Runtimes or (b) remove,
- * delete, alter, or obscure any trademarks or any copyright, trademark, patent,
- * or other intellectual property or proprietary rights notices on or in the
- * Software, including any copy thereof. Redistributions in binary or source
- * form must include this license and terms.
- *
- * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "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 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 THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-using UnityEngine;
-
-namespace Spine.Unity.MeshGeneration {
- public class ArraysSubmeshSetMeshGenerator : ArraysMeshGenerator, ISubmeshSetMeshGenerator {
- #region Settings
- public float ZSpacing { get; set; }
- #endregion
-
- readonly DoubleBuffered doubleBufferedSmartMesh = new DoubleBuffered();
- readonly ExposedList currentInstructions = new ExposedList();
- readonly ExposedList attachmentBuffer = new ExposedList();
- readonly ExposedList submeshBuffers = new ExposedList();
- Material[] sharedMaterials = new Material[0];
-
- ///
- /// Generates a mesh based on a subset of instructions.
- ///
- /// A UnityEngine.Mesh.
- /// A list of SubmeshInstructions.
- /// The index of the starting submesh.
- /// The exclusive upper bound of the last submesh to be included.
- public MeshAndMaterials GenerateMesh (ExposedList instructions, int startSubmesh, int endSubmesh, float scale = 1f) {
- // STEP 0: Prepare instructions.
- var paramItems = instructions.Items;
- currentInstructions.Clear(false);
- for (int i = startSubmesh, n = endSubmesh; i < n; i++) {
- this.currentInstructions.Add(paramItems[i]);
- }
- var smartMesh = doubleBufferedSmartMesh.GetNext();
- var mesh = smartMesh.mesh;
- int submeshCount = currentInstructions.Count;
- var currentInstructionsItems = currentInstructions.Items;
- int vertexCount = 0;
- for (int i = 0; i < submeshCount; i++) {
- currentInstructionsItems[i].firstVertexIndex = vertexCount;// Ensure current instructions have correct cached values.
- vertexCount += currentInstructionsItems[i].vertexCount; // vertexCount will also be used for the rest of this method.
- }
-
- // STEP 1: Ensure correct buffer sizes.
- bool vertBufferResized = ArraysMeshGenerator.EnsureSize(vertexCount, ref this.meshVertices, ref this.meshUVs, ref this.meshColors32);
- bool submeshBuffersResized = ArraysMeshGenerator.EnsureTriangleBuffersSize(submeshBuffers, submeshCount, currentInstructionsItems);
- if (addBlackTint) {
- ArraysMeshGenerator.EnsureSize(vertexCount, ref this.uv2);
- ArraysMeshGenerator.EnsureSize(vertexCount, ref this.uv3);
- }
-
- // STEP 2: Update buffers based on Skeleton.
-
- // Initial values for manual Mesh Bounds calculation
- Vector3 meshBoundsMin;
- Vector3 meshBoundsMax;
- float zSpacing = this.ZSpacing;
- if (vertexCount <= 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;
-
- int endSlot = currentInstructionsItems[submeshCount - 1].endSlot;
- if (zSpacing > 0f) {
- meshBoundsMin.z = 0f;
- meshBoundsMax.z = zSpacing * endSlot;
- } else {
- meshBoundsMin.z = zSpacing * endSlot;
- meshBoundsMax.z = 0f;
- }
- }
-
- // For each submesh, add vertex data from attachments.
- var workingAttachments = this.attachmentBuffer;
- workingAttachments.Clear(false);
- int vertexIndex = 0; // modified by FillVerts
- for (int submeshIndex = 0; submeshIndex < submeshCount; submeshIndex++) {
- var currentInstruction = currentInstructionsItems[submeshIndex];
- int startSlot = currentInstruction.startSlot;
- int endSlot = currentInstruction.endSlot;
- var skeleton = currentInstruction.skeleton;
- var skeletonDrawOrderItems = skeleton.DrawOrder.Items;
- for (int i = startSlot; i < endSlot; i++) {
- var ca = skeletonDrawOrderItems[i].attachment;
- if (ca != null) workingAttachments.Add(ca); // Includes BoundingBoxes. This is ok.
- }
- if (addBlackTint) ArraysMeshGenerator.FillBlackUVs(skeleton, startSlot, endSlot, this.uv2, this.uv3, vertexIndex);
- ArraysMeshGenerator.FillVerts(skeleton, startSlot, endSlot, zSpacing, this.PremultiplyVertexColors, this.meshVertices, this.meshUVs, this.meshColors32, ref vertexIndex, ref this.attachmentVertexBuffer, ref meshBoundsMin, ref meshBoundsMax);
- }
-
- bool structureDoesntMatch = vertBufferResized || submeshBuffersResized || smartMesh.StructureDoesntMatch(workingAttachments, currentInstructions);
- for (int submeshIndex = 0; submeshIndex < submeshCount; submeshIndex++) {
- var currentInstruction = currentInstructionsItems[submeshIndex];
- if (structureDoesntMatch) {
- var currentBuffer = submeshBuffers.Items[submeshIndex];
- bool isLastSubmesh = (submeshIndex == submeshCount - 1);
- ArraysMeshGenerator.FillTriangles(ref currentBuffer.triangles, currentInstruction.skeleton, currentInstruction.triangleCount, currentInstruction.firstVertexIndex, currentInstruction.startSlot, currentInstruction.endSlot, isLastSubmesh);
- currentBuffer.triangleCount = currentInstruction.triangleCount;
- currentBuffer.firstVertex = currentInstruction.firstVertexIndex;
- }
- }
-
- if (structureDoesntMatch) {
- mesh.Clear();
- this.sharedMaterials = currentInstructions.GetUpdatedMaterialArray(this.sharedMaterials);
- }
-
- if (scale != 1f) {
- for (int i = 0; i < vertexCount; i++) {
- meshVertices[i].x *= scale;
- meshVertices[i].y *= scale;
- //meshVertices[i].z *= scale;
- }
- }
-
- // STEP 3: Assign the buffers into the Mesh.
- smartMesh.Set(this.meshVertices, this.meshUVs, this.meshColors32, workingAttachments, currentInstructions);
- mesh.bounds = ArraysMeshGenerator.ToBounds(meshBoundsMin, meshBoundsMax);
- if (addBlackTint) {
- mesh.uv2 = this.uv2;
- mesh.uv3 = this.uv3;
- }
-
- if (structureDoesntMatch) {
- // Push new triangles if doesn't match.
- mesh.subMeshCount = submeshCount;
- for (int i = 0; i < submeshCount; i++)
- mesh.SetTriangles(submeshBuffers.Items[i].triangles, i);
-
- this.TryAddNormalsTo(mesh, vertexCount);
- }
-
- if (addTangents) {
- SolveTangents2DEnsureSize(ref this.meshTangents, ref this.tempTanBuffer, vertexCount);
-
- for (int i = 0, n = submeshCount; i < n; i++) {
- var submesh = submeshBuffers.Items[i];
- SolveTangents2DTriangles(this.tempTanBuffer, submesh.triangles, submesh.triangleCount, meshVertices, meshUVs, vertexCount);
- }
-
- SolveTangents2DBuffer(this.meshTangents, this.tempTanBuffer, vertexCount);
- }
-
- return new MeshAndMaterials(smartMesh.mesh, sharedMaterials);
- }
-
- #region Types
- // 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, ExposedList attachments, ExposedList instructions) {
- mesh.vertices = verts;
- mesh.uv = uvs;
- mesh.colors32 = colors;
-
- attachmentsUsed.Clear(false);
- attachmentsUsed.GrowIfNeeded(attachments.Capacity);
- attachmentsUsed.Count = attachments.Count;
- attachments.CopyTo(attachmentsUsed.Items);
-
- instructionsUsed.Clear(false);
- instructionsUsed.GrowIfNeeded(instructions.Capacity);
- instructionsUsed.Count = instructions.Count;
- instructions.CopyTo(instructionsUsed.Items);
- }
-
- public bool StructureDoesntMatch (ExposedList attachments, ExposedList instructions) {
- // Check count inequality.
- if (attachments.Count != this.attachmentsUsed.Count) return true;
- if (instructions.Count != this.instructionsUsed.Count) return true;
-
- // Check each attachment.
- var attachmentsPassed = attachments.Items;
- var myAttachments = this.attachmentsUsed.Items;
- for (int i = 0, n = attachmentsUsed.Count; i < n; i++)
- if (attachmentsPassed[i] != myAttachments[i]) return true;
-
- // Check each submesh for equal arrangement.
- var instructionListItems = instructions.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.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;
- }
-
- return false;
- }
- }
- #endregion
- }
-
-}
diff --git a/spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysSubmeshedMeshGenerator.cs b/spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysSubmeshedMeshGenerator.cs
index 4edaccaf8..e69de29bb 100644
--- a/spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysSubmeshedMeshGenerator.cs
+++ b/spine-unity/Assets/spine-unity/Mesh Generation/Arrays/ArraysSubmeshedMeshGenerator.cs
@@ -1,315 +0,0 @@
-/******************************************************************************
- * Spine Runtimes Software License v2.5
- *
- * Copyright (c) 2013-2016, Esoteric Software
- * All rights reserved.
- *
- * You are granted a perpetual, non-exclusive, non-sublicensable, and
- * non-transferable license to use, install, execute, and perform the Spine
- * Runtimes software and derivative works solely for personal or internal
- * use. Without the written permission of Esoteric Software (see Section 2 of
- * the Spine Software License Agreement), you may not (a) modify, translate,
- * adapt, or develop new applications using the Spine Runtimes or otherwise
- * create derivative works or improvements of the Spine Runtimes or (b) remove,
- * delete, alter, or obscure any trademarks or any copyright, trademark, patent,
- * or other intellectual property or proprietary rights notices on or in the
- * Software, including any copy thereof. Redistributions in binary or source
- * form must include this license and terms.
- *
- * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "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 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 THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-using UnityEngine;
-using System.Collections.Generic;
-
-namespace Spine.Unity.MeshGeneration {
- ///
- /// Arrays submeshed mesh generator.
- ///
- public class ArraysSubmeshedMeshGenerator : ArraysMeshGenerator, ISubmeshedMeshGenerator, System.IDisposable {
-
- readonly List separators = new List();
- public List Separators { get { return this.separators; } }
-
- #region Settings
- public float ZSpacing { get; set; }
- #endregion
-
- readonly DoubleBuffered doubleBufferedSmartMesh = new DoubleBuffered();
- readonly SubmeshedMeshInstruction currentInstructions = new SubmeshedMeshInstruction();
- readonly ExposedList submeshBuffers = new ExposedList();
- Material[] sharedMaterials = new Material[0];
-
- public void Dispose () {
- doubleBufferedSmartMesh.GetNext().Dispose();
- doubleBufferedSmartMesh.GetNext().Dispose();
- }
-
- public SubmeshedMeshInstruction GenerateInstruction (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(false);
-
- currentInstructions.attachmentList.Clear(false);
-
- 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.worldVerticesLength >> 1;
- attachmentTriangleCount = meshAttachment.triangles.Length;
- } else {
- continue;
- }
- }
-
- var attachmentMaterial = (Material)((AtlasRegion)rendererObject).page.rendererObject;
-
- // Populate submesh when material changes. (or when forced to separate by a submeshSeparator)
- bool separatedBySlot = ( separatorCount > 0 && separators.Contains(slot) );
- if (( runningVertexCount > 0 && lastMaterial.GetInstanceID() != attachmentMaterial.GetInstanceID() ) || separatedBySlot) {
-
- instructionList.Add(
- new SubmeshInstruction {
- skeleton = skeleton,
- material = lastMaterial,
- triangleCount = submeshTriangleCount,
- vertexCount = submeshVertexCount,
- startSlot = submeshStartSlotIndex,
- endSlot = i,
- firstVertexIndex = submeshFirstVertex,
- forceSeparate = separatedBySlot
- }
- );
-
- // 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 SubmeshInstruction {
- skeleton = skeleton,
- material = lastMaterial,
- triangleCount = submeshTriangleCount,
- vertexCount = submeshVertexCount,
- startSlot = submeshStartSlotIndex,
- endSlot = drawOrderCount,
- firstVertexIndex = submeshFirstVertex,
- forceSeparate = false
- }
- );
-
- currentInstructions.vertexCount = runningVertexCount;
- return currentInstructions;
- }
-
- // ISubmeshedMeshGenerator.GenerateMesh
- /// Generates a mesh based on SubmeshedMeshInstructions
- public MeshAndMaterials GenerateMesh (SubmeshedMeshInstruction meshInstructions) {
- var smartMesh = doubleBufferedSmartMesh.GetNext();
- var mesh = smartMesh.mesh;
- int submeshCount = meshInstructions.submeshInstructions.Count;
- var instructionList = meshInstructions.submeshInstructions;
-
- // STEP 1: Ensure correct buffer sizes.
- int vertexCount = meshInstructions.vertexCount;
- bool submeshBuffersResized = ArraysMeshGenerator.EnsureTriangleBuffersSize(submeshBuffers, submeshCount, instructionList.Items);
- bool vertBufferResized = ArraysMeshGenerator.EnsureSize(vertexCount, ref this.meshVertices, ref this.meshUVs, ref this.meshColors32);
- Vector3[] vertices = this.meshVertices;
-
- if (addBlackTint) {
- ArraysMeshGenerator.EnsureSize(vertexCount, ref this.uv2);
- ArraysMeshGenerator.EnsureSize(vertexCount, ref this.uv3);
- }
-
- // STEP 2: Update buffers based on Skeleton.
- float zSpacing = this.ZSpacing;
- Vector3 meshBoundsMin;
- Vector3 meshBoundsMax;
- int attachmentCount = meshInstructions.attachmentList.Count;
- if (attachmentCount <= 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 = vertBufferResized || submeshBuffersResized || smartMesh.StructureDoesntMatch(meshInstructions);
- // For each submesh, add vertex data from attachments. Also triangles, but only if needed.
- int vertexIndex = 0; // modified by FillVerts
- for (int submeshIndex = 0; submeshIndex < submeshCount; submeshIndex++) {
- var submeshInstruction = instructionList.Items[submeshIndex];
- int start = submeshInstruction.startSlot;
- int end = submeshInstruction.endSlot;
- var skeleton = submeshInstruction.skeleton;
- if (addBlackTint) ArraysMeshGenerator.FillBlackUVs(skeleton, start, end, this.uv2, this.uv3, vertexIndex);
- ArraysMeshGenerator.FillVerts(skeleton, start, end, zSpacing, this.PremultiplyVertexColors, vertices, this.meshUVs, this.meshColors32, ref vertexIndex, ref this.attachmentVertexBuffer, ref meshBoundsMin, ref meshBoundsMax);
-
- if (structureDoesntMatch) {
- var currentBuffer = submeshBuffers.Items[submeshIndex];
- bool isLastSubmesh = (submeshIndex == submeshCount - 1);
- ArraysMeshGenerator.FillTriangles(ref currentBuffer.triangles, skeleton, submeshInstruction.triangleCount, submeshInstruction.firstVertexIndex, start, end, isLastSubmesh);
- currentBuffer.triangleCount = submeshInstruction.triangleCount;
- currentBuffer.firstVertex = submeshInstruction.firstVertexIndex;
- }
- }
-
- if (structureDoesntMatch) {
- mesh.Clear();
- this.sharedMaterials = meshInstructions.GetUpdatedMaterialArray(this.sharedMaterials);
- }
-
- // STEP 3: Assign the buffers into the Mesh.
- smartMesh.Set(this.meshVertices, this.meshUVs, this.meshColors32, meshInstructions);
- if (addBlackTint) {
- mesh.uv2 = this.uv2;
- mesh.uv3 = this.uv3;
- }
- mesh.bounds = ArraysMeshGenerator.ToBounds(meshBoundsMin, meshBoundsMax);
-
- if (structureDoesntMatch) {
- // Push new triangles if doesn't match.
- mesh.subMeshCount = submeshCount;
- for (int i = 0; i < submeshCount; i++)
- mesh.SetTriangles(submeshBuffers.Items[i].triangles, i);
-
- TryAddNormalsTo(mesh, vertexCount);
- }
-
- if (addTangents) {
- SolveTangents2DEnsureSize(ref this.meshTangents, ref this.tempTanBuffer, vertexCount);
- for (int i = 0, n = submeshCount; i < n; i++) {
- var submesh = submeshBuffers.Items[i];
- SolveTangents2DTriangles(this.tempTanBuffer, submesh.triangles, submesh.triangleCount, meshVertices, meshUVs, vertexCount);
- }
- SolveTangents2DBuffer(this.meshTangents, this.tempTanBuffer, vertexCount);
- }
-
- return new MeshAndMaterials(smartMesh.mesh, sharedMaterials);
- }
-
- #region Types
- // A SmartMesh is a Mesh (with submeshes) that knows what attachments and instructions were used to generate it.
- class SmartMesh : System.IDisposable {
- public readonly Mesh mesh = SpineMesh.NewMesh();
- readonly ExposedList attachmentsUsed = new ExposedList();
- readonly ExposedList instructionsUsed = new ExposedList();
-
- public void Dispose () {
- if (mesh != null) {
- if (Application.isEditor && !Application.isPlaying) {
- UnityEngine.Object.DestroyImmediate(mesh);
- } else {
- UnityEngine.Object.Destroy(mesh);
- }
- }
- }
-
- public void Set (Vector3[] verts, Vector2[] uvs, Color32[] colors, SubmeshedMeshInstruction instruction) {
- mesh.vertices = verts;
- mesh.uv = uvs;
- mesh.colors32 = colors;
-
- attachmentsUsed.Clear(false);
- attachmentsUsed.GrowIfNeeded(instruction.attachmentList.Capacity);
- attachmentsUsed.Count = instruction.attachmentList.Count;
- instruction.attachmentList.CopyTo(attachmentsUsed.Items);
-
- instructionsUsed.Clear(false);
- instructionsUsed.GrowIfNeeded(instruction.submeshInstructions.Capacity);
- instructionsUsed.Count = instruction.submeshInstructions.Count;
- instruction.submeshInstructions.CopyTo(instructionsUsed.Items);
- }
-
- public bool StructureDoesntMatch (SubmeshedMeshInstruction instructions) {
- // Check count inequality.
- if (instructions.attachmentList.Count != this.attachmentsUsed.Count) return true;
- if (instructions.submeshInstructions.Count != this.instructionsUsed.Count) return true;
-
- // 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;
-
- // 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.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/DoubleBufferedMesh.cs b/spine-unity/Assets/spine-unity/Mesh Generation/DoubleBufferedMesh.cs
index 87e0f119a..e69de29bb 100644
--- a/spine-unity/Assets/spine-unity/Mesh Generation/DoubleBufferedMesh.cs
+++ b/spine-unity/Assets/spine-unity/Mesh Generation/DoubleBufferedMesh.cs
@@ -1,45 +0,0 @@
-/******************************************************************************
- * Spine Runtimes Software License v2.5
- *
- * Copyright (c) 2013-2016, Esoteric Software
- * All rights reserved.
- *
- * You are granted a perpetual, non-exclusive, non-sublicensable, and
- * non-transferable license to use, install, execute, and perform the Spine
- * Runtimes software and derivative works solely for personal or internal
- * use. Without the written permission of Esoteric Software (see Section 2 of
- * the Spine Software License Agreement), you may not (a) modify, translate,
- * adapt, or develop new applications using the Spine Runtimes or otherwise
- * create derivative works or improvements of the Spine Runtimes or (b) remove,
- * delete, alter, or obscure any trademarks or any copyright, trademark, patent,
- * or other intellectual property or proprietary rights notices on or in the
- * Software, including any copy thereof. Redistributions in binary or source
- * form must include this license and terms.
- *
- * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "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 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 THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-using UnityEngine;
-using System.Collections;
-
-namespace Spine.Unity {
- public class DoubleBufferedMesh {
- readonly Mesh mesh1 = SpineMesh.NewMesh();
- readonly Mesh mesh2 = SpineMesh.NewMesh();
- bool usingMesh1;
-
- public Mesh GetNextMesh () {
- usingMesh1 = !usingMesh1;
- return usingMesh1 ? mesh1 : mesh2;
- }
- }
-}
diff --git a/spine-unity/Assets/spine-unity/Mesh Generation/ISimpleMeshGenerator.cs b/spine-unity/Assets/spine-unity/Mesh Generation/ISimpleMeshGenerator.cs
index fc440cc5f..e69de29bb 100644
--- a/spine-unity/Assets/spine-unity/Mesh Generation/ISimpleMeshGenerator.cs
+++ b/spine-unity/Assets/spine-unity/Mesh Generation/ISimpleMeshGenerator.cs
@@ -1,48 +0,0 @@
-/******************************************************************************
- * Spine Runtimes Software License v2.5
- *
- * Copyright (c) 2013-2016, Esoteric Software
- * All rights reserved.
- *
- * You are granted a perpetual, non-exclusive, non-sublicensable, and
- * non-transferable license to use, install, execute, and perform the Spine
- * Runtimes software and derivative works solely for personal or internal
- * use. Without the written permission of Esoteric Software (see Section 2 of
- * the Spine Software License Agreement), you may not (a) modify, translate,
- * adapt, or develop new applications using the Spine Runtimes or otherwise
- * create derivative works or improvements of the Spine Runtimes or (b) remove,
- * delete, alter, or obscure any trademarks or any copyright, trademark, patent,
- * or other intellectual property or proprietary rights notices on or in the
- * Software, including any copy thereof. Redistributions in binary or source
- * form must include this license and terms.
- *
- * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "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 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 THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-namespace Spine.Unity.MeshGeneration {
- // Typically, each ISpineMeshGenerator implementation will handle double-buffering meshes, handling any other optimization behavior
- // and operating on assumptions (eg, only handling one skeleton, not updating triangles all the time).
- // The Scale property allows generated mesh to match external systems like Canvas referencePixelsPerUnit
-
- public interface ISimpleMeshGenerator {
- UnityEngine.Mesh GenerateMesh (Spine.Skeleton skeleton);
- UnityEngine.Mesh LastGeneratedMesh { get; }
-
- float Scale { set; }
- float ZSpacing { get; set; }
- bool PremultiplyVertexColors { get; set; }
-
- bool AddNormals { get; set; }
- bool AddTangents { get; set; }
- bool AddBlackTint { get; set; }
- }
-}
diff --git a/spine-unity/Assets/spine-unity/Mesh Generation/ISubmeshedMeshGenerator.cs b/spine-unity/Assets/spine-unity/Mesh Generation/ISubmeshedMeshGenerator.cs
index dda27ccec..e69de29bb 100644
--- a/spine-unity/Assets/spine-unity/Mesh Generation/ISubmeshedMeshGenerator.cs
+++ b/spine-unity/Assets/spine-unity/Mesh Generation/ISubmeshedMeshGenerator.cs
@@ -1,125 +0,0 @@
-/******************************************************************************
- * Spine Runtimes Software License v2.5
- *
- * Copyright (c) 2013-2016, Esoteric Software
- * All rights reserved.
- *
- * You are granted a perpetual, non-exclusive, non-sublicensable, and
- * non-transferable license to use, install, execute, and perform the Spine
- * Runtimes software and derivative works solely for personal or internal
- * use. Without the written permission of Esoteric Software (see Section 2 of
- * the Spine Software License Agreement), you may not (a) modify, translate,
- * adapt, or develop new applications using the Spine Runtimes or otherwise
- * create derivative works or improvements of the Spine Runtimes or (b) remove,
- * delete, alter, or obscure any trademarks or any copyright, trademark, patent,
- * or other intellectual property or proprietary rights notices on or in the
- * Software, including any copy thereof. Redistributions in binary or source
- * form must include this license and terms.
- *
- * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "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 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 THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-using UnityEngine;
-using System.Collections.Generic;
-
-namespace Spine.Unity.MeshGeneration {
- // ISubmeshedMeshGenerator:
- // How to use:
- // Step 1: Have a SubmeshedMeshGenerator instance, and a Spine.Skeleton
- // Step 2: Call GenerateInstruction. Pass it your Skeleton. Keep the return value (a SubmeshedMeshInstruction, you can use it in other classes too).
- // Step 3: Pass the SubmeshedMeshInstruction into GenerateMesh. You'll get a Mesh and Materials.
- // Step 4: Put the Mesh in MeshFilter. Put the Materials in MeshRenderer.sharedMaterials.
- public interface ISubmeshedMeshGenerator {
- SubmeshedMeshInstruction GenerateInstruction (Skeleton skeleton);
- MeshAndMaterials GenerateMesh (SubmeshedMeshInstruction wholeMeshInstruction);
- List Separators { get; }
-
- float ZSpacing { get; set; }
- bool PremultiplyVertexColors { get; set; }
- bool AddNormals { get; set; }
- bool AddTangents { get; set; }
- bool AddBlackTint { get; set; }
- }
-
- // ISubmeshSetMeshGenerator
- // How to use:
- // Step 1: Get a list of SubmeshInstruction. You can get this from SkeletonRenderer or an ISubmeshedMeshGenerator's returned SubmeshedMeshInstruction.
- // Step 2: Call AddInstruction one by one, or AddInstructions once.
- // Step 3: Call GenerateMesh. You'll get a Mesh and Materials.
- // Step 4: Put the Mesh in MeshFilter. Put the Materials in MeshRenderer.sharedMaterials.
- public interface ISubmeshSetMeshGenerator {
- MeshAndMaterials GenerateMesh (ExposedList instructions, int startSubmesh, int endSubmesh, float scale = 1f);
-
- float ZSpacing { get; set; }
- bool PremultiplyVertexColors { get; set; }
- bool AddNormals { get; set; }
- bool AddTangents { get; set; }
- bool AddBlackTint { get; set; }
- }
-
- /// Primarily a collection of Submesh Instructions. This constitutes instructions for how to construct a mesh containing submeshes.
- public class SubmeshedMeshInstruction {
- public readonly ExposedList submeshInstructions = new ExposedList();
- public readonly ExposedList attachmentList = new ExposedList();
- public int vertexCount = -1;
-
- /// Returns a material array of the SubmeshedMeshInstruction. Fills the passed array if it's the correct size. Creates a new array if it's a different size.
- public Material[] GetUpdatedMaterialArray (Material[] materials) {
- return submeshInstructions.GetUpdatedMaterialArray(materials);
- }
- }
-
- /// Instructions for how to generate a mesh or submesh out of a range of slots in a given skeleton.
- public struct SubmeshInstruction {
- 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;
- public bool forceSeparate;
-
- /// The number of slots in this SubmeshInstruction's range. Not necessarily the number of attachments.
- public int SlotCount { get { return endSlot - startSlot; } }
- }
-
- public static class SubmeshInstructionExtensions {
- /// Returns a material array of the instructions. Fills the passed array if it's the correct size. Creates a new array if it's a different size.
- public static Material[] GetUpdatedMaterialArray (this ExposedList instructions, Material[] materials) {
- int submeshCount = instructions.Count;
-
- if (submeshCount != materials.Length)
- materials = new Material[submeshCount];
-
- for (int i = 0, n = materials.Length; i < n; i++)
- materials[i] = instructions.Items[i].material;
-
- return materials;
- }
- }
-
- public struct MeshAndMaterials {
- public readonly Mesh mesh;
- public readonly Material[] materials;
-
- public MeshAndMaterials (Mesh mesh, Material[] materials) {
- this.mesh = mesh;
- this.materials = materials;
- }
- }
-}
diff --git a/spine-unity/Assets/spine-unity/Mesh Generation/SpineMesh.cs b/spine-unity/Assets/spine-unity/Mesh Generation/SpineMesh.cs
index d2186d974..9278cc940 100644
--- a/spine-unity/Assets/spine-unity/Mesh Generation/SpineMesh.cs
+++ b/spine-unity/Assets/spine-unity/Mesh Generation/SpineMesh.cs
@@ -28,11 +28,14 @@
* POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
+//#define SPINE_TRIANGLECHECK // Avoid calling SetTriangles at the cost of checking for mesh differences (vertex counts, memberwise attachment list compare) every frame.
+
using UnityEngine;
+using System;
+using System.Collections.Generic;
namespace Spine.Unity {
public static class SpineMesh {
-
internal const HideFlags MeshHideflags = HideFlags.DontSaveInBuild | HideFlags.DontSaveInEditor;
/// Factory method for creating a new mesh for use in Spine components. This can be called in field initializers.
@@ -44,4 +47,1123 @@ namespace Spine.Unity {
return m;
}
}
+
+ /// Instructions for how to generate a mesh or submesh out of a range of slots in a given skeleton.
+ public struct SubmeshInstruction {
+ public Skeleton skeleton;
+ public int startSlot;
+ public int endSlot;
+
+ public Material material;
+ public bool forceSeparate;
+ public int preActiveClippingSlotSource;
+
+ #if SPINE_TRIANGLECHECK
+ // 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 int rawTriangleCount;
+ public int rawVertexCount;
+ public int rawFirstVertexIndex;
+ public bool hasClipping;
+ #endif
+
+ /// The number of slots in this SubmeshInstruction's range. Not necessarily the number of attachments.
+ public int SlotCount { get { return endSlot - startSlot; } }
+ }
+
+ [System.Serializable]
+ public class MeshGenerator {
+ public Settings settings = Settings.Default;
+
+ [System.Serializable]
+ public struct Settings {
+ public bool renderMeshes;
+ public bool useClipping;
+ [Space]
+ [Range(-0.1f, 0f)] public float zSpacing;
+ [Space]
+ [Header("Vertex Data")]
+ public bool pmaVertexColors;
+ public bool tintBlack;
+ public bool calculateTangents;
+ public bool addNormals;
+ public bool immutableTriangles;
+
+ static public Settings Default {
+ get {
+ return new Settings {
+ pmaVertexColors = true,
+ zSpacing = 0f,
+ useClipping = true,
+ tintBlack = false,
+ calculateTangents = false,
+ renderMeshes = true,
+ addNormals = false,
+ immutableTriangles = false
+ };
+ }
+ }
+ }
+
+ const float BoundsMinDefault = float.MaxValue;
+ const float BoundsMaxDefault = float.MinValue;
+
+ [NonSerialized] readonly ExposedList vertexBuffer = new ExposedList();
+ [NonSerialized] readonly ExposedList uvBuffer = new ExposedList();
+ [NonSerialized] readonly ExposedList colorBuffer = new ExposedList();
+ [NonSerialized] readonly ExposedList> submeshes = new ExposedList> { new ExposedList(6) }; // start with 1 submesh.
+
+ [NonSerialized] Vector2 meshBoundsMin, meshBoundsMax;
+ [NonSerialized] float meshBoundsThickness;
+ [NonSerialized] int submeshIndex = 0;
+
+ [NonSerialized] SkeletonClipping clipper = new SkeletonClipping();
+ [NonSerialized] float[] tempVerts = new float[8];
+ [NonSerialized] int[] regionTriangles = { 0, 1, 2, 2, 3, 0 };
+
+ #region Optional Buffers
+ [NonSerialized] Vector3[] normals;
+ [NonSerialized] Vector4[] tangents;
+ [NonSerialized] Vector2[] tempTanBuffer;
+ [NonSerialized] ExposedList uv2;
+ [NonSerialized] ExposedList uv3;
+ #endregion
+
+ #region Step 1 : Generate Instructions
+ public static void GenerateSingleSubmeshInstruction (SkeletonRendererInstruction instructionOutput, Skeleton skeleton, bool renderMeshes, Material material) {
+ ExposedList drawOrder = skeleton.drawOrder;
+ int drawOrderCount = drawOrder.Count;
+
+ // Clear last state of attachments and submeshes
+ instructionOutput.Clear(); // submeshInstructions.Clear(); attachments.Clear();
+ var workingSubmeshInstructions = instructionOutput.submeshInstructions;
+ workingSubmeshInstructions.Resize(1);
+
+ #if SPINE_TRIANGLECHECK
+ instructionOutput.attachments.Resize(drawOrderCount);
+ var workingAttachmentsItems = instructionOutput.attachments.Items;
+ int totalRawVertexCount = 0;
+ #endif
+
+ var current = new SubmeshInstruction {
+ skeleton = skeleton,
+ preActiveClippingSlotSource = -1,
+ startSlot = 0,
+ #if SPINE_TRIANGLECHECK
+ rawFirstVertexIndex = 0,
+ #endif
+ material = material,
+ forceSeparate = false,
+ endSlot = drawOrderCount
+ };
+
+ #if SPINE_TRIANGLECHECK
+ bool skeletonHasClipping = false;
+ var drawOrderItems = drawOrder.Items;
+ for (int i = 0; i < drawOrderCount; i++) {
+ Slot slot = drawOrderItems[i];
+ Attachment attachment = slot.attachment;
+
+ workingAttachmentsItems[i] = attachment;
+ int attachmentTriangleCount = 0;
+ int attachmentVertexCount = 0;
+
+
+ var regionAttachment = attachment as RegionAttachment;
+ if (regionAttachment != null) {
+ attachmentVertexCount = 4;
+ attachmentTriangleCount = 6;
+ } else {
+ if (renderMeshes) {
+ var meshAttachment = attachment as MeshAttachment;
+ if (meshAttachment != null) {
+ attachmentVertexCount = meshAttachment.worldVerticesLength >> 1;
+ attachmentTriangleCount = meshAttachment.triangles.Length;
+ } else {
+ var clippingAttachment = attachment as ClippingAttachment;
+ if (clippingAttachment != null) {
+ current.hasClipping = true;
+ skeletonHasClipping = true;
+ }
+ attachmentVertexCount = 0;
+ attachmentTriangleCount = 0;
+ //continue;
+ }
+ }
+ }
+ current.rawTriangleCount += attachmentTriangleCount;
+ current.rawVertexCount += attachmentVertexCount;
+ totalRawVertexCount += attachmentVertexCount;
+
+ }
+
+ instructionOutput.hasActiveClipping = skeletonHasClipping;
+ instructionOutput.rawVertexCount = totalRawVertexCount;
+ #endif
+
+ workingSubmeshInstructions.Items[0] = current;
+ }
+
+ public static void GenerateSkeletonRendererInstruction (SkeletonRendererInstruction instructionOutput, Skeleton skeleton, Dictionary customSlotMaterials, List separatorSlots, bool generateMeshOverride, bool immutableTriangles = false, bool renderMeshes = true) {
+// if (skeleton == null) throw new ArgumentNullException("skeleton");
+// if (instructionOutput == null) throw new ArgumentNullException("instructionOutput");
+
+ ExposedList drawOrder = skeleton.drawOrder;
+ int drawOrderCount = drawOrder.Count;
+
+ // Clear last state of attachments and submeshes
+ instructionOutput.Clear(); // submeshInstructions.Clear(); attachments.Clear();
+ var workingSubmeshInstructions = instructionOutput.submeshInstructions;
+ #if SPINE_TRIANGLECHECK
+ instructionOutput.attachments.Resize(drawOrderCount);
+ var workingAttachmentsItems = instructionOutput.attachments.Items;
+ int totalRawVertexCount = 0;
+ bool skeletonHasClipping = false;
+ #endif
+
+ var current = new SubmeshInstruction {
+ skeleton = skeleton,
+ preActiveClippingSlotSource = -1
+ };
+
+ #if !SPINE_TK2D
+ bool isCustomSlotMaterialsPopulated = customSlotMaterials != null && customSlotMaterials.Count > 0;
+ #endif
+
+ int separatorCount = separatorSlots == null ? 0 : separatorSlots.Count;
+ bool hasSeparators = separatorCount > 0;
+
+ int clippingAttachmentSource = -1;
+ int lastPreActiveClipping = -1;
+ SlotData clippingEndSlot = null;
+ int submeshIndex = 0;
+ var drawOrderItems = drawOrder.Items;
+ bool currentHasRenderable = false;
+ for (int i = 0; i < drawOrderCount; i++) {
+ Slot slot = drawOrderItems[i];
+ Attachment attachment = slot.attachment;
+ #if SPINE_TRIANGLECHECK
+ workingAttachmentsItems[i] = attachment;
+ int attachmentVertexCount = 0, attachmentTriangleCount = 0;
+ #endif
+
+ object rendererObject = null; // An AtlasRegion in plain Spine-Unity. Spine-TK2D hooks into TK2D's system. eventual source of Material object.
+ bool noRender = false; // Using this allows empty slots as separators, and keeps separated parts more stable despite slots being reordered
+
+ var regionAttachment = attachment as RegionAttachment;
+ if (regionAttachment != null) {
+ rendererObject = regionAttachment.RendererObject;
+ #if SPINE_TRIANGLECHECK
+ attachmentVertexCount = 4;
+ attachmentTriangleCount = 6;
+ #endif
+ currentHasRenderable = true;
+ } else {
+ if (renderMeshes) {
+ var meshAttachment = attachment as MeshAttachment;
+ if (meshAttachment != null) {
+ rendererObject = meshAttachment.RendererObject;
+ #if SPINE_TRIANGLECHECK
+ attachmentVertexCount = meshAttachment.worldVerticesLength >> 1;
+ attachmentTriangleCount = meshAttachment.triangles.Length;
+ #endif
+ currentHasRenderable = true;
+ } else {
+ #if SPINE_TRIANGLECHECK
+ var clippingAttachment = attachment as ClippingAttachment;
+ if (clippingAttachment != null) {
+ clippingEndSlot = clippingAttachment.endSlot;
+ clippingAttachmentSource = i;
+ current.hasClipping = true;
+ skeletonHasClipping = true;
+ }
+ #endif
+ noRender = true;
+ //continue;
+ }
+ } else {
+ noRender = true;
+ }
+ }
+
+ if (clippingEndSlot != null && slot.data == clippingEndSlot) {
+ clippingEndSlot = null;
+ clippingAttachmentSource = -1;
+ }
+
+ // Create a new SubmeshInstruction when material changes. (or when forced to separate by a submeshSeparator)
+ // Slot with a separator/new material will become the starting slot of the next new instruction.
+ if (hasSeparators) { //current.forceSeparate = hasSeparators && separatorSlots.Contains(slot);
+ current.forceSeparate = false;
+ for (int s = 0; s < separatorCount; s++) {
+ if (Slot.ReferenceEquals(slot, separatorSlots[s])) {
+ current.forceSeparate = true;
+ break;
+ }
+ }
+ }
+
+ if (noRender) {
+ if (current.forceSeparate && currentHasRenderable && generateMeshOverride) {
+ { // Add
+ current.endSlot = i;
+ current.preActiveClippingSlotSource = lastPreActiveClipping;
+
+ workingSubmeshInstructions.Resize(submeshIndex + 1);
+ workingSubmeshInstructions.Items[submeshIndex] = current;
+
+ submeshIndex++;
+ }
+ currentHasRenderable = false;
+ current.startSlot = i;
+ lastPreActiveClipping = clippingAttachmentSource;
+ #if SPINE_TRIANGLECHECK
+ current.rawTriangleCount = 0;
+ current.rawVertexCount = 0;
+ current.rawFirstVertexIndex = totalRawVertexCount;
+ current.hasClipping = clippingAttachmentSource >= 0;
+ #endif
+ }
+ } else {
+ #if !SPINE_TK2D
+ Material material;
+ if (isCustomSlotMaterialsPopulated) {
+ 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
+
+ if (currentHasRenderable && (current.forceSeparate || !System.Object.ReferenceEquals(current.material, material))) { // Material changed. Add the previous submesh.
+ { // Add
+ current.endSlot = i;
+ current.preActiveClippingSlotSource = lastPreActiveClipping;
+
+ workingSubmeshInstructions.Resize(submeshIndex + 1);
+ workingSubmeshInstructions.Items[submeshIndex] = current;
+ submeshIndex++;
+ }
+ currentHasRenderable = false;
+ current.startSlot = i;
+ lastPreActiveClipping = clippingAttachmentSource;
+ #if SPINE_TRIANGLECHECK
+ current.rawTriangleCount = 0;
+ current.rawVertexCount = 0;
+ current.rawFirstVertexIndex = totalRawVertexCount;
+ current.hasClipping = clippingAttachmentSource >= 0;
+ #endif
+ }
+
+ // Update state for the next Attachment.
+ current.material = material;
+ #if SPINE_TRIANGLECHECK
+ current.rawTriangleCount += attachmentTriangleCount;
+ current.rawVertexCount += attachmentVertexCount;
+ current.rawFirstVertexIndex = totalRawVertexCount;
+ totalRawVertexCount += attachmentVertexCount;
+ #endif
+ }
+ }
+
+ if (currentHasRenderable) {
+ { // Add last or only submesh.
+ current.endSlot = drawOrderCount;
+ current.preActiveClippingSlotSource = lastPreActiveClipping;
+ current.forceSeparate = false;
+
+ workingSubmeshInstructions.Resize(submeshIndex + 1);
+ workingSubmeshInstructions.Items[submeshIndex] = current;
+ //submeshIndex++;
+ }
+ }
+
+ #if SPINE_TRIANGLECHECK
+ instructionOutput.hasActiveClipping = skeletonHasClipping;
+ instructionOutput.rawVertexCount = totalRawVertexCount;
+ #endif
+ instructionOutput.immutableTriangles = immutableTriangles;
+ }
+
+ public static void TryReplaceMaterials (ExposedList workingSubmeshInstructions, Dictionary customMaterialOverride) {
+ // Material overrides are done here so they can be applied per submesh instead of per slot
+ // but they will still be passed through the GenerateMeshOverride delegate,
+ // and will still go through the normal material match check step in STEP 3.
+ var workingSubmeshInstructionsItems = workingSubmeshInstructions.Items;
+ for (int i = 0; i < workingSubmeshInstructions.Count; i++) {
+ var m = workingSubmeshInstructionsItems[i].material;
+ Material mo;
+ if (customMaterialOverride.TryGetValue(m, out mo))
+ workingSubmeshInstructionsItems[i].material = mo;
+ }
+ }
+ #endregion
+
+ #region Step 2 : Populate vertex data and triangle index buffers.
+ public void BeginNewMesh () {
+ vertexBuffer.Clear(false);
+ colorBuffer.Clear(false);
+ uvBuffer.Clear(false);
+ clipper.ClipEnd();
+
+ {
+ meshBoundsMin.x = BoundsMinDefault;
+ meshBoundsMin.y = BoundsMinDefault;
+ meshBoundsMax.x = BoundsMaxDefault;
+ meshBoundsMax.y = BoundsMaxDefault;
+ meshBoundsThickness = 0f;
+ }
+
+ submeshes.Count = 1;
+ submeshes.Items[0].Clear(false);
+ submeshIndex = 0;
+ }
+
+ public void AddSubmesh (SubmeshInstruction instruction) {
+ var settings = this.settings;
+ if (!settings.renderMeshes) {
+ AddSubmeshQuadsOnly(instruction);
+ return;
+ }
+
+ if (submeshes.Count - 1 < submeshIndex) {
+ submeshes.Resize(submeshIndex + 1);
+ if (submeshes.Items[submeshIndex] == null)
+ submeshes.Items[submeshIndex] = new ExposedList();
+ }
+ var submesh = submeshes.Items[submeshIndex];
+ submesh.Clear(false);
+
+ var skeleton = instruction.skeleton;
+ var drawOrderItems = skeleton.drawOrder.Items;
+
+ Color32 color;
+ float skeletonA = skeleton.a * 255, skeletonR = skeleton.r, skeletonG = skeleton.g, skeletonB = skeleton.b;
+ Vector2 meshBoundsMin = this.meshBoundsMin, meshBoundsMax = this.meshBoundsMax;
+
+ // Settings
+ float zSpacing = settings.zSpacing;
+ #if SPINE_TRIANGLECHECK
+ bool useClipping = settings.useClipping && instruction.hasClipping;
+ #else
+ bool useClipping = settings.useClipping;
+ #endif
+
+ if (useClipping) {
+ if (instruction.preActiveClippingSlotSource >= 0) {
+ Debug.Log("PreActiveClipping");
+ var slot = drawOrderItems[instruction.preActiveClippingSlotSource];
+ clipper.ClipStart(slot, slot.attachment as ClippingAttachment);
+ }
+ }
+
+ bool pmaVertexColors = settings.pmaVertexColors;
+ for (int slotIndex = instruction.startSlot; slotIndex < instruction.endSlot; slotIndex++) {
+ var slot = drawOrderItems[slotIndex];
+ var attachment = slot.attachment;
+ float z = zSpacing * slotIndex;
+
+ var workingVerts = this.tempVerts;
+ float[] uvs;
+ int[] attachmentTriangleIndices;
+ int attachmentVertexCount;
+ int attachmentIndexCount;
+
+ Color c = default(Color);
+
+ var region = attachment as RegionAttachment;
+ if (region != null) {
+ region.ComputeWorldVertices(slot.bone, workingVerts, 0);
+ uvs = region.uvs;
+ attachmentTriangleIndices = regionTriangles;
+ c.r = region.r; c.g = region.g; c.b = region.b; c.a = region.a;
+ attachmentVertexCount = 4;
+ attachmentIndexCount = 6;
+ } else {
+ var mesh = attachment as MeshAttachment;
+ if (mesh != null) {
+ int meshVertexCount = mesh.worldVerticesLength;
+ if (workingVerts.Length < meshVertexCount) {
+ workingVerts = new float[meshVertexCount];
+ this.tempVerts = workingVerts;
+ }
+ mesh.ComputeWorldVertices(slot, 0, meshVertexCount, workingVerts, 0); //meshAttachment.ComputeWorldVertices(slot, tempVerts);
+ uvs = mesh.uvs;
+ attachmentTriangleIndices = mesh.triangles;
+ c.r = mesh.r; c.g = mesh.g; c.b = mesh.b; c.a = mesh.a;
+ attachmentVertexCount = meshVertexCount >> 1; // meshVertexCount / 2;
+ attachmentIndexCount = mesh.triangles.Length;
+ } else {
+ if (useClipping) {
+ var clippingAttachment = attachment as ClippingAttachment;
+ if (clippingAttachment != null) {
+ clipper.ClipStart(slot, clippingAttachment);
+ continue;
+ }
+ }
+
+ continue;
+ }
+ }
+
+ if (pmaVertexColors) {
+ color.a = (byte)(skeletonA * slot.a * c.a);
+ color.r = (byte)(skeletonR * slot.r * c.r * color.a);
+ color.g = (byte)(skeletonG * slot.g * c.g * color.a);
+ color.b = (byte)(skeletonB * slot.b * c.b * color.a);
+ if (slot.data.blendMode == BlendMode.Additive) color.a = 0;
+ } else {
+ color.a = (byte)(skeletonA * slot.a * c.a);
+ color.r = (byte)(skeletonR * slot.r * c.r * 255);
+ color.g = (byte)(skeletonG * slot.g * c.g * 255);
+ color.b = (byte)(skeletonB * slot.b * c.b * 255);
+ }
+
+ if (useClipping && clipper.IsClipping()) {
+ clipper.ClipTriangles(workingVerts, attachmentVertexCount << 1, attachmentTriangleIndices, attachmentIndexCount, uvs);
+ workingVerts = clipper.clippedVertices.Items;
+ attachmentVertexCount = clipper.clippedVertices.Count >> 1;
+ attachmentTriangleIndices = clipper.clippedTriangles.Items;
+ attachmentIndexCount = clipper.clippedTriangles.Count;
+ uvs = clipper.clippedUVs.Items;
+ }
+
+ if (attachmentVertexCount != 0 && attachmentIndexCount != 0) {
+ if (settings.tintBlack)
+ AddAttachmentTintBlack(slot.r2, slot.g2, slot.b2, attachmentVertexCount);
+
+ //AddAttachment(workingVerts, uvs, color, attachmentTriangleIndices, attachmentVertexCount, attachmentIndexCount, ref meshBoundsMin, ref meshBoundsMax, z);
+ int ovc = vertexBuffer.Count;
+ // Add data to vertex buffers
+ {
+ int newVertexCount = ovc + attachmentVertexCount;
+ if (newVertexCount > vertexBuffer.Items.Length) { // Manual ExposedList.Resize()
+ Array.Resize(ref vertexBuffer.Items, newVertexCount);
+ Array.Resize(ref uvBuffer.Items, newVertexCount);
+ Array.Resize(ref colorBuffer.Items, newVertexCount);
+ }
+ vertexBuffer.Count = uvBuffer.Count = colorBuffer.Count = newVertexCount;
+ }
+
+ var vbi = vertexBuffer.Items;
+ var ubi = uvBuffer.Items;
+ var cbi = colorBuffer.Items;
+ for (int i = 0; i < attachmentVertexCount; i++) {
+ int vi = ovc + i;
+ int i2 = i << 1; // i * 2
+ float x = workingVerts[i2];
+ float y = workingVerts[i2 + 1];
+
+ vbi[vi].x = x;
+ vbi[vi].y = y;
+ vbi[vi].z = z;
+ ubi[vi].x = uvs[i2];
+ ubi[vi].y = uvs[i2 + 1];
+ cbi[vi] = color;
+
+ // Calculate bounds.
+ 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;
+ }
+
+ // Add data to triangle buffer
+ int oldTriangleCount = submesh.Count;
+ { //submesh.Resize(oldTriangleCount + attachmentIndexCount);
+ int newTriangleCount = oldTriangleCount + attachmentIndexCount;
+ if (newTriangleCount > submesh.Items.Length) Array.Resize(ref submesh.Items, newTriangleCount);
+ submesh.Count = newTriangleCount;
+ }
+ var submeshItems = submesh.Items;
+ for (int i = 0; i < attachmentIndexCount; i++)
+ submeshItems[oldTriangleCount + i] = attachmentTriangleIndices[i] + ovc;
+ }
+
+ clipper.ClipEnd(slot);
+ }
+ clipper.ClipEnd();
+
+ this.meshBoundsMin = meshBoundsMin;
+ this.meshBoundsMax = meshBoundsMax;
+ meshBoundsThickness = instruction.endSlot * zSpacing;
+
+ // Trim or zero submesh triangles.
+ var currentSubmeshItems = submesh.Items;
+ for (int i = submesh.Count, n = currentSubmeshItems.Length; i < n; i++)
+ currentSubmeshItems[i] = 0;
+
+ // Next AddSubmesh will use a new submeshIndex value.
+ submeshIndex++;
+ }
+
+ public void ScaleVertexData (float scale) {
+ var vbi = vertexBuffer.Items;
+ for (int i = 0, n = vertexBuffer.Count; i < n; i++) {
+// vbi[i].x *= scale;
+// vbi[i].y *= scale;
+ vbi[i] *= scale;
+ }
+
+ meshBoundsMin *= scale;
+ meshBoundsMax *= scale;
+ meshBoundsThickness *= scale;
+ }
+
+ void AddSubmeshQuadsOnly (SubmeshInstruction instruction) {
+ const int attachmentVertexCount = 4;
+ const int attachmentIndexCount = 6;
+ int[] attachmentTriangleIndices = regionTriangles;
+
+ var settings = this.settings;
+
+ if (submeshes.Count - 1 < submeshIndex) {
+ submeshes.Resize(submeshIndex + 1);
+ if (submeshes.Items[submeshIndex] == null)
+ submeshes.Items[submeshIndex] = new ExposedList();
+ }
+ var submesh = submeshes.Items[submeshIndex];
+ submesh.Clear(false);
+
+ var skeleton = instruction.skeleton;
+ var drawOrderItems = skeleton.drawOrder.Items;
+
+ Color32 color;
+ float skeletonA = skeleton.a * 255, skeletonR = skeleton.r, skeletonG = skeleton.g, skeletonB = skeleton.b;
+ Vector2 meshBoundsMin = this.meshBoundsMin, meshBoundsMax = this.meshBoundsMax;
+
+ // Settings
+ float zSpacing = settings.zSpacing;
+
+ bool pmaVertexColors = settings.pmaVertexColors;
+ for (int slotIndex = instruction.startSlot; slotIndex < instruction.endSlot; slotIndex++) {
+ var slot = drawOrderItems[slotIndex];
+ var attachment = slot.attachment;
+ float z = zSpacing * slotIndex;
+
+ var workingVerts = this.tempVerts;
+ float[] uvs;
+
+ Color c = default(Color);
+
+ var region = attachment as RegionAttachment;
+ if (region != null) {
+ region.ComputeWorldVertices(slot.bone, workingVerts, 0);
+ uvs = region.uvs;
+ c.r = region.r; c.g = region.g; c.b = region.b; c.a = region.a;
+ } else {
+ continue;
+ }
+
+ if (pmaVertexColors) {
+ color.a = (byte)(skeletonA * slot.a * c.a);
+ color.r = (byte)(skeletonR * slot.r * c.r * color.a);
+ color.g = (byte)(skeletonG * slot.g * c.g * color.a);
+ color.b = (byte)(skeletonB * slot.b * c.b * color.a);
+ if (slot.data.blendMode == BlendMode.Additive) color.a = 0;
+ } else {
+ color.a = (byte)(skeletonA * slot.a * c.a);
+ color.r = (byte)(skeletonR * slot.r * c.r * 255);
+ color.g = (byte)(skeletonG * slot.g * c.g * 255);
+ color.b = (byte)(skeletonB * slot.b * c.b * 255);
+ }
+
+ {
+ if (settings.tintBlack)
+ AddAttachmentTintBlack(slot.r2, slot.g2, slot.b2, attachmentVertexCount);
+
+ //AddAttachment(workingVerts, uvs, color, attachmentTriangleIndices, attachmentVertexCount, attachmentIndexCount, ref meshBoundsMin, ref meshBoundsMax, z);
+ int ovc = vertexBuffer.Count;
+ // Add data to vertex buffers
+ {
+ int newVertexCount = ovc + attachmentVertexCount;
+ if (newVertexCount > vertexBuffer.Items.Length) { // Manual ExposedList.Resize()
+ Array.Resize(ref vertexBuffer.Items, newVertexCount);
+ Array.Resize(ref uvBuffer.Items, newVertexCount);
+ Array.Resize(ref colorBuffer.Items, newVertexCount);
+ }
+ vertexBuffer.Count = uvBuffer.Count = colorBuffer.Count = newVertexCount;
+ }
+
+ var vbi = vertexBuffer.Items;
+ var ubi = uvBuffer.Items;
+ var cbi = colorBuffer.Items;
+ for (int i = 0; i < attachmentVertexCount; i++) {
+ int vi = ovc + i;
+ int i2 = i << 1; // i * 2
+ float x = workingVerts[i2];
+ float y = workingVerts[i2 + 1];
+
+ vbi[vi].x = x;
+ vbi[vi].y = y;
+ vbi[vi].z = z;
+ ubi[vi].x = uvs[i2];
+ ubi[vi].y = uvs[i2 + 1];
+ cbi[vi] = color;
+
+ // Calculate bounds.
+ 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;
+ }
+
+ // TODO: Simplify triangle buffer handling.
+ // Add data to triangle buffer
+ int oldTriangleCount = submesh.Count;
+ { //submesh.Resize(oldTriangleCount + attachmentIndexCount);
+ int newTriangleCount = oldTriangleCount + attachmentIndexCount;
+ if (newTriangleCount > submesh.Items.Length) Array.Resize(ref submesh.Items, newTriangleCount);
+ submesh.Count = newTriangleCount;
+ }
+ var submeshItems = submesh.Items;
+ for (int i = 0; i < attachmentIndexCount; i++)
+ submeshItems[oldTriangleCount + i] = attachmentTriangleIndices[i] + ovc;
+ }
+ }
+
+ this.meshBoundsMin = meshBoundsMin;
+ this.meshBoundsMax = meshBoundsMax;
+ meshBoundsThickness = instruction.endSlot * zSpacing;
+
+ // Trim or zero submesh triangles.
+ var currentSubmeshItems = submesh.Items;
+ for (int i = submesh.Count, n = currentSubmeshItems.Length; i < n; i++)
+ currentSubmeshItems[i] = 0;
+
+ // Next AddSubmesh will use a new submeshIndex value.
+ submeshIndex++;
+ }
+
+ void AddAttachmentTintBlack (float r2, float g2, float b2, int vertexCount) {
+ var rg = new Vector2(r2, g2);
+ var bo = new Vector2(b2, 1f);
+
+ int ovc = vertexBuffer.Count;
+ int newVertexCount = ovc + vertexCount;
+ {
+ if (uv2 == null) {
+ uv2 = new ExposedList();
+ uv3 = new ExposedList();
+ }
+ if (newVertexCount > uv2.Items.Length) { // Manual ExposedList.Resize()
+ Array.Resize(ref uv2.Items, newVertexCount);
+ Array.Resize(ref uv3.Items, newVertexCount);
+ }
+ uv2.Count = uv3.Count = newVertexCount;
+ }
+
+ var uv2i = uv2.Items;
+ var uv3i = uv3.Items;
+ for (int i = 0; i < vertexCount; i++) {
+ uv2i[ovc + i] = rg;
+ uv3i[ovc + i] = bo;
+ }
+ }
+ #endregion
+
+ #region Step 3 : Transfer vertex and triangle data to UnityEngine.Mesh
+ public void FillVertexData (Mesh mesh) {
+ var vbi = vertexBuffer.Items;
+ var ubi = uvBuffer.Items;
+ var cbi = colorBuffer.Items;
+ var sbi = submeshes.Items;
+ int submeshCount = submeshes.Count;
+
+ // Zero the extra.
+ {
+ int listCount = vertexBuffer.Count;
+ int arrayLength = vertexBuffer.Items.Length;
+ var vector3zero = Vector3.zero;
+ for (int i = listCount; i < arrayLength; i++)
+ vbi[i] = vector3zero;
+ }
+
+ // Set the vertex buffer.
+ {
+ mesh.vertices = vbi;
+ mesh.uv = ubi;
+ mesh.colors32 = cbi;
+
+ if (meshBoundsMin.x == BoundsMinDefault) {
+ mesh.bounds = new Bounds();
+ } else {
+ Vector2 halfSize = (meshBoundsMax - meshBoundsMin) * 0.5f;
+ mesh.bounds = new Bounds {
+ center = (Vector3)(meshBoundsMin + halfSize),
+ extents = new Vector3(halfSize.x, halfSize.y, meshBoundsThickness * 0.5f)
+ };
+ //mesh.bounds = ArraysMeshGenerator.ToBounds(meshBoundsMin, meshBoundsMax);
+ }
+ }
+
+ {
+ int vertexCount = this.vertexBuffer.Count;
+ if (settings.addNormals) {
+ int oldLength = 0;
+
+ if (normals == null)
+ normals = new Vector3[vertexCount];
+ else
+ oldLength = normals.Length;
+
+ if (oldLength < vertexCount) {
+ Array.Resize(ref this.normals, vertexCount);
+ var localNormals = this.normals;
+ for (int i = oldLength; i < vertexCount; i++) localNormals[i] = Vector3.back;
+ }
+ mesh.normals = this.normals;
+ }
+
+ if (settings.tintBlack) {
+ mesh.uv2 = this.uv2.Items;
+ mesh.uv3 = this.uv3.Items;
+ }
+
+ if (settings.calculateTangents) {
+ MeshGenerator.SolveTangents2DEnsureSize(ref this.tangents, ref this.tempTanBuffer, vertexCount);
+ for (int i = 0; i < submeshCount; i++) {
+ var submesh = sbi[i].Items;
+ int triangleCount = sbi[i].Count;
+ MeshGenerator.SolveTangents2DTriangles(this.tempTanBuffer, submesh, triangleCount, vbi, ubi, vertexCount);
+ }
+ MeshGenerator.SolveTangents2DBuffer(this.tangents, this.tempTanBuffer, vertexCount);
+ mesh.tangents = this.tangents;
+ }
+ }
+ }
+
+ public void FillTriangles (Mesh mesh) {
+ int submeshCount = submeshes.Count;
+ var submeshesItems = submeshes.Items;
+ mesh.subMeshCount = submeshCount;
+
+ for (int i = 0; i < submeshCount; i++)
+ mesh.SetTriangles(submeshesItems[i].Items, i, false);
+ }
+
+ public void FillTrianglesSingle (Mesh mesh) {
+ mesh.SetTriangles(submeshes.Items[0].Items, 0, false);
+ }
+ #endregion
+
+ public void TrimExcess () {
+ vertexBuffer.TrimExcess();
+ uvBuffer.TrimExcess();
+ colorBuffer.TrimExcess();
+
+ if (uv2 != null) uv2.TrimExcess();
+ if (uv3 != null) uv3.TrimExcess();
+
+ int count = vertexBuffer.Count;
+ if (normals != null) Array.Resize(ref normals, count);
+ if (tangents != null) Array.Resize(ref tangents, count);
+ }
+
+ #region TangentSolver2D
+ // Thanks to contributions from forum user ToddRivers
+
+ /// Step 1 of solving tangents. Ensure you have buffers of the correct size.
+ /// Eventual Vector4[] tangent buffer to assign to Mesh.tangents.
+ /// Temporary Vector2 buffer for calculating directions.
+ /// Number of vertices that require tangents (or the size of the vertex array)
+ internal static void SolveTangents2DEnsureSize (ref Vector4[] tangentBuffer, ref Vector2[] tempTanBuffer, int vertexCount) {
+ if (tangentBuffer == null || tangentBuffer.Length < vertexCount)
+ tangentBuffer = new Vector4[vertexCount];
+
+ if (tempTanBuffer == null || tempTanBuffer.Length < vertexCount * 2)
+ tempTanBuffer = new Vector2[vertexCount * 2]; // two arrays in one.
+ }
+
+ /// Step 2 of solving tangents. Fills (part of) a temporary tangent-solution buffer based on the vertices and uvs defined by a submesh's triangle buffer. Only needs to be called once for single-submesh meshes.
+ /// A temporary Vector3[] for calculating tangents.
+ /// The mesh's current vertex position buffer.
+ /// The mesh's current triangles buffer.
+ /// The mesh's current uvs buffer.
+ /// Number of vertices that require tangents (or the size of the vertex array)
+ /// The number of triangle indexes in the triangle array to be used.
+ internal static void SolveTangents2DTriangles (Vector2[] tempTanBuffer, int[] triangles, int triangleCount, Vector3[] vertices, Vector2[] uvs, int vertexCount) {
+ Vector2 sdir;
+ Vector2 tdir;
+ for (int t = 0; t < triangleCount; t += 3) {
+ int i1 = triangles[t + 0];
+ int i2 = triangles[t + 1];
+ int i3 = triangles[t + 2];
+
+ Vector3 v1 = vertices[i1];
+ Vector3 v2 = vertices[i2];
+ Vector3 v3 = vertices[i3];
+
+ Vector2 w1 = uvs[i1];
+ Vector2 w2 = uvs[i2];
+ Vector2 w3 = uvs[i3];
+
+ float x1 = v2.x - v1.x;
+ float x2 = v3.x - v1.x;
+ float y1 = v2.y - v1.y;
+ float y2 = v3.y - v1.y;
+
+ float s1 = w2.x - w1.x;
+ float s2 = w3.x - w1.x;
+ float t1 = w2.y - w1.y;
+ float t2 = w3.y - w1.y;
+
+ float div = s1 * t2 - s2 * t1;
+ float r = (div == 0f) ? 0f : 1f / div;
+
+ sdir.x = (t2 * x1 - t1 * x2) * r;
+ sdir.y = (t2 * y1 - t1 * y2) * r;
+ tempTanBuffer[i1] = tempTanBuffer[i2] = tempTanBuffer[i3] = sdir;
+
+ tdir.x = (s1 * x2 - s2 * x1) * r;
+ tdir.y = (s1 * y2 - s2 * y1) * r;
+ tempTanBuffer[vertexCount + i1] = tempTanBuffer[vertexCount + i2] = tempTanBuffer[vertexCount + i3] = tdir;
+ }
+ }
+
+ /// Step 3 of solving tangents. Fills a Vector4[] tangents array according to values calculated in step 2.
+ /// A Vector4[] that will eventually be used to set Mesh.tangents
+ /// A temporary Vector3[] for calculating tangents.
+ /// Number of vertices that require tangents (or the size of the vertex array)
+ internal static void SolveTangents2DBuffer (Vector4[] tangents, Vector2[] tempTanBuffer, int vertexCount) {
+
+ Vector4 tangent;
+ tangent.z = 0;
+ for (int i = 0; i < vertexCount; ++i) {
+ Vector2 t = tempTanBuffer[i];
+
+ // t.Normalize() (aggressively inlined). Even better if offloaded to GPU via vertex shader.
+ float magnitude = Mathf.Sqrt(t.x * t.x + t.y * t.y);
+ if (magnitude > 1E-05) {
+ float reciprocalMagnitude = 1f/magnitude;
+ t.x *= reciprocalMagnitude;
+ t.y *= reciprocalMagnitude;
+ }
+
+ Vector2 t2 = tempTanBuffer[vertexCount + i];
+ tangent.x = t.x;
+ tangent.y = t.y;
+ //tangent.z = 0;
+ tangent.w = (t.y * t2.x > t.x * t2.y) ? 1 : -1; // 2D direction calculation. Used for binormals.
+ tangents[i] = tangent;
+ }
+
+ }
+ #endregion
+ }
+
+ public class MeshRendererBuffers : IDisposable {
+ DoubleBuffered doubleBufferedMesh;
+ internal readonly ExposedList submeshMaterials = new ExposedList();
+ internal Material[] sharedMaterials = new Material[0];
+
+ public void Initialize () {
+ doubleBufferedMesh = new DoubleBuffered();
+ }
+
+ public Material[] GetUpdatedShaderdMaterialsArray () {
+ if (submeshMaterials.Count == sharedMaterials.Length)
+ submeshMaterials.CopyTo(sharedMaterials);
+ else
+ sharedMaterials = submeshMaterials.ToArray();
+
+ return sharedMaterials;
+ }
+
+ public bool MaterialsChangedInLastUpdate () {
+ int newSubmeshMaterials = submeshMaterials.Count;
+ var sharedMaterials = this.sharedMaterials;
+ if (newSubmeshMaterials != sharedMaterials.Length) return true;
+
+ var submeshMaterialsItems = submeshMaterials.Items;
+ for (int i = 0; i < newSubmeshMaterials; i++)
+ if (!Material.ReferenceEquals(submeshMaterialsItems[i], sharedMaterials[i])) return true; //if (submeshMaterialsItems[i].GetInstanceID() != sharedMaterials[i].GetInstanceID()) return true;
+
+ return false;
+ }
+
+ public void UpdateSharedMaterials (ExposedList instructions) {
+ int newSize = instructions.Count;
+ { //submeshMaterials.Resize(instructions.Count);
+ if (newSize > submeshMaterials.Items.Length)
+ Array.Resize(ref submeshMaterials.Items, newSize);
+ submeshMaterials.Count = newSize;
+ }
+
+ var submeshMaterialsItems = submeshMaterials.Items;
+ var instructionsItems = instructions.Items;
+ for (int i = 0; i < newSize; i++)
+ submeshMaterialsItems[i] = instructionsItems[i].material;
+ }
+
+ public SmartMesh GetNextMesh () {
+ return doubleBufferedMesh.GetNext();
+ }
+
+ public void Clear () {
+ sharedMaterials = new Material[0];
+ submeshMaterials.Clear();
+ }
+
+ public void Dispose () {
+ if (doubleBufferedMesh == null) return;
+ doubleBufferedMesh.GetNext().Dispose();
+ doubleBufferedMesh.GetNext().Dispose();
+ doubleBufferedMesh = null;
+ }
+
+ ///This is a Mesh that also stores the instructions SkeletonRenderer generated for it.
+ public class SmartMesh : IDisposable {
+ public Mesh mesh = SpineMesh.NewMesh();
+ public SkeletonRendererInstruction instructionUsed = new SkeletonRendererInstruction();
+
+ public void Dispose () {
+ if (mesh != null) {
+ #if UNITY_EDITOR
+ if (Application.isEditor && !Application.isPlaying)
+ UnityEngine.Object.DestroyImmediate(mesh);
+ else
+ UnityEngine.Object.Destroy(mesh);
+ #else
+ UnityEngine.Object.Destroy(mesh);
+ #endif
+ }
+ mesh = null;
+ }
+ }
+ }
+
+ public class SkeletonRendererInstruction {
+ public bool immutableTriangles;
+ public readonly ExposedList submeshInstructions = new ExposedList();
+
+ #if SPINE_TRIANGLECHECK
+ public bool hasActiveClipping;
+ public int rawVertexCount = -1;
+ public readonly ExposedList attachments = new ExposedList();
+ #endif
+
+ public void Clear () {
+ #if SPINE_TRIANGLECHECK
+ this.attachments.Clear(false);
+ rawVertexCount = -1;
+ hasActiveClipping = false;
+ #endif
+ this.submeshInstructions.Clear(false);
+ }
+
+ public void SetWithSubset (ExposedList instructions, int startSubmesh, int endSubmesh) {
+ #if SPINE_TRIANGLECHECK
+ int runningVertexCount = 0;
+ #endif
+
+ var submeshes = this.submeshInstructions;
+ submeshes.Clear(false);
+ int submeshCount = endSubmesh - startSubmesh;
+ submeshes.Resize(submeshCount);
+ var submeshesItems = submeshes.Items;
+ var instructionsItems = instructions.Items;
+ for (int i = 0; i < submeshCount; i++) {
+ var instruction = instructionsItems[startSubmesh + i];
+ submeshesItems[i] = instruction;
+ #if SPINE_TRIANGLECHECK
+ this.hasActiveClipping = instruction.hasClipping;
+ submeshesItems[i].rawFirstVertexIndex = runningVertexCount; // Ensure current instructions have correct cached values.
+ runningVertexCount += instruction.rawVertexCount; // vertexCount will also be used for the rest of this method.
+ #endif
+ }
+ #if SPINE_TRIANGLECHECK
+ this.rawVertexCount = runningVertexCount;
+
+ // assumption: instructions are contiguous. start and end are valid within instructions.
+
+ int startSlot = instructionsItems[startSubmesh].startSlot;
+ int endSlot = instructionsItems[endSubmesh - 1].endSlot;
+ attachments.Clear(false);
+ int attachmentCount = endSlot - startSlot;
+ attachments.Resize(attachmentCount);
+ var attachmentsItems = attachments.Items;
+
+ var drawOrder = instructionsItems[0].skeleton.drawOrder.Items;
+ for (int i = 0; i < attachmentCount; i++)
+ attachmentsItems[i] = drawOrder[startSlot + i].attachment;
+ #endif
+ }
+
+ public void Set (SkeletonRendererInstruction other) {
+ this.immutableTriangles = other.immutableTriangles;
+
+ #if SPINE_TRIANGLECHECK
+ this.hasActiveClipping = other.hasActiveClipping;
+ this.rawVertexCount = other.rawVertexCount;
+ this.attachments.Clear(false);
+ this.attachments.GrowIfNeeded(other.attachments.Capacity);
+ this.attachments.Count = other.attachments.Count;
+ other.attachments.CopyTo(this.attachments.Items);
+ #endif
+
+ this.submeshInstructions.Clear(false);
+ this.submeshInstructions.GrowIfNeeded(other.submeshInstructions.Capacity);
+ this.submeshInstructions.Count = other.submeshInstructions.Count;
+ other.submeshInstructions.CopyTo(this.submeshInstructions.Items);
+ }
+
+ public static bool GeometryNotEqual (SkeletonRendererInstruction a, SkeletonRendererInstruction b) {
+ #if SPINE_TRIANGLECHECK
+ #if UNITY_EDITOR
+ if (!Application.isPlaying)
+ return true;
+ #endif
+
+ if (a.hasActiveClipping || b.hasActiveClipping) return true; // Triangles are unpredictable when clipping is active.
+
+ // Everything below assumes the raw vertex and triangle counts were used. (ie, no clipping was done)
+ if (a.rawVertexCount != b.rawVertexCount) return true;
+
+ if (a.immutableTriangles != b.immutableTriangles) return true;
+
+ int attachmentCountB = b.attachments.Count;
+ if (a.attachments.Count != attachmentCountB) return true; // Bounds check for the looped storedAttachments count below.
+
+ // Submesh count changed
+ int submeshCountA = a.submeshInstructions.Count;
+ int submeshCountB = b.submeshInstructions.Count;
+ if (submeshCountA != submeshCountB) return true;
+
+ // Submesh Instruction mismatch
+ var submeshInstructionsItemsA = a.submeshInstructions.Items;
+ var submeshInstructionsItemsB = b.submeshInstructions.Items;
+
+ var attachmentsA = a.attachments.Items;
+ var attachmentsB = b.attachments.Items;
+ for (int i = 0; i < attachmentCountB; i++)
+ if (!System.Object.ReferenceEquals(attachmentsA[i], attachmentsB[i])) return true;
+
+ for (int i = 0; i < submeshCountB; i++) {
+ var submeshA = submeshInstructionsItemsA[i];
+ var submeshB = submeshInstructionsItemsB[i];
+
+ if (!(
+ submeshA.rawVertexCount == submeshB.rawVertexCount &&
+ submeshA.startSlot == submeshB.startSlot &&
+ submeshA.endSlot == submeshB.endSlot
+ && submeshA.rawTriangleCount == submeshB.rawTriangleCount &&
+ submeshA.rawFirstVertexIndex == submeshB.rawFirstVertexIndex
+ ))
+ return true;
+ }
+
+ return false;
+ #else
+ // In normal immutable triangle use, immutableTriangles will be initially false, forcing the smartmesh to update the first time but never again after that, unless there was an immutableTriangles flag mismatch..
+ if (a.immutableTriangles || b.immutableTriangles)
+ return (a.immutableTriangles != b.immutableTriangles);
+
+ return true;
+ #endif
+ }
+ }
+
}
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 a24bc4aa1..2bd6d1603 100644
--- a/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs
+++ b/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs
@@ -41,6 +41,7 @@ namespace Spine.Unity.Editor {
SerializedProperty material_, color_;
SerializedProperty skeletonDataAsset_, initialSkinName_;
SerializedProperty startingAnimation_, startingLoop_, timeScale_, freeze_, unscaledTime_, tintBlack_;
+ SerializedProperty meshGeneratorSettings_;
SerializedProperty raycastTarget_;
SkeletonGraphic thisSkeletonGraphic;
@@ -65,6 +66,8 @@ namespace Spine.Unity.Editor {
timeScale_ = so.FindProperty("timeScale");
unscaledTime_ = so.FindProperty("unscaledTime");
freeze_ = so.FindProperty("freeze");
+
+ meshGeneratorSettings_ = so.FindProperty("meshGenerator").FindPropertyRelative("settings");
}
public override void OnInspectorGUI () {
@@ -80,10 +83,12 @@ namespace Spine.Unity.Editor {
serializedObject.Update();
return;
}
+ using (new SpineInspectorUtility.BoxScope()) {
+ EditorGUILayout.PropertyField(meshGeneratorSettings_, new GUIContent("Advanced..."), includeChildren: true);
+ }
EditorGUILayout.Space();
EditorGUILayout.PropertyField(initialSkinName_);
- //EditorGUILayout.PropertyField(tintBlack_);
EditorGUILayout.Space();
EditorGUILayout.LabelField("Animation", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(startingAnimation_);
@@ -106,7 +111,7 @@ namespace Spine.Unity.Editor {
[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.GetComponent().sharedMesh;
mesh.RecalculateBounds();
var bounds = mesh.bounds;
diff --git a/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs b/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs
index 1e2854e01..a86f9f013 100644
--- a/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs
+++ b/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs
@@ -150,9 +150,10 @@ namespace Spine.Unity {
protected Spine.AnimationState state;
public Spine.AnimationState AnimationState { get { return state; } }
- // This is any object that can give you a mesh when you give it a skeleton to render.
- protected Spine.Unity.MeshGeneration.ISimpleMeshGenerator spineMeshGenerator;
- public Spine.Unity.MeshGeneration.ISimpleMeshGenerator SpineMeshGenerator { get { return this.spineMeshGenerator; } }
+ [SerializeField] protected Spine.Unity.MeshGenerator meshGenerator = new MeshGenerator();
+ public Spine.Unity.MeshGenerator MeshGenerator { get { return this.meshGenerator; } }
+ DoubleBuffered meshBuffers;
+ SkeletonRendererInstruction currentInstructions = new SkeletonRendererInstruction();
public event UpdateBonesDelegate UpdateLocal;
public event UpdateBonesDelegate UpdateWorld;
@@ -180,9 +181,7 @@ namespace Spine.Unity {
}
this.skeleton = new Skeleton(skeletonData);
- this.spineMeshGenerator = new Spine.Unity.MeshGeneration.ArraysSimpleMeshGenerator(); // You can switch this out with any other implementer of Spine.Unity.MeshGeneration.ISimpleMeshGenerator
- //this.spineMeshGenerator.AddBlackTint = this.tintBlack;
- this.spineMeshGenerator.PremultiplyVertexColors = true;
+ meshBuffers = new DoubleBuffered();
// Set the initial Skin and Animation
if (!string.IsNullOrEmpty(initialSkinName))
@@ -193,14 +192,28 @@ namespace Spine.Unity {
}
public void UpdateMesh () {
- if (this.IsValid) {
- skeleton.SetColor(this.color);
- if (canvas != null)
- spineMeshGenerator.Scale = canvas.referencePixelsPerUnit; //JOHN: left a todo: move this to a listener to of the canvas?
+ if (!this.IsValid) return;
- canvasRenderer.SetMesh(spineMeshGenerator.GenerateMesh(skeleton));
- //this.UpdateMaterial(); // TODO: This allocates memory.
- }
+ skeleton.SetColor(this.color);
+
+ var currentInstructions = this.currentInstructions;
+ MeshGenerator.GenerateSingleSubmeshInstruction(currentInstructions, skeleton, true, this.material);
+ meshGenerator.BeginNewMesh();
+ meshGenerator.AddSubmesh(currentInstructions.submeshInstructions.Items[0]);
+ if (canvas != null)
+ meshGenerator.ScaleVertexData(canvas.referencePixelsPerUnit);
+
+ var smartMesh = meshBuffers.GetNext();
+ var mesh = smartMesh.mesh;
+
+ meshGenerator.FillVertexData(mesh);
+ if (SkeletonRendererInstruction.GeometryNotEqual(currentInstructions, smartMesh.instructionUsed))
+ meshGenerator.FillTrianglesSingle(mesh);
+
+ canvasRenderer.SetMesh(mesh);
+ smartMesh.instructionUsed.Set(currentInstructions);
+
+ //this.UpdateMaterial(); // TODO: This allocates memory.
}
#endregion
}
diff --git a/spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/SkeletonPartsRenderer.cs b/spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/SkeletonPartsRenderer.cs
index b8fbb5952..a5326bab9 100644
--- a/spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/SkeletonPartsRenderer.cs
+++ b/spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/SkeletonPartsRenderer.cs
@@ -29,15 +29,14 @@
*****************************************************************************/
using UnityEngine;
-using Spine.Unity.MeshGeneration;
namespace Spine.Unity.Modules {
[RequireComponent(typeof(MeshRenderer), typeof(MeshFilter))]
public class SkeletonPartsRenderer : MonoBehaviour {
#region Properties
- ISubmeshSetMeshGenerator meshGenerator;
- public ISubmeshSetMeshGenerator MeshGenerator {
+ MeshGenerator meshGenerator;
+ public MeshGenerator MeshGenerator {
get {
LazyIntialize();
return meshGenerator;
@@ -61,11 +60,21 @@ namespace Spine.Unity.Modules {
}
#endregion
+ MeshRendererBuffers buffers;
+ SkeletonRendererInstruction currentInstructions = new SkeletonRendererInstruction();
+
+
void LazyIntialize () {
- if (meshGenerator != null) return;
- meshGenerator = new ArraysSubmeshSetMeshGenerator();
- meshFilter = GetComponent();
- meshRenderer = GetComponent();
+ if (buffers == null) {
+ buffers = new MeshRendererBuffers();
+ buffers.Initialize();
+
+ if (meshGenerator != null) return;
+ meshGenerator = new MeshGenerator();
+ meshFilter = GetComponent();
+ meshRenderer = GetComponent();
+ currentInstructions.Clear();
+ }
}
public void ClearMesh () {
@@ -75,9 +84,31 @@ namespace Spine.Unity.Modules {
public void RenderParts (ExposedList instructions, int startSubmesh, int endSubmesh) {
LazyIntialize();
- MeshAndMaterials m = meshGenerator.GenerateMesh(instructions, startSubmesh, endSubmesh);
- meshFilter.sharedMesh = m.mesh;
- meshRenderer.sharedMaterials = m.materials;
+
+ // STEP 1: Create instruction
+ currentInstructions.SetWithSubset(instructions, startSubmesh, endSubmesh);
+
+ // STEP 2: Generate mesh buffers.
+ var currentInstructionsSubmeshesItems = currentInstructions.submeshInstructions.Items;
+ meshGenerator.BeginNewMesh();
+ for (int i = 0; i < currentInstructions.submeshInstructions.Count; i++)
+ meshGenerator.AddSubmesh(currentInstructionsSubmeshesItems[i]);
+
+ buffers.UpdateSharedMaterials(currentInstructions.submeshInstructions);
+
+ // STEP 3: modify mesh.
+ var smartMesh = buffers.GetNextMesh();
+ var mesh = smartMesh.mesh;
+ meshGenerator.FillVertexData(mesh);
+ if (SkeletonRendererInstruction.GeometryNotEqual(currentInstructions, smartMesh.instructionUsed)) {
+ meshGenerator.FillTriangles(mesh);
+ meshRenderer.sharedMaterials = buffers.GetUpdatedShaderdMaterialsArray();
+ } else if (buffers.MaterialsChangedInLastUpdate()) {
+ meshRenderer.sharedMaterials = buffers.GetUpdatedShaderdMaterialsArray();
+ }
+
+ meshFilter.sharedMesh = mesh;
+ smartMesh.instructionUsed.Set(currentInstructions);
}
public void SetPropertyBlock (MaterialPropertyBlock block) {
diff --git a/spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/SkeletonRenderSeparator.cs b/spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/SkeletonRenderSeparator.cs
index 061ffeb6e..6f4a87acd 100644
--- a/spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/SkeletonRenderSeparator.cs
+++ b/spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/SkeletonRenderSeparator.cs
@@ -130,40 +130,36 @@ namespace Spine.Unity.Modules {
MaterialPropertyBlock copiedBlock;
- void HandleRender (SkeletonRenderer.SmartMesh.Instruction instruction) {
+ void HandleRender (SkeletonRendererInstruction instruction) {
int rendererCount = partsRenderers.Count;
if (rendererCount <= 0) return;
if (copyPropertyBlock)
mainMeshRenderer.GetPropertyBlock(copiedBlock);
+ var settings = new MeshGenerator.Settings {
+ addNormals = skeletonRenderer.addNormals,
+ calculateTangents = skeletonRenderer.calculateTangents,
+ immutableTriangles = false, // parts cannot do immutable triangles.
+ pmaVertexColors = skeletonRenderer.pmaVertexColors,
+ renderMeshes = skeletonRenderer.renderMeshes,
+ tintBlack = skeletonRenderer.tintBlack,
+ useClipping = true,
+ zSpacing = skeletonRenderer.zSpacing
+ };
+
var submeshInstructions = instruction.submeshInstructions;
var submeshInstructionsItems = submeshInstructions.Items;
int lastSubmeshInstruction = submeshInstructions.Count - 1;
- #if SPINE_OPTIONAL_NORMALS
- bool addNormals = skeletonRenderer.calculateNormals;
- #endif
-
- #if SPINE_OPTIONAL_SOLVETANGENTS
- bool addTangents = skeletonRenderer.calculateTangents;
- #endif
-
- bool pmaVertexColors = skeletonRenderer.pmaVertexColors;
-
int rendererIndex = 0;
var currentRenderer = partsRenderers[rendererIndex];
for (int si = 0, start = 0; si <= lastSubmeshInstruction; si++) {
if (submeshInstructionsItems[si].forceSeparate || si == lastSubmeshInstruction) {
// Apply properties
var meshGenerator = currentRenderer.MeshGenerator;
- #if SPINE_OPTIONAL_NORMALS
- meshGenerator.AddNormals = addNormals;
- #endif
- #if SPINE_OPTIONAL_SOLVETANGENTS
- meshGenerator.AddTangents = addTangents;
- #endif
- meshGenerator.PremultiplyVertexColors = pmaVertexColors;
+ meshGenerator.settings = settings;
+
if (copyPropertyBlock)
currentRenderer.SetPropertyBlock(copiedBlock);
diff --git a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs
index 80e0de1c8..ad6dccd61 100644
--- a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs
+++ b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs
@@ -30,14 +30,9 @@
#define SPINE_OPTIONAL_RENDEROVERRIDE
#define SPINE_OPTIONAL_MATERIALOVERRIDE
-#define SPINE_OPTIONAL_NORMALS
-#define SPINE_OPTIONAL_SOLVETANGENTS
-//#define SPINE_OPTIONAL_FRONTFACING
-using System;
using System.Collections.Generic;
using UnityEngine;
-using Spine.Unity.MeshGeneration;
namespace Spine.Unity {
/// Renders a skeleton.
@@ -54,30 +49,25 @@ namespace Spine.Unity {
#region Advanced
// Submesh Separation
- [UnityEngine.Serialization.FormerlySerializedAs("submeshSeparators")]
- [SpineSlot]
- public string[] separatorSlotNames = new string[0];
- [System.NonSerialized]
- public readonly List separatorSlots = new List();
+ [UnityEngine.Serialization.FormerlySerializedAs("submeshSeparators")] [SpineSlot] public string[] separatorSlotNames = new string[0];
+ [System.NonSerialized] public readonly List separatorSlots = new List();
- public float zSpacing;
- public bool renderMeshes = true, immutableTriangles;
+ [Range(-0.1f, 0f)] public float zSpacing;
+ public bool renderMeshes = true;
+ public bool immutableTriangles = false;
public bool pmaVertexColors = true;
public bool clearStateOnDisable = false;
public bool tintBlack = false;
- #if SPINE_OPTIONAL_NORMALS
- public bool calculateNormals;
- #endif
- #if SPINE_OPTIONAL_SOLVETANGENTS
+ [UnityEngine.Serialization.FormerlySerializedAs("calculateNormals")]
+ public bool addNormals;
public bool calculateTangents;
- #endif
public bool logErrors = false;
#if SPINE_OPTIONAL_RENDEROVERRIDE
public bool disableRenderingOnOverride = true;
- public delegate void InstructionDelegate (SkeletonRenderer.SmartMesh.Instruction instruction);
+ public delegate void InstructionDelegate (SkeletonRendererInstruction instruction);
event InstructionDelegate generateMeshOverride;
public event InstructionDelegate GenerateMeshOverride {
add {
@@ -118,27 +108,10 @@ namespace Spine.Unity {
return skeleton;
}
}
-
- Spine.Unity.DoubleBuffered doubleBufferedMesh;
- readonly SmartMesh.Instruction currentInstructions = new SmartMesh.Instruction();
- readonly ExposedList submeshes = new ExposedList();
- readonly ExposedList submeshMaterials = new ExposedList();
- Material[] sharedMaterials = new Material[0];
- float[] tempVertices = new float[8];
- Vector3[] vertices;
- Color32[] colors;
- Vector2[] uvs;
-
- Vector2[] uv2;
- Vector2[] uv3;
-
- #if SPINE_OPTIONAL_NORMALS
- Vector3[] normals;
- #endif
- #if SPINE_OPTIONAL_SOLVETANGENTS
- Vector4[] tangents;
- Vector2[] tempTanBuffer;
- #endif
+
+ [System.NonSerialized] readonly SkeletonRendererInstruction currentInstructions = new SkeletonRendererInstruction();
+ [System.NonSerialized] readonly MeshGenerator meshGenerator = new MeshGenerator();
+ [System.NonSerialized] readonly MeshRendererBuffers rendererBuffers = new MeshRendererBuffers();
#region Runtime Instantiation
public static T NewSpineGameObject (SkeletonDataAsset skeletonDataAsset) where T : SkeletonRenderer {
@@ -167,10 +140,7 @@ namespace Spine.Unity {
}
void OnDestroy () {
- if (doubleBufferedMesh == null) return;
- doubleBufferedMesh.GetNext().Dispose();
- doubleBufferedMesh.GetNext().Dispose();
- doubleBufferedMesh = null;
+ rendererBuffers.Dispose();
}
protected virtual void ClearState () {
@@ -192,14 +162,9 @@ namespace Spine.Unity {
if (meshRenderer != null) meshRenderer.sharedMaterial = null;
currentInstructions.Clear();
- vertices = null;
- colors = null;
- uvs = null;
- sharedMaterials = new Material[0];
- submeshMaterials.Clear();
- submeshes.Clear();
+ rendererBuffers.Clear();
+ meshGenerator.BeginNewMesh();
skeleton = null;
-
valid = false;
}
@@ -216,8 +181,7 @@ namespace Spine.Unity {
meshFilter = GetComponent();
meshRenderer = GetComponent();
- doubleBufferedMesh = new DoubleBuffered();
- vertices = new Vector3[0];
+ rendererBuffers.Initialize();
skeleton = new Skeleton(skeletonData);
if (!string.IsNullOrEmpty(initialSkinName) && initialSkinName != "default")
@@ -234,446 +198,72 @@ namespace Spine.Unity {
}
public virtual void LateUpdate () {
- if (!valid)
- return;
+ if (!valid) return;
- if (
- (!meshRenderer.enabled)
- #if SPINE_OPTIONAL_RENDEROVERRIDE
- && this.generateMeshOverride == null
- #endif
- )
- return;
-
-
- // STEP 1. Determine a SmartMesh.Instruction. Split up instructions into submeshes. ============================================================
- ExposedList drawOrder = skeleton.drawOrder;
- var drawOrderItems = drawOrder.Items;
- int drawOrderCount = drawOrder.Count;
- bool renderMeshes = this.renderMeshes;
-
- // Clear last state of attachments and submeshes
- var workingInstruction = this.currentInstructions;
- var workingAttachments = workingInstruction.attachments;
- workingAttachments.Clear(false);
- workingAttachments.GrowIfNeeded(drawOrderCount);
- workingAttachments.Count = drawOrderCount;
- var workingAttachmentsItems = workingInstruction.attachments.Items;
-
- var workingSubmeshInstructions = workingInstruction.submeshInstructions; // Items array should not be cached. There is dynamic writing to this list.
- workingSubmeshInstructions.Clear(false);
-
- #if !SPINE_TK2D
- bool isCustomSlotMaterialsPopulated = customSlotMaterials.Count > 0;
+ #if SPINE_OPTIONAL_RENDEROVERRIDE
+ bool doMeshOverride = generateMeshOverride != null;
+ if ((!meshRenderer.enabled) && !doMeshOverride) return;
+ #else
+ const bool doMeshOverride = false;
+ if (!meshRenderer.enabled) return;
#endif
- bool hasSeparators = separatorSlots.Count > 0;
- int vertexCount = 0;
- int submeshVertexCount = 0;
- int submeshTriangleCount = 0, submeshFirstVertex = 0, submeshStartSlotIndex = 0;
- Material lastMaterial = null;
- for (int i = 0; i < drawOrderCount; i++) {
- Slot slot = drawOrderItems[i];
- Attachment attachment = slot.attachment;
- workingAttachmentsItems[i] = attachment;
+ var currentInstructions = this.currentInstructions;
- object rendererObject = null; // An AtlasRegion in plain Spine-Unity. Spine-TK2D hooks into TK2D's system. eventual source of Material object.
- int attachmentVertexCount, attachmentTriangleCount;
- bool noRender = false;
-
- var regionAttachment = attachment as RegionAttachment;
- if (regionAttachment != null) {
- rendererObject = regionAttachment.RendererObject;
- attachmentVertexCount = 4;
- attachmentTriangleCount = 6;
- } else {
- if (!renderMeshes) {
- noRender = true;
- attachmentVertexCount = 0;
- attachmentTriangleCount = 0;
- //continue;
- } else {
- var meshAttachment = attachment as MeshAttachment;
- if (meshAttachment != null) {
- rendererObject = meshAttachment.RendererObject;
- attachmentVertexCount = meshAttachment.worldVerticesLength >> 1;
- attachmentTriangleCount = meshAttachment.triangles.Length;
- } else {
- noRender = true;
- attachmentVertexCount = 0;
- attachmentTriangleCount = 0;
- //continue;
- }
- }
- }
-
- // Create a new SubmeshInstruction when material changes. (or when forced to separate by a submeshSeparator)
- // Slot with a separator/new material will become the starting slot of the next new instruction.
- bool forceSeparate = (hasSeparators && separatorSlots.Contains(slot));
- if (noRender) {
- if (forceSeparate && vertexCount > 0
- #if SPINE_OPTIONAL_RENDEROVERRIDE
- && this.generateMeshOverride != null
- #endif
- ) {
- workingSubmeshInstructions.Add(
- new Spine.Unity.MeshGeneration.SubmeshInstruction {
- skeleton = this.skeleton,
- material = lastMaterial,
- startSlot = submeshStartSlotIndex,
- endSlot = i,
- triangleCount = submeshTriangleCount,
- firstVertexIndex = submeshFirstVertex,
- vertexCount = submeshVertexCount,
- forceSeparate = forceSeparate
- }
- );
- submeshTriangleCount = 0;
- submeshVertexCount = 0;
- submeshFirstVertex = vertexCount;
- submeshStartSlotIndex = i;
- }
- } else {
- #if !SPINE_TK2D
- Material material;
- if (isCustomSlotMaterialsPopulated) {
- 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
-
- if (vertexCount > 0 && (forceSeparate || lastMaterial.GetInstanceID() != material.GetInstanceID())) {
- workingSubmeshInstructions.Add(
- new Spine.Unity.MeshGeneration.SubmeshInstruction {
- skeleton = this.skeleton,
- material = lastMaterial,
- startSlot = submeshStartSlotIndex,
- endSlot = i,
- triangleCount = submeshTriangleCount,
- firstVertexIndex = submeshFirstVertex,
- vertexCount = submeshVertexCount,
- forceSeparate = forceSeparate
- }
- );
- submeshTriangleCount = 0;
- submeshVertexCount = 0;
- submeshFirstVertex = vertexCount;
- submeshStartSlotIndex = i;
- }
- // Update state for the next iteration.
- lastMaterial = material;
- submeshTriangleCount += attachmentTriangleCount;
- vertexCount += attachmentVertexCount;
- submeshVertexCount += attachmentVertexCount;
- }
- }
-
- if (submeshVertexCount != 0) {
- workingSubmeshInstructions.Add(
- new Spine.Unity.MeshGeneration.SubmeshInstruction {
- skeleton = this.skeleton,
- material = lastMaterial,
- startSlot = submeshStartSlotIndex,
- endSlot = drawOrderCount,
- triangleCount = submeshTriangleCount,
- firstVertexIndex = submeshFirstVertex,
- vertexCount = submeshVertexCount,
- forceSeparate = false
- }
- );
- }
-
- workingInstruction.vertexCount = vertexCount;
- workingInstruction.immutableTriangles = this.immutableTriangles;
+ // STEP 1. Determine a SmartMesh.Instruction. Split up instructions into submeshes. ============================================================
+ MeshGenerator.GenerateSkeletonRendererInstruction(currentInstructions, skeleton, customSlotMaterials, separatorSlots, doMeshOverride, this.immutableTriangles, this.renderMeshes);
// STEP 1.9. Post-process workingInstructions. ============================================================
-
#if SPINE_OPTIONAL_MATERIALOVERRIDE
- // Material overrides are done here so they can be applied per submesh instead of per slot
- // but they will still be passed through the GenerateMeshOverride delegate,
- // and will still go through the normal material match check step in STEP 3.
- if (customMaterialOverride.Count > 0) { // isCustomMaterialOverridePopulated
- var workingSubmeshInstructionsItems = workingSubmeshInstructions.Items;
- for (int i = 0; i < workingSubmeshInstructions.Count; i++) {
- var m = workingSubmeshInstructionsItems[i].material;
- Material mo;
- if (customMaterialOverride.TryGetValue(m, out mo)) {
- workingSubmeshInstructionsItems[i].material = mo;
- }
- }
- }
+ if (customMaterialOverride.Count > 0) // isCustomMaterialOverridePopulated
+ MeshGenerator.TryReplaceMaterials(currentInstructions.submeshInstructions, customMaterialOverride);
#endif
+
#if SPINE_OPTIONAL_RENDEROVERRIDE
- if (this.generateMeshOverride != null) {
- this.generateMeshOverride(workingInstruction);
+ if (doMeshOverride) {
+ this.generateMeshOverride(currentInstructions);
if (disableRenderingOnOverride) return;
}
#endif
// STEP 2. Update vertex buffer based on verts from the attachments. ============================================================
- // Uses values that were also stored in workingInstruction.
- if (tintBlack) {
- ArraysMeshGenerator.EnsureSize(vertexCount, ref this.uv2);
- ArraysMeshGenerator.EnsureSize(vertexCount, ref this.uv3);
+ meshGenerator.BeginNewMesh();
+ meshGenerator.settings = new MeshGenerator.Settings {
+ pmaVertexColors = this.pmaVertexColors,
+ zSpacing = this.zSpacing,
+ useClipping = true,
+ tintBlack = this.tintBlack,
+ calculateTangents = this.calculateTangents,
+ renderMeshes = this.renderMeshes,
+ addNormals = this.addNormals
+ };
+
+ var workingSubmeshInstructions = currentInstructions.submeshInstructions;
+ foreach (var submeshInstruction in workingSubmeshInstructions) {
+ meshGenerator.AddSubmesh(submeshInstruction);
}
-
- #if SPINE_OPTIONAL_NORMALS
- bool vertexCountIncreased = ArraysMeshGenerator.EnsureSize(vertexCount, ref this.vertices, ref this.uvs, ref this.colors);
- if (vertexCountIncreased && calculateNormals) {
- Vector3[] localNormals = this.normals = new Vector3[vertexCount];
- Vector3 normal = new Vector3(0, 0, -1);
- for (int i = 0; i < vertexCount; i++)
- localNormals[i] = normal;
- }
- #else
- ArraysMeshGenerator.EnsureSize(vertexCount, ref this.vertices, ref this.uvs, ref this.colors);
- #endif
-
- Vector3 meshBoundsMin;
- Vector3 meshBoundsMax;
- if (vertexCount <= 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 * (drawOrderCount - 1);
- } else {
- meshBoundsMin.z = zSpacing * (drawOrderCount - 1);
- meshBoundsMax.z = 0f;
- }
- }
- int vertexIndex = 0;
-
- if (tintBlack)
- ArraysMeshGenerator.FillBlackUVs(skeleton, 0, drawOrderCount, this.uv2, this.uv3, vertexIndex, renderMeshes); // This needs to be called before FillVerts so we have the correct vertexIndex argument.
-
- ArraysMeshGenerator.FillVerts(skeleton, 0, drawOrderCount, this.zSpacing, pmaVertexColors, this.vertices, this.uvs, this.colors, ref vertexIndex, ref tempVertices, ref meshBoundsMin, ref meshBoundsMax, renderMeshes);
-
-
+
// Step 3. Move the mesh data into a UnityEngine.Mesh ============================================================
- var currentSmartMesh = doubleBufferedMesh.GetNext(); // Double-buffer for performance.
+ var currentSmartMesh = rendererBuffers.GetNextMesh(); // Double-buffer for performance.
var currentMesh = currentSmartMesh.mesh;
- currentMesh.vertices = this.vertices;
- currentMesh.colors32 = colors;
- currentMesh.uv = uvs;
- currentMesh.bounds = ArraysMeshGenerator.ToBounds(meshBoundsMin, meshBoundsMax);
+ meshGenerator.FillVertexData(currentMesh);
- if (tintBlack) {
- currentMesh.uv2 = this.uv2;
- currentMesh.uv3 = this.uv3;
- }
-
- var currentSmartMeshInstructionUsed = currentSmartMesh.instructionUsed;
- #if SPINE_OPTIONAL_NORMALS
- if (calculateNormals && currentSmartMeshInstructionUsed.vertexCount < vertexCount)
- currentMesh.normals = normals;
- #endif
+ rendererBuffers.UpdateSharedMaterials(workingSubmeshInstructions);
// Check if the triangles should also be updated.
- // This thorough structure check is cheaper than updating triangles every frame.
- bool mustUpdateMeshStructure = CheckIfMustUpdateMeshStructure(workingInstruction, currentSmartMeshInstructionUsed);
- int submeshCount = workingSubmeshInstructions.Count;
- if (mustUpdateMeshStructure) {
- var thisSubmeshMaterials = this.submeshMaterials;
- thisSubmeshMaterials.Clear(false);
-
- int oldSubmeshCount = submeshes.Count;
-
- if (submeshes.Capacity < submeshCount)
- submeshes.Capacity = submeshCount;
- for (int i = oldSubmeshCount; i < submeshCount; i++)
- submeshes.Items[i] = new ArraysMeshGenerator.SubmeshTriangleBuffer(workingSubmeshInstructions.Items[i].triangleCount);
- submeshes.Count = submeshCount;
-
- var mutableTriangles = !workingInstruction.immutableTriangles;
- for (int i = 0, last = submeshCount - 1; i < submeshCount; i++) {
- var submeshInstruction = workingSubmeshInstructions.Items[i];
-
- if (mutableTriangles || i >= oldSubmeshCount) {
-
- var currentSubmesh = submeshes.Items[i];
- int instructionTriangleCount = submeshInstruction.triangleCount;
- if (renderMeshes) {
- ArraysMeshGenerator.FillTriangles(ref currentSubmesh.triangles, skeleton, instructionTriangleCount, submeshInstruction.firstVertexIndex, submeshInstruction.startSlot, submeshInstruction.endSlot, (i == last));
- currentSubmesh.triangleCount = instructionTriangleCount;
- } else {
- ArraysMeshGenerator.FillTrianglesQuads(ref currentSubmesh.triangles, ref currentSubmesh.triangleCount, ref currentSubmesh.firstVertex, submeshInstruction.firstVertexIndex, instructionTriangleCount, (i == last));
- }
-
- }
-
- thisSubmeshMaterials.Add(submeshInstruction.material);
- }
-
- currentMesh.subMeshCount = submeshCount;
-
- for (int i = 0; i < submeshCount; ++i)
- currentMesh.SetTriangles(submeshes.Items[i].triangles, i);
+ if (SkeletonRendererInstruction.GeometryNotEqual(currentInstructions, currentSmartMesh.instructionUsed)) { // This thorough structure check is cheaper than updating triangles every frame.
+ meshGenerator.FillTriangles(currentMesh);
+ meshRenderer.sharedMaterials = rendererBuffers.GetUpdatedShaderdMaterialsArray();
+ } else if (rendererBuffers.MaterialsChangedInLastUpdate()) {
+ meshRenderer.sharedMaterials = rendererBuffers.GetUpdatedShaderdMaterialsArray();
}
- #if SPINE_OPTIONAL_SOLVETANGENTS
- if (calculateTangents) {
- ArraysMeshGenerator.SolveTangents2DEnsureSize(ref this.tangents, ref this.tempTanBuffer, vertices.Length);
- for (int i = 0; i < submeshCount; i++) {
- var submesh = submeshes.Items[i];
- ArraysMeshGenerator.SolveTangents2DTriangles(this.tempTanBuffer, submesh.triangles, submesh.triangleCount, this.vertices, this.uvs, vertexCount);
- }
- ArraysMeshGenerator.SolveTangents2DBuffer(this.tangents, this.tempTanBuffer, vertexCount);
- currentMesh.tangents = this.tangents;
- }
- #endif
-
- // CheckIfMustUpdateMaterialArray (last pushed materials vs currently parsed materials)
- // Needs to check against the Working Submesh Instructions Materials instead of the cached submeshMaterials.
- {
- var lastPushedMaterials = this.sharedMaterials;
- bool mustUpdateRendererMaterials = mustUpdateMeshStructure ||
- (lastPushedMaterials.Length != submeshCount);
-
- // Assumption at this point: (lastPushedMaterials.Count == workingSubmeshInstructions.Count == thisSubmeshMaterials.Count == submeshCount)
-
- // Case: mesh structure or submesh count did not change but materials changed.
- if (!mustUpdateRendererMaterials) {
- var workingSubmeshInstructionsItems = workingSubmeshInstructions.Items;
- for (int i = 0; i < submeshCount; i++) {
- if (lastPushedMaterials[i].GetInstanceID() != workingSubmeshInstructionsItems[i].material.GetInstanceID()) { // Bounds check is implied by submeshCount above.
- mustUpdateRendererMaterials = true;
- {
- var thisSubmeshMaterials = this.submeshMaterials.Items;
- if (mustUpdateRendererMaterials)
- for (int j = 0; j < submeshCount; j++)
- thisSubmeshMaterials[j] = workingSubmeshInstructionsItems[j].material;
- }
- break;
- }
- }
- }
-
- if (mustUpdateRendererMaterials) {
- if (submeshMaterials.Count == sharedMaterials.Length)
- submeshMaterials.CopyTo(sharedMaterials);
- else
- sharedMaterials = submeshMaterials.ToArray();
-
- meshRenderer.sharedMaterials = sharedMaterials;
- }
- }
-
-
// Step 4. The UnityEngine.Mesh is ready. Set it as the MeshFilter's mesh. Store the instructions used for that mesh. ============================================================
meshFilter.sharedMesh = currentMesh;
- currentSmartMesh.instructionUsed.Set(workingInstruction);
-
- }
-
- static bool CheckIfMustUpdateMeshStructure (SmartMesh.Instruction a, SmartMesh.Instruction b) {
-
- #if UNITY_EDITOR
- if (!Application.isPlaying)
- return true;
- #endif
-
- if (a.vertexCount != b.vertexCount)
- return true;
-
- if (a.immutableTriangles != b.immutableTriangles)
- return true;
-
- int attachmentCountB = b.attachments.Count;
- if (a.attachments.Count != attachmentCountB) // Bounds check for the looped storedAttachments count below.
- return true;
-
- var attachmentsA = a.attachments.Items;
- var attachmentsB = b.attachments.Items;
- for (int i = 0; i < attachmentCountB; i++) {
- if (attachmentsA[i] != attachmentsB[i])
- return true;
- }
-
- // Submesh count changed
- int submeshCountA = a.submeshInstructions.Count;
- int submeshCountB = b.submeshInstructions.Count;
- if (submeshCountA != submeshCountB)
- return true;
-
- // Submesh Instruction mismatch
- var submeshInstructionsItemsA = a.submeshInstructions.Items;
- var submeshInstructionsItemsB = b.submeshInstructions.Items;
- for (int i = 0; i < submeshCountB; i++) {
- var submeshA = submeshInstructionsItemsA[i];
- var submeshB = submeshInstructionsItemsB[i];
-
- if (!(
- submeshA.vertexCount == submeshB.vertexCount &&
- submeshA.startSlot == submeshB.startSlot &&
- submeshA.endSlot == submeshB.endSlot &&
- submeshA.triangleCount == submeshB.triangleCount &&
- submeshA.firstVertexIndex == submeshB.firstVertexIndex
- ))
- return true;
- }
-
- return false;
- }
-
- ///This is a Mesh that also stores the instructions SkeletonRenderer generated for it.
- public class SmartMesh : System.IDisposable {
- public Mesh mesh = Spine.Unity.SpineMesh.NewMesh();
- public SmartMesh.Instruction instructionUsed = new SmartMesh.Instruction();
-
- public void Dispose () {
- if (mesh != null) {
- #if UNITY_EDITOR
- if (Application.isEditor && !Application.isPlaying)
- UnityEngine.Object.DestroyImmediate(mesh);
- else
- UnityEngine.Object.Destroy(mesh);
- #else
- UnityEngine.Object.Destroy(mesh);
- #endif
- }
- mesh = null;
- }
-
- public class Instruction {
- public bool immutableTriangles;
- public int vertexCount = -1;
- public readonly ExposedList attachments = new ExposedList();
- public readonly ExposedList submeshInstructions = new ExposedList();
-
- public void Clear () {
- this.attachments.Clear(false);
- this.submeshInstructions.Clear(false);
- }
-
- public void Set (Instruction other) {
- this.immutableTriangles = other.immutableTriangles;
- this.vertexCount = other.vertexCount;
-
- this.attachments.Clear(false);
- this.attachments.GrowIfNeeded(other.attachments.Capacity);
- this.attachments.Count = other.attachments.Count;
- other.attachments.CopyTo(this.attachments.Items);
-
- this.submeshInstructions.Clear(false);
- this.submeshInstructions.GrowIfNeeded(other.submeshInstructions.Capacity);
- this.submeshInstructions.Count = other.submeshInstructions.Count;
- other.submeshInstructions.CopyTo(this.submeshInstructions.Items);
- }
- }
+ currentSmartMesh.instructionUsed.Set(currentInstructions);
}
}
}