/****************************************************************************** 
 | 
 * Spine Runtimes Software License v2.5 
 | 
 * 
 | 
 * Copyright (c) 2013-2016, Esoteric Software 
 | 
 * All rights reserved. 
 | 
 * 
 | 
 * You are granted a perpetual, non-exclusive, non-sublicensable, and 
 | 
 * non-transferable license to use, install, execute, and perform the Spine 
 | 
 * Runtimes software and derivative works solely for personal or internal 
 | 
 * use. Without the written permission of Esoteric Software (see Section 2 of 
 | 
 * the Spine Software License Agreement), you may not (a) modify, translate, 
 | 
 * adapt, or develop new applications using the Spine Runtimes or otherwise 
 | 
 * create derivative works or improvements of the Spine Runtimes or (b) remove, 
 | 
 * delete, alter, or obscure any trademarks or any copyright, trademark, patent, 
 | 
 * or other intellectual property or proprietary rights notices on or in the 
 | 
 * Software, including any copy thereof. Redistributions in binary or source 
 | 
 * form must include this license and terms. 
 | 
 * 
 | 
 * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR 
 | 
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 | 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 
 | 
 * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 | 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 | 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF 
 | 
 * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 
 | 
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 | 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 | 
 * POSSIBILITY OF SUCH DAMAGE. 
 | 
 *****************************************************************************/ 
 | 
  
 | 
#define SPINE_OPTIONAL_RENDEROVERRIDE 
 | 
#define SPINE_OPTIONAL_MATERIALOVERRIDE 
 | 
#define SPINE_OPTIONAL_NORMALS 
 | 
#define SPINE_OPTIONAL_SOLVETANGENTS 
 | 
  
 | 
//#define SPINE_OPTIONAL_FRONTFACING 
 | 
  
 | 
using System; 
 | 
using System.Collections.Generic; 
 | 
using UnityEngine; 
 | 
using Spine.Unity.MeshGeneration; 
 | 
  
 | 
namespace Spine.Unity { 
 | 
    /// <summary>Renders a skeleton.</summary> 
 | 
    [ExecuteAlways, RequireComponent(typeof(MeshFilter), typeof(MeshRenderer)), DisallowMultipleComponent] 
 | 
    [HelpURL("http://esotericsoftware.com/spine-unity-documentation#Rendering")] 
 | 
    public class SkeletonRenderer : MonoBehaviour, ISkeletonComponent { 
 | 
  
 | 
        public delegate void SkeletonRendererDelegate (SkeletonRenderer skeletonRenderer); 
 | 
        public SkeletonRendererDelegate OnRebuild; 
 | 
  
 | 
        public SkeletonDataAsset skeletonDataAsset; 
 | 
        public SkeletonDataAsset SkeletonDataAsset { get { return skeletonDataAsset; } } // ISkeletonComponent 
 | 
        public string initialSkinName; 
 | 
  
 | 
        #region Advanced 
 | 
        // Submesh Separation 
 | 
        [UnityEngine.Serialization.FormerlySerializedAs("submeshSeparators")] 
 | 
        [SpineSlot] 
 | 
        public string[] separatorSlotNames = new string[0]; 
 | 
        [System.NonSerialized] 
 | 
        public readonly List<Slot> separatorSlots = new List<Slot>(); 
 | 
  
 | 
        public float zSpacing; 
 | 
        public bool renderMeshes = true, immutableTriangles; 
 | 
        public bool pmaVertexColors = true; 
 | 
        public bool clearStateOnDisable = false; 
 | 
  
 | 
        #if SPINE_OPTIONAL_NORMALS 
 | 
        public bool calculateNormals; 
 | 
        #endif 
 | 
        #if SPINE_OPTIONAL_SOLVETANGENTS 
 | 
        public bool calculateTangents; 
 | 
        #endif 
 | 
        #if SPINE_OPTIONAL_FRONTFACING 
 | 
        public bool frontFacing; 
 | 
        #endif 
 | 
  
 | 
        public bool logErrors = false; 
 | 
  
 | 
        #if SPINE_OPTIONAL_RENDEROVERRIDE 
 | 
        public bool disableRenderingOnOverride = true; 
 | 
        public delegate void InstructionDelegate (SkeletonRenderer.SmartMesh.Instruction instruction); 
 | 
        event InstructionDelegate generateMeshOverride; 
 | 
        public event InstructionDelegate GenerateMeshOverride { 
 | 
            add { 
 | 
                generateMeshOverride += value; 
 | 
                if (disableRenderingOnOverride && generateMeshOverride != null) { 
 | 
                    Initialize(false); 
 | 
                    meshRenderer.enabled = false; 
 | 
                } 
 | 
            } 
 | 
            remove { 
 | 
                generateMeshOverride -= value; 
 | 
                if (disableRenderingOnOverride && generateMeshOverride == null) { 
 | 
                    Initialize(false); 
 | 
                    meshRenderer.enabled = true; 
 | 
                } 
 | 
            } 
 | 
        } 
 | 
        #endif 
 | 
  
 | 
        #if SPINE_OPTIONAL_MATERIALOVERRIDE 
 | 
        [System.NonSerialized] readonly Dictionary<Material, Material> customMaterialOverride = new Dictionary<Material, Material>(); 
 | 
        public Dictionary<Material, Material> CustomMaterialOverride { get { return customMaterialOverride; } } 
 | 
        #endif 
 | 
  
