static List <ProBuilderMesh> CombineToNewMeshes(IEnumerable <ProBuilderMesh> meshes)
        {
            if (meshes == null)
            {
                throw new ArgumentNullException("meshes");
            }

            if (!meshes.Any() || meshes.Count() < 2)
            {
                return(null);
            }

            var vertices       = new List <Vertex>();
            var faces          = new List <Face>();
            var autoUvFaces    = new List <Face>();
            var sharedVertices = new List <SharedVertex>();
            var sharedTextures = new List <SharedVertex>();
            int offset         = 0;
            var materialMap    = new List <Material>();

            AccumulateMeshesInfo(
                meshes,
                offset,
                ref vertices,
                ref faces,
                ref autoUvFaces,
                ref sharedVertices,
                ref sharedTextures,
                ref materialMap
                );

            var res   = SplitByMaxVertexCount(vertices, faces, sharedVertices, sharedTextures);
            var pivot = meshes.LastOrDefault().transform.position;

            foreach (var m in res)
            {
                m.renderer.sharedMaterials = materialMap.ToArray();
                InternalMeshUtility.FilterUnusedSubmeshIndexes(m);
                m.SetPivot(pivot);
                UVEditing.SetAutoAndAlignUnwrapParamsToUVs(m, autoUvFaces);
            }

            return(res);
        }
        /// <summary>
        /// Merge a collection of <see cref="ProBuilderMesh"/> objects to as few meshes as possible. This may result in
        /// more than one mesh due to a max vertex count limit of 65535.
        /// </summary>
        /// <param name="meshes">A collection of meshes to be merged.</param>
        /// <returns>
        /// A list of merged meshes. In most cases this will be a single mesh. However it can be multiple in cases
        /// where the resulting vertex count exceeds the maximum allowable value.
        /// </returns>
        public static List <ProBuilderMesh> Combine(IEnumerable <ProBuilderMesh> meshes)
        {
            if (meshes == null)
            {
                throw new ArgumentNullException("meshes");
            }

            if (!meshes.Any() || meshes.Count() < 2)
            {
                return(null);
            }

            var vertices       = new List <Vertex>();
            var faces          = new List <Face>();
            var autoUvFaces    = new List <Face>();
            var sharedVertices = new List <SharedVertex>();
            var sharedTextures = new List <SharedVertex>();
            int offset         = 0;
            var materialMap    = new List <Material>();

            foreach (var mesh in meshes)
            {
                var meshVertexCount    = mesh.vertexCount;
                var transform          = mesh.transform;
                var meshVertices       = mesh.GetVertices();
                var meshFaces          = mesh.facesInternal;
                var meshSharedVertices = mesh.sharedVertices;
                var meshSharedTextures = mesh.sharedTextures;
                var materials          = mesh.renderer.sharedMaterials;
                var materialCount      = materials.Length;

                for (int i = 0; i < meshVertexCount; i++)
                {
                    vertices.Add(transform.TransformVertex(meshVertices[i]));
                }

                foreach (var face in meshFaces)
                {
                    var newFace = new Face(face);
                    newFace.ShiftIndexes(offset);

                    // prevents uvs from shifting when being converted from local coords to world space
                    if (!newFace.manualUV && !newFace.uv.useWorldSpace)
                    {
                        newFace.manualUV = true;
                        autoUvFaces.Add(newFace);
                    }
                    var material     = materials[Math.Clamp(face.submeshIndex, 0, materialCount - 1)];
                    var submeshIndex = materialMap.IndexOf(material);

                    if (submeshIndex > -1)
                    {
                        newFace.submeshIndex = submeshIndex;
                    }
                    else
                    {
                        if (material == null)
                        {
                            newFace.submeshIndex = 0;
                        }
                        else
                        {
                            newFace.submeshIndex = materialMap.Count;
                            materialMap.Add(material);
                        }
                    }

                    faces.Add(newFace);
                }

                foreach (var sv in meshSharedVertices)
                {
                    var nsv = new SharedVertex(sv);
                    nsv.ShiftIndexes(offset);
                    sharedVertices.Add(nsv);
                }

                foreach (var st in meshSharedTextures)
                {
                    var nst = new SharedVertex(st);
                    nst.ShiftIndexes(offset);
                    sharedTextures.Add(nst);
                }

                offset += meshVertexCount;
            }

            var res   = SplitByMaxVertexCount(vertices, faces, sharedVertices, sharedTextures);
            var pivot = meshes.LastOrDefault().transform.position;

            foreach (var m in res)
            {
                m.renderer.sharedMaterials = materialMap.ToArray();
                InternalMeshUtility.FilterUnusedSubmeshIndexes(m);
                m.SetPivot(pivot);
                UVEditing.SetAutoAndAlignUnwrapParamsToUVs(m, autoUvFaces);
            }

            return(res);
        }
        /// <summary>
        /// Merge a collection of <see cref="ProBuilderMesh"/> objects to as few meshes as possible. It will re-use the meshTarget object as the first
        /// destination for the first <see cref="ProBuilderMesh.maxVertexCount"/> -1 vertices. If the sum of vertices is above <see cref="ProBuilderMesh.maxVertexCount"/> - 1
        /// it will generate new meshes unless there is a single mesh left in which case it will append it to the return list.
        /// </summary>
        /// <param name="meshes">A collection of meshes to be merged. Note: it is expected that meshes includes meshTarget.</param>
        /// <param name="meshTarget">A mesh which will be used as the starting point for merging and which will be kept as reference/target.</param>
        /// <returns>
        /// A list of merged meshes. In most cases this will be a single mesh corresponding to meshTarget. However it can be multiple in cases
        /// where the resulting vertex count exceeds the maximum allowable value.
        /// </returns>
        public static List <ProBuilderMesh> Combine(IEnumerable <ProBuilderMesh> meshes, ProBuilderMesh meshTarget)
        {
            if (meshes == null)
            {
                throw new ArgumentNullException("meshes");
            }

            if (meshTarget == null)
            {
                throw new ArgumentNullException("meshTarget");
            }

            if (!meshes.Any() || meshes.Count() < 2)
            {
                return(null);
            }

            if (!meshes.Contains(meshTarget))
            {
                return(null);
            }

            var vertices        = new List <Vertex>(meshTarget.GetVertices());
            var faces           = new List <Face>(meshTarget.facesInternal);
            var sharedVertices  = new List <SharedVertex>(meshTarget.sharedVertices);
            var sharedTextures  = new List <SharedVertex>(meshTarget.sharedTextures);
            int offset          = meshTarget.vertexCount;
            var materialMap     = new List <Material>(meshTarget.renderer.sharedMaterials);
            var targetTransform = meshTarget.transform;

            var firstMeshContributors     = new List <ProBuilderMesh>();
            var remainderMeshContributors = new List <ProBuilderMesh>();

            var currentMeshVertexCount = offset;

            foreach (var mesh in meshes)
            {
                if (mesh != meshTarget)
                {
                    if (currentMeshVertexCount + mesh.vertexCount < ProBuilderMesh.maxVertexCount)
                    {
                        currentMeshVertexCount += mesh.vertexCount;
                        firstMeshContributors.Add(mesh);
                    }
                    else
                    {
                        remainderMeshContributors.Add(mesh);
                    }
                }
            }

            var autoUvFaces = new List <Face>();

            AccumulateMeshesInfo(
                firstMeshContributors,
                offset,
                ref vertices,
                ref faces,
                ref autoUvFaces,
                ref sharedVertices,
                ref sharedTextures,
                ref materialMap,
                targetTransform
                );

            meshTarget.SetVertices(vertices);
            meshTarget.faces          = faces;
            meshTarget.sharedVertices = sharedVertices;
            meshTarget.sharedVertices = sharedTextures != null?sharedTextures.ToArray() : null;

            meshTarget.renderer.sharedMaterials = materialMap.ToArray();
            meshTarget.ToMesh();
            meshTarget.Refresh();
            UVEditing.SetAutoAndAlignUnwrapParamsToUVs(meshTarget, autoUvFaces);

            var returnedMesh = new List <ProBuilderMesh>()
            {
                meshTarget
            };

            if (remainderMeshContributors.Count > 1)
            {
                var newMeshes = CombineToNewMeshes(remainderMeshContributors);
                foreach (var mesh in newMeshes)
                {
                    returnedMesh.Add(mesh);
                }
            }
            else if (remainderMeshContributors.Count == 1)
            {
                returnedMesh.Add(remainderMeshContributors[0]);
            }

            return(returnedMesh);
        }