// SkinnedMeshAttachment protected void AddAttachment(Slot slot, WeightedMeshAttachment attachment) { var tempVertices = this.tempVertices; float[] meshUVs = attachment.uvs; int meshVertexCount = attachment.uvs.Length; if (tempVertices.Length < meshVertexCount) { this.tempVertices = tempVertices = new float[meshVertexCount]; } attachment.ComputeWorldVertices(slot, tempVertices); Color color = skeletonColor; color.r = color.r * attachment.r * slot.r; color.g = color.g * attachment.g * slot.g; color.b = color.b * attachment.b * slot.b; color.a = color.a * attachment.a * slot.a; if (premultiplyAlpha) { color.r *= color.a; color.g *= color.a; color.b *= color.a; if (slot.data.blendMode == BlendMode.additive) { color.a = 0; } } int fv = positions.Count; // first vertex index for (int ii = 0; ii < meshVertexCount; ii += 2) { AddVert(new Vector3(tempVertices[ii], tempVertices[ii + 1]) * scale, color, new Vector2(meshUVs[ii], meshUVs[ii + 1])); } var attachmentTriangles = attachment.triangles; for (int ii = 0, n = attachmentTriangles.Length; ii < n; ii++) { indices.Add(attachmentTriangles[ii] + fv); } }
public virtual void LateUpdate() { if (!valid) { return; } // Exit early if there is nothing to render if (!meshRenderer.enabled && submeshRenderers.Length == 0) { return; } // This method caches several .Items arrays. Whenever it does, there should be no mutations done on the overlying ExposedList object. // Count vertices and submesh triangles. int vertexCount = 0; int submeshTriangleCount = 0, submeshFirstVertex = 0, submeshStartSlotIndex = 0; Material lastMaterial = null; ExposedList <Slot> drawOrder = skeleton.drawOrder; var drawOrderItems = drawOrder.Items; int drawOrderCount = drawOrder.Count; int submeshSeparatorSlotsCount = submeshSeparatorSlots.Count; bool renderMeshes = this.renderMeshes; // Clear last state of attachments and submeshes MeshState.SingleMeshState workingState = meshState.buffer; var workingAttachments = workingState.attachments; workingAttachments.Clear(true); workingState.UpdateAttachmentCount(drawOrderCount); var workingAttachmentsItems = workingAttachments.Items; var workingFlips = workingState.attachmentsFlipState; var workingFlipsItems = workingState.attachmentsFlipState.Items; var workingSubmeshArguments = workingState.addSubmeshArguments; // Items array should not be cached. There is dynamic writing to this object. workingSubmeshArguments.Clear(false); MeshState.SingleMeshState storedState = useMesh1 ? meshState.stateMesh1 : meshState.stateMesh2; var storedAttachments = storedState.attachments; var storedAttachmentsItems = storedAttachments.Items; var storedFlips = storedState.attachmentsFlipState; var storedFlipsItems = storedFlips.Items; bool mustUpdateMeshStructure = storedState.requiresUpdate || // Force update if the mesh was cleared. (prevents flickering due to incorrect state) drawOrderCount != storedAttachments.Count || // Number of slots changed (when does this happen?) immutableTriangles != storedState.immutableTriangles; // Immutable Triangles flag changed. bool isCustomMaterialsPopulated = customSlotMaterials.Count > 0; for (int i = 0; i < drawOrderCount; i++) { Slot slot = drawOrderItems[i]; Bone bone = slot.bone; Attachment attachment = slot.attachment; object rendererObject; // An AtlasRegion in plain Spine-Unity. Spine-TK2D hooks into TK2D's system. eventual source of Material object. int attachmentVertexCount, attachmentTriangleCount; // Handle flipping for triangle winding (for lighting?). bool flip = frontFacing && (bone.WorldSignX != bone.WorldSignY); workingFlipsItems[i] = flip; workingAttachmentsItems[i] = attachment; mustUpdateMeshStructure = mustUpdateMeshStructure || // Always prefer short circuited or. || and not |=. (attachment != storedAttachmentsItems[i]) || // Attachment order changed. // This relies on the drawOrder.Count != storedAttachments.Count check above as a bounds check. (flip != storedFlipsItems[i]); // Flip states changed. var regionAttachment = attachment as RegionAttachment; if (regionAttachment != null) { rendererObject = regionAttachment.RendererObject; attachmentVertexCount = 4; attachmentTriangleCount = 6; } else { if (!renderMeshes) { continue; } var meshAttachment = attachment as MeshAttachment; if (meshAttachment != null) { rendererObject = meshAttachment.RendererObject; attachmentVertexCount = meshAttachment.vertices.Length >> 1; attachmentTriangleCount = meshAttachment.triangles.Length; } else { var skinnedMeshAttachment = attachment as WeightedMeshAttachment; if (skinnedMeshAttachment != null) { rendererObject = skinnedMeshAttachment.RendererObject; attachmentVertexCount = skinnedMeshAttachment.uvs.Length >> 1; attachmentTriangleCount = skinnedMeshAttachment.triangles.Length; } else { continue; } } } #if !SPINE_TK2D // Material material = (Material)((AtlasRegion)rendererObject).page.rendererObject; // For no customSlotMaterials Material material; if (isCustomMaterialsPopulated) { if (!customSlotMaterials.TryGetValue(slot, out material)) { material = (Material)((AtlasRegion)rendererObject).page.rendererObject; } } else { material = (Material)((AtlasRegion)rendererObject).page.rendererObject; } #else Material material = (rendererObject.GetType() == typeof(Material)) ? (Material)rendererObject : (Material)((AtlasRegion)rendererObject).page.rendererObject; #endif // Populate submesh when material changes. (or when forced to separate by a submeshSeparator) if ((vertexCount > 0 && lastMaterial.GetInstanceID() != material.GetInstanceID()) || (submeshSeparatorSlotsCount > 0 && submeshSeparatorSlots.Contains(slot))) { workingSubmeshArguments.Add( new MeshState.AddSubmeshArguments { material = lastMaterial, startSlot = submeshStartSlotIndex, endSlot = i, triangleCount = submeshTriangleCount, firstVertex = submeshFirstVertex, isLastSubmesh = false } ); submeshTriangleCount = 0; submeshFirstVertex = vertexCount; submeshStartSlotIndex = i; } lastMaterial = material; submeshTriangleCount += attachmentTriangleCount; vertexCount += attachmentVertexCount; } workingSubmeshArguments.Add( new MeshState.AddSubmeshArguments { material = lastMaterial, startSlot = submeshStartSlotIndex, endSlot = drawOrderCount, triangleCount = submeshTriangleCount, firstVertex = submeshFirstVertex, isLastSubmesh = true } ); mustUpdateMeshStructure = mustUpdateMeshStructure || this.sharedMaterials.Length != workingSubmeshArguments.Count || // Material array changed in size CheckIfMustUpdateMeshStructure(workingSubmeshArguments); // Submesh Argument Array changed. // CheckIfMustUpdateMaterialArray (workingMaterials, sharedMaterials) if (!mustUpdateMeshStructure) { // Narrow phase material array check. var workingMaterials = workingSubmeshArguments.Items; for (int i = 0, n = sharedMaterials.Length; i < n; i++) { if (this.sharedMaterials[i] != workingMaterials[i].material) // Bounds check is implied above. { mustUpdateMeshStructure = true; break; } } } // NOT ELSE if (mustUpdateMeshStructure) { this.submeshMaterials.Clear(); var workingSubmeshArgumentsItems = workingSubmeshArguments.Items; for (int i = 0, n = workingSubmeshArguments.Count; i < n; i++) { AddSubmesh(workingSubmeshArgumentsItems[i], workingFlips); } // Set materials. if (submeshMaterials.Count == sharedMaterials.Length) { submeshMaterials.CopyTo(sharedMaterials); } else { sharedMaterials = submeshMaterials.ToArray(); } meshRenderer.sharedMaterials = sharedMaterials; } // Ensure mesh data is the right size. Vector3[] vertices = this.vertices; bool newTriangles = vertexCount > vertices.Length; if (newTriangles) { // Not enough vertices, increase size. this.vertices = vertices = new Vector3[vertexCount]; this.colors = new Color32[vertexCount]; this.uvs = new Vector2[vertexCount]; mesh1.Clear(); mesh2.Clear(); meshState.stateMesh1.requiresUpdate = true; meshState.stateMesh2.requiresUpdate = true; } else { // Too many vertices, zero the extra. Vector3 zero = Vector3.zero; for (int i = vertexCount, n = meshState.vertexCount; i < n; i++) { vertices[i] = zero; } } meshState.vertexCount = vertexCount; // Setup mesh. float zSpacing = this.zSpacing; float[] tempVertices = this.tempVertices; Vector2[] uvs = this.uvs; Color32[] colors = this.colors; int vertexIndex = 0; Color32 color; float a = skeleton.a * 255, r = skeleton.r, g = skeleton.g, b = skeleton.b; 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 i = 0; do { Slot slot = drawOrderItems[i]; Attachment attachment = slot.attachment; RegionAttachment regionAttachment = attachment as RegionAttachment; if (regionAttachment != null) { regionAttachment.ComputeWorldVertices(slot.bone, tempVertices); float z = i * zSpacing; float x1 = tempVertices[RegionAttachment.X1], y1 = tempVertices[RegionAttachment.Y1]; float x2 = tempVertices[RegionAttachment.X2], y2 = tempVertices[RegionAttachment.Y2]; float x3 = tempVertices[RegionAttachment.X3], y3 = tempVertices[RegionAttachment.Y3]; float x4 = tempVertices[RegionAttachment.X4], y4 = tempVertices[RegionAttachment.Y4]; vertices[vertexIndex].x = x1; vertices[vertexIndex].y = y1; vertices[vertexIndex].z = z; vertices[vertexIndex + 1].x = x4; vertices[vertexIndex + 1].y = y4; vertices[vertexIndex + 1].z = z; vertices[vertexIndex + 2].x = x2; vertices[vertexIndex + 2].y = y2; vertices[vertexIndex + 2].z = z; vertices[vertexIndex + 3].x = x3; vertices[vertexIndex + 3].y = y3; vertices[vertexIndex + 3].z = z; color.a = (byte)(a * slot.a * regionAttachment.a); color.r = (byte)(r * slot.r * regionAttachment.r * color.a); color.g = (byte)(g * slot.g * regionAttachment.g * color.a); color.b = (byte)(b * slot.b * regionAttachment.b * color.a); if (slot.data.blendMode == BlendMode.additive) { color.a = 0; } colors[vertexIndex] = color; colors[vertexIndex + 1] = color; colors[vertexIndex + 2] = color; colors[vertexIndex + 3] = color; float[] regionUVs = regionAttachment.uvs; uvs[vertexIndex].x = regionUVs[RegionAttachment.X1]; uvs[vertexIndex].y = regionUVs[RegionAttachment.Y1]; uvs[vertexIndex + 1].x = regionUVs[RegionAttachment.X4]; uvs[vertexIndex + 1].y = regionUVs[RegionAttachment.Y4]; uvs[vertexIndex + 2].x = regionUVs[RegionAttachment.X2]; uvs[vertexIndex + 2].y = regionUVs[RegionAttachment.Y2]; uvs[vertexIndex + 3].x = regionUVs[RegionAttachment.X3]; uvs[vertexIndex + 3].y = regionUVs[RegionAttachment.Y3]; // Calculate min/max X if (x1 < meshBoundsMin.x) { meshBoundsMin.x = x1; } else if (x1 > meshBoundsMax.x) { meshBoundsMax.x = x1; } if (x2 < meshBoundsMin.x) { meshBoundsMin.x = x2; } else if (x2 > meshBoundsMax.x) { meshBoundsMax.x = x2; } if (x3 < meshBoundsMin.x) { meshBoundsMin.x = x3; } else if (x3 > meshBoundsMax.x) { meshBoundsMax.x = x3; } if (x4 < meshBoundsMin.x) { meshBoundsMin.x = x4; } else if (x4 > meshBoundsMax.x) { meshBoundsMax.x = x4; } // Calculate min/max Y if (y1 < meshBoundsMin.y) { meshBoundsMin.y = y1; } else if (y1 > meshBoundsMax.y) { meshBoundsMax.y = y1; } if (y2 < meshBoundsMin.y) { meshBoundsMin.y = y2; } else if (y2 > meshBoundsMax.y) { meshBoundsMax.y = y2; } if (y3 < meshBoundsMin.y) { meshBoundsMin.y = y3; } else if (y3 > meshBoundsMax.y) { meshBoundsMax.y = y3; } if (y4 < meshBoundsMin.y) { meshBoundsMin.y = y4; } else if (y4 > meshBoundsMax.y) { meshBoundsMax.y = y4; } vertexIndex += 4; } else { if (!renderMeshes) { continue; } MeshAttachment meshAttachment = attachment as MeshAttachment; if (meshAttachment != null) { int meshVertexCount = meshAttachment.vertices.Length; if (tempVertices.Length < meshVertexCount) { this.tempVertices = tempVertices = new float[meshVertexCount]; } meshAttachment.ComputeWorldVertices(slot, tempVertices); color.a = (byte)(a * slot.a * meshAttachment.a); color.r = (byte)(r * slot.r * meshAttachment.r * color.a); color.g = (byte)(g * slot.g * meshAttachment.g * color.a); color.b = (byte)(b * slot.b * meshAttachment.b * color.a); if (slot.data.blendMode == BlendMode.additive) { color.a = 0; } float[] meshUVs = meshAttachment.uvs; float z = i * zSpacing; for (int ii = 0; ii < meshVertexCount; ii += 2, vertexIndex++) { float x = tempVertices[ii], y = tempVertices[ii + 1]; vertices[vertexIndex].x = x; vertices[vertexIndex].y = y; vertices[vertexIndex].z = z; colors[vertexIndex] = color; uvs[vertexIndex].x = meshUVs[ii]; uvs[vertexIndex].y = meshUVs[ii + 1]; if (x < meshBoundsMin.x) { meshBoundsMin.x = x; } else if (x > meshBoundsMax.x) { meshBoundsMax.x = x; } if (y < meshBoundsMin.y) { meshBoundsMin.y = y; } else if (y > meshBoundsMax.y) { meshBoundsMax.y = y; } } } else { WeightedMeshAttachment weightedMeshAttachment = attachment as WeightedMeshAttachment; if (weightedMeshAttachment != null) { int meshVertexCount = weightedMeshAttachment.uvs.Length; if (tempVertices.Length < meshVertexCount) { this.tempVertices = tempVertices = new float[meshVertexCount]; } weightedMeshAttachment.ComputeWorldVertices(slot, tempVertices); color.a = (byte)(a * slot.a * weightedMeshAttachment.a); color.r = (byte)(r * slot.r * weightedMeshAttachment.r * color.a); color.g = (byte)(g * slot.g * weightedMeshAttachment.g * color.a); color.b = (byte)(b * slot.b * weightedMeshAttachment.b * color.a); if (slot.data.blendMode == BlendMode.additive) { color.a = 0; } float[] meshUVs = weightedMeshAttachment.uvs; float z = i * zSpacing; for (int ii = 0; ii < meshVertexCount; ii += 2, vertexIndex++) { float x = tempVertices[ii], y = tempVertices[ii + 1]; vertices[vertexIndex].x = x; vertices[vertexIndex].y = y; vertices[vertexIndex].z = z; colors[vertexIndex] = color; uvs[vertexIndex].x = meshUVs[ii]; uvs[vertexIndex].y = meshUVs[ii + 1]; if (x < meshBoundsMin.x) { meshBoundsMin.x = x; } else if (x > meshBoundsMax.x) { meshBoundsMax.x = x; } if (y < meshBoundsMin.y) { meshBoundsMin.y = y; } else if (y > meshBoundsMax.y) { meshBoundsMax.y = y; } } } } } } while (++i < drawOrderCount); } // Double buffer mesh. Mesh mesh = useMesh1 ? mesh1 : mesh2; meshFilter.sharedMesh = mesh; mesh.vertices = vertices; mesh.colors32 = colors; mesh.uv = uvs; if (mustUpdateMeshStructure) { int submeshCount = submeshMaterials.Count; mesh.subMeshCount = submeshCount; for (int i = 0; i < submeshCount; ++i) { mesh.SetTriangles(submeshes.Items[i].triangles, i); } // Done updating mesh. storedState.requiresUpdate = false; } Vector3 meshBoundsExtents = meshBoundsMax - meshBoundsMin; Vector3 meshBoundsCenter = meshBoundsMin + meshBoundsExtents * 0.5f; mesh.bounds = new Bounds(meshBoundsCenter, meshBoundsExtents); if (newTriangles && calculateNormals) { Vector3[] normals = new Vector3[vertexCount]; Vector3 normal = new Vector3(0, 0, -1); for (int i = 0; i < vertexCount; i++) { normals[i] = normal; } (useMesh1 ? mesh2 : mesh1).vertices = vertices; // Set other mesh vertices. mesh1.normals = normals; mesh2.normals = normals; if (calculateTangents) { Vector4[] tangents = new Vector4[vertexCount]; Vector4 tangent = new Vector4(1, 0, 0, -1); for (int i = 0; i < vertexCount; i++) { tangents[i] = tangent; } mesh1.tangents = tangents; mesh2.tangents = tangents; } } // Update previous state storedState.immutableTriangles = immutableTriangles; storedAttachments.Clear(true); storedAttachments.GrowIfNeeded(workingAttachments.Capacity); storedAttachments.Count = workingAttachments.Count; workingAttachments.CopyTo(storedAttachments.Items); storedFlips.GrowIfNeeded(workingFlips.Capacity); storedFlips.Count = workingFlips.Count; workingFlips.CopyTo(storedFlips.Items); storedState.addSubmeshArguments.GrowIfNeeded(workingSubmeshArguments.Capacity); storedState.addSubmeshArguments.Count = workingSubmeshArguments.Count; workingSubmeshArguments.CopyTo(storedState.addSubmeshArguments.Items); // Submesh Renderers if (submeshRenderers.Length > 0) { for (int i = 0; i < submeshRenderers.Length; i++) { SkeletonUtilitySubmeshRenderer submeshRenderer = submeshRenderers[i]; if (submeshRenderer.submeshIndex < sharedMaterials.Length) { submeshRenderer.SetMesh(meshRenderer, useMesh1 ? mesh1 : mesh2, sharedMaterials[submeshRenderer.submeshIndex]); } else { submeshRenderer.GetComponent <Renderer>().enabled = false; } } } useMesh1 = !useMesh1; }
// SkinnedMeshAttachment protected void AddAttachment(Slot slot, WeightedMeshAttachment attachment) { var tempVertices = this.tempVertices; float[] meshUVs = attachment.uvs; int meshVertexCount = attachment.uvs.Length; if (tempVertices.Length < meshVertexCount) this.tempVertices = tempVertices = new float[meshVertexCount]; attachment.ComputeWorldVertices(slot, tempVertices); Color color = skeletonColor; color.r = color.r * attachment.r * slot.r; color.g = color.g * attachment.g * slot.g; color.b = color.b * attachment.b * slot.b; color.a = color.a * attachment.a * slot.a; if (premultiplyAlpha) { color.r *= color.a; color.g *= color.a; color.b *= color.a; if (slot.data.blendMode == BlendMode.additive) color.a = 0; } int fv = positions.Count; // first vertex index for (int ii = 0; ii < meshVertexCount; ii += 2) AddVert(new Vector3(tempVertices[ii], tempVertices[ii + 1]) * scale, color, new Vector2(meshUVs[ii], meshUVs[ii + 1])); var attachmentTriangles = attachment.triangles; for (int ii = 0, n = attachmentTriangles.Length; ii < n; ii++) indices.Add(attachmentTriangles[ii] + fv); }