Exemplo n.º 1
0
        /// <summary>
        /// Combines a set of meshes into the target mesh.
        /// </summary>
        /// <param name="target">Target.</param>
        /// <param name="sources">Sources.</param>
        /// <param name="blendShapeSettings">BlendShape Settings.</param>
        public static void CombineMeshes(UMAMeshData target, CombineInstance[] sources, UMAData.BlendShapeSettings blendShapeSettings = null)
        {
            if (blendShapeSettings == null)
            {
                blendShapeSettings = new UMAData.BlendShapeSettings();
            }

            int vertexCount             = 0;
            int bindPoseCount           = 0;
            int transformHierarchyCount = 0;
            int blendShapeCount         = 0;

            MeshComponents meshComponents        = MeshComponents.none;
            int            subMeshCount          = FindTargetSubMeshCount(sources);
            var            subMeshTriangleLength = new int[subMeshCount];

            AnalyzeSources(sources, subMeshTriangleLength, ref vertexCount, ref bindPoseCount, ref transformHierarchyCount, ref meshComponents, ref blendShapeCount);

            int[][] submeshTriangles = new int[subMeshCount][];
            for (int i = 0; i < subMeshTriangleLength.Length; i++)
            {
                submeshTriangles[i]      = target.GetSubmeshBuffer(subMeshTriangleLength[i], i);
                subMeshTriangleLength[i] = 0;
            }

            bool has_normals     = (meshComponents & MeshComponents.has_normals) != MeshComponents.none;
            bool has_tangents    = (meshComponents & MeshComponents.has_tangents) != MeshComponents.none;
            bool has_uv          = (meshComponents & MeshComponents.has_uv) != MeshComponents.none;
            bool has_uv2         = (meshComponents & MeshComponents.has_uv2) != MeshComponents.none;
            bool has_uv3         = (meshComponents & MeshComponents.has_uv3) != MeshComponents.none;
            bool has_uv4         = (meshComponents & MeshComponents.has_uv4) != MeshComponents.none;
            bool has_colors32    = (meshComponents & MeshComponents.has_colors32) != MeshComponents.none;
            bool has_blendShapes = (meshComponents & MeshComponents.has_blendShapes) != MeshComponents.none;

            if (blendShapeSettings.ignoreBlendShapes)
            {
                has_blendShapes = false;
            }
            bool has_clothSkinning = (meshComponents & MeshComponents.has_clothSkinning) != MeshComponents.none;

            Vector3[]                  vertices           = EnsureArrayLength(target.vertices, vertexCount);
            BoneWeight[]               boneWeights        = EnsureArrayLength(target.unityBoneWeights, vertexCount);
            Vector3[]                  normals            = has_normals ? EnsureArrayLength(target.normals, vertexCount) : null;
            Vector4[]                  tangents           = has_tangents ? EnsureArrayLength(target.tangents, vertexCount) : null;
            Vector2[]                  uv                 = has_uv ? EnsureArrayLength(target.uv, vertexCount) : null;
            Vector2[]                  uv2                = has_uv2 ? EnsureArrayLength(target.uv2, vertexCount) : null;
            Vector2[]                  uv3                = has_uv3 ? EnsureArrayLength(target.uv3, vertexCount) : null;
            Vector2[]                  uv4                = has_uv4 ? EnsureArrayLength(target.uv4, vertexCount) : null;
            Color32[]                  colors32           = has_colors32 ? EnsureArrayLength(target.colors32, vertexCount) : null;
            UMABlendShape[]            blendShapes        = has_blendShapes ? new UMABlendShape[blendShapeCount] : null;
            UMATransform[]             umaTransforms      = EnsureArrayLength(target.umaBones, transformHierarchyCount);
            ClothSkinningCoefficient[] clothSkinning      = has_clothSkinning ? EnsureArrayLength(target.clothSkinning, vertexCount) : null;
            Dictionary <Vector3, int>  clothVertices      = has_clothSkinning ? new Dictionary <Vector3, int>(vertexCount) : null;
            Dictionary <Vector3, int>  localClothVertices = has_clothSkinning ? new Dictionary <Vector3, int>(vertexCount) : null;

            int boneCount = 0;

            foreach (var source in sources)
            {
                MergeSortedTransforms(umaTransforms, ref boneCount, source.meshData.umaBones);
            }
            int vertexIndex = 0;

            if (bonesCollection == null)
            {
                bonesCollection = new Dictionary <int, BoneIndexEntry>(boneCount);
            }
            else
            {
                bonesCollection.Clear();
            }
            if (bindPoses == null)
            {
                bindPoses = new List <Matrix4x4>(bindPoseCount);
            }
            else
            {
                bindPoses.Clear();
            }
            if (bonesList == null)
            {
                bonesList = new List <int>(boneCount);
            }
            else
            {
                bonesList.Clear();
            }

            int blendShapeIndex = 0;

            foreach (var source in sources)
            {
                int sourceVertexCount = source.meshData.vertices.Length;
                BuildBoneWeights(source.meshData.boneWeights, 0, boneWeights, vertexIndex, sourceVertexCount, source.meshData.boneNameHashes, source.meshData.bindPoses, bonesCollection, bindPoses, bonesList);

                Array.Copy(source.meshData.vertices, 0, vertices, vertexIndex, sourceVertexCount);

                if (has_normals)
                {
                    if (source.meshData.normals != null && source.meshData.normals.Length > 0)
                    {
                        Array.Copy(source.meshData.normals, 0, normals, vertexIndex, sourceVertexCount);
                    }
                    else
                    {
                        FillArray(tangents, vertexIndex, sourceVertexCount, Vector3.zero);
                    }
                }
                if (has_tangents)
                {
                    if (source.meshData.tangents != null && source.meshData.tangents.Length > 0)
                    {
                        Array.Copy(source.meshData.tangents, 0, tangents, vertexIndex, sourceVertexCount);
                    }
                    else
                    {
                        FillArray(tangents, vertexIndex, sourceVertexCount, Vector4.zero);
                    }
                }
                if (has_uv)
                {
                    if (source.meshData.uv != null && source.meshData.uv.Length >= sourceVertexCount)
                    {
                        Array.Copy(source.meshData.uv, 0, uv, vertexIndex, sourceVertexCount);
                    }
                    else
                    {
                        FillArray(uv, vertexIndex, sourceVertexCount, Vector4.zero);
                    }
                }
                if (has_uv2)
                {
                    if (source.meshData.uv2 != null && source.meshData.uv2.Length >= sourceVertexCount)
                    {
                        Array.Copy(source.meshData.uv2, 0, uv2, vertexIndex, sourceVertexCount);
                    }
                    else
                    {
                        FillArray(uv2, vertexIndex, sourceVertexCount, Vector4.zero);
                    }
                }
                if (has_uv3)
                {
                    if (source.meshData.uv3 != null && source.meshData.uv3.Length >= sourceVertexCount)
                    {
                        Array.Copy(source.meshData.uv3, 0, uv3, vertexIndex, sourceVertexCount);
                    }
                    else
                    {
                        FillArray(uv3, vertexIndex, sourceVertexCount, Vector4.zero);
                    }
                }
                if (has_uv4)
                {
                    if (source.meshData.uv4 != null && source.meshData.uv4.Length >= sourceVertexCount)
                    {
                        Array.Copy(source.meshData.uv4, 0, uv4, vertexIndex, sourceVertexCount);
                    }
                    else
                    {
                        FillArray(uv4, vertexIndex, sourceVertexCount, Vector4.zero);
                    }
                }

                if (has_colors32)
                {
                    if (source.meshData.colors32 != null && source.meshData.colors32.Length > 0)
                    {
                        Array.Copy(source.meshData.colors32, 0, colors32, vertexIndex, sourceVertexCount);
                    }
                    else
                    {
                        Color32 white32 = Color.white;
                        FillArray(colors32, vertexIndex, sourceVertexCount, white32);
                    }
                }

                if (has_blendShapes)
                {
                    if (source.meshData.blendShapes != null && source.meshData.blendShapes.Length > 0)
                    {
                        for (int shapeIndex = 0; shapeIndex < source.meshData.blendShapes.Length; shapeIndex++)
                        {
                            #region BlendShape Baking
                            if (blendShapeSettings.bakeBlendShapes != null && blendShapeSettings.bakeBlendShapes.Count > 0)
                            {
                                // If there are names in the bakeBlendShape dictionary and we find them in the meshData blendshape list, then lets bake them instead of adding them.
                                UMABlendShape currentShape = source.meshData.blendShapes[shapeIndex];

                                if (blendShapeSettings.bakeBlendShapes.ContainsKey(currentShape.shapeName))
                                {
                                    float weight = blendShapeSettings.bakeBlendShapes[currentShape.shapeName] * 100.0f;
                                    if (weight <= 0f)
                                    {
                                        continue;                                                   // Baking in nothing, so skip it entirely
                                    }
                                    // Let's find the frame this weight is in
                                    int frameIndex;
                                    int prevIndex;
                                    for (frameIndex = 0; frameIndex < currentShape.frames.Length; frameIndex++)
                                    {
                                        if (currentShape.frames[frameIndex].frameWeight >= weight)
                                        {
                                            break;
                                        }
                                    }

                                    // Let's calculate the weight for the frame we're in
                                    float frameWeight = 1f;
                                    float prevWeight  = 0f;
                                    bool  doLerp      = false;
                                    // Weight is higher than the last frame, shape is over 100%
                                    if (frameIndex >= currentShape.frames.Length)
                                    {
                                        frameIndex  = currentShape.frames.Length - 1;
                                        frameWeight = (weight / currentShape.frames[frameIndex].frameWeight);
                                    }
                                    else if (frameIndex > 0)
                                    {
                                        doLerp      = true;
                                        prevWeight  = currentShape.frames[frameIndex - 1].frameWeight;
                                        frameWeight = ((weight - prevWeight) / (currentShape.frames[frameIndex].frameWeight - prevWeight));
                                        prevWeight  = 1f - frameWeight;
                                    }
                                    else
                                    {
                                        frameWeight = (weight / currentShape.frames[frameIndex].frameWeight);
                                    }
                                    prevIndex = frameIndex - 1;

                                    // The blend shape frames lerp between the deltas of two adjacent frames.
                                    int vertIndex = vertexIndex;
                                    for (int bakeIndex = 0; bakeIndex < currentShape.frames[frameIndex].deltaVertices.Length; bakeIndex++, vertIndex++)
                                    {
                                        // Add the current frame's deltas
                                        vertices[vertIndex] += currentShape.frames[frameIndex].deltaVertices[bakeIndex] * frameWeight;
                                        // Add in the previous frame's deltas
                                        if (doLerp)
                                        {
                                            vertices[vertIndex] += currentShape.frames[prevIndex].deltaVertices[bakeIndex] * prevWeight;
                                        }
                                    }

                                    if (has_normals)
                                    {
                                        vertIndex = vertexIndex;
                                        for (int bakeIndex = 0; bakeIndex < currentShape.frames[frameIndex].deltaNormals.Length; bakeIndex++, vertIndex++)
                                        {
                                            normals[vertIndex] += currentShape.frames[frameIndex].deltaNormals[bakeIndex] * frameWeight;
                                            if (doLerp)
                                            {
                                                normals[vertIndex] += currentShape.frames[prevIndex].deltaNormals[bakeIndex] * prevWeight;
                                            }
                                        }
                                    }

                                    if (has_tangents)
                                    {
                                        vertIndex = vertexIndex;
                                        for (int bakeIndex = 0; bakeIndex < currentShape.frames[frameIndex].deltaTangents.Length; bakeIndex++, vertIndex++)
                                        {
                                            tangents[vertIndex] += (Vector4)currentShape.frames[frameIndex].deltaTangents[bakeIndex] * frameWeight;
                                            if (doLerp)
                                            {
                                                tangents[vertIndex] += (Vector4)currentShape.frames[prevIndex].deltaTangents[bakeIndex] * prevWeight;
                                            }
                                        }
                                    }
                                    continue; // If we bake then don't perform the rest of this interation of the loop.
                                }
                            }
                            #endregion

                            bool nameAlreadyExists = false;
                            int  i = 0;
                            //Probably this would be better with a dictionary
                            for (i = 0; i < blendShapeIndex; i++)
                            {
                                if (blendShapes[i].shapeName == source.meshData.blendShapes[shapeIndex].shapeName)
                                {
                                    nameAlreadyExists = true;
                                    break;
                                }
                            }

                            if (nameAlreadyExists)                            //Lets add the vertices data to the existing blendShape
                            {
                                if (blendShapes[i].frames.Length != source.meshData.blendShapes[shapeIndex].frames.Length)
                                {
                                    Debug.LogError("SkinnedMeshCombiner: mesh blendShape frame counts don't match!");
                                    break;
                                }
                                for (int frameIndex = 0; frameIndex < source.meshData.blendShapes[shapeIndex].frames.Length; frameIndex++)
                                {
                                    Array.Copy(source.meshData.blendShapes[shapeIndex].frames[frameIndex].deltaVertices, 0, blendShapes[i].frames[frameIndex].deltaVertices, vertexIndex, sourceVertexCount);
                                    Array.Copy(source.meshData.blendShapes[shapeIndex].frames[frameIndex].deltaNormals, 0, blendShapes[i].frames[frameIndex].deltaNormals, vertexIndex, sourceVertexCount);
                                    Array.Copy(source.meshData.blendShapes[shapeIndex].frames[frameIndex].deltaTangents, 0, blendShapes[i].frames[frameIndex].deltaTangents, vertexIndex, sourceVertexCount);
                                }
                            }
                            else
                            {
                                blendShapes[blendShapeIndex]           = new UMABlendShape();
                                blendShapes[blendShapeIndex].shapeName = source.meshData.blendShapes[shapeIndex].shapeName;
                                blendShapes[blendShapeIndex].frames    = new UMABlendFrame[source.meshData.blendShapes[shapeIndex].frames.Length];

                                for (int frameIndex = 0; frameIndex < source.meshData.blendShapes[shapeIndex].frames.Length; frameIndex++)
                                {
                                    blendShapes[blendShapeIndex].frames[frameIndex]             = new UMABlendFrame(vertexCount);
                                    blendShapes[blendShapeIndex].frames[frameIndex].frameWeight = source.meshData.blendShapes[shapeIndex].frames[frameIndex].frameWeight;
                                    Array.Copy(source.meshData.blendShapes[shapeIndex].frames[frameIndex].deltaVertices, 0, blendShapes[blendShapeIndex].frames[frameIndex].deltaVertices, vertexIndex, sourceVertexCount);
                                    Array.Copy(source.meshData.blendShapes[shapeIndex].frames[frameIndex].deltaNormals, 0, blendShapes[blendShapeIndex].frames[frameIndex].deltaNormals, vertexIndex, sourceVertexCount);
                                    Array.Copy(source.meshData.blendShapes[shapeIndex].frames[frameIndex].deltaTangents, 0, blendShapes[blendShapeIndex].frames[frameIndex].deltaTangents, vertexIndex, sourceVertexCount);
                                }
                                blendShapeIndex++;
                            }
                        }
                    }
                }
                if (has_clothSkinning)
                {
                    localClothVertices.Clear();
                    if (source.meshData.clothSkinningSerialized != null && source.meshData.clothSkinningSerialized.Length > 0)
                    {
                        for (int i = 0; i < source.meshData.vertexCount; i++)
                        {
                            var vertice = source.meshData.vertices[i];
                            if (!localClothVertices.ContainsKey(vertice))
                            {
                                int localCount = localClothVertices.Count;
                                localClothVertices.Add(vertice, localCount);
                                if (!clothVertices.ContainsKey(vertice))
                                {
                                    ConvertData(ref source.meshData.clothSkinningSerialized[localCount], ref clothSkinning[clothVertices.Count]);
                                    clothVertices.Add(vertice, clothVertices.Count);
                                }
                                else
                                {
                                    ConvertData(ref source.meshData.clothSkinningSerialized[localCount], ref clothSkinning[clothVertices[vertice]]);
                                }
                            }
                        }
                    }
                    else
                    {
                        for (int i = 0; i < source.meshData.vertexCount; i++)
                        {
                            var vertice = source.meshData.vertices[i];
                            if (!clothVertices.ContainsKey(vertice))
                            {
                                clothSkinning[clothVertices.Count].maxDistance             = 0;
                                clothSkinning[clothVertices.Count].collisionSphereDistance = float.MaxValue;
                                clothVertices.Add(vertice, clothVertices.Count);
                                localClothVertices.Add(vertice, clothVertices.Count);
                            }
                        }
                    }
                }

                for (int i = 0; i < source.meshData.subMeshCount; i++)
                {
                    if (source.targetSubmeshIndices[i] >= 0)
                    {
                        int[] subTriangles   = source.meshData.submeshes[i].triangles;
                        int   triangleLength = subTriangles.Length;
                        int   destMesh       = source.targetSubmeshIndices[i];

                        if (source.triangleMask == null)
                        {
                            CopyIntArrayAdd(subTriangles, 0, submeshTriangles[destMesh], subMeshTriangleLength[destMesh], triangleLength, vertexIndex);
                            subMeshTriangleLength[destMesh] += triangleLength;
                        }
                        else
                        {
                            MaskedCopyIntArrayAdd(subTriangles, 0, submeshTriangles[destMesh], subMeshTriangleLength[destMesh], triangleLength, vertexIndex, source.triangleMask[i]);
                            subMeshTriangleLength[destMesh] += (triangleLength - (UMAUtils.GetCardinality(source.triangleMask[i]) * 3));
                        }
                    }
                }

                vertexIndex += sourceVertexCount;
            }

            if (vertexCount != vertexIndex)
            {
                Debug.LogError("Combined vertices size didn't match precomputed value!");
            }

            // fill in new values.
            target.vertexCount      = vertexCount;
            target.vertices         = vertices;
            target.unityBoneWeights = boneWeights;
            target.bindPoses        = bindPoses.ToArray();
            target.normals          = normals;
            target.tangents         = tangents;
            target.uv       = uv;
            target.uv2      = uv2;
            target.uv3      = uv3;
            target.uv4      = uv4;
            target.colors32 = colors32;

            if (has_blendShapes)
            {
                target.blendShapes = blendShapes;
            }

            if (has_clothSkinning)
            {
                Array.Resize(ref clothSkinning, clothVertices.Count);
            }
            target.clothSkinning = clothSkinning;

            target.subMeshCount = subMeshCount;
            target.submeshes    = new SubMeshTriangles[subMeshCount];
            target.umaBones     = umaTransforms;
            target.umaBoneCount = boneCount;
            for (int i = 0; i < subMeshCount; i++)
            {
                target.submeshes[i].triangles = submeshTriangles[i];
            }
            target.boneNameHashes = bonesList.ToArray();
        }
