diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6232a830a..e16a93371 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -183,7 +183,8 @@
* Moved `Modules/AnimationMatchModifier` directory to `Spine Examples/Scripts/MecanimAnimationMatchModifier`.
* Moved `SkeletonRagdoll` and `SkeletonRagdoll2D` components from `Modules/Ragdoll` directory to `Spine Examples/Scripts/Sample Components/SkeletonUtility Modules`.
* Moved `AttachmentTools.cs` to `Utility` directory.
- * Split the file `AttachmentTools` into 4 new files for each contained class. No namespace or other API changes performed.
+ * Split the file `AttachmentTools` into 5 separate files for each contained class. No namespace or other API changes performed.
+ * Split the file `Mesh Generation/SpineMesh` into 4 separate files for each contained class. No namespace or other API changes performed.
* Moved `SkeletonExtensions.cs` to `Utility` directory.
* Moved `Modules/YieldInstructions` directory to `Utility/YieldInstructions`.
* Moved corresponding editor scripts of the above components to restructured directories as well.
diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Mesh Generation/MeshGenerator.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Mesh Generation/MeshGenerator.cs
new file mode 100644
index 000000000..765b161f1
--- /dev/null
+++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Mesh Generation/MeshGenerator.cs
@@ -0,0 +1,1286 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated May 1, 2019. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2019, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS
+ * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+// Not for optimization. Do not disable.
+#define SPINE_TRIANGLECHECK // Avoid calling SetTriangles at the cost of checking for mesh differences (vertex counts, memberwise attachment list compare) every frame.
+//#define SPINE_DEBUG
+
+using UnityEngine;
+using System;
+using System.Collections.Generic;
+
+namespace Spine.Unity {
+ public delegate void MeshGeneratorDelegate (MeshGeneratorBuffers buffers);
+ public struct MeshGeneratorBuffers {
+ /// The vertex count that will actually be used for the mesh. The Lengths of the buffer arrays may be larger than this number.
+ public int vertexCount;
+
+ /// Vertex positions. To be used for UnityEngine.Mesh.vertices.
+ public Vector3[] vertexBuffer;
+
+ /// Vertex UVs. To be used for UnityEngine.Mesh.uvs.
+ public Vector2[] uvBuffer;
+
+ /// Vertex colors. To be used for UnityEngine.Mesh.colors32.
+ public Color32[] colorBuffer;
+
+ /// The Spine rendering component's MeshGenerator.
+ public MeshGenerator meshGenerator;
+ }
+
+ /// Holds several methods to prepare and generate a UnityEngine mesh based on a skeleton. Contains buffers needed to perform the operation, and serializes settings for mesh generation.
+ [System.Serializable]
+ public class MeshGenerator {
+ public Settings settings = Settings.Default;
+
+ [System.Serializable]
+ public struct Settings {
+ 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.PositiveInfinity;
+ const float BoundsMaxDefault = float.NegativeInfinity;
+
+ [NonSerialized] readonly ExposedList vertexBuffer = new ExposedList(4);
+ [NonSerialized] readonly ExposedList uvBuffer = new ExposedList(4);
+ [NonSerialized] readonly ExposedList colorBuffer = new ExposedList(4);
+ [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
+ // These optional buffers are lazy-instantiated when the feature is used.
+ [NonSerialized] Vector3[] normals;
+ [NonSerialized] Vector4[] tangents;
+ [NonSerialized] Vector2[] tempTanBuffer;
+ [NonSerialized] ExposedList uv2;
+ [NonSerialized] ExposedList uv3;
+ #endregion
+
+ public int VertexCount { get { return vertexBuffer.Count; } }
+
+ /// A set of mesh arrays whose values are modifiable by the user. Modify these values before they are passed to the UnityEngine mesh object in order to see the effect.
+ public MeshGeneratorBuffers Buffers {
+ get {
+ return new MeshGeneratorBuffers {
+ vertexCount = this.VertexCount,
+ vertexBuffer = this.vertexBuffer.Items,
+ uvBuffer = this.uvBuffer.Items,
+ colorBuffer = this.colorBuffer.Items,
+ meshGenerator = this
+ };
+ }
+ }
+
+ public MeshGenerator () {
+ submeshes.TrimExcess();
+ }
+
+ #region Step 1 : Generate Instructions
+ public static void GenerateSingleSubmeshInstruction (SkeletonRendererInstruction instructionOutput, Skeleton skeleton, 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];
+ if (!slot.bone.active) continue;
+ Attachment attachment = slot.attachment;
+
+ workingAttachmentsItems[i] = attachment;
+ int attachmentTriangleCount;
+ int attachmentVertexCount;
+
+ 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 {
+ var clippingAttachment = attachment as ClippingAttachment;
+ if (clippingAttachment != null) {
+ current.hasClipping = true;
+ skeletonHasClipping = true;
+ }
+ attachmentVertexCount = 0;
+ attachmentTriangleCount = 0;
+ }
+ }
+ 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) {
+ // 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; // The index of the last slot that had an active ClippingAttachment.
+ SlotData clippingEndSlot = null;
+ int submeshIndex = 0;
+ var drawOrderItems = drawOrder.Items;
+ for (int i = 0; i < drawOrderCount; i++) {
+ Slot slot = drawOrderItems[i];
+ if (!slot.bone.active) continue;
+ 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
+ } else {
+ var meshAttachment = attachment as MeshAttachment;
+ if (meshAttachment != null) {
+ rendererObject = meshAttachment.RendererObject;
+ #if SPINE_TRIANGLECHECK
+ attachmentVertexCount = meshAttachment.worldVerticesLength >> 1;
+ attachmentTriangleCount = meshAttachment.triangles.Length;
+ #endif
+ } 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;
+ }
+ }
+
+ if (clippingEndSlot != null && slot.data == clippingEndSlot && i != clippingAttachmentSource) {
+ 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 && generateMeshOverride) { // && current.rawVertexCount > 0) {
+ { // Add
+ current.endSlot = i;
+ current.preActiveClippingSlotSource = lastPreActiveClipping;
+
+ workingSubmeshInstructions.Resize(submeshIndex + 1);
+ workingSubmeshInstructions.Items[submeshIndex] = current;
+
+ submeshIndex++;
+ }
+
+ 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 is Material) ? (Material)rendererObject : (Material)((AtlasRegion)rendererObject).page.rendererObject;
+ #endif
+
+ if (current.forceSeparate || (current.rawVertexCount > 0 && !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++;
+ }
+ 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 (current.rawVertexCount > 0) {
+ { // 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 wsii = workingSubmeshInstructions.Items;
+ for (int i = 0; i < workingSubmeshInstructions.Count; i++) {
+ var m = wsii[i].material;
+ Material mo;
+ if (customMaterialOverride.TryGetValue(m, out mo))
+ wsii[i].material = mo;
+ }
+ }
+ #endregion
+
+ #region Step 2 : Populate vertex data and triangle index buffers.
+ public void Begin () {
+ 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;
+ }
+
+ submeshIndex = 0;
+ submeshes.Count = 1;
+ //submeshes.Items[0].Clear(false);
+ }
+
+ public void AddSubmesh (SubmeshInstruction instruction, bool updateTriangles = true) {
+ var settings = this.settings;
+
+ int newSubmeshCount = submeshIndex + 1;
+ if (submeshes.Items.Length < newSubmeshCount)
+ submeshes.Resize(newSubmeshCount);
+ submeshes.Count = newSubmeshCount;
+ var submesh = submeshes.Items[submeshIndex];
+ if (submesh == null)
+ submeshes.Items[submeshIndex] = submesh = new ExposedList();
+ submesh.Clear(false);
+
+ var skeleton = instruction.skeleton;
+ var drawOrderItems = skeleton.drawOrder.Items;
+
+ Color32 color = default(Color32);
+ 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;
+ bool tintBlack = settings.tintBlack;
+ #if SPINE_TRIANGLECHECK
+ bool useClipping = settings.useClipping && instruction.hasClipping;
+ #else
+ bool useClipping = settings.useClipping;
+ #endif
+
+ if (useClipping) {
+ if (instruction.preActiveClippingSlotSource >= 0) {
+ var slot = drawOrderItems[instruction.preActiveClippingSlotSource];
+ clipper.ClipStart(slot, slot.attachment as ClippingAttachment);
+ }
+ }
+
+ for (int slotIndex = instruction.startSlot; slotIndex < instruction.endSlot; slotIndex++) {
+ var slot = drawOrderItems[slotIndex];
+ if (!slot.bone.active) continue;
+ var attachment = slot.attachment;
+ float z = zSpacing * slotIndex;
+
+ var workingVerts = this.tempVerts;
+ float[] uvs;
+ int[] attachmentTriangleIndices;
+ int attachmentVertexCount;
+ int attachmentIndexCount;
+
+ Color c = default(Color);
+
+ // Identify and prepare values.
+ 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 meshVerticesLength = mesh.worldVerticesLength;
+ if (workingVerts.Length < meshVerticesLength) {
+ workingVerts = new float[meshVerticesLength];
+ this.tempVerts = workingVerts;
+ }
+ mesh.ComputeWorldVertices(slot, 0, meshVerticesLength, 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 = meshVerticesLength >> 1; // meshVertexCount / 2;
+ attachmentIndexCount = mesh.triangles.Length;
+ } else {
+ if (useClipping) {
+ var clippingAttachment = attachment as ClippingAttachment;
+ if (clippingAttachment != null) {
+ clipper.ClipStart(slot, clippingAttachment);
+ continue;
+ }
+ }
+
+ // If not any renderable attachment.
+ clipper.ClipEnd(slot);
+ 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;
+ }
+
+ // Actually add slot/attachment data into buffers.
+ if (attachmentVertexCount != 0 && attachmentIndexCount != 0) {
+ if (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;
+ int oldArraySize = vertexBuffer.Items.Length;
+ if (newVertexCount > oldArraySize) {
+ int newArraySize = (int)(oldArraySize * 1.3f);
+ if (newArraySize < newVertexCount) newArraySize = newVertexCount;
+ Array.Resize(ref vertexBuffer.Items, newArraySize);
+ Array.Resize(ref uvBuffer.Items, newArraySize);
+ Array.Resize(ref colorBuffer.Items, newArraySize);
+ }
+ vertexBuffer.Count = uvBuffer.Count = colorBuffer.Count = newVertexCount;
+ }
+
+ var vbi = vertexBuffer.Items;
+ var ubi = uvBuffer.Items;
+ var cbi = colorBuffer.Items;
+ if (ovc == 0) {
+ 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;
+ if (x > meshBoundsMax.x) meshBoundsMax.x = x;
+ if (y < meshBoundsMin.y) meshBoundsMin.y = y;
+ if (y > meshBoundsMax.y) meshBoundsMax.y = y;
+ }
+ } else {
+ 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
+ if (updateTriangles) {
+ 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;
+
+ submeshIndex++; // Next AddSubmesh will use a new submeshIndex value.
+ }
+
+ public void BuildMesh (SkeletonRendererInstruction instruction, bool updateTriangles) {
+ var wsii = instruction.submeshInstructions.Items;
+ for (int i = 0, n = instruction.submeshInstructions.Count; i < n; i++)
+ this.AddSubmesh(wsii[i], updateTriangles);
+ }
+
+ // Use this faster method when no clipping is involved.
+ public void BuildMeshWithArrays (SkeletonRendererInstruction instruction, bool updateTriangles) {
+ var settings = this.settings;
+ int totalVertexCount = instruction.rawVertexCount;
+
+ // Add data to vertex buffers
+ {
+ if (totalVertexCount > vertexBuffer.Items.Length) { // Manual ExposedList.Resize()
+ Array.Resize(ref vertexBuffer.Items, totalVertexCount);
+ Array.Resize(ref uvBuffer.Items, totalVertexCount);
+ Array.Resize(ref colorBuffer.Items, totalVertexCount);
+ }
+ vertexBuffer.Count = uvBuffer.Count = colorBuffer.Count = totalVertexCount;
+ }
+
+ // Populate Verts
+ Color32 color = default(Color32);
+
+ int vertexIndex = 0;
+ var tempVerts = this.tempVerts;
+ Vector2 bmin = this.meshBoundsMin;
+ Vector2 bmax = this.meshBoundsMax;
+
+ var vbi = vertexBuffer.Items;
+ var ubi = uvBuffer.Items;
+ var cbi = colorBuffer.Items;
+ int lastSlotIndex = 0;
+
+ // drawOrder[endSlot] is excluded
+ for (int si = 0, n = instruction.submeshInstructions.Count; si < n; si++) {
+ var submesh = instruction.submeshInstructions.Items[si];
+ var skeleton = submesh.skeleton;
+ var drawOrderItems = skeleton.drawOrder.Items;
+ float a = skeleton.a * 255, r = skeleton.r, g = skeleton.g, b = skeleton.b;
+
+ int endSlot = submesh.endSlot;
+ int startSlot = submesh.startSlot;
+ lastSlotIndex = endSlot;
+
+ if (settings.tintBlack) {
+ Vector2 rg, b2;
+ int vi = vertexIndex;
+ b2.y = 1f;
+
+ {
+ if (uv2 == null) {
+ uv2 = new ExposedList();
+ uv3 = new ExposedList();
+ }
+ if (totalVertexCount > uv2.Items.Length) { // Manual ExposedList.Resize()
+ Array.Resize(ref uv2.Items, totalVertexCount);
+ Array.Resize(ref uv3.Items, totalVertexCount);
+ }
+ uv2.Count = uv3.Count = totalVertexCount;
+ }
+
+ var uv2i = uv2.Items;
+ var uv3i = uv3.Items;
+
+ for (int slotIndex = startSlot; slotIndex < endSlot; slotIndex++) {
+ var slot = drawOrderItems[slotIndex];
+ if (!slot.bone.active) continue;
+ 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) {
+ uv2i[vi] = rg; uv2i[vi + 1] = rg; uv2i[vi + 2] = rg; uv2i[vi + 3] = rg;
+ uv3i[vi] = b2; uv3i[vi + 1] = b2; uv3i[vi + 2] = b2; uv3i[vi + 3] = b2;
+ vi += 4;
+ } else { //} if (settings.renderMeshes) {
+ var meshAttachment = attachment as MeshAttachment;
+ if (meshAttachment != null) {
+ int meshVertexCount = meshAttachment.worldVerticesLength;
+ for (int iii = 0; iii < meshVertexCount; iii += 2) {
+ uv2i[vi] = rg;
+ uv3i[vi] = b2;
+ vi++;
+ }
+ }
+ }
+ }
+ }
+
+ for (int slotIndex = startSlot; slotIndex < endSlot; slotIndex++) {
+ var slot = drawOrderItems[slotIndex];
+ if (!slot.bone.active) continue;
+ var attachment = slot.attachment;
+ float z = slotIndex * settings.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];
+ vbi[vertexIndex].x = x1; vbi[vertexIndex].y = y1; vbi[vertexIndex].z = z;
+ vbi[vertexIndex + 1].x = x4; vbi[vertexIndex + 1].y = y4; vbi[vertexIndex + 1].z = z;
+ vbi[vertexIndex + 2].x = x2; vbi[vertexIndex + 2].y = y2; vbi[vertexIndex + 2].z = z;
+ vbi[vertexIndex + 3].x = x3; vbi[vertexIndex + 3].y = y3; vbi[vertexIndex + 3].z = z;
+
+ if (settings.pmaVertexColors) {
+ 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);
+ }
+
+ cbi[vertexIndex] = color; cbi[vertexIndex + 1] = color; cbi[vertexIndex + 2] = color; cbi[vertexIndex + 3] = color;
+
+ float[] regionUVs = regionAttachment.uvs;
+ ubi[vertexIndex].x = regionUVs[RegionAttachment.BLX]; ubi[vertexIndex].y = regionUVs[RegionAttachment.BLY];
+ ubi[vertexIndex + 1].x = regionUVs[RegionAttachment.BRX]; ubi[vertexIndex + 1].y = regionUVs[RegionAttachment.BRY];
+ ubi[vertexIndex + 2].x = regionUVs[RegionAttachment.ULX]; ubi[vertexIndex + 2].y = regionUVs[RegionAttachment.ULY];
+ ubi[vertexIndex + 3].x = regionUVs[RegionAttachment.URX]; ubi[vertexIndex + 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;
+
+ vertexIndex += 4;
+ } else { //if (settings.renderMeshes) {
+ var meshAttachment = attachment as MeshAttachment;
+ if (meshAttachment != null) {
+ int meshVertexCount = meshAttachment.worldVerticesLength;
+ if (tempVerts.Length < meshVertexCount) this.tempVerts = tempVerts = new float[meshVertexCount];
+ meshAttachment.ComputeWorldVertices(slot, tempVerts);
+
+ if (settings.pmaVertexColors) {
+ 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 (vertexIndex == 0) {
+ // 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];
+ vbi[vertexIndex].x = x; vbi[vertexIndex].y = y; vbi[vertexIndex].z = z;
+ cbi[vertexIndex] = color; ubi[vertexIndex].x = attachmentUVs[iii]; ubi[vertexIndex].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;
+
+ vertexIndex++;
+ }
+ }
+ }
+ }
+ }
+
+ this.meshBoundsMin = bmin;
+ this.meshBoundsMax = bmax;
+ this.meshBoundsThickness = lastSlotIndex * settings.zSpacing;
+
+ int submeshInstructionCount = instruction.submeshInstructions.Count;
+ submeshes.Count = submeshInstructionCount;
+
+ // Add triangles
+ if (updateTriangles) {
+ // Match submesh buffers count with submeshInstruction count.
+ if (this.submeshes.Items.Length < submeshInstructionCount) {
+ this.submeshes.Resize(submeshInstructionCount);
+ for (int i = 0, n = submeshInstructionCount; i < n; i++) {
+ var submeshBuffer = this.submeshes.Items[i];
+ if (submeshBuffer == null)
+ this.submeshes.Items[i] = new ExposedList();
+ else
+ submeshBuffer.Clear(false);
+ }
+ }
+
+ var submeshInstructionsItems = instruction.submeshInstructions.Items; // This relies on the resize above.
+
+ // Fill the buffers.
+ int attachmentFirstVertex = 0;
+ for (int smbi = 0; smbi < submeshInstructionCount; smbi++) {
+ var submeshInstruction = submeshInstructionsItems[smbi];
+ var currentSubmeshBuffer = this.submeshes.Items[smbi];
+ { //submesh.Resize(submesh.rawTriangleCount);
+ int newTriangleCount = submeshInstruction.rawTriangleCount;
+ if (newTriangleCount > currentSubmeshBuffer.Items.Length)
+ Array.Resize(ref currentSubmeshBuffer.Items, newTriangleCount);
+ else if (newTriangleCount < currentSubmeshBuffer.Items.Length) {
+ // Zero the extra.
+ var sbi = currentSubmeshBuffer.Items;
+ for (int ei = newTriangleCount, nn = sbi.Length; ei < nn; ei++)
+ sbi[ei] = 0;
+ }
+ currentSubmeshBuffer.Count = newTriangleCount;
+ }
+
+ var tris = currentSubmeshBuffer.Items;
+ int triangleIndex = 0;
+ var skeleton = submeshInstruction.skeleton;
+ var drawOrderItems = skeleton.drawOrder.Items;
+ for (int slotIndex = submeshInstruction.startSlot, endSlot = submeshInstruction.endSlot; slotIndex < endSlot; slotIndex++) {
+ var slot = drawOrderItems[slotIndex];
+ if (!slot.bone.active) continue;
+
+ var attachment = drawOrderItems[slotIndex].attachment;
+ if (attachment is RegionAttachment) {
+ tris[triangleIndex] = attachmentFirstVertex;
+ tris[triangleIndex + 1] = attachmentFirstVertex + 2;
+ tris[triangleIndex + 2] = attachmentFirstVertex + 1;
+ tris[triangleIndex + 3] = attachmentFirstVertex + 2;
+ tris[triangleIndex + 4] = attachmentFirstVertex + 3;
+ tris[triangleIndex + 5] = attachmentFirstVertex + 1;
+ triangleIndex += 6;
+ attachmentFirstVertex += 4;
+ continue;
+ }
+ var meshAttachment = attachment as MeshAttachment;
+ if (meshAttachment != null) {
+ int[] attachmentTriangles = meshAttachment.triangles;
+ for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii++, triangleIndex++)
+ tris[triangleIndex] = attachmentFirstVertex + attachmentTriangles[ii];
+ attachmentFirstVertex += meshAttachment.worldVerticesLength >> 1; // length/2;
+ }
+ }
+ }
+ }
+ }
+
+ public void ScaleVertexData (float scale) {
+ var vbi = vertexBuffer.Items;
+ for (int i = 0, n = vertexBuffer.Count; i < n; i++) {
+ vbi[i] *= scale; // vbi[i].x *= scale; vbi[i].y *= scale;
+ }
+
+ meshBoundsMin *= scale;
+ meshBoundsMax *= scale;
+ meshBoundsThickness *= scale;
+ }
+
+ 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;
+ int vbiLength = vbi.Length;
+
+ // Zero the extra.
+ {
+ int listCount = vertexBuffer.Count;
+ var vector3zero = Vector3.zero;
+ for (int i = listCount; i < vbiLength; i++)
+ vbi[i] = vector3zero;
+ }
+
+ // Set the vertex buffer.
+ {
+ mesh.vertices = vbi;
+ mesh.uv = ubi;
+ mesh.colors32 = cbi;
+
+ if (float.IsInfinity(meshBoundsMin.x)) { // meshBoundsMin.x == BoundsMinDefault // == doesn't work on float Infinity constants.
+ mesh.bounds = new Bounds();
+ } else {
+ //mesh.bounds = ArraysMeshGenerator.ToBounds(meshBoundsMin, meshBoundsMax);
+ float halfWidth = (meshBoundsMax.x - meshBoundsMin.x) * 0.5f;
+ float halfHeight = (meshBoundsMax.y - meshBoundsMin.y) * 0.5f;
+ mesh.bounds = new Bounds {
+ center = new Vector3(meshBoundsMin.x + halfWidth, meshBoundsMin.y + halfHeight),
+ extents = new Vector3(halfWidth, halfHeight, meshBoundsThickness * 0.5f)
+ };
+ }
+ }
+
+ {
+ if (settings.addNormals) {
+ int oldLength = 0;
+
+ if (normals == null)
+ normals = new Vector3[vbiLength];
+ else
+ oldLength = normals.Length;
+
+ if (oldLength != vbiLength) {
+ Array.Resize(ref this.normals, vbiLength);
+ var localNormals = this.normals;
+ for (int i = oldLength; i < vbiLength; i++) localNormals[i] = Vector3.back;
+ }
+ mesh.normals = this.normals;
+ }
+
+ if (settings.tintBlack) {
+ if (uv2 != null) {
+ // Sometimes, the vertex buffer becomes smaller. We need to trim the size of the tint black buffers to match.
+ if (vbiLength != uv2.Items.Length) {
+ Array.Resize(ref uv2.Items, vbiLength);
+ Array.Resize(ref uv3.Items, vbiLength);
+ uv2.Count = uv3.Count = vbiLength;
+ }
+ mesh.uv2 = this.uv2.Items;
+ mesh.uv3 = this.uv3.Items;
+ }
+ }
+ }
+ }
+
+ public void FillLateVertexData (Mesh mesh) {
+ if (settings.calculateTangents) {
+ int vertexCount = this.vertexBuffer.Count;
+ var sbi = submeshes.Items;
+ int submeshCount = submeshes.Count;
+ var vbi = vertexBuffer.Items;
+ var ubi = uvBuffer.Items;
+
+ MeshGenerator.SolveTangents2DEnsureSize(ref this.tangents, ref this.tempTanBuffer, vertexCount, vbi.Length);
+ 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 EnsureVertexCapacity (int minimumVertexCount, bool inlcudeTintBlack = false, bool includeTangents = false, bool includeNormals = false) {
+ if (minimumVertexCount > vertexBuffer.Items.Length) {
+ Array.Resize(ref vertexBuffer.Items, minimumVertexCount);
+ Array.Resize(ref uvBuffer.Items, minimumVertexCount);
+ Array.Resize(ref colorBuffer.Items, minimumVertexCount);
+
+ if (inlcudeTintBlack) {
+ if (uv2 == null) {
+ uv2 = new ExposedList(minimumVertexCount);
+ uv3 = new ExposedList(minimumVertexCount);
+ }
+ uv2.Resize(minimumVertexCount);
+ uv3.Resize(minimumVertexCount);
+ }
+
+ if (includeNormals) {
+ if (normals == null)
+ normals = new Vector3[minimumVertexCount];
+ else
+ Array.Resize(ref normals, minimumVertexCount);
+
+ }
+
+ if (includeTangents) {
+ if (tangents == null)
+ tangents = new Vector4[minimumVertexCount];
+ else
+ Array.Resize(ref tangents, minimumVertexCount);
+ }
+ }
+ }
+
+ /// Trims internal buffers to reduce the resulting mesh data stream size.
+ public void TrimExcess () {
+ vertexBuffer.TrimExcess();
+ uvBuffer.TrimExcess();
+ colorBuffer.TrimExcess();
+
+ if (uv2 != null) uv2.TrimExcess();
+ if (uv3 != null) uv3.TrimExcess();
+
+ int vbiLength = vertexBuffer.Items.Length;
+ if (normals != null) Array.Resize(ref normals, vbiLength);
+ if (tangents != null) Array.Resize(ref tangents, vbiLength);
+ }
+
+ #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, int vertexBufferLength) {
+ if (tangentBuffer == null || tangentBuffer.Length != vertexBufferLength)
+ tangentBuffer = new Vector4[vertexBufferLength];
+
+ 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
+
+ #region AttachmentRendering
+ static List AttachmentVerts = new List();
+ static List AttachmentUVs = new List();
+ static List AttachmentColors32 = new List();
+ static List AttachmentIndices = new List();
+
+ /// Fills mesh vertex data to render a RegionAttachment.
+ public static void FillMeshLocal (Mesh mesh, RegionAttachment regionAttachment) {
+ if (mesh == null) return;
+ if (regionAttachment == null) return;
+
+ AttachmentVerts.Clear();
+ var offsets = regionAttachment.Offset;
+ AttachmentVerts.Add(new Vector3(offsets[RegionAttachment.BLX], offsets[RegionAttachment.BLY]));
+ AttachmentVerts.Add(new Vector3(offsets[RegionAttachment.ULX], offsets[RegionAttachment.ULY]));
+ AttachmentVerts.Add(new Vector3(offsets[RegionAttachment.URX], offsets[RegionAttachment.URY]));
+ AttachmentVerts.Add(new Vector3(offsets[RegionAttachment.BRX], offsets[RegionAttachment.BRY]));
+
+ AttachmentUVs.Clear();
+ var uvs = regionAttachment.UVs;
+ AttachmentUVs.Add(new Vector2(uvs[RegionAttachment.ULX], uvs[RegionAttachment.ULY]));
+ AttachmentUVs.Add(new Vector2(uvs[RegionAttachment.URX], uvs[RegionAttachment.URY]));
+ AttachmentUVs.Add(new Vector2(uvs[RegionAttachment.BRX], uvs[RegionAttachment.BRY]));
+ AttachmentUVs.Add(new Vector2(uvs[RegionAttachment.BLX], uvs[RegionAttachment.BLY]));
+
+ AttachmentColors32.Clear();
+ Color32 c = (Color32)(new Color(regionAttachment.r, regionAttachment.g, regionAttachment.b, regionAttachment.a));
+ for (int i = 0; i < 4; i++)
+ AttachmentColors32.Add(c);
+
+ AttachmentIndices.Clear();
+ AttachmentIndices.AddRange(new[] { 0, 2, 1, 0, 3, 2 });
+
+ mesh.Clear();
+ mesh.name = regionAttachment.Name;
+ mesh.SetVertices(AttachmentVerts);
+ mesh.SetUVs(0, AttachmentUVs);
+ mesh.SetColors(AttachmentColors32);
+ mesh.SetTriangles(AttachmentIndices, 0);
+ mesh.RecalculateBounds();
+
+ AttachmentVerts.Clear();
+ AttachmentUVs.Clear();
+ AttachmentColors32.Clear();
+ AttachmentIndices.Clear();
+ }
+
+ public static void FillMeshLocal (Mesh mesh, MeshAttachment meshAttachment, SkeletonData skeletonData) {
+ if (mesh == null) return;
+ if (meshAttachment == null) return;
+ int vertexCount = meshAttachment.WorldVerticesLength / 2;
+
+ AttachmentVerts.Clear();
+ if (meshAttachment.IsWeighted()) {
+ int count = meshAttachment.WorldVerticesLength;
+ int[] meshAttachmentBones = meshAttachment.bones;
+ int v = 0;
+
+ float[] vertices = meshAttachment.vertices;
+ for (int w = 0, b = 0; w < count; w += 2) {
+ float wx = 0, wy = 0;
+ int n = meshAttachmentBones[v++];
+ n += v;
+ for (; v < n; v++, b += 3) {
+ BoneMatrix bm = BoneMatrix.CalculateSetupWorld(skeletonData.bones.Items[meshAttachmentBones[v]]);
+ float vx = vertices[b], vy = vertices[b + 1], weight = vertices[b + 2];
+ wx += (vx * bm.a + vy * bm.b + bm.x) * weight;
+ wy += (vx * bm.c + vy * bm.d + bm.y) * weight;
+ }
+ AttachmentVerts.Add(new Vector3(wx, wy));
+ }
+ } else {
+ var localVerts = meshAttachment.Vertices;
+ Vector3 pos = default(Vector3);
+ for (int i = 0; i < vertexCount; i++) {
+ int ii = i * 2;
+ pos.x = localVerts[ii];
+ pos.y = localVerts[ii + 1];
+ AttachmentVerts.Add(pos);
+ }
+ }
+
+ var uvs = meshAttachment.uvs;
+ Vector2 uv = default(Vector2);
+ Color32 c = (Color32)(new Color(meshAttachment.r, meshAttachment.g, meshAttachment.b, meshAttachment.a));
+ AttachmentUVs.Clear();
+ AttachmentColors32.Clear();
+ for (int i = 0; i < vertexCount; i++) {
+ int ii = i * 2;
+ uv.x = uvs[ii];
+ uv.y = uvs[ii + 1];
+ AttachmentUVs.Add(uv);
+
+ AttachmentColors32.Add(c);
+ }
+
+ AttachmentIndices.Clear();
+ AttachmentIndices.AddRange(meshAttachment.triangles);
+
+ mesh.Clear();
+ mesh.name = meshAttachment.Name;
+ mesh.SetVertices(AttachmentVerts);
+ mesh.SetUVs(0, AttachmentUVs);
+ mesh.SetColors(AttachmentColors32);
+ mesh.SetTriangles(AttachmentIndices, 0);
+ mesh.RecalculateBounds();
+
+ AttachmentVerts.Clear();
+ AttachmentUVs.Clear();
+ AttachmentColors32.Clear();
+ AttachmentIndices.Clear();
+ }
+ #endregion
+ }
+}
diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Mesh Generation/MeshGenerator.cs.meta b/spine-unity/Assets/Spine/Runtime/spine-unity/Mesh Generation/MeshGenerator.cs.meta
new file mode 100644
index 000000000..712b49275
--- /dev/null
+++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Mesh Generation/MeshGenerator.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 311447d6e56727c4dad7611d5fa5afbf
+timeCreated: 1563322425
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Mesh Generation/MeshRendererBuffers.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Mesh Generation/MeshRendererBuffers.cs
new file mode 100644
index 000000000..7494c4231
--- /dev/null
+++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Mesh Generation/MeshRendererBuffers.cs
@@ -0,0 +1,135 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated May 1, 2019. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2019, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS
+ * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+// Not for optimization. Do not disable.
+#define SPINE_TRIANGLECHECK // Avoid calling SetTriangles at the cost of checking for mesh differences (vertex counts, memberwise attachment list compare) every frame.
+//#define SPINE_DEBUG
+
+using UnityEngine;
+using System;
+using System.Collections.Generic;
+
+namespace Spine.Unity {
+ /// A double-buffered Mesh, and a shared material array, bundled for use by Spine components that need to push a Mesh and materials to a Unity MeshRenderer and MeshFilter.
+ public class MeshRendererBuffers : IDisposable {
+ DoubleBuffered doubleBufferedMesh;
+ internal readonly ExposedList submeshMaterials = new ExposedList();
+ internal Material[] sharedMaterials = new Material[0];
+
+ public void Initialize () {
+ if (doubleBufferedMesh != null) {
+ doubleBufferedMesh.GetNext().Clear();
+ doubleBufferedMesh.GetNext().Clear();
+ submeshMaterials.Clear();
+ } else {
+ doubleBufferedMesh = new DoubleBuffered();
+ }
+ }
+
+ /// Returns a sharedMaterials array for use on a MeshRenderer.
+ ///
+ public Material[] GetUpdatedSharedMaterialsArray () {
+ if (submeshMaterials.Count == sharedMaterials.Length)
+ submeshMaterials.CopyTo(sharedMaterials);
+ else
+ sharedMaterials = submeshMaterials.ToArray();
+
+ return sharedMaterials;
+ }
+
+ /// Returns true if the materials were modified since the buffers were last updated.
+ 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;
+ }
+
+ /// Updates the internal shared materials array with the given instruction list.
+ 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.NewSkeletonMesh();
+ public SkeletonRendererInstruction instructionUsed = new SkeletonRendererInstruction();
+
+ public void Clear () {
+ mesh.Clear();
+ instructionUsed.Clear();
+ }
+
+ 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;
+ }
+ }
+ }
+}
diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Mesh Generation/MeshRendererBuffers.cs.meta b/spine-unity/Assets/Spine/Runtime/spine-unity/Mesh Generation/MeshRendererBuffers.cs.meta
new file mode 100644
index 000000000..3e4c8a1b2
--- /dev/null
+++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Mesh Generation/MeshRendererBuffers.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: b1ab80744ac17724dbc0d15fdb6f4727
+timeCreated: 1563322425
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Mesh Generation/SkeletonRendererInstruction.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Mesh Generation/SkeletonRendererInstruction.cs
new file mode 100644
index 000000000..3657834a4
--- /dev/null
+++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Mesh Generation/SkeletonRendererInstruction.cs
@@ -0,0 +1,178 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated May 1, 2019. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2019, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software
+ * or otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS
+ * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+// Not for optimization. Do not disable.
+#define SPINE_TRIANGLECHECK // Avoid calling SetTriangles at the cost of checking for mesh differences (vertex counts, memberwise attachment list compare) every frame.
+//#define SPINE_DEBUG
+
+using UnityEngine;
+using System;
+using System.Collections.Generic;
+
+namespace Spine.Unity {
+ /// Instructions used by a SkeletonRenderer to render a mesh.
+ public class SkeletonRendererInstruction {
+ public readonly ExposedList submeshInstructions = new ExposedList();
+
+ public bool immutableTriangles;
+ #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 Dispose () {
+ attachments.Clear(true);
+ }
+
+ 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 drawOrderItems = instructionsItems[0].skeleton.drawOrder.Items;
+ for (int i = 0; i < attachmentCount; i++) {
+ Slot slot = drawOrderItems[startSlot + i];
+ if (!slot.bone.active) continue;
+ attachmentsItems[i] = slot.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.EnsureCapacity(other.attachments.Capacity);
+ this.attachments.Count = other.attachments.Count;
+ other.attachments.CopyTo(this.attachments.Items);
+ #endif
+
+ this.submeshInstructions.Clear(false);
+ this.submeshInstructions.EnsureCapacity(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/Runtime/spine-unity/Mesh Generation/SkeletonRendererInstruction.cs.meta b/spine-unity/Assets/Spine/Runtime/spine-unity/Mesh Generation/SkeletonRendererInstruction.cs.meta
new file mode 100644
index 000000000..50a8a97d8
--- /dev/null
+++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Mesh Generation/SkeletonRendererInstruction.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: d07866ade25bd0b44a7bb1d59bacf4cb
+timeCreated: 1563322425
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Mesh Generation/SpineMesh.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Mesh Generation/SpineMesh.cs
index 85e7dfe47..fc0935440 100644
--- a/spine-unity/Assets/Spine/Runtime/spine-unity/Mesh Generation/SpineMesh.cs
+++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Mesh Generation/SpineMesh.cs
@@ -80,1492 +80,5 @@ namespace Spine.Unity {
preActiveClippingSlotSource
);
}
- }
-
- public delegate void MeshGeneratorDelegate (MeshGeneratorBuffers buffers);
- public struct MeshGeneratorBuffers {
- /// The vertex count that will actually be used for the mesh. The Lengths of the buffer arrays may be larger than this number.
- public int vertexCount;
-
- /// Vertex positions. To be used for UnityEngine.Mesh.vertices.
- public Vector3[] vertexBuffer;
-
- /// Vertex UVs. To be used for UnityEngine.Mesh.uvs.
- public Vector2[] uvBuffer;
-
- /// Vertex colors. To be used for UnityEngine.Mesh.colors32.
- public Color32[] colorBuffer;
-
- /// The Spine rendering component's MeshGenerator.
- public MeshGenerator meshGenerator;
- }
-
- /// Holds several methods to prepare and generate a UnityEngine mesh based on a skeleton. Contains buffers needed to perform the operation, and serializes settings for mesh generation.
- [System.Serializable]
- public class MeshGenerator {
- public Settings settings = Settings.Default;
-
- [System.Serializable]
- public struct Settings {
- 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.PositiveInfinity;
- const float BoundsMaxDefault = float.NegativeInfinity;
-
- [NonSerialized] readonly ExposedList vertexBuffer = new ExposedList(4);
- [NonSerialized] readonly ExposedList uvBuffer = new ExposedList(4);
- [NonSerialized] readonly ExposedList colorBuffer = new ExposedList(4);
- [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
- // These optional buffers are lazy-instantiated when the feature is used.
- [NonSerialized] Vector3[] normals;
- [NonSerialized] Vector4[] tangents;
- [NonSerialized] Vector2[] tempTanBuffer;
- [NonSerialized] ExposedList uv2;
- [NonSerialized] ExposedList uv3;
- #endregion
-
- public int VertexCount { get { return vertexBuffer.Count; } }
-
- /// A set of mesh arrays whose values are modifiable by the user. Modify these values before they are passed to the UnityEngine mesh object in order to see the effect.
- public MeshGeneratorBuffers Buffers {
- get {
- return new MeshGeneratorBuffers {
- vertexCount = this.VertexCount,
- vertexBuffer = this.vertexBuffer.Items,
- uvBuffer = this.uvBuffer.Items,
- colorBuffer = this.colorBuffer.Items,
- meshGenerator = this
- };
- }
- }
-
- public MeshGenerator () {
- submeshes.TrimExcess();
- }
-
- #region Step 1 : Generate Instructions
- public static void GenerateSingleSubmeshInstruction (SkeletonRendererInstruction instructionOutput, Skeleton skeleton, 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];
- if (!slot.bone.active) continue;
- Attachment attachment = slot.attachment;
-
- workingAttachmentsItems[i] = attachment;
- int attachmentTriangleCount;
- int attachmentVertexCount;
-
- 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 {
- var clippingAttachment = attachment as ClippingAttachment;
- if (clippingAttachment != null) {
- current.hasClipping = true;
- skeletonHasClipping = true;
- }
- attachmentVertexCount = 0;
- attachmentTriangleCount = 0;
- }
- }
- 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) {
- // 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; // The index of the last slot that had an active ClippingAttachment.
- SlotData clippingEndSlot = null;
- int submeshIndex = 0;
- var drawOrderItems = drawOrder.Items;
- for (int i = 0; i < drawOrderCount; i++) {
- Slot slot = drawOrderItems[i];
- if (!slot.bone.active) continue;
- 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
- } else {
- var meshAttachment = attachment as MeshAttachment;
- if (meshAttachment != null) {
- rendererObject = meshAttachment.RendererObject;
- #if SPINE_TRIANGLECHECK
- attachmentVertexCount = meshAttachment.worldVerticesLength >> 1;
- attachmentTriangleCount = meshAttachment.triangles.Length;
- #endif
- } 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;
- }
- }
-
- if (clippingEndSlot != null && slot.data == clippingEndSlot && i != clippingAttachmentSource) {
- 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 && generateMeshOverride) { // && current.rawVertexCount > 0) {
- { // Add
- current.endSlot = i;
- current.preActiveClippingSlotSource = lastPreActiveClipping;
-
- workingSubmeshInstructions.Resize(submeshIndex + 1);
- workingSubmeshInstructions.Items[submeshIndex] = current;
-
- submeshIndex++;
- }
-
- 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 is Material) ? (Material)rendererObject : (Material)((AtlasRegion)rendererObject).page.rendererObject;
- #endif
-
- if (current.forceSeparate || (current.rawVertexCount > 0 && !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++;
- }
- 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 (current.rawVertexCount > 0) {
- { // 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 wsii = workingSubmeshInstructions.Items;
- for (int i = 0; i < workingSubmeshInstructions.Count; i++) {
- var m = wsii[i].material;
- Material mo;
- if (customMaterialOverride.TryGetValue(m, out mo))
- wsii[i].material = mo;
- }
- }
- #endregion
-
- #region Step 2 : Populate vertex data and triangle index buffers.
- public void Begin () {
- 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;
- }
-
- submeshIndex = 0;
- submeshes.Count = 1;
- //submeshes.Items[0].Clear(false);
- }
-
- public void AddSubmesh (SubmeshInstruction instruction, bool updateTriangles = true) {
- var settings = this.settings;
-
- int newSubmeshCount = submeshIndex + 1;
- if (submeshes.Items.Length < newSubmeshCount)
- submeshes.Resize(newSubmeshCount);
- submeshes.Count = newSubmeshCount;
- var submesh = submeshes.Items[submeshIndex];
- if (submesh == null)
- submeshes.Items[submeshIndex] = submesh = new ExposedList();
- submesh.Clear(false);
-
- var skeleton = instruction.skeleton;
- var drawOrderItems = skeleton.drawOrder.Items;
-
- Color32 color = default(Color32);
- 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;
- bool tintBlack = settings.tintBlack;
- #if SPINE_TRIANGLECHECK
- bool useClipping = settings.useClipping && instruction.hasClipping;
- #else
- bool useClipping = settings.useClipping;
- #endif
-
- if (useClipping) {
- if (instruction.preActiveClippingSlotSource >= 0) {
- var slot = drawOrderItems[instruction.preActiveClippingSlotSource];
- clipper.ClipStart(slot, slot.attachment as ClippingAttachment);
- }
- }
-
- for (int slotIndex = instruction.startSlot; slotIndex < instruction.endSlot; slotIndex++) {
- var slot = drawOrderItems[slotIndex];
- if (!slot.bone.active) continue;
- var attachment = slot.attachment;
- float z = zSpacing * slotIndex;
-
- var workingVerts = this.tempVerts;
- float[] uvs;
- int[] attachmentTriangleIndices;
- int attachmentVertexCount;
- int attachmentIndexCount;
-
- Color c = default(Color);
-
- // Identify and prepare values.
- 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 meshVerticesLength = mesh.worldVerticesLength;
- if (workingVerts.Length < meshVerticesLength) {
- workingVerts = new float[meshVerticesLength];
- this.tempVerts = workingVerts;
- }
- mesh.ComputeWorldVertices(slot, 0, meshVerticesLength, 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 = meshVerticesLength >> 1; // meshVertexCount / 2;
- attachmentIndexCount = mesh.triangles.Length;
- } else {
- if (useClipping) {
- var clippingAttachment = attachment as ClippingAttachment;
- if (clippingAttachment != null) {
- clipper.ClipStart(slot, clippingAttachment);
- continue;
- }
- }
-
- // If not any renderable attachment.
- clipper.ClipEnd(slot);
- 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;
- }
-
- // Actually add slot/attachment data into buffers.
- if (attachmentVertexCount != 0 && attachmentIndexCount != 0) {
- if (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;
- int oldArraySize = vertexBuffer.Items.Length;
- if (newVertexCount > oldArraySize) {
- int newArraySize = (int)(oldArraySize * 1.3f);
- if (newArraySize < newVertexCount) newArraySize = newVertexCount;
- Array.Resize(ref vertexBuffer.Items, newArraySize);
- Array.Resize(ref uvBuffer.Items, newArraySize);
- Array.Resize(ref colorBuffer.Items, newArraySize);
- }
- vertexBuffer.Count = uvBuffer.Count = colorBuffer.Count = newVertexCount;
- }
-
- var vbi = vertexBuffer.Items;
- var ubi = uvBuffer.Items;
- var cbi = colorBuffer.Items;
- if (ovc == 0) {
- 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;
- if (x > meshBoundsMax.x) meshBoundsMax.x = x;
- if (y < meshBoundsMin.y) meshBoundsMin.y = y;
- if (y > meshBoundsMax.y) meshBoundsMax.y = y;
- }
- } else {
- 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
- if (updateTriangles) {
- 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;
-
- submeshIndex++; // Next AddSubmesh will use a new submeshIndex value.
- }
-
- public void BuildMesh (SkeletonRendererInstruction instruction, bool updateTriangles) {
- var wsii = instruction.submeshInstructions.Items;
- for (int i = 0, n = instruction.submeshInstructions.Count; i < n; i++)
- this.AddSubmesh(wsii[i], updateTriangles);
- }
-
- // Use this faster method when no clipping is involved.
- public void BuildMeshWithArrays (SkeletonRendererInstruction instruction, bool updateTriangles) {
- var settings = this.settings;
- int totalVertexCount = instruction.rawVertexCount;
-
- // Add data to vertex buffers
- {
- if (totalVertexCount > vertexBuffer.Items.Length) { // Manual ExposedList.Resize()
- Array.Resize(ref vertexBuffer.Items, totalVertexCount);
- Array.Resize(ref uvBuffer.Items, totalVertexCount);
- Array.Resize(ref colorBuffer.Items, totalVertexCount);
- }
- vertexBuffer.Count = uvBuffer.Count = colorBuffer.Count = totalVertexCount;
- }
-
- // Populate Verts
- Color32 color = default(Color32);
-
- int vertexIndex = 0;
- var tempVerts = this.tempVerts;
- Vector2 bmin = this.meshBoundsMin;
- Vector2 bmax = this.meshBoundsMax;
-
- var vbi = vertexBuffer.Items;
- var ubi = uvBuffer.Items;
- var cbi = colorBuffer.Items;
- int lastSlotIndex = 0;
-
- // drawOrder[endSlot] is excluded
- for (int si = 0, n = instruction.submeshInstructions.Count; si < n; si++) {
- var submesh = instruction.submeshInstructions.Items[si];
- var skeleton = submesh.skeleton;
- var drawOrderItems = skeleton.drawOrder.Items;
- float a = skeleton.a * 255, r = skeleton.r, g = skeleton.g, b = skeleton.b;
-
- int endSlot = submesh.endSlot;
- int startSlot = submesh.startSlot;
- lastSlotIndex = endSlot;
-
- if (settings.tintBlack) {
- Vector2 rg, b2;
- int vi = vertexIndex;
- b2.y = 1f;
-
- {
- if (uv2 == null) {
- uv2 = new ExposedList();
- uv3 = new ExposedList();
- }
- if (totalVertexCount > uv2.Items.Length) { // Manual ExposedList.Resize()
- Array.Resize(ref uv2.Items, totalVertexCount);
- Array.Resize(ref uv3.Items, totalVertexCount);
- }
- uv2.Count = uv3.Count = totalVertexCount;
- }
-
- var uv2i = uv2.Items;
- var uv3i = uv3.Items;
-
- for (int slotIndex = startSlot; slotIndex < endSlot; slotIndex++) {
- var slot = drawOrderItems[slotIndex];
- if (!slot.bone.active) continue;
- 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) {
- uv2i[vi] = rg; uv2i[vi + 1] = rg; uv2i[vi + 2] = rg; uv2i[vi + 3] = rg;
- uv3i[vi] = b2; uv3i[vi + 1] = b2; uv3i[vi + 2] = b2; uv3i[vi + 3] = b2;
- vi += 4;
- } else { //} if (settings.renderMeshes) {
- var meshAttachment = attachment as MeshAttachment;
- if (meshAttachment != null) {
- int meshVertexCount = meshAttachment.worldVerticesLength;
- for (int iii = 0; iii < meshVertexCount; iii += 2) {
- uv2i[vi] = rg;
- uv3i[vi] = b2;
- vi++;
- }
- }
- }
- }
- }
-
- for (int slotIndex = startSlot; slotIndex < endSlot; slotIndex++) {
- var slot = drawOrderItems[slotIndex];
- if (!slot.bone.active) continue;
- var attachment = slot.attachment;
- float z = slotIndex * settings.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];
- vbi[vertexIndex].x = x1; vbi[vertexIndex].y = y1; vbi[vertexIndex].z = z;
- vbi[vertexIndex + 1].x = x4; vbi[vertexIndex + 1].y = y4; vbi[vertexIndex + 1].z = z;
- vbi[vertexIndex + 2].x = x2; vbi[vertexIndex + 2].y = y2; vbi[vertexIndex + 2].z = z;
- vbi[vertexIndex + 3].x = x3; vbi[vertexIndex + 3].y = y3; vbi[vertexIndex + 3].z = z;
-
- if (settings.pmaVertexColors) {
- 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);
- }
-
- cbi[vertexIndex] = color; cbi[vertexIndex + 1] = color; cbi[vertexIndex + 2] = color; cbi[vertexIndex + 3] = color;
-
- float[] regionUVs = regionAttachment.uvs;
- ubi[vertexIndex].x = regionUVs[RegionAttachment.BLX]; ubi[vertexIndex].y = regionUVs[RegionAttachment.BLY];
- ubi[vertexIndex + 1].x = regionUVs[RegionAttachment.BRX]; ubi[vertexIndex + 1].y = regionUVs[RegionAttachment.BRY];
- ubi[vertexIndex + 2].x = regionUVs[RegionAttachment.ULX]; ubi[vertexIndex + 2].y = regionUVs[RegionAttachment.ULY];
- ubi[vertexIndex + 3].x = regionUVs[RegionAttachment.URX]; ubi[vertexIndex + 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;
-
- vertexIndex += 4;
- } else { //if (settings.renderMeshes) {
- var meshAttachment = attachment as MeshAttachment;
- if (meshAttachment != null) {
- int meshVertexCount = meshAttachment.worldVerticesLength;
- if (tempVerts.Length < meshVertexCount) this.tempVerts = tempVerts = new float[meshVertexCount];
- meshAttachment.ComputeWorldVertices(slot, tempVerts);
-
- if (settings.pmaVertexColors) {
- 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 (vertexIndex == 0) {
- // 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];
- vbi[vertexIndex].x = x; vbi[vertexIndex].y = y; vbi[vertexIndex].z = z;
- cbi[vertexIndex] = color; ubi[vertexIndex].x = attachmentUVs[iii]; ubi[vertexIndex].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;
-
- vertexIndex++;
- }
- }
- }
- }
- }
-
- this.meshBoundsMin = bmin;
- this.meshBoundsMax = bmax;
- this.meshBoundsThickness = lastSlotIndex * settings.zSpacing;
-
- int submeshInstructionCount = instruction.submeshInstructions.Count;
- submeshes.Count = submeshInstructionCount;
-
- // Add triangles
- if (updateTriangles) {
- // Match submesh buffers count with submeshInstruction count.
- if (this.submeshes.Items.Length < submeshInstructionCount) {
- this.submeshes.Resize(submeshInstructionCount);
- for (int i = 0, n = submeshInstructionCount; i < n; i++) {
- var submeshBuffer = this.submeshes.Items[i];
- if (submeshBuffer == null)
- this.submeshes.Items[i] = new ExposedList();
- else
- submeshBuffer.Clear(false);
- }
- }
-
- var submeshInstructionsItems = instruction.submeshInstructions.Items; // This relies on the resize above.
-
- // Fill the buffers.
- int attachmentFirstVertex = 0;
- for (int smbi = 0; smbi < submeshInstructionCount; smbi++) {
- var submeshInstruction = submeshInstructionsItems[smbi];
- var currentSubmeshBuffer = this.submeshes.Items[smbi];
- { //submesh.Resize(submesh.rawTriangleCount);
- int newTriangleCount = submeshInstruction.rawTriangleCount;
- if (newTriangleCount > currentSubmeshBuffer.Items.Length)
- Array.Resize(ref currentSubmeshBuffer.Items, newTriangleCount);
- else if (newTriangleCount < currentSubmeshBuffer.Items.Length) {
- // Zero the extra.
- var sbi = currentSubmeshBuffer.Items;
- for (int ei = newTriangleCount, nn = sbi.Length; ei < nn; ei++)
- sbi[ei] = 0;
- }
- currentSubmeshBuffer.Count = newTriangleCount;
- }
-
- var tris = currentSubmeshBuffer.Items;
- int triangleIndex = 0;
- var skeleton = submeshInstruction.skeleton;
- var drawOrderItems = skeleton.drawOrder.Items;
- for (int slotIndex = submeshInstruction.startSlot, endSlot = submeshInstruction.endSlot; slotIndex < endSlot; slotIndex++) {
- var slot = drawOrderItems[slotIndex];
- if (!slot.bone.active) continue;
-
- var attachment = drawOrderItems[slotIndex].attachment;
- if (attachment is RegionAttachment) {
- tris[triangleIndex] = attachmentFirstVertex;
- tris[triangleIndex + 1] = attachmentFirstVertex + 2;
- tris[triangleIndex + 2] = attachmentFirstVertex + 1;
- tris[triangleIndex + 3] = attachmentFirstVertex + 2;
- tris[triangleIndex + 4] = attachmentFirstVertex + 3;
- tris[triangleIndex + 5] = attachmentFirstVertex + 1;
- triangleIndex += 6;
- attachmentFirstVertex += 4;
- continue;
- }
- var meshAttachment = attachment as MeshAttachment;
- if (meshAttachment != null) {
- int[] attachmentTriangles = meshAttachment.triangles;
- for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii++, triangleIndex++)
- tris[triangleIndex] = attachmentFirstVertex + attachmentTriangles[ii];
- attachmentFirstVertex += meshAttachment.worldVerticesLength >> 1; // length/2;
- }
- }
- }
- }
- }
-
- public void ScaleVertexData (float scale) {
- var vbi = vertexBuffer.Items;
- for (int i = 0, n = vertexBuffer.Count; i < n; i++) {
- vbi[i] *= scale; // vbi[i].x *= scale; vbi[i].y *= scale;
- }
-
- meshBoundsMin *= scale;
- meshBoundsMax *= scale;
- meshBoundsThickness *= scale;
- }
-
- 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;
- int vbiLength = vbi.Length;
-
- // Zero the extra.
- {
- int listCount = vertexBuffer.Count;
- var vector3zero = Vector3.zero;
- for (int i = listCount; i < vbiLength; i++)
- vbi[i] = vector3zero;
- }
-
- // Set the vertex buffer.
- {
- mesh.vertices = vbi;
- mesh.uv = ubi;
- mesh.colors32 = cbi;
-
- if (float.IsInfinity(meshBoundsMin.x)) { // meshBoundsMin.x == BoundsMinDefault // == doesn't work on float Infinity constants.
- mesh.bounds = new Bounds();
- } else {
- //mesh.bounds = ArraysMeshGenerator.ToBounds(meshBoundsMin, meshBoundsMax);
- float halfWidth = (meshBoundsMax.x - meshBoundsMin.x) * 0.5f;
- float halfHeight = (meshBoundsMax.y - meshBoundsMin.y) * 0.5f;
- mesh.bounds = new Bounds {
- center = new Vector3(meshBoundsMin.x + halfWidth, meshBoundsMin.y + halfHeight),
- extents = new Vector3(halfWidth, halfHeight, meshBoundsThickness * 0.5f)
- };
- }
- }
-
- {
- if (settings.addNormals) {
- int oldLength = 0;
-
- if (normals == null)
- normals = new Vector3[vbiLength];
- else
- oldLength = normals.Length;
-
- if (oldLength != vbiLength) {
- Array.Resize(ref this.normals, vbiLength);
- var localNormals = this.normals;
- for (int i = oldLength; i < vbiLength; i++) localNormals[i] = Vector3.back;
- }
- mesh.normals = this.normals;
- }
-
- if (settings.tintBlack) {
- if (uv2 != null) {
- // Sometimes, the vertex buffer becomes smaller. We need to trim the size of the tint black buffers to match.
- if (vbiLength != uv2.Items.Length) {
- Array.Resize(ref uv2.Items, vbiLength);
- Array.Resize(ref uv3.Items, vbiLength);
- uv2.Count = uv3.Count = vbiLength;
- }
- mesh.uv2 = this.uv2.Items;
- mesh.uv3 = this.uv3.Items;
- }
- }
- }
- }
-
- public void FillLateVertexData (Mesh mesh) {
- if (settings.calculateTangents) {
- int vertexCount = this.vertexBuffer.Count;
- var sbi = submeshes.Items;
- int submeshCount = submeshes.Count;
- var vbi = vertexBuffer.Items;
- var ubi = uvBuffer.Items;
-
- MeshGenerator.SolveTangents2DEnsureSize(ref this.tangents, ref this.tempTanBuffer, vertexCount, vbi.Length);
- 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 EnsureVertexCapacity (int minimumVertexCount, bool inlcudeTintBlack = false, bool includeTangents = false, bool includeNormals = false) {
- if (minimumVertexCount > vertexBuffer.Items.Length) {
- Array.Resize(ref vertexBuffer.Items, minimumVertexCount);
- Array.Resize(ref uvBuffer.Items, minimumVertexCount);
- Array.Resize(ref colorBuffer.Items, minimumVertexCount);
-
- if (inlcudeTintBlack) {
- if (uv2 == null) {
- uv2 = new ExposedList(minimumVertexCount);
- uv3 = new ExposedList(minimumVertexCount);
- }
- uv2.Resize(minimumVertexCount);
- uv3.Resize(minimumVertexCount);
- }
-
- if (includeNormals) {
- if (normals == null)
- normals = new Vector3[minimumVertexCount];
- else
- Array.Resize(ref normals, minimumVertexCount);
-
- }
-
- if (includeTangents) {
- if (tangents == null)
- tangents = new Vector4[minimumVertexCount];
- else
- Array.Resize(ref tangents, minimumVertexCount);
- }
- }
- }
-
- /// Trims internal buffers to reduce the resulting mesh data stream size.
- public void TrimExcess () {
- vertexBuffer.TrimExcess();
- uvBuffer.TrimExcess();
- colorBuffer.TrimExcess();
-
- if (uv2 != null) uv2.TrimExcess();
- if (uv3 != null) uv3.TrimExcess();
-
- int vbiLength = vertexBuffer.Items.Length;
- if (normals != null) Array.Resize(ref normals, vbiLength);
- if (tangents != null) Array.Resize(ref tangents, vbiLength);
- }
-
- #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, int vertexBufferLength) {
- if (tangentBuffer == null || tangentBuffer.Length != vertexBufferLength)
- tangentBuffer = new Vector4[vertexBufferLength];
-
- 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
-
- #region AttachmentRendering
- static List AttachmentVerts = new List();
- static List AttachmentUVs = new List();
- static List AttachmentColors32 = new List();
- static List AttachmentIndices = new List();
-
- /// Fills mesh vertex data to render a RegionAttachment.
- public static void FillMeshLocal (Mesh mesh, RegionAttachment regionAttachment) {
- if (mesh == null) return;
- if (regionAttachment == null) return;
-
- AttachmentVerts.Clear();
- var offsets = regionAttachment.Offset;
- AttachmentVerts.Add(new Vector3(offsets[RegionAttachment.BLX], offsets[RegionAttachment.BLY]));
- AttachmentVerts.Add(new Vector3(offsets[RegionAttachment.ULX], offsets[RegionAttachment.ULY]));
- AttachmentVerts.Add(new Vector3(offsets[RegionAttachment.URX], offsets[RegionAttachment.URY]));
- AttachmentVerts.Add(new Vector3(offsets[RegionAttachment.BRX], offsets[RegionAttachment.BRY]));
-
- AttachmentUVs.Clear();
- var uvs = regionAttachment.UVs;
- AttachmentUVs.Add(new Vector2(uvs[RegionAttachment.ULX], uvs[RegionAttachment.ULY]));
- AttachmentUVs.Add(new Vector2(uvs[RegionAttachment.URX], uvs[RegionAttachment.URY]));
- AttachmentUVs.Add(new Vector2(uvs[RegionAttachment.BRX], uvs[RegionAttachment.BRY]));
- AttachmentUVs.Add(new Vector2(uvs[RegionAttachment.BLX], uvs[RegionAttachment.BLY]));
-
- AttachmentColors32.Clear();
- Color32 c = (Color32)(new Color(regionAttachment.r, regionAttachment.g, regionAttachment.b, regionAttachment.a));
- for (int i = 0; i < 4; i++)
- AttachmentColors32.Add(c);
-
- AttachmentIndices.Clear();
- AttachmentIndices.AddRange(new[] { 0, 2, 1, 0, 3, 2 });
-
- mesh.Clear();
- mesh.name = regionAttachment.Name;
- mesh.SetVertices(AttachmentVerts);
- mesh.SetUVs(0, AttachmentUVs);
- mesh.SetColors(AttachmentColors32);
- mesh.SetTriangles(AttachmentIndices, 0);
- mesh.RecalculateBounds();
-
- AttachmentVerts.Clear();
- AttachmentUVs.Clear();
- AttachmentColors32.Clear();
- AttachmentIndices.Clear();
- }
-
- public static void FillMeshLocal (Mesh mesh, MeshAttachment meshAttachment, SkeletonData skeletonData) {
- if (mesh == null) return;
- if (meshAttachment == null) return;
- int vertexCount = meshAttachment.WorldVerticesLength / 2;
-
- AttachmentVerts.Clear();
- if (meshAttachment.IsWeighted()) {
- int count = meshAttachment.WorldVerticesLength;
- int[] meshAttachmentBones = meshAttachment.bones;
- int v = 0;
-
- float[] vertices = meshAttachment.vertices;
- for (int w = 0, b = 0; w < count; w += 2) {
- float wx = 0, wy = 0;
- int n = meshAttachmentBones[v++];
- n += v;
- for (; v < n; v++, b += 3) {
- BoneMatrix bm = BoneMatrix.CalculateSetupWorld(skeletonData.bones.Items[meshAttachmentBones[v]]);
- float vx = vertices[b], vy = vertices[b + 1], weight = vertices[b + 2];
- wx += (vx * bm.a + vy * bm.b + bm.x) * weight;
- wy += (vx * bm.c + vy * bm.d + bm.y) * weight;
- }
- AttachmentVerts.Add(new Vector3(wx, wy));
- }
- } else {
- var localVerts = meshAttachment.Vertices;
- Vector3 pos = default(Vector3);
- for (int i = 0; i < vertexCount; i++) {
- int ii = i * 2;
- pos.x = localVerts[ii];
- pos.y = localVerts[ii + 1];
- AttachmentVerts.Add(pos);
- }
- }
-
- var uvs = meshAttachment.uvs;
- Vector2 uv = default(Vector2);
- Color32 c = (Color32)(new Color(meshAttachment.r, meshAttachment.g, meshAttachment.b, meshAttachment.a));
- AttachmentUVs.Clear();
- AttachmentColors32.Clear();
- for (int i = 0; i < vertexCount; i++) {
- int ii = i * 2;
- uv.x = uvs[ii];
- uv.y = uvs[ii + 1];
- AttachmentUVs.Add(uv);
-
- AttachmentColors32.Add(c);
- }
-
- AttachmentIndices.Clear();
- AttachmentIndices.AddRange(meshAttachment.triangles);
-
- mesh.Clear();
- mesh.name = meshAttachment.Name;
- mesh.SetVertices(AttachmentVerts);
- mesh.SetUVs(0, AttachmentUVs);
- mesh.SetColors(AttachmentColors32);
- mesh.SetTriangles(AttachmentIndices, 0);
- mesh.RecalculateBounds();
-
- AttachmentVerts.Clear();
- AttachmentUVs.Clear();
- AttachmentColors32.Clear();
- AttachmentIndices.Clear();
- }
- #endregion
- }
-
- /// A double-buffered Mesh, and a shared material array, bundled for use by Spine components that need to push a Mesh and materials to a Unity MeshRenderer and MeshFilter.
- public class MeshRendererBuffers : IDisposable {
- DoubleBuffered doubleBufferedMesh;
- internal readonly ExposedList submeshMaterials = new ExposedList();
- internal Material[] sharedMaterials = new Material[0];
-
- public void Initialize () {
- if (doubleBufferedMesh != null) {
- doubleBufferedMesh.GetNext().Clear();
- doubleBufferedMesh.GetNext().Clear();
- submeshMaterials.Clear();
- } else {
- doubleBufferedMesh = new DoubleBuffered();
- }
- }
-
- /// Returns a sharedMaterials array for use on a MeshRenderer.
- ///
- public Material[] GetUpdatedSharedMaterialsArray () {
- if (submeshMaterials.Count == sharedMaterials.Length)
- submeshMaterials.CopyTo(sharedMaterials);
- else
- sharedMaterials = submeshMaterials.ToArray();
-
- return sharedMaterials;
- }
-
- /// Returns true if the materials were modified since the buffers were last updated.
- 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;
- }
-
- /// Updates the internal shared materials array with the given instruction list.
- 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.NewSkeletonMesh();
- public SkeletonRendererInstruction instructionUsed = new SkeletonRendererInstruction();
-
- public void Clear () {
- mesh.Clear();
- instructionUsed.Clear();
- }
-
- 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;
- }
- }
- }
-
- /// Instructions used by a SkeletonRenderer to render a mesh.
- public class SkeletonRendererInstruction {
- public readonly ExposedList submeshInstructions = new ExposedList();
-
- public bool immutableTriangles;
- #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 Dispose () {
- attachments.Clear(true);
- }
-
- 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 drawOrderItems = instructionsItems[0].skeleton.drawOrder.Items;
- for (int i = 0; i < attachmentCount; i++) {
- Slot slot = drawOrderItems[startSlot + i];
- if (!slot.bone.active) continue;
- attachmentsItems[i] = slot.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.EnsureCapacity(other.attachments.Capacity);
- this.attachments.Count = other.attachments.Count;
- other.attachments.CopyTo(this.attachments.Items);
- #endif
-
- this.submeshInstructions.Clear(false);
- this.submeshInstructions.EnsureCapacity(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
- }
- }
-
-
+ }
}