 | 
        // Custom Slot Material 
 | 
        [System.NonSerialized] readonly Dictionary<Slot, Material> customSlotMaterials = new Dictionary<Slot, Material>(); 
 | 
        public Dictionary<Slot, Material> CustomSlotMaterials { get { return customSlotMaterials; } } 
 | 
        #endregion 
 | 
  
 | 
        MeshRenderer meshRenderer; 
 | 
        MeshFilter meshFilter; 
 | 
  
 | 
        [System.NonSerialized] public bool valid; 
 | 
        [System.NonSerialized] public Skeleton skeleton; 
 | 
        public Skeleton Skeleton { 
 | 
            get { 
 | 
                Initialize(false); 
 | 
                return skeleton; 
 | 
            } 
 | 
        } 
 | 
  
 | 
        Spine.Unity.DoubleBuffered<SkeletonRenderer.SmartMesh> doubleBufferedMesh; 
 | 
        readonly SmartMesh.Instruction currentInstructions = new SmartMesh.Instruction(); 
 | 
        readonly ExposedList<ArraysMeshGenerator.SubmeshTriangleBuffer> submeshes = new ExposedList<ArraysMeshGenerator.SubmeshTriangleBuffer>(); 
 | 
        readonly ExposedList<Material> submeshMaterials = new ExposedList<Material>(); 
 | 
        Material[] sharedMaterials = new Material[0]; 
 | 
        float[] tempVertices = new float[8]; 
 | 
        Vector3[] vertices; 
 | 
        Color32[] colors; 
 | 
        Vector2[] uvs; 
 | 
        #if SPINE_OPTIONAL_NORMALS 
 | 
        Vector3[] normals; 
 | 
        #endif 
 | 
        #if SPINE_OPTIONAL_SOLVETANGENTS 
 | 
        Vector4[] tangents; 
 | 
        Vector2[] tempTanBuffer; 
 | 
        #endif 
 | 
  
 | 
        #region Runtime Instantiation 
 | 
        public static T NewSpineGameObject<T> (SkeletonDataAsset skeletonDataAsset) where T : SkeletonRenderer { 
 | 
            return SkeletonRenderer.AddSpineComponent<T>(new GameObject("New Spine GameObject"), skeletonDataAsset); 
 | 
        } 
 | 
  
 | 
        /// <summary>Add and prepare a Spine component that derives from SkeletonRenderer to a GameObject at runtime.</summary> 
 | 
        /// <typeparam name="T">T should be SkeletonRenderer or any of its derived classes.</typeparam> 
 | 
        public static T AddSpineComponent<T> (GameObject gameObject, SkeletonDataAsset skeletonDataAsset) where T : SkeletonRenderer { 
 | 
            var c = gameObject.AddComponent<T>(); 
 | 
            if (skeletonDataAsset != null) { 
 | 
                c.skeletonDataAsset = skeletonDataAsset; 
 | 
                c.Initialize(false); 
 | 
            } 
 | 
            return c; 
 | 
        } 
 | 
        #endregion 
 | 
  
 | 
        public virtual void Awake () { 
 | 
            Initialize(false); 
 | 
        } 
 | 
  
 | 
        void OnDisable () { 
 | 
            if (clearStateOnDisable && valid) 
 | 
                ClearState(); 
 | 
        } 
 | 
  
 | 
        protected virtual void ClearState () { 
 | 
            meshFilter.sharedMesh = null; 
 | 
            currentInstructions.Clear(); 
 | 
            if (skeleton != null) skeleton.SetToSetupPose(); 
 | 
        } 
 | 
  
 | 
        public virtual void Initialize (bool overwrite) { 
 | 
            if (valid && !overwrite) 
 | 
                return; 
 | 
  
 | 
            // Clear 
 | 
            { 
 | 
                if (meshFilter != null) 
 | 
                    meshFilter.sharedMesh = null; 
 | 
  
 | 
                meshRenderer = GetComponent<MeshRenderer>(); 
 | 
                if (meshRenderer != null) meshRenderer.sharedMaterial = null; 
 | 
  
 | 
                currentInstructions.Clear(); 
 | 
                vertices = null; 
 | 
                colors = null; 
 | 
                uvs = null; 
 | 
                sharedMaterials = new Material[0]; 
 | 
                submeshMaterials.Clear(); 
 | 
                submeshes.Clear(); 
 | 
                skeleton = null; 
 | 
  
 | 
                valid = false; 
 | 
            } 
 | 
  
 | 
            if (!skeletonDataAsset) { 
 | 
                if (logErrors) 
 | 
                    Debug.LogError("Missing SkeletonData asset.", this); 
 | 
  
 | 
                return; 
 | 
            } 
 | 
            SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(false); 
 | 
            if (skeletonData == null) 
 | 
                return; 
 | 
            valid = true; 
 | 
  
 | 
            meshFilter = GetComponent<MeshFilter>(); 
 | 
            meshRenderer = GetComponent<MeshRenderer>(); 
 | 
            doubleBufferedMesh = new DoubleBuffered<SmartMesh>(); 
 | 
            vertices = new Vector3[0]; 
 | 
  
 | 
            skeleton = new Skeleton(skeletonData); 
 | 
            if (!string.IsNullOrEmpty(initialSkinName) && initialSkinName != "default") 
 | 
                skeleton.SetSkin(initialSkinName); 
 | 
  
 | 
            separatorSlots.Clear(); 
 | 
            for (int i = 0; i < separatorSlotNames.Length; i++) 
 | 
                separatorSlots.Add(skeleton.FindSlot(separatorSlotNames[i])); 
 | 
  
 | 
            LateUpdate(); 
 | 
  
 | 
            if (OnRebuild != null) 
 | 
                OnRebuild(this); 
 | 
        } 
 | 
  
