Ejemplo n.º 1
0
 public static void SetColor(this WeightedMeshAttachment attachment, Color color)
 {
     attachment.A = color.a;
     attachment.R = color.r;
     attachment.G = color.g;
     attachment.B = color.b;
 }
Ejemplo n.º 2
0
 public static void SetColor(this WeightedMeshAttachment attachment, Color32 color)
 {
     attachment.A = color.a * ByteToFloat;
     attachment.R = color.r * ByteToFloat;
     attachment.G = color.g * ByteToFloat;
     attachment.B = color.b * ByteToFloat;
 }
        // 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 WeightedMeshAttachment NewWeightedMeshAttachment(Skin skin, String name, String path)
    {
        ProcessSpriteDefinition(path);

        WeightedMeshAttachment mesh = new WeightedMeshAttachment(name);

        mesh.Path                 = path;
        mesh.RendererObject       = material;
        mesh.RegionU              = u;
        mesh.RegionV              = v;
        mesh.RegionU2             = u2;
        mesh.RegionV2             = v2;
        mesh.RegionRotate         = regionRotated;
        mesh.RegionOriginalWidth  = regionOriginalWidth;
        mesh.RegionOriginalHeight = regionOriginalHeight;
        mesh.RegionWidth          = regionWidth;
        mesh.RegionHeight         = regionHeight;
        mesh.RegionOffsetX        = regionOffsetX;
        mesh.RegionOffsetY        = regionOffsetY;
        return(mesh);
    }
Ejemplo n.º 5
0
    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;
    }
Ejemplo n.º 6
0
 public static Color GetColor(this WeightedMeshAttachment a)
 {
     return(new Color(a.r, a.g, a.b, a.a));
 }
        // 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);
        }