Exemplo n.º 2
0
        /// <summary>
        /// Combines a set of meshes into the target mesh.
        /// </summary>
        /// <param name="target">Target.</param>
        /// <param name="sources">Sources.</param>
        /// <param name="blendShapeSettings">BlendShape Settings.</param>
        public static void CombineMeshes(UMAMeshData target, CombineInstance[] sources, BlendShapeSettings blendShapeSettings = null)
        {
            if (blendShapeSettings == null)
            {
                blendShapeSettings = new BlendShapeSettings();
            }

            int vertexCount             = 0;
            int bindPoseCount           = 0;
            int transformHierarchyCount = 0;
            Dictionary <string, BlendShapeVertexData> blendShapeNames = new Dictionary <string, BlendShapeVertexData>();

            MeshComponents meshComponents        = MeshComponents.none;
            int            subMeshCount          = FindTargetSubMeshCount(sources);
            var            subMeshTriangleLength = new int[subMeshCount];

            AnalyzeSources(sources, subMeshTriangleLength, ref vertexCount, ref bindPoseCount, ref transformHierarchyCount, ref meshComponents);

            if (!blendShapeSettings.ignoreBlendShapes)
            {
                AnalyzeBlendShapeSources(sources, blendShapeSettings, ref meshComponents, out blendShapeNames);
            }

            int[][] submeshTriangles = new int[subMeshCount][];
            for (int i = 0; i < subMeshTriangleLength.Length; i++)
            {
                submeshTriangles[i]      = target.GetSubmeshBuffer(subMeshTriangleLength[i], i);
                subMeshTriangleLength[i] = 0;
            }

            bool has_normals     = (meshComponents & MeshComponents.has_normals) != MeshComponents.none;
            bool has_tangents    = (meshComponents & MeshComponents.has_tangents) != MeshComponents.none;
            bool has_uv          = (meshComponents & MeshComponents.has_uv) != MeshComponents.none;
            bool has_uv2         = (meshComponents & MeshComponents.has_uv2) != MeshComponents.none;
            bool has_uv3         = (meshComponents & MeshComponents.has_uv3) != MeshComponents.none;
            bool has_uv4         = (meshComponents & MeshComponents.has_uv4) != MeshComponents.none;
            bool has_colors32    = (meshComponents & MeshComponents.has_colors32) != MeshComponents.none;
            bool has_blendShapes = (meshComponents & MeshComponents.has_blendShapes) != MeshComponents.none;

            if (blendShapeSettings.ignoreBlendShapes)
            {
                has_blendShapes = false;
            }
            bool has_clothSkinning = (meshComponents & MeshComponents.has_clothSkinning) != MeshComponents.none;

#if UNITY_2019_3_OR_NEWER
            if (nativeBoneWeights.Length < vertexCount)
            {
                if (nativeBoneWeights.IsCreated)
                {
                    nativeBoneWeights.Dispose();
                }

                nativeBoneWeights = new NativeArray <BoneWeight>(vertexCount, Allocator.Persistent);
            }
#else
            BoneWeight[] boneWeights = EnsureArrayLength(target.unityBoneWeights, vertexCount);
#endif
            Vector3[]                  vertices           = EnsureArrayLength(target.vertices, vertexCount);
            Vector3[]                  normals            = has_normals ? EnsureArrayLength(target.normals, vertexCount) : null;
            Vector4[]                  tangents           = has_tangents ? EnsureArrayLength(target.tangents, vertexCount) : null;
            Vector2[]                  uv                 = has_uv ? EnsureArrayLength(target.uv, vertexCount) : null;
            Vector2[]                  uv2                = has_uv2 ? EnsureArrayLength(target.uv2, vertexCount) : null;
            Vector2[]                  uv3                = has_uv3 ? EnsureArrayLength(target.uv3, vertexCount) : null;
            Vector2[]                  uv4                = has_uv4 ? EnsureArrayLength(target.uv4, vertexCount) : null;
            Color32[]                  colors32           = has_colors32 ? EnsureArrayLength(target.colors32, vertexCount) : null;
            UMABlendShape[]            blendShapes        = has_blendShapes ? new UMABlendShape[blendShapeNames.Keys.Count] : null;
            UMATransform[]             umaTransforms      = EnsureArrayLength(target.umaBones, transformHierarchyCount);
            ClothSkinningCoefficient[] clothSkinning      = has_clothSkinning ? EnsureArrayLength(target.clothSkinning, vertexCount) : null;
            Dictionary <Vector3, int>  clothVertices      = has_clothSkinning ? new Dictionary <Vector3, int>(vertexCount) : null;
            Dictionary <Vector3, int>  localClothVertices = has_clothSkinning ? new Dictionary <Vector3, int>(vertexCount) : null;

            InitializeBlendShapeData(ref vertexCount, blendShapeNames, blendShapes);

            int boneCount = 0;
            foreach (var source in sources)
            {
                MergeSortedTransforms(umaTransforms, ref boneCount, source.meshData.umaBones);
            }
            int vertexIndex = 0;

            if (bonesCollection == null)
            {
                bonesCollection = new Dictionary <int, BoneIndexEntry>(boneCount);
            }
            else
            {
                bonesCollection.Clear();
            }
            if (bindPoses == null)
            {
                bindPoses = new List <Matrix4x4>(bindPoseCount);
            }
            else
            {
                bindPoses.Clear();
            }
            if (bonesList == null)
            {
                bonesList = new List <int>(boneCount);
            }
            else
            {
                bonesList.Clear();
            }

            foreach (var source in sources)
            {
                int sourceVertexCount = source.meshData.vertices.Length;
#if UNITY_2019_3_OR_NEWER
                BuildBoneWeights(source.meshData.boneWeights, 0, nativeBoneWeights, vertexIndex, sourceVertexCount, source.meshData.boneNameHashes, source.meshData.bindPoses, bonesCollection, bindPoses, bonesList);
#else
                BuildBoneWeights(source.meshData.boneWeights, 0, boneWeights, vertexIndex, sourceVertexCount, source.meshData.boneNameHashes, source.meshData.bindPoses, bonesCollection, bindPoses, bonesList);
#endif
                Array.Copy(source.meshData.vertices, 0, vertices, vertexIndex, sourceVertexCount);

                if (has_normals)
                {
                    if (source.meshData.normals != null && source.meshData.normals.Length > 0)
                    {
                        Array.Copy(source.meshData.normals, 0, normals, vertexIndex, sourceVertexCount);
                    }
                    else
                    {
                        FillArray(tangents, vertexIndex, sourceVertexCount, Vector3.zero);
                    }
                }
                if (has_tangents)
                {
                    if (source.meshData.tangents != null && source.meshData.tangents.Length > 0)
                    {
                        Array.Copy(source.meshData.tangents, 0, tangents, vertexIndex, sourceVertexCount);
                    }
                    else
                    {
                        FillArray(tangents, vertexIndex, sourceVertexCount, Vector4.zero);
                    }
                }
                if (has_uv)
                {
                    if (source.meshData.uv != null && source.meshData.uv.Length >= sourceVertexCount)
                    {
                        Array.Copy(source.meshData.uv, 0, uv, vertexIndex, sourceVertexCount);
                    }
                    else
                    {
                        FillArray(uv, vertexIndex, sourceVertexCount, Vector4.zero);
                    }
                }
                if (has_uv2)
                {
                    if (source.meshData.uv2 != null && source.meshData.uv2.Length >= sourceVertexCount)
                    {
                        Array.Copy(source.meshData.uv2, 0, uv2, vertexIndex, sourceVertexCount);
                    }
                    else
                    {
                        FillArray(uv2, vertexIndex, sourceVertexCount, Vector4.zero);
                    }
                }
                if (has_uv3)
                {
                    if (source.meshData.uv3 != null && source.meshData.uv3.Length >= sourceVertexCount)
                    {
                        Array.Copy(source.meshData.uv3, 0, uv3, vertexIndex, sourceVertexCount);
                    }
                    else
                    {
                        FillArray(uv3, vertexIndex, sourceVertexCount, Vector4.zero);
                    }
                }
                if (has_uv4)
                {
                    if (source.meshData.uv4 != null && source.meshData.uv4.Length >= sourceVertexCount)
                    {
                        Array.Copy(source.meshData.uv4, 0, uv4, vertexIndex, sourceVertexCount);
                    }
                    else
                    {
                        FillArray(uv4, vertexIndex, sourceVertexCount, Vector4.zero);
                    }
                }

                if (has_colors32)
                {
                    if (source.meshData.colors32 != null && source.meshData.colors32.Length > 0)
                    {
                        Array.Copy(source.meshData.colors32, 0, colors32, vertexIndex, sourceVertexCount);
                    }
                    else
                    {
                        Color32 white32 = Color.white;
                        FillArray(colors32, vertexIndex, sourceVertexCount, white32);
                    }
                }

                if (has_blendShapes)
                {
                    if (source.meshData.blendShapes != null && source.meshData.blendShapes.Length > 0)
                    {
                        int sourceBlendShapeLength = source.meshData.blendShapes.Length;
                        for (int shapeIndex = 0; shapeIndex < sourceBlendShapeLength; shapeIndex++)
                        {
                            string shapeName = source.meshData.blendShapes[shapeIndex].shapeName;

                            //If we aren't loading all blendshapes and we don't find the blendshape name in the list of explicit blendshapes to combine, then skip to the next one.
                            if (!blendShapeSettings.loadAllBlendShapes && !blendShapeSettings.blendShapes.ContainsKey(shapeName))
                            {
                                continue;
                            }

                            #region BlendShape Baking
                            if (BakeBlendShape(blendShapeSettings.blendShapes, source.meshData.blendShapes[shapeIndex], ref vertexIndex, vertices, normals, tangents, has_normals, has_tangents))
                            {
                                continue;                                 //If we baked this blendshape, then continue to the next one and skip adding the regular blendshape.
                            }
                            #endregion

                            //If our dictionary contains the shape name, which it should
                            if (blendShapeNames.ContainsKey(shapeName))
                            {
                                UMABlendShape[] sourceBlendShapes = source.meshData.blendShapes;
                                int             i = blendShapeNames[shapeName].index;

                                if (blendShapes[i].frames.Length != sourceBlendShapes[shapeIndex].frames.Length)
                                {
                                    if (Debug.isDebugBuild)
                                    {
                                        Debug.LogError("SkinnedMeshCombiner: mesh blendShape frame counts don't match!");
                                    }
                                    break;
                                }

                                for (int frameIndex = 0; frameIndex < sourceBlendShapes[shapeIndex].frames.Length; frameIndex++)
                                {
                                    Array.Copy(sourceBlendShapes[shapeIndex].frames[frameIndex].deltaVertices, 0, blendShapes[i].frames[frameIndex].deltaVertices, vertexIndex, sourceVertexCount);

                                    Vector3[] sourceDeltaNormals  = sourceBlendShapes[shapeIndex].frames[frameIndex].deltaNormals;
                                    Vector3[] sourceDeltaTangents = sourceBlendShapes[shapeIndex].frames[frameIndex].deltaTangents;

                                    //if out dictionary says at least one source has normals or tangents and the current source has normals or tangents then copy them.
                                    if (blendShapeNames[shapeName].hasNormals && sourceDeltaNormals.Length > 0)
                                    {
                                        Array.Copy(sourceDeltaNormals, 0, blendShapes[i].frames[frameIndex].deltaNormals, vertexIndex, sourceVertexCount);
                                    }

                                    if (blendShapeNames[shapeName].hasTangents && sourceDeltaTangents.Length > 0)
                                    {
                                        Array.Copy(sourceDeltaTangents, 0, blendShapes[i].frames[frameIndex].deltaTangents, vertexIndex, sourceVertexCount);
                                    }
                                }
                            }
                            else
                            {
                                if (Debug.isDebugBuild)
                                {
                                    Debug.LogError("BlendShape " + shapeName + " not found in dictionary!");
                                }
                            }
                        }
                    }
                }
                if (has_clothSkinning)
                {
                    localClothVertices.Clear();
                    if (source.meshData.clothSkinningSerialized != null && source.meshData.clothSkinningSerialized.Length > 0)
                    {
                        for (int i = 0; i < source.meshData.vertexCount; i++)
                        {
                            var vertice = source.meshData.vertices[i];
                            if (!localClothVertices.ContainsKey(vertice))
                            {
                                int localCount = localClothVertices.Count;
                                localClothVertices.Add(vertice, localCount);
                                if (!clothVertices.ContainsKey(vertice))
                                {
                                    ConvertData(ref source.meshData.clothSkinningSerialized[localCount], ref clothSkinning[clothVertices.Count]);
                                    clothVertices.Add(vertice, clothVertices.Count);
                                }
                                else
                                {
                                    ConvertData(ref source.meshData.clothSkinningSerialized[localCount], ref clothSkinning[clothVertices[vertice]]);
                                }
                            }
                        }
                    }
                    else
                    {
                        for (int i = 0; i < source.meshData.vertexCount; i++)
                        {
                            var vertice = source.meshData.vertices[i];
                            if (!clothVertices.ContainsKey(vertice))
                            {
                                clothSkinning[clothVertices.Count].maxDistance             = 0;
                                clothSkinning[clothVertices.Count].collisionSphereDistance = float.MaxValue;
                                clothVertices.Add(vertice, clothVertices.Count);
                                localClothVertices.Add(vertice, clothVertices.Count);
                            }
                        }
                    }
                }

                for (int i = 0; i < source.meshData.subMeshCount; i++)
                {
                    if (source.targetSubmeshIndices[i] >= 0)
                    {
                        int[] subTriangles   = source.meshData.submeshes[i].triangles;
                        int   triangleLength = subTriangles.Length;
                        int   destMesh       = source.targetSubmeshIndices[i];

                        if (source.triangleMask == null)
                        {
                            CopyIntArrayAdd(subTriangles, 0, submeshTriangles[destMesh], subMeshTriangleLength[destMesh], triangleLength, vertexIndex);
                            subMeshTriangleLength[destMesh] += triangleLength;
                        }
                        else
                        {
                            MaskedCopyIntArrayAdd(subTriangles, 0, submeshTriangles[destMesh], subMeshTriangleLength[destMesh], triangleLength, vertexIndex, source.triangleMask[i]);
                            subMeshTriangleLength[destMesh] += (triangleLength - (UMAUtils.GetCardinality(source.triangleMask[i]) * 3));
                        }
                    }
                }

                vertexIndex += sourceVertexCount;
            }

            if (vertexCount != vertexIndex)
            {
                if (Debug.isDebugBuild)
                {
                    Debug.LogError("Combined vertices size didn't match precomputed value!");
                }
            }

            // fill in new values.
            target.vertexCount = vertexCount;
            target.vertices    = vertices;
#if UNITY_2019_3_OR_NEWER
            target.unityBoneWeights = nativeBoneWeights.GetSubArray(0, vertexCount).ToArray();
#else
            target.unityBoneWeights = boneWeights;
#endif
            target.bindPoses = bindPoses.ToArray();
            target.normals   = normals;
            target.tangents  = tangents;
            target.uv        = uv;
            target.uv2       = uv2;
            target.uv3       = uv3;
            target.uv4       = uv4;
            target.colors32  = colors32;

            if (has_blendShapes)
            {
                target.blendShapes = blendShapes;
            }

            if (has_clothSkinning)
            {
                Array.Resize(ref clothSkinning, clothVertices.Count);
            }
            target.clothSkinning = clothSkinning;

            target.subMeshCount = subMeshCount;
            target.submeshes    = new SubMeshTriangles[subMeshCount];
            target.umaBones     = umaTransforms;
            target.umaBoneCount = boneCount;
            for (int i = 0; i < subMeshCount; i++)
            {
                target.submeshes[i].triangles = submeshTriangles[i];
            }
            target.boneNameHashes = bonesList.ToArray();
        }