Ejemplo n.º 1
0
        /// <summary>
        /// Creates a new mesh.
        /// </summary>
        /// <param name="vertices">The mesh vertices.</param>
        /// <param name="indices">The mesh sub-mesh indices.</param>
        /// <param name="normals">The mesh normals.</param>
        /// <param name="tangents">The mesh tangents.</param>
        /// <param name="colors">The mesh colors.</param>
        /// <param name="boneWeights">The mesh bone-weights.</param>
        /// <param name="uvs2D">The mesh 2D UV sets.</param>
        /// <param name="uvs3D">The mesh 3D UV sets.</param>
        /// <param name="uvs4D">The mesh 4D UV sets.</param>
        /// <param name="bindposes">The mesh bindposes.</param>
        /// <returns>The created mesh.</returns>
        public static Mesh CreateMesh(Vector3[] vertices, int[][] indices, Vector3[] normals, Vector4[] tangents, Color[] colors, BoneWeight[] boneWeights, List <Vector2>[] uvs2D, List <Vector3>[] uvs3D, List <Vector4>[] uvs4D, Matrix4x4[] bindposes, BlendShape[] blendShapes)
        {
            if (vertices == null)
            {
                throw new ArgumentNullException(nameof(vertices));
            }
            else if (indices == null)
            {
                throw new ArgumentNullException(nameof(indices));
            }

            var newMesh      = new Mesh();
            int subMeshCount = indices.Length;

#if UNITY_MESH_INDEXFORMAT_SUPPORT
            IndexFormat indexFormat;
            var         indexMinMax = MeshUtils.GetSubMeshIndexMinMax(indices, out indexFormat);
            newMesh.indexFormat = indexFormat;
#endif

            if (bindposes != null && bindposes.Length > 0)
            {
                newMesh.bindposes = bindposes;
            }

            newMesh.subMeshCount = subMeshCount;
            newMesh.vertices     = vertices;
            if (normals != null && normals.Length > 0)
            {
                newMesh.normals = normals;
            }
            if (tangents != null && tangents.Length > 0)
            {
                newMesh.tangents = tangents;
            }
            if (colors != null && colors.Length > 0)
            {
                newMesh.colors = colors;
            }
            if (boneWeights != null && boneWeights.Length > 0)
            {
                newMesh.boneWeights = boneWeights;
            }

            if (uvs2D != null)
            {
                for (int uvChannel = 0; uvChannel < uvs2D.Length; uvChannel++)
                {
                    if (uvs2D[uvChannel] != null && uvs2D[uvChannel].Count > 0)
                    {
                        newMesh.SetUVs(uvChannel, uvs2D[uvChannel]);
                    }
                }
            }

            if (uvs3D != null)
            {
                for (int uvChannel = 0; uvChannel < uvs3D.Length; uvChannel++)
                {
                    if (uvs3D[uvChannel] != null && uvs3D[uvChannel].Count > 0)
                    {
                        newMesh.SetUVs(uvChannel, uvs3D[uvChannel]);
                    }
                }
            }

            if (uvs4D != null)
            {
                for (int uvChannel = 0; uvChannel < uvs4D.Length; uvChannel++)
                {
                    if (uvs4D[uvChannel] != null && uvs4D[uvChannel].Count > 0)
                    {
                        newMesh.SetUVs(uvChannel, uvs4D[uvChannel]);
                    }
                }
            }

            if (blendShapes != null)
            {
                MeshUtils.ApplyMeshBlendShapes(newMesh, blendShapes);
            }

            for (int subMeshIndex = 0; subMeshIndex < subMeshCount; subMeshIndex++)
            {
                var subMeshTriangles = indices[subMeshIndex];
#if UNITY_MESH_INDEXFORMAT_SUPPORT
                var minMax = indexMinMax[subMeshIndex];
                if (indexFormat == UnityEngine.Rendering.IndexFormat.UInt16 && minMax.y > ushort.MaxValue)
                {
                    int baseVertex = minMax.x;
                    for (int index = 0; index < subMeshTriangles.Length; index++)
                    {
                        subMeshTriangles[index] -= baseVertex;
                    }
                    newMesh.SetTriangles(subMeshTriangles, subMeshIndex, false, baseVertex);
                }
                else
                {
                    newMesh.SetTriangles(subMeshTriangles, subMeshIndex, false, 0);
                }
#else
                newMesh.SetTriangles(subMeshTriangles, subMeshIndex, false);
#endif
            }

            newMesh.RecalculateBounds();
            return(newMesh);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Creates a new mesh.
        /// </summary>
        /// <param name="vertices">The mesh vertices.</param>
        /// <param name="indices">The mesh sub-mesh indices.</param>
        /// <param name="normals">The mesh normals.</param>
        /// <param name="tangents">The mesh tangents.</param>
        /// <param name="colors">The mesh colors.</param>
        /// <param name="boneWeights">The mesh bone-weights.</param>
        /// <param name="uvs2D">The mesh 2D UV sets.</param>
        /// <param name="uvs3D">The mesh 3D UV sets.</param>
        /// <param name="uvs4D">The mesh 4D UV sets.</param>
        /// <param name="bindposes">The mesh bindposes.</param>
        /// <returns>The created mesh.</returns>
        public static Mesh CreateMesh(Vector3[] vertices, int[][] indices, Vector3[] normals, Vector4[] tangents, Color[] colors, BoneWeight[] boneWeights, List <Vector2>[] uvs2D, List <Vector3>[] uvs3D, List <Vector4>[] uvs4D, Matrix4x4[] bindposes, BlendShape[] blendShapes)
        {
            var newMesh      = new Mesh();
            int subMeshCount = indices.Length;

#if UNITY_MESH_INDEXFORMAT_SUPPORT
            IndexFormat indexFormat;
            var         indexMinMax = MeshUtils.GetSubMeshIndexMinMax(indices, out indexFormat);
            newMesh.indexFormat = indexFormat;
#endif

            if (bindposes != null && bindposes.Length > 0)
            {
                newMesh.bindposes = bindposes;
            }

            newMesh.subMeshCount = subMeshCount;
            newMesh.vertices     = vertices;


            // If after assigning normals blendshapes are assigned, then blendshapes do not work correctly
            // In URP and HDRP configurations, so we add blendshapes first and then assign normals
            if (blendShapes != null)
            {
                MeshUtils.ApplyMeshBlendShapes(newMesh, blendShapes);
            }

            if (normals != null && normals.Length > 0)
            {
                newMesh.normals = normals;
            }
            if (tangents != null && tangents.Length > 0)
            {
                newMesh.tangents = tangents;
            }
            if (colors != null && colors.Length > 0)
            {
                newMesh.colors = colors;
            }
            if (boneWeights != null && boneWeights.Length > 0)
            {
                newMesh.boneWeights = boneWeights;
            }

            if (uvs2D != null)
            {
                for (int uvChannel = 0; uvChannel < uvs2D.Length; uvChannel++)
                {
                    if (uvs2D[uvChannel] != null && uvs2D[uvChannel].Count > 0)
                    {
                        newMesh.SetUVs(uvChannel, uvs2D[uvChannel]);
                    }
                }
            }

            if (uvs3D != null)
            {
                for (int uvChannel = 0; uvChannel < uvs3D.Length; uvChannel++)
                {
                    if (uvs3D[uvChannel] != null && uvs3D[uvChannel].Count > 0)
                    {
                        newMesh.SetUVs(uvChannel, uvs3D[uvChannel]);
                    }
                }
            }

            if (uvs4D != null)
            {
                for (int uvChannel = 0; uvChannel < uvs4D.Length; uvChannel++)
                {
                    if (uvs4D[uvChannel] != null && uvs4D[uvChannel].Count > 0)
                    {
                        newMesh.SetUVs(uvChannel, uvs4D[uvChannel]);
                    }
                }
            }

            //if (blendShapes != null)
            //{
            //    MeshUtils.ApplyMeshBlendShapes(newMesh, blendShapes);  //baw did
            //}

            for (int subMeshIndex = 0; subMeshIndex < subMeshCount; subMeshIndex++)
            {
                var subMeshTriangles = indices[subMeshIndex];
#if UNITY_MESH_INDEXFORMAT_SUPPORT
                var minMax = indexMinMax[subMeshIndex];
                if (indexFormat == UnityEngine.Rendering.IndexFormat.UInt16 && minMax.y > ushort.MaxValue)
                {
                    int baseVertex = minMax.x;
                    for (int index = 0; index < subMeshTriangles.Length; index++)
                    {
                        subMeshTriangles[index] -= baseVertex;
                    }
                    newMesh.SetTriangles(subMeshTriangles, subMeshIndex, false, baseVertex);
                }
                else
                {
                    newMesh.SetTriangles(subMeshTriangles, subMeshIndex, false, 0);
                }
#else
                newMesh.SetTriangles(subMeshTriangles, subMeshIndex, false);
#endif
            }

            newMesh.RecalculateBounds();
            return(newMesh);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Generates the LODs and sets up a LOD Group for the specified game object.
        /// </summary>
        /// <param name="gameObject">The game object to set up.</param>
        /// <param name="levels">The LOD levels to set up.</param>
        /// <param name="autoCollectRenderers">If the renderers under the game object and any children should be automatically collected.
        /// Enabling this will ignore any renderers defined under each LOD level.</param>
        /// <param name="simplificationOptions">The mesh simplification options.</param>
        /// <param name="saveAssetsPath">The path to where the generated assets should be saved. Can be null or empty to use the default path.</param>
        /// <returns>The generated LOD Group.</returns>
        public static LODGroup GenerateLODs(GameObject gameObject, LODLevel[] levels, bool autoCollectRenderers, SimplificationOptions simplificationOptions, string saveAssetsPath)
        {
            if (gameObject == null)
            {
                throw new System.ArgumentNullException(nameof(gameObject));
            }
            else if (levels == null)
            {
                throw new System.ArgumentNullException(nameof(levels));
            }

            var transform         = gameObject.transform;
            var existingLodParent = transform.Find(LODParentGameObjectName);

            if (existingLodParent != null)
            {
                throw new System.InvalidOperationException("The game object already appears to have LODs. Please remove them first.");
            }

            var existingLodGroup = gameObject.GetComponent <LODGroup>();

            if (existingLodGroup != null)
            {
                throw new System.InvalidOperationException("The game object already appears to have a LOD Group. Please remove it first.");
            }

            saveAssetsPath = ValidateSaveAssetsPath(saveAssetsPath);

            var lodParentGameObject = new GameObject(LODParentGameObjectName);
            var lodParent           = lodParentGameObject.transform;

            ParentAndResetTransform(lodParent, transform);

            var lodGroup = gameObject.AddComponent <LODGroup>();

            Renderer[] allRenderers = null;
            if (autoCollectRenderers)
            {
                // Collect all enabled renderers under the game object
                allRenderers = GetChildRenderersForLOD(gameObject);
            }

            var renderersToDisable = new List <Renderer>((allRenderers != null ? allRenderers.Length : 10));
            var lods = new LOD[levels.Length];



            // No need to get the Renderers again and again we just cache it for once

            var meshRenderers = (from renderer in allRenderers
                                 let meshFilter = renderer.transform.GetComponent <MeshFilter>()
                                                  where renderer.enabled && renderer as MeshRenderer != null &&
                                                  meshFilter != null &&
                                                  meshFilter.sharedMesh != null
                                                  select renderer as MeshRenderer).ToArray();
            var skinnedMeshRenderers = (from renderer in allRenderers
                                        where renderer.enabled && renderer as SkinnedMeshRenderer != null &&
                                        (renderer as SkinnedMeshRenderer).sharedMesh != null
                                        select renderer as SkinnedMeshRenderer).ToArray();


            List <Mesh> meshesChangedToReadible = new List <Mesh>();
            bool        areAnyCombinedLevels    = false;

            foreach (var lodLevel in levels)
            {
                if (lodLevel.CombineMeshes)
                {
                    areAnyCombinedLevels = true; break;
                }
            }

            // If there is any Lod level with the Combine meshes option selected then
            // we enable Read/Write flag on the original meshes
            if (areAnyCombinedLevels)
            {
                foreach (var renderer in meshRenderers)
                {
                    var meshFilter = renderer.GetComponent <MeshFilter>();

                    if (meshFilter == null || meshFilter.sharedMesh == null)
                    {
                        continue;
                    }

                    if (!meshFilter.sharedMesh.isReadable)
                    {
                        MeshUtils.ChangeMeshReadibility(meshFilter.sharedMesh, true, false);

                        if (meshFilter.sharedMesh.isReadable)
                        {
                            meshesChangedToReadible.Add(meshFilter.sharedMesh);
                        }
                    }
                }

                foreach (var renderer in skinnedMeshRenderers)
                {
                    if (renderer == null || renderer.sharedMesh == null)
                    {
                        continue;
                    }

                    if (!renderer.sharedMesh.isReadable)
                    {
                        MeshUtils.ChangeMeshReadibility(renderer.sharedMesh, true, false);

                        if (renderer.sharedMesh.isReadable)
                        {
                            meshesChangedToReadible.Add(renderer.sharedMesh);
                        }
                    }
                }
            }


            System.Exception exception = null;

            try
            {
                for (int levelIndex = 0; levelIndex < levels.Length; levelIndex++)
                {
                    var level           = levels[levelIndex];
                    var levelGameObject = new GameObject(string.Format("Level{0:00}", levelIndex));
                    var levelTransform  = levelGameObject.transform;
                    ParentAndResetTransform(levelTransform, lodParent);

                    Renderer[] originalLevelRenderers = allRenderers ?? level.Renderers;
                    var        levelRenderers         = new List <Renderer>((originalLevelRenderers != null ? originalLevelRenderers.Length : 0));

                    if (originalLevelRenderers != null && originalLevelRenderers.Length > 0)
                    {
                        StaticRenderer[]  staticRenderers;
                        SkinnedRenderer[] skinnedRenderers;
                        if (level.CombineMeshes)
                        {
                            staticRenderers  = CombineStaticMeshes(transform, levelIndex, meshRenderers);
                            skinnedRenderers = CombineSkinnedMeshes(transform, levelIndex, skinnedMeshRenderers);
                        }
                        else
                        {
                            staticRenderers  = GetStaticRenderers(meshRenderers);
                            skinnedRenderers = GetSkinnedRenderers(skinnedMeshRenderers);
                        }

                        if (staticRenderers != null)
                        {
                            for (int rendererIndex = 0; rendererIndex < staticRenderers.Length; rendererIndex++)
                            {
                                var renderer = staticRenderers[rendererIndex];
                                var mesh     = renderer.mesh;

                                // Simplify the mesh if necessary
                                if (level.Quality < 1f)
                                {
                                    mesh = SimplifyMesh(mesh, level.Quality, simplificationOptions);
                                    SaveLODMeshAsset(mesh, gameObject.name, renderer.name, levelIndex, renderer.mesh.name, saveAssetsPath);

                                    if (renderer.isNewMesh)
                                    {
                                        DestroyObject(renderer.mesh);
                                        renderer.mesh = null;
                                    }
                                }

                                string rendererName  = string.Format("{0:000}_static_{1}", rendererIndex, renderer.name);
                                var    levelRenderer = CreateLevelRenderer(rendererName, levelTransform, renderer.transform, mesh, renderer.materials, ref level);
                                levelRenderers.Add(levelRenderer);
                            }
                        }

                        if (skinnedRenderers != null)
                        {
                            for (int rendererIndex = 0; rendererIndex < skinnedRenderers.Length; rendererIndex++)
                            {
                                var renderer = skinnedRenderers[rendererIndex];
                                var mesh     = renderer.mesh;

                                // Simplify the mesh if necessary
                                if (level.Quality < 1f)
                                {
                                    mesh = SimplifyMesh(mesh, level.Quality, simplificationOptions);
                                    SaveLODMeshAsset(mesh, gameObject.name, renderer.name, levelIndex, renderer.mesh.name, saveAssetsPath);

                                    if (renderer.isNewMesh)
                                    {
                                        DestroyObject(renderer.mesh);
                                        renderer.mesh = null;
                                    }
                                }

                                string rendererName  = string.Format("{0:000}_skinned_{1}", rendererIndex, renderer.name);
                                var    levelRenderer = CreateSkinnedLevelRenderer(rendererName, levelTransform, renderer.transform, mesh, renderer.materials, renderer.rootBone, renderer.bones, ref level);
                                levelRenderers.Add(levelRenderer);
                            }
                        }
                    }

                    foreach (var renderer in originalLevelRenderers)
                    {
                        if (!renderersToDisable.Contains(renderer))
                        {
                            renderersToDisable.Add(renderer);
                        }
                    }

                    lods[levelIndex] = new LOD(level.ScreenRelativeTransitionHeight, levelRenderers.ToArray());
                }
            }

            catch (System.Exception ex)
            {
                exception = ex;
            }


            //Restore meshes Read/Write flag
            foreach (var mesh in meshesChangedToReadible)
            {
                Debug.LogWarning($"Mesh \"{mesh.name}\" was not readible so we marked it readible for the mesh combining process to complete and changed it back to non-readible after completion. This process can slow down LOD generation. You may want to mark this mesh Read/Write enabled in the model import settings, so that next time LOD generation on this model can be faster.");
                MeshUtils.ChangeMeshReadibility(mesh, false, false);
            }


            if (exception != null)
            {
                throw exception;
            }



            CreateBackup(gameObject, renderersToDisable.ToArray());

            foreach (var renderer in renderersToDisable)
            {
                renderer.enabled = false;
            }

            lodGroup.animateCrossFading = false;
            lodGroup.SetLODs(lods);
            return(lodGroup);
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Combines an array of meshes into a single mesh.
        /// </summary>
        /// <param name="meshes">The array of meshes to combine.</param>
        /// <param name="transforms">The array of transforms for the meshes.</param>
        /// <param name="materials">The array of materials for each mesh to combine.</param>
        /// <param name="bones">The array of bones for each mesh to combine.</param>
        /// <param name="resultMaterials">The resulting materials for the combined mesh.</param>
        /// <param name="resultBones">The resulting bones for the combined mesh.</param>
        /// <returns>The combined mesh.</returns>
        public static Mesh CombineMeshes(Mesh[] meshes, Matrix4x4[] transforms, Material[][] materials, Transform[][] bones, out Material[] resultMaterials, out Transform[] resultBones)
        {
            if (meshes == null)
            {
                throw new System.ArgumentNullException(nameof(meshes));
            }
            else if (transforms == null)
            {
                throw new System.ArgumentNullException(nameof(transforms));
            }
            else if (materials == null)
            {
                throw new System.ArgumentNullException(nameof(materials));
            }
            else if (transforms.Length != meshes.Length)
            {
                throw new System.ArgumentException("The array of transforms doesn't have the same length as the array of meshes.", nameof(transforms));
            }
            else if (materials.Length != meshes.Length)
            {
                throw new System.ArgumentException("The array of materials doesn't have the same length as the array of meshes.", nameof(materials));
            }
            else if (bones != null && bones.Length != meshes.Length)
            {
                throw new System.ArgumentException("The array of bones doesn't have the same length as the array of meshes.", nameof(bones));
            }

            int totalVertexCount  = 0;
            int totalSubMeshCount = 0;

            for (int meshIndex = 0; meshIndex < meshes.Length; meshIndex++)
            {
                var mesh = meshes[meshIndex];
                if (mesh == null)
                {
                    throw new System.ArgumentException(string.Format("The mesh at index {0} is null.", meshIndex), nameof(meshes));
                }
                else if (!mesh.isReadable)
                {
                    throw new System.ArgumentException(string.Format("The mesh at index {0} is not readable.", meshIndex), nameof(meshes));
                }

                totalVertexCount  += mesh.vertexCount;
                totalSubMeshCount += mesh.subMeshCount;

                // Validate the mesh materials
                var meshMaterials = materials[meshIndex];
                if (meshMaterials == null)
                {
                    throw new System.ArgumentException(string.Format("The materials for mesh at index {0} is null.", meshIndex), nameof(materials));
                }
                else if (meshMaterials.Length != mesh.subMeshCount)
                {
                    throw new System.ArgumentException(string.Format("The materials for mesh at index {0} doesn't match the submesh count ({1} != {2}).", meshIndex, meshMaterials.Length, mesh.subMeshCount), nameof(materials));
                }

                for (int materialIndex = 0; materialIndex < meshMaterials.Length; materialIndex++)
                {
                    if (meshMaterials[materialIndex] == null)
                    {
                        throw new System.ArgumentException(string.Format("The material at index {0} for mesh at index {1} is null.", materialIndex, meshIndex), nameof(materials));
                    }
                }

                // Validate the mesh bones
                if (bones != null)
                {
                    var meshBones = bones[meshIndex];
                    if (meshBones == null)
                    {
                        throw new System.ArgumentException(string.Format("The bones for mesh at index {0} is null.", meshIndex), nameof(meshBones));
                    }

                    for (int boneIndex = 0; boneIndex < meshBones.Length; boneIndex++)
                    {
                        if (meshBones[boneIndex] == null)
                        {
                            throw new System.ArgumentException(string.Format("The bone at index {0} for mesh at index {1} is null.", boneIndex, meshIndex), nameof(meshBones));
                        }
                    }
                }
            }

            var               combinedVertices    = new List <Vector3>(totalVertexCount);
            var               combinedIndices     = new List <int[]>(totalSubMeshCount);
            List <Vector3>    combinedNormals     = null;
            List <Vector4>    combinedTangents    = null;
            List <Color>      combinedColors      = null;
            List <BoneWeight> combinedBoneWeights = null;
            var               combinedUVs         = new List <Vector4> [MeshUtils.UVChannelCount];

            List <Matrix4x4> usedBindposes = null;
            List <Transform> usedBones     = null;
            var usedMaterials = new List <Material>(totalSubMeshCount);
            var materialMap   = new Dictionary <Material, int>(totalSubMeshCount);

            int currentVertexCount = 0;

            for (int meshIndex = 0; meshIndex < meshes.Length; meshIndex++)
            {
                var mesh          = meshes[meshIndex];
                var meshTransform = transforms[meshIndex];
                var meshMaterials = materials[meshIndex];
                var meshBones     = (bones != null ? bones[meshIndex] : null);

                int subMeshCount    = mesh.subMeshCount;
                int meshVertexCount = mesh.vertexCount;
                var meshVertices    = mesh.vertices;
                var meshNormals     = mesh.normals;
                var meshTangents    = mesh.tangents;
                var meshUVs         = MeshUtils.GetMeshUVs(mesh);
                var meshColors      = mesh.colors;
                var meshBoneWeights = mesh.boneWeights;
                var meshBindposes   = mesh.bindposes;

                // Transform vertices with bones to keep only one bindpose
                if (meshBones != null && meshBoneWeights != null && meshBoneWeights.Length > 0 && meshBindposes != null && meshBindposes.Length > 0 && meshBones.Length == meshBindposes.Length)
                {
                    if (usedBindposes == null)
                    {
                        usedBindposes = new List <Matrix4x4>(meshBindposes);
                        usedBones     = new List <Transform>(meshBones);
                    }

                    bool  bindPoseMismatch = false;
                    int[] boneIndices      = new int[meshBones.Length];
                    for (int i = 0; i < meshBones.Length; i++)
                    {
                        int usedBoneIndex = usedBones.IndexOf(meshBones[i]);
                        if (usedBoneIndex == -1)
                        {
                            usedBoneIndex = usedBones.Count;
                            usedBones.Add(meshBones[i]);
                            usedBindposes.Add(meshBindposes[i]);
                        }
                        else
                        {
                            if (meshBindposes[i] != usedBindposes[usedBoneIndex])
                            {
                                bindPoseMismatch = true;
                            }
                        }
                        boneIndices[i] = usedBoneIndex;
                    }

                    // If any bindpose is mismatching, we correct it first
                    if (bindPoseMismatch)
                    {
                        var correctedBindposes = new Matrix4x4[meshBindposes.Length];
                        for (int i = 0; i < meshBindposes.Length; i++)
                        {
                            int usedBoneIndex = boneIndices[i];
                            correctedBindposes[i] = usedBindposes[usedBoneIndex];
                        }
                        TransformVertices(meshVertices, meshBoneWeights, meshBindposes, correctedBindposes);
                    }

                    // Then we remap the bones
                    RemapBones(meshBoneWeights, boneIndices);
                }

                // Transforms the vertices, normals and tangents using the mesh transform
                TransformVertices(meshVertices, ref meshTransform);
                TransformNormals(meshNormals, ref meshTransform);
                TransformTangents(meshTangents, ref meshTransform);

                // Copy vertex positions & attributes
                CopyVertexPositions(combinedVertices, meshVertices);
                CopyVertexAttributes(ref combinedNormals, meshNormals, currentVertexCount, meshVertexCount, totalVertexCount, new Vector3(1f, 0f, 0f));
                CopyVertexAttributes(ref combinedTangents, meshTangents, currentVertexCount, meshVertexCount, totalVertexCount, new Vector4(0f, 0f, 1f, 1f));
                CopyVertexAttributes(ref combinedColors, meshColors, currentVertexCount, meshVertexCount, totalVertexCount, new Color(1f, 1f, 1f, 1f));
                CopyVertexAttributes(ref combinedBoneWeights, meshBoneWeights, currentVertexCount, meshVertexCount, totalVertexCount, new BoneWeight());

                for (int channel = 0; channel < meshUVs.Length; channel++)
                {
                    CopyVertexAttributes(ref combinedUVs[channel], meshUVs[channel], currentVertexCount, meshVertexCount, totalVertexCount, new Vector4(0f, 0f, 0f, 0f));
                }

                for (int subMeshIndex = 0; subMeshIndex < subMeshCount; subMeshIndex++)
                {
                    var subMeshMaterial = meshMaterials[subMeshIndex];
#if UNITY_MESH_INDEXFORMAT_SUPPORT
                    var subMeshIndices = mesh.GetTriangles(subMeshIndex, true);
#else
                    var subMeshIndices = mesh.GetTriangles(subMeshIndex);
#endif

                    if (currentVertexCount > 0)
                    {
                        for (int index = 0; index < subMeshIndices.Length; index++)
                        {
                            subMeshIndices[index] += currentVertexCount;
                        }
                    }

                    int existingSubMeshIndex;
                    if (materialMap.TryGetValue(subMeshMaterial, out existingSubMeshIndex))
                    {
                        combinedIndices[existingSubMeshIndex] = MergeArrays(combinedIndices[existingSubMeshIndex], subMeshIndices);
                    }
                    else
                    {
                        int materialIndex = combinedIndices.Count;
                        materialMap.Add(subMeshMaterial, materialIndex);
                        usedMaterials.Add(subMeshMaterial);
                        combinedIndices.Add(subMeshIndices);
                    }
                }

                currentVertexCount += meshVertexCount;
            }

            var resultVertices    = combinedVertices.ToArray();
            var resultIndices     = combinedIndices.ToArray();
            var resultNormals     = (combinedNormals != null ? combinedNormals.ToArray() : null);
            var resultTangents    = (combinedTangents != null ? combinedTangents.ToArray() : null);
            var resultColors      = (combinedColors != null ? combinedColors.ToArray() : null);
            var resultBoneWeights = (combinedBoneWeights != null ? combinedBoneWeights.ToArray() : null);
            var resultUVs         = combinedUVs.ToArray();
            var resultBindposes   = (usedBindposes != null ? usedBindposes.ToArray() : null);
            resultMaterials = usedMaterials.ToArray();
            resultBones     = (usedBones != null ? usedBones.ToArray() : null);
            return(MeshUtils.CreateMesh(resultVertices, resultIndices, resultNormals, resultTangents, resultColors, resultBoneWeights, resultUVs, resultBindposes, null));
        }