 | 
        public virtual void LateUpdate () { 
 | 
            if (!valid) 
 | 
                return; 
 | 
  
 | 
            if ( 
 | 
                (!meshRenderer.enabled) 
 | 
                #if SPINE_OPTIONAL_RENDEROVERRIDE 
 | 
                && this.generateMeshOverride == null 
 | 
                #endif 
 | 
            ) 
 | 
                return; 
 | 
             
 | 
  
 | 
            // STEP 1. Determine a SmartMesh.Instruction. Split up instructions into submeshes. ============================================================ 
 | 
            ExposedList<Slot> drawOrder = skeleton.drawOrder; 
 | 
            var drawOrderItems = drawOrder.Items; 
 | 
            int drawOrderCount = drawOrder.Count; 
 | 
            bool renderMeshes = this.renderMeshes; 
 | 
  
 | 
            // Clear last state of attachments and submeshes 
 | 
            var workingInstruction = this.currentInstructions; 
 | 
            var workingAttachments = workingInstruction.attachments; 
 | 
            workingAttachments.Clear(false); 
 | 
            workingAttachments.GrowIfNeeded(drawOrderCount); 
 | 
            workingAttachments.Count = drawOrderCount; 
 | 
            var workingAttachmentsItems = workingInstruction.attachments.Items; 
 | 
  
 | 
            #if SPINE_OPTIONAL_FRONTFACING 
 | 
            var workingFlips = workingInstruction.attachmentFlips; 
 | 
            workingFlips.Clear(false); 
 | 
            workingFlips.GrowIfNeeded(drawOrderCount); 
 | 
            workingFlips.Count = drawOrderCount; 
 | 
            var workingFlipsItems = workingFlips.Items; 
 | 
            #endif 
 | 
  
 | 
            var workingSubmeshInstructions = workingInstruction.submeshInstructions;    // Items array should not be cached. There is dynamic writing to this list. 
 | 
            workingSubmeshInstructions.Clear(false); 
 | 
  
 | 
            #if !SPINE_TK2D 
 | 
            bool isCustomSlotMaterialsPopulated = customSlotMaterials.Count > 0; 
 | 
            #endif 
 | 
  
 | 
            bool hasSeparators = separatorSlots.Count > 0; 
 | 
            int vertexCount = 0; 
 | 
            int submeshVertexCount = 0; 
 | 
            int submeshTriangleCount = 0, submeshFirstVertex = 0, submeshStartSlotIndex = 0; 
 | 
            Material lastMaterial = null; 
 | 
            for (int i = 0; i < drawOrderCount; i++) { 
 | 
                Slot slot = drawOrderItems[i]; 
 | 
                Attachment attachment = slot.attachment; 
 | 
                workingAttachmentsItems[i] = attachment; 
 | 
  
 | 
                #if SPINE_OPTIONAL_FRONTFACING 
 | 
                bool flip = frontFacing && (slot.bone.WorldSignX != slot.bone.WorldSignY); 
 | 
                workingFlipsItems[i] = flip; 
 | 
                #endif 
 | 
  
 | 
                object rendererObject = null; // An AtlasRegion in plain Spine-Unity. Spine-TK2D hooks into TK2D's system. eventual source of Material object. 
 | 
                int attachmentVertexCount, attachmentTriangleCount; 
 | 
                bool noRender = false; 
 | 
  
 | 
                var regionAttachment = attachment as RegionAttachment; 
 | 
                if (regionAttachment != null) { 
 | 
                    rendererObject = regionAttachment.RendererObject; 
 | 
                    attachmentVertexCount = 4; 
 | 
                    attachmentTriangleCount = 6; 
 | 
                } else { 
 | 
                    if (!renderMeshes) { 
 | 
                        noRender = true; 
 | 
                        attachmentVertexCount = 0; 
 | 
                        attachmentTriangleCount = 0; 
 | 
                        //continue; 
 | 
                    } else { 
 | 
                        var meshAttachment = attachment as MeshAttachment; 
 | 
                        if (meshAttachment != null) { 
 | 
                            rendererObject = meshAttachment.RendererObject; 
 | 
                            attachmentVertexCount = meshAttachment.worldVerticesLength >> 1; 
 | 
                            attachmentTriangleCount = meshAttachment.triangles.Length; 
 | 
                        } else { 
 | 
                            noRender = true; 
 | 
                            attachmentVertexCount = 0; 
 | 
                            attachmentTriangleCount = 0; 
 | 
                            //continue; 
 | 
                        } 
 | 
                    } 
 | 
                } 
 | 
  
 | 
                // Create a new SubmeshInstruction when material changes. (or when forced to separate by a submeshSeparator) 
 | 
                // Slot with a separator/new material will become the starting slot of the next new instruction. 
 | 
                bool forceSeparate = (hasSeparators && separatorSlots.Contains(slot)); 
 | 
                if (noRender) { 
 | 
                    if (forceSeparate && vertexCount > 0 && this.generateMeshOverride != null) { 
 | 
                        workingSubmeshInstructions.Add( 
 | 
                            new Spine.Unity.MeshGeneration.SubmeshInstruction { 
 | 
                                skeleton = this.skeleton, 
 | 
                                material = lastMaterial, 
 | 
                                startSlot = submeshStartSlotIndex, 
 | 
                                endSlot = i, 
 | 
                                triangleCount = submeshTriangleCount, 
 | 
                                firstVertexIndex = submeshFirstVertex, 
 | 
                                vertexCount = submeshVertexCount, 
 | 
                                forceSeparate = forceSeparate 
 | 
                            } 
 | 
                        ); 
 | 
                        submeshTriangleCount = 0; 
 | 
                        submeshVertexCount = 0; 
 | 
                        submeshFirstVertex = vertexCount; 
 | 
                        submeshStartSlotIndex = i; 
 | 
                    } 
 | 
                } else { 
 | 
                    #if !SPINE_TK2D 
 | 
                    Material material; 
 | 
                    if (isCustomSlotMaterialsPopulated) { 
 | 
                        if (!customSlotMaterials.TryGetValue(slot, out material)) 
 | 
                            material = (Material)((AtlasRegion)rendererObject).page.rendererObject;                 
 | 
                    } else { 
 | 
                        material = (Material)((AtlasRegion)rendererObject).page.rendererObject; 
 | 
                    } 
 | 
                    #else 
 | 
                    Material material = (rendererObject.GetType() == typeof(Material)) ? (Material)rendererObject : (Material)((AtlasRegion)rendererObject).page.rendererObject; 
 | 
                    #endif 
 | 
  
 | 
                    if (vertexCount > 0 && (forceSeparate || lastMaterial.GetInstanceID() != material.GetInstanceID())) { 
 | 
                        workingSubmeshInstructions.Add( 
 | 
                            new Spine.Unity.MeshGeneration.SubmeshInstruction { 
 | 
                                skeleton = this.skeleton, 
 | 
                                material = lastMaterial, 
 | 
                                startSlot = submeshStartSlotIndex, 
 | 
                                endSlot = i, 
 | 
                                triangleCount = submeshTriangleCount, 
 | 
                                firstVertexIndex = submeshFirstVertex, 
 | 
                                vertexCount = submeshVertexCount, 
 | 
                                forceSeparate = forceSeparate 
 | 
                            } 
 | 
                        ); 
 | 
                        submeshTriangleCount = 0; 
 | 
                        submeshVertexCount = 0; 
 | 
                        submeshFirstVertex = vertexCount; 
 | 
                        submeshStartSlotIndex = i; 
 | 
                    } 
 | 
                    // Update state for the next iteration. 
 | 
                    lastMaterial = material; 
 | 
                    submeshTriangleCount += attachmentTriangleCount; 
 | 
                    vertexCount += attachmentVertexCount; 
 | 
                    submeshVertexCount += attachmentVertexCount; 
 | 
                } 
 | 
            } 
 | 
  
 | 
            if (submeshVertexCount != 0) { 
 | 
                workingSubmeshInstructions.Add( 
 | 
                    new Spine.Unity.MeshGeneration.SubmeshInstruction { 
 | 
                        skeleton = this.skeleton, 
 | 
                        material = lastMaterial, 
 | 
                        startSlot = submeshStartSlotIndex, 
 | 
                        endSlot = drawOrderCount, 
 | 
                        triangleCount = submeshTriangleCount, 
 | 
                        firstVertexIndex = submeshFirstVertex, 
 | 
                        vertexCount = submeshVertexCount, 
 | 
                        forceSeparate = false 
 | 
                    } 
 | 
                ); 
 | 
            } 
 | 
  
 | 
            workingInstruction.vertexCount = vertexCount; 
 | 
            workingInstruction.immutableTriangles = this.immutableTriangles; 
 | 
            #if SPINE_OPTIONAL_FRONTFACING 
 | 
            workingInstruction.frontFacing = this.frontFacing; 
 | 
            #endif 
 | 
  
 | 
  
 | 
            // STEP 1.9. Post-process workingInstructions. ============================================================ 
 | 
  
 | 
            #if SPINE_OPTIONAL_MATERIALOVERRIDE 
 | 
            // Material overrides are done here so they can be applied per submesh instead of per slot 
 | 
            // but they will still be passed through the GenerateMeshOverride delegate, 
 | 
            // and will still go through the normal material match check step in STEP 3. 
 | 
            if (customMaterialOverride.Count > 0) { // isCustomMaterialOverridePopulated  
 | 
                var workingSubmeshInstructionsItems = workingSubmeshInstructions.Items; 
 | 
                for (int i = 0; i < workingSubmeshInstructions.Count; i++) { 
 | 
                    var m = workingSubmeshInstructionsItems[i].material; 
 | 
                    Material mo; 
 | 
                    if (customMaterialOverride.TryGetValue(m, out mo)) { 
 | 
                        workingSubmeshInstructionsItems[i].material = mo; 
 | 
                    } 
 | 
                } 
 | 
            } 
 | 
            #endif 
 | 
            #if SPINE_OPTIONAL_RENDEROVERRIDE 
 | 
            if (this.generateMeshOverride != null) { 
 | 
                this.generateMeshOverride(workingInstruction); 
 | 
                if (disableRenderingOnOverride) return; 
 | 
            } 
 | 
            #endif 
 | 
  
 | 
  
 | 
            // STEP 2. Update vertex buffer based on verts from the attachments.  ============================================================ 
 | 
            // Uses values that were also stored in workingInstruction. 
 | 
            bool vertexCountIncreased = ArraysMeshGenerator.EnsureSize(vertexCount, ref this.vertices, ref this.uvs, ref this.colors); 
 | 
            #if SPINE_OPTIONAL_NORMALS 
 | 
            if (vertexCountIncreased && calculateNormals) { 
 | 
                Vector3[] localNormals = this.normals = new Vector3[vertexCount]; 
 | 
                Vector3 normal = new Vector3(0, 0, -1); 
 | 
                for (int i = 0; i < vertexCount; i++) 
 | 
                    localNormals[i] = normal; 
 | 
            } 
 | 
            #endif 
 | 
  
 | 
            Vector3 meshBoundsMin; 
 | 
            Vector3 meshBoundsMax; 
 | 
            if (vertexCount <= 0) { 
 | 
                meshBoundsMin = new Vector3(0, 0, 0); 
 | 
                meshBoundsMax = new Vector3(0, 0, 0); 
 | 
            } else { 
 | 
                meshBoundsMin.x = int.MaxValue; 
 | 
                meshBoundsMin.y = int.MaxValue; 
 | 
                meshBoundsMax.x = int.MinValue; 
 | 
                meshBoundsMax.y = int.MinValue; 
 | 
  
 | 
                if (zSpacing > 0f) { 
 | 
                    meshBoundsMin.z = 0f; 
 | 
                    meshBoundsMax.z = zSpacing * (drawOrderCount - 1); 
 | 
                } else { 
 | 
                    meshBoundsMin.z = zSpacing * (drawOrderCount - 1); 
 | 
                    meshBoundsMax.z = 0f; 
 | 
                } 
 | 
            } 
 | 
            int vertexIndex = 0; 
 | 
            ArraysMeshGenerator.FillVerts(skeleton, 0, drawOrderCount, this.zSpacing, pmaVertexColors, this.vertices, this.uvs, this.colors, ref vertexIndex, ref tempVertices, ref meshBoundsMin, ref meshBoundsMax, renderMeshes); 
 | 
  
 | 
  
 | 
            // Step 3. Move the mesh data into a UnityEngine.Mesh ============================================================ 
 | 
            var currentSmartMesh = doubleBufferedMesh.GetNext();    // Double-buffer for performance. 
 | 
            var currentMesh = currentSmartMesh.mesh; 
 | 
            currentMesh.vertices = this.vertices; 
 | 
            currentMesh.colors32 = colors; 
 | 
            currentMesh.uv = uvs; 
 | 
            currentMesh.bounds = ArraysMeshGenerator.ToBounds(meshBoundsMin, meshBoundsMax); 
 | 
  
 | 
            var currentSmartMeshInstructionUsed = currentSmartMesh.instructionUsed; 
 | 
            #if SPINE_OPTIONAL_NORMALS 
 | 
            if (calculateNormals && currentSmartMeshInstructionUsed.vertexCount < vertexCount) 
 | 
                currentMesh.normals = normals; 
 | 
            #endif 
 | 
  
 | 
            // Check if the triangles should also be updated. 
 | 
            // This thorough structure check is cheaper than updating triangles every frame. 
 | 
            bool mustUpdateMeshStructure = CheckIfMustUpdateMeshStructure(workingInstruction, currentSmartMeshInstructionUsed); 
 | 
            int submeshCount = workingSubmeshInstructions.Count; 
 | 
            if (mustUpdateMeshStructure) { 
 | 
                var thisSubmeshMaterials = this.submeshMaterials; 
 | 
                thisSubmeshMaterials.Clear(false); 
 | 
  
 | 
                int oldSubmeshCount = submeshes.Count; 
 | 
  
 | 
                if (submeshes.Capacity < submeshCount) 
 | 
                    submeshes.Capacity = submeshCount; 
 | 
                for (int i = oldSubmeshCount; i < submeshCount; i++) 
 | 
                    submeshes.Items[i] = new ArraysMeshGenerator.SubmeshTriangleBuffer(workingSubmeshInstructions.Items[i].triangleCount); 
 | 
                submeshes.Count = submeshCount; 
 | 
                     
 | 
                var mutableTriangles = !workingInstruction.immutableTriangles; 
 | 
                for (int i = 0, last = submeshCount - 1; i < submeshCount; i++) { 
 | 
                    var submeshInstruction = workingSubmeshInstructions.Items[i]; 
 | 
  
 | 
                    if (mutableTriangles || i >= oldSubmeshCount) { 
 | 
     
 | 
                        #if !SPINE_OPTIONAL_FRONTFACING 
 | 
                        var currentSubmesh = submeshes.Items[i]; 
 | 
                        int instructionTriangleCount = submeshInstruction.triangleCount; 
 | 
                        if (renderMeshes) { 
 | 
                            ArraysMeshGenerator.FillTriangles(ref currentSubmesh.triangles, skeleton, instructionTriangleCount, submeshInstruction.firstVertexIndex, submeshInstruction.startSlot, submeshInstruction.endSlot, (i == last)); 
 | 
                            currentSubmesh.triangleCount = instructionTriangleCount; 
 | 
                        } else { 
 | 
                            ArraysMeshGenerator.FillTrianglesQuads(ref currentSubmesh.triangles, ref currentSubmesh.triangleCount, ref currentSubmesh.firstVertex, submeshInstruction.firstVertexIndex, instructionTriangleCount, (i == last)); 
 | 
                        } 
 | 
                        #else 
 | 
                        SetSubmesh(i, submeshInstruction, currentInstructions.attachmentFlips, i == last); 
 | 
                        #endif 
 | 
  
 | 
                    } 
 | 
  
 | 
                    thisSubmeshMaterials.Add(submeshInstruction.material); 
 | 
                } 
 | 
  
 | 
                currentMesh.subMeshCount = submeshCount; 
 | 
  
 | 
                for (int i = 0; i < submeshCount; ++i) 
 | 
                    currentMesh.SetTriangles(submeshes.Items[i].triangles, i); 
 | 
            } 
 | 
  
 | 
            #if SPINE_OPTIONAL_SOLVETANGENTS 
 | 
            if (calculateTangents) { 
 | 
                ArraysMeshGenerator.SolveTangents2DEnsureSize(ref this.tangents, ref this.tempTanBuffer, vertices.Length); 
 | 
                for (int i = 0; i < submeshCount; i++) { 
 | 
                    var submesh = submeshes.Items[i]; 
 | 
                    ArraysMeshGenerator.SolveTangents2DTriangles(this.tempTanBuffer, submesh.triangles, submesh.triangleCount, this.vertices, this.uvs, vertexCount); 
 | 
                } 
 | 
                ArraysMeshGenerator.SolveTangents2DBuffer(this.tangents, this.tempTanBuffer, vertexCount); 
 | 
                currentMesh.tangents = this.tangents; 
 | 
            } 
 | 
            #endif 
 | 
                 
 | 
            // CheckIfMustUpdateMaterialArray (last pushed materials vs currently parsed materials) 
 | 
            // Needs to check against the Working Submesh Instructions Materials instead of the cached submeshMaterials. 
 | 
            { 
 | 
                var lastPushedMaterials = this.sharedMaterials; 
 | 
                bool mustUpdateRendererMaterials = mustUpdateMeshStructure || 
 | 
                    (lastPushedMaterials.Length != submeshCount); 
 | 
  
 | 
                // Assumption at this point: (lastPushedMaterials.Count == workingSubmeshInstructions.Count == thisSubmeshMaterials.Count == submeshCount) 
 | 
  
 | 
                // Case: mesh structure or submesh count did not change but materials changed. 
 | 
                if (!mustUpdateRendererMaterials) { 
 | 
                    var workingSubmeshInstructionsItems = workingSubmeshInstructions.Items; 
 | 
                    for (int i = 0; i < submeshCount; i++) { 
 | 
                        if (lastPushedMaterials[i].GetInstanceID() != workingSubmeshInstructionsItems[i].material.GetInstanceID()) {   // Bounds check is implied by submeshCount above. 
 | 
                            mustUpdateRendererMaterials = true; 
 | 
                            { 
 | 
                                var thisSubmeshMaterials = this.submeshMaterials.Items; 
 | 
                                if (mustUpdateRendererMaterials) 
 | 
                                    for (int j = 0; j < submeshCount; j++) 
 | 
                                        thisSubmeshMaterials[j] = workingSubmeshInstructionsItems[j].material; 
 | 
                            } 
 | 
                            break; 
 | 
                        } 
 | 
                    } 
 | 
                } 
 | 
  
 | 
                if (mustUpdateRendererMaterials) { 
 | 
                    if (submeshMaterials.Count == sharedMaterials.Length) 
 | 
                        submeshMaterials.CopyTo(sharedMaterials); 
 | 
                    else 
 | 
                        sharedMaterials = submeshMaterials.ToArray(); 
 | 
  
 | 
                    meshRenderer.sharedMaterials = sharedMaterials; 
 | 
                } 
 | 
            } 
 | 
  
 | 
  
 | 
            // Step 4. The UnityEngine.Mesh is ready. Set it as the MeshFilter's mesh. Store the instructions used for that mesh. ============================================================ 
 | 
            meshFilter.sharedMesh = currentMesh; 
 | 
            currentSmartMesh.instructionUsed.Set(workingInstruction); 
 | 
  
 | 
        } 
 | 
  
