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; 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; object rendererObject = null; // An AtlasRegion in plain Spine-Unity. Spine-TK2D hooks into TK2D's system. eventual source of Material object. int attachmentVertexCount, attachmentTriangleCount; bool noRender = false; var regionAttachment = attachment as RegionAttachment; if (regionAttachment != null) { rendererObject = regionAttachment.RendererObject; attachmentVertexCount = 4; attachmentTriangleCount = 6; } else { if (!renderMeshes) { noRender = true; attachmentVertexCount = 0; attachmentTriangleCount = 0; //continue; } else { var meshAttachment = attachment as MeshAttachment; if (meshAttachment != null) { rendererObject = meshAttachment.RendererObject; attachmentVertexCount = meshAttachment.worldVerticesLength >> 1; attachmentTriangleCount = meshAttachment.triangles.Length; } else { noRender = true; attachmentVertexCount = 0; attachmentTriangleCount = 0; //continue; } } } // Create a new SubmeshInstruction when material changes. (or when forced to separate by a submeshSeparator) // Slot with a separator/new material will become the starting slot of the next new instruction. bool forceSeparate = (hasSeparators && separatorSlots.Contains(slot)); if (noRender) { if (forceSeparate && vertexCount > 0 #if SPINE_OPTIONAL_RENDEROVERRIDE && this.generateMeshOverride != null #endif ) { workingSubmeshInstructions.Add( new Spine.Unity.MeshGeneration.SubmeshInstruction { skeleton = this.skeleton, material = lastMaterial, startSlot = submeshStartSlotIndex, endSlot = i, triangleCount = submeshTriangleCount, firstVertexIndex = submeshFirstVertex, vertexCount = submeshVertexCount, forceSeparate = forceSeparate } ); submeshTriangleCount = 0; submeshVertexCount = 0; submeshFirstVertex = vertexCount; submeshStartSlotIndex = i; } } else { #if !SPINE_TK2D Material material; if (isCustomSlotMaterialsPopulated) { if (!customSlotMaterials.TryGetValue(slot, out material)) { material = (Material)((AtlasRegion)rendererObject).page.rendererObject; } } else { material = (Material)((AtlasRegion)rendererObject).page.rendererObject; } #else Material material = (rendererObject.GetType() == typeof(Material)) ? (Material)rendererObject : (Material)((AtlasRegion)rendererObject).page.rendererObject; #endif if (vertexCount > 0 && (forceSeparate || lastMaterial.GetInstanceID() != material.GetInstanceID())) { workingSubmeshInstructions.Add( new Spine.Unity.MeshGeneration.SubmeshInstruction { skeleton = this.skeleton, material = lastMaterial, startSlot = submeshStartSlotIndex, endSlot = i, triangleCount = submeshTriangleCount, firstVertexIndex = submeshFirstVertex, vertexCount = submeshVertexCount, forceSeparate = forceSeparate } ); submeshTriangleCount = 0; submeshVertexCount = 0; submeshFirstVertex = vertexCount; submeshStartSlotIndex = i; } // Update state for the next iteration. lastMaterial = material; submeshTriangleCount += attachmentTriangleCount; vertexCount += attachmentVertexCount; submeshVertexCount += attachmentVertexCount; } } if (submeshVertexCount != 0) { workingSubmeshInstructions.Add( new Spine.Unity.MeshGeneration.SubmeshInstruction { skeleton = this.skeleton, material = lastMaterial, startSlot = submeshStartSlotIndex, endSlot = drawOrderCount, triangleCount = submeshTriangleCount, firstVertexIndex = submeshFirstVertex, vertexCount = submeshVertexCount, forceSeparate = false } ); } workingInstruction.vertexCount = vertexCount; workingInstruction.immutableTriangles = this.immutableTriangles; // STEP 1.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. if (tintBlack) { ArraysMeshGenerator.EnsureSize(vertexCount, ref this.uv2); ArraysMeshGenerator.EnsureSize(vertexCount, ref this.uv3); } #if SPINE_OPTIONAL_NORMALS bool vertexCountIncreased = ArraysMeshGenerator.EnsureSize(vertexCount, ref this.vertices, ref this.uvs, ref this.colors); if (vertexCountIncreased && calculateNormals) { Vector3[] localNormals = this.normals = new Vector3[vertexCount]; Vector3 normal = new Vector3(0, 0, -1); for (int i = 0; i < vertexCount; i++) { localNormals[i] = normal; } } #else ArraysMeshGenerator.EnsureSize(vertexCount, ref this.vertices, ref this.uvs, ref this.colors); #endif Vector3 meshBoundsMin; Vector3 meshBoundsMax; if (vertexCount <= 0) { meshBoundsMin = new Vector3(0, 0, 0); meshBoundsMax = new Vector3(0, 0, 0); } else { meshBoundsMin.x = int.MaxValue; meshBoundsMin.y = int.MaxValue; meshBoundsMax.x = int.MinValue; meshBoundsMax.y = int.MinValue; if (zSpacing > 0f) { meshBoundsMin.z = 0f; meshBoundsMax.z = zSpacing * (drawOrderCount - 1); } else { meshBoundsMin.z = zSpacing * (drawOrderCount - 1); meshBoundsMax.z = 0f; } } int vertexIndex = 0; if (tintBlack) { ArraysMeshGenerator.FillBlackUVs(skeleton, 0, drawOrderCount, this.uv2, this.uv3, vertexIndex, renderMeshes); // This needs to be called before FillVerts so we have the correct vertexIndex argument. } ArraysMeshGenerator.FillVerts(skeleton, 0, drawOrderCount, this.zSpacing, pmaVertexColors, this.vertices, this.uvs, this.colors, ref vertexIndex, ref tempVertices, ref meshBoundsMin, ref meshBoundsMax, renderMeshes); // Step 3. Move the mesh data into a UnityEngine.Mesh ============================================================ var currentSmartMesh = doubleBufferedMesh.GetNext(); // Double-buffer for performance. var currentMesh = currentSmartMesh.mesh; currentMesh.vertices = this.vertices; currentMesh.colors32 = colors; currentMesh.uv = uvs; currentMesh.bounds = ArraysMeshGenerator.ToBounds(meshBoundsMin, meshBoundsMax); if (tintBlack) { currentMesh.uv2 = this.uv2; currentMesh.uv3 = this.uv3; } var currentSmartMeshInstructionUsed = currentSmartMesh.instructionUsed; #if SPINE_OPTIONAL_NORMALS if (calculateNormals && currentSmartMeshInstructionUsed.vertexCount < vertexCount) { currentMesh.normals = normals; } #endif // Check if the triangles should also be updated. // This thorough structure check is cheaper than updating triangles every frame. bool mustUpdateMeshStructure = CheckIfMustUpdateMeshStructure(workingInstruction, currentSmartMeshInstructionUsed); int submeshCount = workingSubmeshInstructions.Count; if (mustUpdateMeshStructure) { var thisSubmeshMaterials = this.submeshMaterials; thisSubmeshMaterials.Clear(false); int oldSubmeshCount = submeshes.Count; if (submeshes.Capacity < submeshCount) { submeshes.Capacity = submeshCount; } for (int i = oldSubmeshCount; i < submeshCount; i++) { submeshes.Items[i] = new ArraysMeshGenerator.SubmeshTriangleBuffer(workingSubmeshInstructions.Items[i].triangleCount); } submeshes.Count = submeshCount; var mutableTriangles = !workingInstruction.immutableTriangles; for (int i = 0, last = submeshCount - 1; i < submeshCount; i++) { var submeshInstruction = workingSubmeshInstructions.Items[i]; if (mutableTriangles || i >= oldSubmeshCount) { var currentSubmesh = submeshes.Items[i]; int instructionTriangleCount = submeshInstruction.triangleCount; if (renderMeshes) { ArraysMeshGenerator.FillTriangles(ref currentSubmesh.triangles, skeleton, instructionTriangleCount, submeshInstruction.firstVertexIndex, submeshInstruction.startSlot, submeshInstruction.endSlot, (i == last)); currentSubmesh.triangleCount = instructionTriangleCount; } else { ArraysMeshGenerator.FillTrianglesQuads(ref currentSubmesh.triangles, ref currentSubmesh.triangleCount, ref currentSubmesh.firstVertex, submeshInstruction.firstVertexIndex, instructionTriangleCount, (i == last)); } } thisSubmeshMaterials.Add(submeshInstruction.material); } currentMesh.subMeshCount = submeshCount; for (int i = 0; i < submeshCount; ++i) { currentMesh.SetTriangles(submeshes.Items[i].triangles, i); } } #if 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); }
public virtual void LateUpdate() { if (!valid || (!meshRenderer.enabled && this.generateMeshOverride == null)) { return; } ExposedList <Slot> drawOrder = skeleton.drawOrder; Slot[] items = drawOrder.Items; int count = drawOrder.Count; bool flag = renderMeshes; SmartMesh.Instruction instruction = currentInstructions; ExposedList <Attachment> attachments = instruction.attachments; attachments.Clear(clearArray: false); attachments.GrowIfNeeded(count); attachments.Count = count; Attachment[] items2 = instruction.attachments.Items; ExposedList <SubmeshInstruction> submeshInstructions = instruction.submeshInstructions; submeshInstructions.Clear(clearArray: false); bool flag2 = customSlotMaterials.Count > 0; int num = 0; int num2 = 0; int num3 = 0; int firstVertexIndex = 0; int startSlot = 0; Material material = null; for (int i = 0; i < count; i++) { Slot slot = items[i]; Attachment attachment = items2[i] = slot.attachment; RegionAttachment regionAttachment = attachment as RegionAttachment; object rendererObject; int num4; int num5; if (regionAttachment != null) { rendererObject = regionAttachment.RendererObject; num4 = 4; num5 = 6; } else { if (!flag) { continue; } MeshAttachment meshAttachment = attachment as MeshAttachment; if (meshAttachment == null) { continue; } rendererObject = meshAttachment.RendererObject; num4 = meshAttachment.worldVerticesLength >> 1; num5 = meshAttachment.triangles.Length; } Material value; if (flag2) { if (!customSlotMaterials.TryGetValue(slot, out value)) { value = (Material)((AtlasRegion)rendererObject).page.rendererObject; } } else { value = (Material)((AtlasRegion)rendererObject).page.rendererObject; } bool flag3 = separatorSlots.Count > 0 && separatorSlots.Contains(slot); if (num > 0 && (material.GetInstanceID() != value.GetInstanceID() || flag3)) { submeshInstructions.Add(new SubmeshInstruction { skeleton = skeleton, material = material, startSlot = startSlot, endSlot = i, triangleCount = num3, firstVertexIndex = firstVertexIndex, vertexCount = num2, forceSeparate = flag3 }); num3 = 0; num2 = 0; firstVertexIndex = num; startSlot = i; } material = value; num3 += num5; num += num4; num2 += num4; } if (num2 != 0) { submeshInstructions.Add(new SubmeshInstruction { skeleton = skeleton, material = material, startSlot = startSlot, endSlot = count, triangleCount = num3, firstVertexIndex = firstVertexIndex, vertexCount = num2, forceSeparate = false }); } instruction.vertexCount = num; instruction.immutableTriangles = immutableTriangles; if (customMaterialOverride.Count > 0) { SubmeshInstruction[] items3 = submeshInstructions.Items; for (int j = 0; j < submeshInstructions.Count; j++) { Material material2 = items3[j].material; Material value2; if (customMaterialOverride.TryGetValue(material2, out value2)) { items3[j].material = value2; } } } if (this.generateMeshOverride != null) { this.generateMeshOverride(instruction); if (disableRenderingOnOverride) { return; } } if (ArraysMeshGenerator.EnsureSize(num, ref vertices, ref uvs, ref colors) && calculateNormals) { Vector3[] array = normals = new Vector3[num]; Vector3 vector = new Vector3(0f, 0f, -1f); for (int k = 0; k < num; k++) { array[k] = vector; } } Vector3 boundsMin = default(Vector3); Vector3 boundsMax = default(Vector3); if (num <= 0) { boundsMin = new Vector3(0f, 0f, 0f); boundsMax = new Vector3(0f, 0f, 0f); } else { boundsMin.x = 2.14748365E+09f; boundsMin.y = 2.14748365E+09f; boundsMax.x = -2.14748365E+09f; boundsMax.y = -2.14748365E+09f; if (zSpacing > 0f) { boundsMin.z = 0f; boundsMax.z = zSpacing * (float)(count - 1); } else { boundsMin.z = zSpacing * (float)(count - 1); boundsMax.z = 0f; } } int vertexIndex = 0; ArraysMeshGenerator.FillVerts(skeleton, 0, count, zSpacing, pmaVertexColors, vertices, uvs, colors, ref vertexIndex, ref tempVertices, ref boundsMin, ref boundsMax, flag); SmartMesh next = doubleBufferedMesh.GetNext(); Mesh mesh = next.mesh; mesh.vertices = vertices; mesh.colors32 = colors; mesh.uv = uvs; mesh.bounds = ArraysMeshGenerator.ToBounds(boundsMin, boundsMax); SmartMesh.Instruction instructionUsed = next.instructionUsed; if (calculateNormals && instructionUsed.vertexCount < num) { mesh.normals = normals; } bool flag4 = CheckIfMustUpdateMeshStructure(instruction, instructionUsed); int count2 = submeshInstructions.Count; if (flag4) { ExposedList <Material> exposedList = submeshMaterials; exposedList.Clear(clearArray: false); int count3 = submeshes.Count; if (submeshes.Capacity < count2) { submeshes.Capacity = count2; } for (int l = count3; l < count2; l++) { submeshes.Items[l] = new ArraysMeshGenerator.SubmeshTriangleBuffer(submeshInstructions.Items[l].triangleCount); } submeshes.Count = count2; bool flag5 = !instruction.immutableTriangles; int m = 0; int num6 = count2 - 1; for (; m < count2; m++) { SubmeshInstruction submeshInstruction = submeshInstructions.Items[m]; if (flag5 || m >= count3) { ArraysMeshGenerator.SubmeshTriangleBuffer submeshTriangleBuffer = submeshes.Items[m]; int triangleCount = submeshInstruction.triangleCount; if (flag) { ArraysMeshGenerator.FillTriangles(ref submeshTriangleBuffer.triangles, skeleton, triangleCount, submeshInstruction.firstVertexIndex, submeshInstruction.startSlot, submeshInstruction.endSlot, m == num6); submeshTriangleBuffer.triangleCount = triangleCount; } else { ArraysMeshGenerator.FillTrianglesQuads(ref submeshTriangleBuffer.triangles, ref submeshTriangleBuffer.triangleCount, ref submeshTriangleBuffer.firstVertex, submeshInstruction.firstVertexIndex, triangleCount, m == num6); } } exposedList.Add(submeshInstruction.material); } mesh.subMeshCount = count2; for (int n = 0; n < count2; n++) { mesh.SetTriangles(submeshes.Items[n].triangles, n); } } if (calculateTangents) { ArraysMeshGenerator.SolveTangents2DEnsureSize(ref tangents, ref tempTanBuffer, vertices.Length); for (int num7 = 0; num7 < count2; num7++) { ArraysMeshGenerator.SubmeshTriangleBuffer submeshTriangleBuffer2 = submeshes.Items[num7]; ArraysMeshGenerator.SolveTangents2DTriangles(tempTanBuffer, submeshTriangleBuffer2.triangles, submeshTriangleBuffer2.triangleCount, vertices, uvs, num); } ArraysMeshGenerator.SolveTangents2DBuffer(tangents, tempTanBuffer, num); mesh.tangents = tangents; } Material[] array2 = sharedMaterials; bool flag6 = flag4 || array2.Length != count2; if (!flag6) { SubmeshInstruction[] items4 = submeshInstructions.Items; int num8 = 0; for (int num9 = array2.Length; num8 < num9; num8++) { if (array2[num8].GetInstanceID() != items4[num8].material.GetInstanceID()) { flag6 = true; break; } } } if (flag6) { if (submeshMaterials.Count == sharedMaterials.Length) { submeshMaterials.CopyTo(sharedMaterials); } else { sharedMaterials = submeshMaterials.ToArray(); } meshRenderer.sharedMaterials = sharedMaterials; } meshFilter.sharedMesh = mesh; next.instructionUsed.Set(instruction); }