public static UMesh DecimateMeshBasic(Mesh sourceMesh, UMatrix transform, float quality, bool recalculateNormals, DecimationAlgorithm.StatusReportCallback statusCallback = null)
        {
            var algorithm = MeshDecimation.CreateAlgorithm(Algorithm.FastQuadraticMesh);

            algorithm.MaxVertexCount = ushort.MaxValue;
            if (statusCallback != null)
            {
                algorithm.StatusReport += statusCallback;
            }

            int totalTriangleCount  = sourceMesh.Indices.Length / 3;
            int targetTriangleCount = UMath.CeilToInt(totalTriangleCount * quality);

            //var destMesh = MeshDecimation.DecimateMeshLossless( sourceMesh);
            var destMesh = MeshDecimation.DecimateMesh(algorithm, sourceMesh, targetTriangleCount);

            var newMeshVertices = FromSimplifyVertices(destMesh.Vertices);

            if (statusCallback != null)
            {
                algorithm.StatusReport -= statusCallback;
            }

            return(CreateMesh(null, newMeshVertices, destMesh, recalculateNormals));
        }
        /// <summary>
        /// Decimates an array of meshes, and combines them into one mesh.
        /// </summary>
        /// <param name="meshes">The meshes to decimate.</param>
        /// <param name="transforms">The mesh transforms.</param>
        /// <param name="materials">The mesh materials, with submesh materials.</param>
        /// <param name="meshBones">The bones for each mesh.</param>
        /// <param name="quality">The desired quality.</param>
        /// <param name="recalculateNormals">If normals should be recalculated.</param>
        /// <param name="resultMaterials">The resulting materials array.</param>
        /// <param name="mergedBones">The output merged bones.</param>
        /// <param name="statusCallback">The optional status report callback.</param>
        /// <returns>The decimated mesh.</returns>
        public static UMesh DecimateMeshes(UMesh[] meshes, UMatrix[] transforms, UMaterial[][] materials, UTransform[][] meshBones, float quality, bool recalculateNormals, out UMaterial[] resultMaterials, out UTransform[] mergedBones, DecimationAlgorithm.StatusReportCallback statusCallback = null)
        {
            if (meshes == null)
            {
                throw new ArgumentNullException("meshes");
            }
            else if (meshes.Length == 0)
            {
                throw new ArgumentException("You have to simplify at least one mesh.", "meshes");
            }
            else if (transforms == null)
            {
                throw new ArgumentNullException("transforms");
            }
            else if (transforms.Length != meshes.Length)
            {
                throw new ArgumentException("The array of transforms must match the length of the meshes array.", "transforms");
            }
            else if (materials == null)
            {
                throw new ArgumentNullException("materials");
            }
            else if (materials.Length != meshes.Length)
            {
                throw new ArgumentException("If materials are provided, the length of the array must match the length of the meshes array.", "materials");
            }
            else if (meshBones != null && meshBones.Length != meshes.Length)
            {
                throw new ArgumentException("If mesh bones are provided, the length of the array must match the length of the meshes array.", "meshBones");
            }

            int totalVertexCount  = 0;
            int totalSubMeshCount = 0;

            for (int meshIndex = 0; meshIndex < meshes.Length; meshIndex++)
            {
                UMesh mesh = meshes[meshIndex];
                totalVertexCount  += mesh.vertexCount;
                totalSubMeshCount += mesh.subMeshCount;

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

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

            int totalTriangleCount = 0;
            var vertices           = new List <Vector3d>(totalVertexCount);
            var indices            = new List <int[]>(totalSubMeshCount);

            List <Vector3>    normals     = null;
            List <Vector4>    tangents    = null;
            List <Vector2>    uv1         = null;
            List <Vector2>    uv2         = null;
            List <Vector2>    uv3         = null;
            List <Vector2>    uv4         = null;
            List <Vector4>    colors      = null;
            List <BoneWeight> boneWeights = null;

            List <UMatrix>              usedBindposes = null;
            List <UTransform>           usedBones     = null;
            List <UMaterial>            usedMaterials = new List <UMaterial>(totalSubMeshCount);
            Dictionary <UMaterial, int> materialMap   = new Dictionary <UMaterial, int>();
            int currentVertexCount = 0;

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

                int meshVertexCount = mesh.vertexCount;
                var meshVertices    = mesh.vertices;
                var meshNormals     = mesh.normals;
                var meshTangents    = mesh.tangents;
                var meshUV1         = mesh.uv;
                var meshUV2         = mesh.uv2;
                var meshUV3         = mesh.uv3;
                var meshUV4         = mesh.uv4;
                var meshColors      = mesh.colors;
                var meshBoneWeights = mesh.boneWeights;
                var meshBindposes   = mesh.bindposes;

                if (bones != null && meshBoneWeights != null && meshBoneWeights.Length > 0 && meshBindposes != null && meshBindposes.Length > 0 && bones.Length == meshBindposes.Length)
                {
                    if (usedBindposes == null)
                    {
                        usedBindposes = new List <UMatrix>(meshBindposes);
                        usedBones     = new List <UTransform>(bones);
                    }
                    else
                    {
                        bool  bindPoseMismatch = false;
                        int[] boneIndices      = new int[bones.Length];
                        for (int i = 0; i < bones.Length; i++)
                        {
                            int usedBoneIndex = usedBones.IndexOf(bones[i]);
                            if (usedBoneIndex == -1)
                            {
                                usedBoneIndex = usedBones.Count;
                                usedBones.Add(bones[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 UMatrix[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
                TransformVertices(meshVertices, ref transform);

                AddToList(vertices, meshVertices, currentVertexCount, totalVertexCount);
                AddToList(ref normals, meshNormals, currentVertexCount, meshVertexCount, totalVertexCount, new Vector3(1f, 0f, 0f));
                AddToList(ref tangents, meshTangents, currentVertexCount, meshVertexCount, totalVertexCount, new Vector4(0f, 0f, 1f, 1f)); // Is the default value correct?
                AddToList(ref uv1, meshUV1, currentVertexCount, meshVertexCount, totalVertexCount, new Vector2());
                AddToList(ref uv2, meshUV2, currentVertexCount, meshVertexCount, totalVertexCount, new Vector2());
                AddToList(ref uv3, meshUV3, currentVertexCount, meshVertexCount, totalVertexCount, new Vector2());
                AddToList(ref uv4, meshUV4, currentVertexCount, meshVertexCount, totalVertexCount, new Vector2());
                AddToList(ref colors, meshColors, currentVertexCount, meshVertexCount, totalVertexCount);
                AddToList(ref boneWeights, meshBoneWeights, currentVertexCount, meshVertexCount, totalVertexCount);

                int subMeshCount = mesh.subMeshCount;
                for (int subMeshIndex = 0; subMeshIndex < subMeshCount; subMeshIndex++)
                {
                    var   subMeshMaterial = meshMaterials[subMeshIndex];
                    int[] subMeshIndices  = mesh.GetTriangles(subMeshIndex);
                    if (currentVertexCount > 0)
                    {
                        for (int i = 0; i < subMeshIndices.Length; i++)
                        {
                            subMeshIndices[i] += currentVertexCount;
                        }
                    }
                    totalTriangleCount += subMeshIndices.Length / 3;

                    int mergeWithIndex;
                    if (materialMap.TryGetValue(subMeshMaterial, out mergeWithIndex))
                    {
                        int[] currentIndices = indices[mergeWithIndex];
                        indices[mergeWithIndex] = MergeArrays(currentIndices, subMeshIndices);
                    }
                    else
                    {
                        materialMap.Add(subMeshMaterial, indices.Count);
                        usedMaterials.Add(subMeshMaterial);
                        indices.Add(subMeshIndices);
                    }
                }
                currentVertexCount += meshVertices.Length;
            }

            quality = UMath.Clamp01(quality);
            int targetTriangleCount = UMath.CeilToInt(totalTriangleCount * quality);
            var sourceMesh          = new Mesh(vertices.ToArray(), indices.ToArray());

            if (normals != null)
            {
                sourceMesh.Normals = normals.ToArray();
            }
            if (tangents != null)
            {
                sourceMesh.Tangents = tangents.ToArray();
            }
            if (uv1 != null)
            {
                sourceMesh.UV1 = uv1.ToArray();
            }
            if (uv2 != null)
            {
                sourceMesh.UV2 = uv2.ToArray();
            }
            if (uv3 != null)
            {
                sourceMesh.UV3 = uv3.ToArray();
            }
            if (uv4 != null)
            {
                sourceMesh.UV4 = uv4.ToArray();
            }
            if (colors != null)
            {
                sourceMesh.Colors = colors.ToArray();
            }
            if (boneWeights != null)
            {
                sourceMesh.BoneWeights = boneWeights.ToArray();
            }

            var algorithm = MeshDecimation.CreateAlgorithm(Algorithm.Default);

            algorithm.MaxVertexCount = ushort.MaxValue;
            if (statusCallback != null)
            {
                algorithm.StatusReport += statusCallback;
            }

            var destMesh        = MeshDecimation.DecimateMesh(algorithm, sourceMesh, targetTriangleCount);
            var newMeshVertices = FromSimplifyVertices(destMesh.Vertices);

            if (statusCallback != null)
            {
                algorithm.StatusReport -= statusCallback;
            }

            var bindposes = (usedBindposes != null ? usedBindposes.ToArray() : null);

            resultMaterials = usedMaterials.ToArray();
            mergedBones     = (usedBones != null ? usedBones.ToArray() : null);
            return(CreateMesh(bindposes, newMeshVertices, destMesh, recalculateNormals));
        }
        /// <summary>
        /// Decimates a mesh.
        /// </summary>
        /// <param name="mesh">The mesh to decimate.</param>
        /// <param name="transform">The mesh transform.</param>
        /// <param name="quality">The desired quality.</param>
        /// <param name="recalculateNormals">If normals should be recalculated.</param>
        /// <param name="statusCallback">The optional status report callback.</param>
        /// <returns>The decimated mesh.</returns>
        public static UMesh DecimateMesh(UMesh mesh, UMatrix transform, float quality, bool recalculateNormals, DecimationAlgorithm.StatusReportCallback statusCallback = null)
        {
            if (mesh == null)
            {
                throw new ArgumentNullException("mesh");
            }

            int subMeshCount    = mesh.subMeshCount;
            var meshVertices    = mesh.vertices;
            var meshNormals     = mesh.normals;
            var meshTangents    = mesh.tangents;
            var meshUV1         = mesh.uv;
            var meshUV2         = mesh.uv2;
            var meshUV3         = mesh.uv3;
            var meshUV4         = mesh.uv4;
            var meshColors      = mesh.colors;
            var meshBoneWeights = mesh.boneWeights;
            var meshBindposes   = mesh.bindposes;

            int totalTriangleCount = 0;
            var meshIndices        = new int[subMeshCount][];

            for (int i = 0; i < subMeshCount; i++)
            {
                meshIndices[i]      = mesh.GetTriangles(i);
                totalTriangleCount += meshIndices[i].Length / 3;
            }

            // Transforms the vertices
            TransformVertices(meshVertices, ref transform);

            var vertices = ToSimplifyVertices(meshVertices);

            quality = UMath.Clamp01(quality);
            int targetTriangleCount = UMath.CeilToInt(totalTriangleCount * quality);
            var sourceMesh          = new Mesh(vertices, meshIndices);

            if (meshNormals != null && meshNormals.Length > 0)
            {
                sourceMesh.Normals = ToSimplifyVec(meshNormals);
            }
            if (meshTangents != null && meshTangents.Length > 0)
            {
                sourceMesh.Tangents = ToSimplifyVec(meshTangents);
            }
            if (meshUV1 != null && meshUV1.Length > 0)
            {
                sourceMesh.UV1 = ToSimplifyVec(meshUV1);
            }
            if (meshUV2 != null && meshUV2.Length > 0)
            {
                sourceMesh.UV2 = ToSimplifyVec(meshUV2);
            }
            if (meshUV3 != null && meshUV3.Length > 0)
            {
                sourceMesh.UV3 = ToSimplifyVec(meshUV3);
            }
            if (meshUV4 != null && meshUV4.Length > 0)
            {
                sourceMesh.UV4 = ToSimplifyVec(meshUV4);
            }
            if (meshColors != null && meshColors.Length > 0)
            {
                sourceMesh.Colors = ToSimplifyVec(meshColors);
            }
            if (meshBoneWeights != null && meshBoneWeights.Length > 0)
            {
                sourceMesh.BoneWeights = ToSimplifyBoneWeights(meshBoneWeights);
            }

            var algorithm = MeshDecimation.CreateAlgorithm(Algorithm.Default);

            algorithm.MaxVertexCount = ushort.MaxValue;
            if (statusCallback != null)
            {
                algorithm.StatusReport += statusCallback;
            }

            //var destMesh = MeshDecimation.DecimateMeshLossless( sourceMesh);
            var destMesh        = MeshDecimation.DecimateMesh(algorithm, sourceMesh, targetTriangleCount);
            var newMeshVertices = FromSimplifyVertices(destMesh.Vertices);

            if (statusCallback != null)
            {
                algorithm.StatusReport -= statusCallback;
            }

            return(CreateMesh(meshBindposes, newMeshVertices, destMesh, recalculateNormals));
        }