 | 
        static bool CheckIfMustUpdateMeshStructure (SmartMesh.Instruction a, SmartMesh.Instruction b) { 
 | 
  
 | 
            #if UNITY_EDITOR 
 | 
            if (!Application.isPlaying) 
 | 
                return true; 
 | 
            #endif 
 | 
  
 | 
            if (a.vertexCount != b.vertexCount) 
 | 
                return true; 
 | 
  
 | 
            if (a.immutableTriangles != b.immutableTriangles) 
 | 
                return true; 
 | 
  
 | 
            int attachmentCountB = b.attachments.Count; 
 | 
            if (a.attachments.Count != attachmentCountB) // Bounds check for the looped storedAttachments count below. 
 | 
                return true; 
 | 
  
 | 
            var attachmentsA = a.attachments.Items; 
 | 
            var attachmentsB = b.attachments.Items;         
 | 
            for (int i = 0; i < attachmentCountB; i++) { 
 | 
                if (attachmentsA[i] != attachmentsB[i]) 
 | 
                    return true; 
 | 
            } 
 | 
  
 | 
            #if SPINE_OPTIONAL_FRONTFACING 
 | 
            if (a.frontFacing != b.frontFacing) {     // if settings changed 
 | 
                return true; 
 | 
            } else if (a.frontFacing) {             // if settings matched, only need to check one. 
 | 
                var flipsA = a.attachmentFlips.Items; 
 | 
                var flipsB = b.attachmentFlips.Items; 
 | 
                for (int i = 0; i < attachmentCountB; i++) { 
 | 
                    if (flipsA[i] != flipsB[i]) 
 | 
                        return true; 
 | 
                } 
 | 
            } 
 | 
            #endif 
 | 
  
 | 
            // Submesh count changed 
 | 
            int submeshCountA = a.submeshInstructions.Count; 
 | 
            int submeshCountB = b.submeshInstructions.Count; 
 | 
            if (submeshCountA != submeshCountB) 
 | 
                return true; 
 | 
  
 | 
            // Submesh Instruction mismatch 
 | 
            var submeshInstructionsItemsA = a.submeshInstructions.Items; 
 | 
            var submeshInstructionsItemsB = b.submeshInstructions.Items; 
 | 
            for (int i = 0; i < submeshCountB; i++) { 
 | 
                var submeshA = submeshInstructionsItemsA[i]; 
 | 
                var submeshB = submeshInstructionsItemsB[i]; 
 | 
  
 | 
                if (!( 
 | 
                    submeshA.vertexCount == submeshB.vertexCount && 
 | 
                    submeshA.startSlot == submeshB.startSlot && 
 | 
                    submeshA.endSlot == submeshB.endSlot && 
 | 
                    submeshA.triangleCount == submeshB.triangleCount && 
 | 
                    submeshA.firstVertexIndex == submeshB.firstVertexIndex 
 | 
                )) 
 | 
                    return true;             
 | 
            } 
 | 
  
 | 
            return false; 
 | 
        } 
 | 
  
 | 
        #if SPINE_OPTIONAL_FRONTFACING 
 | 
        void SetSubmesh (int submeshIndex, Spine.Unity.MeshGeneration.SubmeshInstruction submeshInstructions, ExposedList<bool> flipStates, bool isLastSubmesh) { 
 | 
            var currentSubmesh = submeshes.Items[submeshIndex]; 
 | 
            int[] triangles = currentSubmesh.triangles; 
 | 
  
 | 
            int triangleCount = submeshInstructions.triangleCount; 
 | 
            int firstVertex = submeshInstructions.firstVertexIndex; 
 | 
  
 | 
            int trianglesCapacity = triangles.Length; 
 | 
            if (isLastSubmesh && trianglesCapacity > triangleCount) { 
 | 
                // Last submesh may have more triangles than required, so zero triangles to the end. 
 | 
                for (int i = triangleCount; i < trianglesCapacity; i++) 
 | 
                    triangles[i] = 0; 
 | 
  
 | 
                currentSubmesh.triangleCount = triangleCount; 
 | 
  
 | 
            } else if (trianglesCapacity != triangleCount) { 
 | 
                // Reallocate triangles when not the exact size needed. 
 | 
                currentSubmesh.triangles = triangles = new int[triangleCount]; 
 | 
                currentSubmesh.triangleCount = 0; 
 | 
            } 
 | 
                 
 | 
            if (!this.renderMeshes && !this.frontFacing) { 
 | 
                // Use stored triangles if possible. 
 | 
                if (currentSubmesh.firstVertex != firstVertex || currentSubmesh.triangleCount < triangleCount) { //|| currentSubmesh.triangleCount == 0 
 | 
                    currentSubmesh.triangleCount = triangleCount; 
 | 
                    currentSubmesh.firstVertex = firstVertex; 
 | 
  
 | 
                    for (int i = 0; i < triangleCount; i += 6, firstVertex += 4) { 
 | 
                        triangles[i] = firstVertex; 
 | 
                        triangles[i + 1] = firstVertex + 2; 
 | 
                        triangles[i + 2] = firstVertex + 1; 
 | 
                        triangles[i + 3] = firstVertex + 2; 
 | 
                        triangles[i + 4] = firstVertex + 3; 
 | 
                        triangles[i + 5] = firstVertex + 1; 
 | 
                    } 
 | 
                } 
 | 
                return; 
 | 
            } 
 | 
                 
 | 
            var flipStatesItems = flipStates.Items; 
 | 
  
 | 
            // Iterate through all slots and store their triangles.  
 | 
            var drawOrderItems = skeleton.DrawOrder.Items; 
 | 
            int triangleIndex = 0; // Modified by loop 
 | 
            for (int i = submeshInstructions.startSlot, n = submeshInstructions.endSlot; i < n; i++) {             
 | 
                Attachment attachment = drawOrderItems[i].attachment; 
 | 
                bool flip = frontFacing && flipStatesItems[i]; 
 | 
  
 | 
                // Add RegionAttachment triangles 
 | 
                if (attachment is RegionAttachment) { 
 | 
                    if (!flip) { 
 | 
                        triangles[triangleIndex] = firstVertex; 
 | 
                        triangles[triangleIndex + 1] = firstVertex + 2; 
 | 
                        triangles[triangleIndex + 2] = firstVertex + 1; 
 | 
                        triangles[triangleIndex + 3] = firstVertex + 2; 
 | 
                        triangles[triangleIndex + 4] = firstVertex + 3; 
 | 
                        triangles[triangleIndex + 5] = firstVertex + 1; 
 | 
                    } else { 
 | 
                        triangles[triangleIndex] = firstVertex + 1; 
 | 
                        triangles[triangleIndex + 1] = firstVertex + 2; 
 | 
                        triangles[triangleIndex + 2] = firstVertex; 
 | 
                        triangles[triangleIndex + 3] = firstVertex + 1; 
 | 
                        triangles[triangleIndex + 4] = firstVertex + 3; 
 | 
                        triangles[triangleIndex + 5] = firstVertex + 2; 
 | 
                    } 
 | 
  
 | 
                    triangleIndex += 6; 
 | 
                    firstVertex += 4; 
 | 
                    continue; 
 | 
                } 
 | 
  
 | 
                // Add (Weighted)MeshAttachment triangles 
 | 
                int[] attachmentTriangles; 
 | 
                int attachmentVertexCount; 
 | 
                var meshAttachment = attachment as MeshAttachment; 
 | 
                if (meshAttachment != null) { 
 | 
                    attachmentVertexCount = meshAttachment.worldVerticesLength >> 1; // length/2 
 | 
                    attachmentTriangles = meshAttachment.triangles; 
 | 
                } else { 
 | 
                    continue; 
 | 
                } 
 | 
  
 | 
                if (flip) { 
 | 
                    for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii += 3, triangleIndex += 3) { 
 | 
                        triangles[triangleIndex + 2] = firstVertex + attachmentTriangles[ii]; 
 | 
                        triangles[triangleIndex + 1] = firstVertex + attachmentTriangles[ii + 1]; 
 | 
                        triangles[triangleIndex] = firstVertex + attachmentTriangles[ii + 2]; 
 | 
                    } 
 | 
                } else { 
 | 
                    for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii++, triangleIndex++) { 
 | 
                        triangles[triangleIndex] = firstVertex + attachmentTriangles[ii]; 
 | 
                    } 
 | 
                } 
 | 
  
 | 
                firstVertex += attachmentVertexCount; 
 | 
            } 
 | 
        } 
 | 
        #endif 
 | 
  
 | 
        ///<summary>This is a Mesh that also stores the instructions SkeletonRenderer generated for it.</summary> 
 | 
        public class SmartMesh { 
 | 
            public Mesh mesh = Spine.Unity.SpineMesh.NewMesh(); 
 | 
            public SmartMesh.Instruction instructionUsed = new SmartMesh.Instruction();         
 | 
  
 | 
            public class Instruction { 
 | 
                public bool immutableTriangles; 
 | 
                public int vertexCount = -1; 
 | 
                public readonly ExposedList<Attachment> attachments = new ExposedList<Attachment>(); 
 | 
                public readonly ExposedList<Spine.Unity.MeshGeneration.SubmeshInstruction> submeshInstructions = new ExposedList<Spine.Unity.MeshGeneration.SubmeshInstruction>(); 
 | 
  
 | 
                #if SPINE_OPTIONAL_FRONTFACING 
 | 
                public bool frontFacing; 
 | 
                public readonly ExposedList<bool> attachmentFlips = new ExposedList<bool>(); 
 | 
                #endif 
 | 
  
 | 
                public void Clear () { 
 | 
                    this.attachments.Clear(false); 
 | 
                    this.submeshInstructions.Clear(false); 
 | 
  
 | 
                    #if SPINE_OPTIONAL_FRONTFACING 
 | 
                    this.attachmentFlips.Clear(false); 
 | 
                    #endif 
 | 
                } 
 | 
  
 | 
                public void Set (Instruction other) { 
 | 
                    this.immutableTriangles = other.immutableTriangles; 
 | 
                    this.vertexCount = other.vertexCount; 
 | 
  
 | 
                    this.attachments.Clear(false); 
 | 
                    this.attachments.GrowIfNeeded(other.attachments.Capacity); 
 | 
                    this.attachments.Count = other.attachments.Count; 
 | 
                    other.attachments.CopyTo(this.attachments.Items); 
 | 
  
 | 
                    #if SPINE_OPTIONAL_FRONTFACING 
 | 
                    this.frontFacing = other.frontFacing; 
 | 
                    this.attachmentFlips.Clear(false); 
 | 
                    this.attachmentFlips.GrowIfNeeded(other.attachmentFlips.Capacity); 
 | 
                    this.attachmentFlips.Count = other.attachmentFlips.Count; 
 | 
                    if (this.frontFacing) 
 | 
                        other.attachmentFlips.CopyTo(this.attachmentFlips.Items); 
 | 
                    #endif 
 | 
  
 | 
                    this.submeshInstructions.Clear(false); 
 | 
                    this.submeshInstructions.GrowIfNeeded(other.submeshInstructions.Capacity); 
 | 
                    this.submeshInstructions.Count = other.submeshInstructions.Count; 
 | 
                    other.submeshInstructions.CopyTo(this.submeshInstructions.Items); 
 | 
                } 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
} 
 |