Exemple #1
0
        /// <summary>
        /// Method for mesh decimation
        /// </summary>
        /// <param name="meshStore">mesh data</param>
        /// <param name="targetTriangleCount">target triangle count for decimation</param>
        public static void Decimate(ref MeshStore meshStore, int targetTriangleCount)
        {
            // extract origin mesh data and create mesh instance
            Vector3d[] vertices = new Vector3d[meshStore.Vertexes.Count / 3];
            for (int i = 0, j = 0; i < meshStore.Vertexes.Count; i += 3, ++j)
            {
                vertices[j] = new Vector3d(meshStore.Vertexes[i], meshStore.Vertexes[i + 1], meshStore.Vertexes[i + 2]);
            }
            Mesh sourceMesh = new Mesh(vertices, meshStore.Indexes.ToArray());

            // decimate mesh
            var algorithm = MeshDecimation.CreateAlgorithm(Algorithm.Default);

            algorithm.Verbose = true;
            Mesh destMesh = MeshDecimation.DecimateMesh(algorithm, sourceMesh, targetTriangleCount);

            // change meshstore
            meshStore.Indexes = destMesh.Indices.ToList();
            List <double> newVertices = new List <double>();

            foreach (Vector3d vec in destMesh.Vertices)
            {
                newVertices.Add(vec.x);
                newVertices.Add(vec.y);
                newVertices.Add(vec.z);
            }
            meshStore.Vertexes = newVertices;
        }
        public void Simplify(WMesh inputMesh, WMesh outputMesh, float quality)
        {
            var enableLogging = false;
            int totalTriangleCount;
            var sourceMesh          = ToMeshDecimatorMesh(inputMesh, out totalTriangleCount);
            int targetTriangleCount = Mathf.CeilToInt(totalTriangleCount * quality);

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

            DecimationAlgorithm.StatusReportCallback statusCallback = (iteration, tris, currentTris, targetTris) =>
            {
                Debug.LogFormat("Iteration {0}: tris {1} current {2} target {3}", iteration, tris, currentTris, targetTris);
            };

            if (enableLogging)
            {
                algorithm.StatusReport += statusCallback;
            }

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

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

            FromMeshDecimatorMesh(destMesh, false, ref outputMesh);
        }
        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));
        }
Exemple #4
0
        static void Main(string[] args)
        {
            if (args.Length < 2)
            {
                PrintUsage();
                return;
            }

            try
            {
                string sourcePath = Path.GetFullPath(args[0]);
                string destPath   = Path.GetFullPath(args[1]);

                float quality = 0.5f;
                if (args.Length > 2)
                {
                    if (!float.TryParse(args[2], NumberStyles.Float, CultureInfo.InvariantCulture, out quality))
                    {
                        Console.Error.WriteLine("Unable to convert '{0}' into a float.", args[2]);
                        return;
                    }
                }

                quality = MathHelper.Clamp01(quality);
                ObjMesh sourceObjMesh = new ObjMesh();
                sourceObjMesh.ReadFile(sourcePath);
                var sourceVertices = sourceObjMesh.Vertices;
                var sourceIndices  = sourceObjMesh.Indices;
                var sourceMesh     = new Mesh(sourceVertices, sourceIndices);

                int currentTriangleCount = sourceIndices.Length / 3;
                int targetTriangleCount  = (int)Math.Ceiling(currentTriangleCount * quality);
                Console.WriteLine("Input: {0} vertices, {1} triangles (target {2})",
                                  sourceVertices.Length, currentTriangleCount, targetTriangleCount);

                var stopwatch = new System.Diagnostics.Stopwatch();
                stopwatch.Reset();
                stopwatch.Start();

                var algorithm = MeshDecimation.CreateAlgorithm(Algorithm.Default);
                algorithm.Verbose = true;
                Mesh destMesh = MeshDecimation.DecimateMesh(algorithm, sourceMesh, targetTriangleCount);
                stopwatch.Stop();

                var     destVertices = destMesh.Vertices;
                var     destIndices  = destMesh.Indices;
                ObjMesh destObjMesh  = new ObjMesh(destVertices, destIndices);
                destObjMesh.WriteFile(destPath);

                float reduction = (float)(destIndices.Length / 3) / (float)currentTriangleCount;
                float timeTaken = (float)stopwatch.Elapsed.TotalSeconds;
                Console.WriteLine("Output: {0} vertices, {1} triangles ({2} reduction; {3:0.0000} sec)",
                                  destVertices.Length, destIndices.Length / 3, reduction, timeTaken);
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine("Error decimating mesh. Reason: {0}", ex.Message);
            }
        }
    public async void SimplifyObj(string sourcePath, string destPath, float quality)
    {
        simplifyTask = new Task(() =>
        {
            quality = MathHelper.Clamp01(quality);
            ObjMesh sourceObjMesh = new ObjMesh();
            sourceObjMesh.ReadFile(sourcePath);
            var sourceVertices       = sourceObjMesh.Vertices;
            var sourceNormals        = sourceObjMesh.Normals;
            var sourceTexCoords2D    = sourceObjMesh.TexCoords2D;
            var sourceTexCoords3D    = sourceObjMesh.TexCoords3D;
            var sourceSubMeshIndices = sourceObjMesh.SubMeshIndices;

            var sourceMesh     = new MeshDecimator.Mesh(sourceVertices, sourceSubMeshIndices);
            sourceMesh.Normals = sourceNormals;

            if (sourceTexCoords2D != null)
            {
                sourceMesh.SetUVs(0, sourceTexCoords2D);
            }
            else if (sourceTexCoords3D != null)
            {
                sourceMesh.SetUVs(0, sourceTexCoords3D);
            }

            int currentTriangleCount = 0;
            for (int i = 0; i < sourceSubMeshIndices.Length; i++)
            {
                currentTriangleCount += (sourceSubMeshIndices[i].Length / 3);
            }

            int targetTriangleCount = (int)Math.Ceiling(currentTriangleCount * quality);
            string info_1           = string.Format("Input: {0} vertices, {1} triangles (target {2}) \n",
                                                    sourceVertices.Length, currentTriangleCount, targetTriangleCount);

            // Console.WriteLine(info_1);
            simplifyInfo.Invoke(info_1);

            var stopwatch = new System.Diagnostics.Stopwatch();
            stopwatch.Reset();
            stopwatch.Start();

            var algorithm               = MeshDecimation.CreateAlgorithm(Algorithm.Default);
            algorithm.Verbose           = true;
            MeshDecimator.Mesh destMesh = MeshDecimation.DecimateMesh(algorithm, sourceMesh, targetTriangleCount);
            stopwatch.Stop();

            var destVertices = destMesh.Vertices;
            var destNormals  = destMesh.Normals;
            var destIndices  = destMesh.GetSubMeshIndices();

            ObjMesh destObjMesh           = new ObjMesh(destVertices, destIndices);
            destObjMesh.Normals           = destNormals;
            destObjMesh.MaterialLibraries = sourceObjMesh.MaterialLibraries;
            destObjMesh.SubMeshMaterials  = sourceObjMesh.SubMeshMaterials;


            if (sourceTexCoords2D != null)
            {
                var destUVs             = destMesh.GetUVs2D(0);
                destObjMesh.TexCoords2D = destUVs;
            }
            else if (sourceTexCoords3D != null)
            {
                var destUVs             = destMesh.GetUVs3D(0);
                destObjMesh.TexCoords3D = destUVs;
            }

            destObjMesh.WriteFile(destPath);

            int outputTriangleCount = 0;
            for (int i = 0; i < destIndices.Length; i++)
            {
                outputTriangleCount += (destIndices[i].Length / 3);
            }

            float reduction = (float)outputTriangleCount / (float)currentTriangleCount;
            float timeTaken = (float)stopwatch.Elapsed.TotalSeconds;

            string info_2 = string.Format("Output: {0} vertices, {1} triangles ({2} reduction; {3:0.0000} sec) \n",
                                          destVertices.Length, outputTriangleCount, reduction, timeTaken);
            // Console.WriteLine(info_2);
            simplifyInfo.Invoke(info_2);
            simplifyInfo.Invoke("finish");
            DisposeTask();
        });
        simplifyTask.Start();
    }
        /// <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));
        }
        public Rmv2Geometry CreatedReducedCopy(float factor)
        {
            var quality = factor;
            // ObjMesh sourceObjMesh = new ObjMesh();
            // sourceObjMesh.ReadFile(sourcePath);
            var sourceVertices = _vertexArray.Select(x => new MeshDecimator.Math.Vector3d(x.Position.X, x.Position.Y, x.Position.Z)).ToArray();


            // var sourceTexCoords3D = sourceObjMesh.TexCoords3D;
            var sourceSubMeshIndices = _indexList.Select(x => (int)x).ToArray();

            var sourceMesh = new Mesh(sourceVertices, sourceSubMeshIndices);

            sourceMesh.Normals  = _vertexArray.Select(x => new MeshDecimator.Math.Vector3(x.Normal.X, x.Normal.Y, x.Normal.Z)).ToArray();
            sourceMesh.Tangents = _vertexArray.Select(x => new MeshDecimator.Math.Vector4(x.Tangent.X, x.Tangent.Y, x.Tangent.Z, 0)).ToArray(); // Should last 0 be 1?
            sourceMesh.SetUVs(0, _vertexArray.Select(x => new MeshDecimator.Math.Vector2(x.TextureCoordinate.X, x.TextureCoordinate.Y)).ToArray());

            if (WeightCount == 4)
            {
                sourceMesh.BoneWeights = _vertexArray.Select(x => new BoneWeight(
                                                                 (int)x.BlendIndices.X, (int)x.BlendIndices.Y, (int)x.BlendIndices.Z, (int)x.BlendIndices.W,
                                                                 x.BlendWeights.X, x.BlendWeights.Y, x.BlendWeights.Z, x.BlendWeights.W)).ToArray();
            }
            else if (WeightCount == 2)
            {
                sourceMesh.BoneWeights = _vertexArray.Select(x => new BoneWeight(
                                                                 (int)x.BlendIndices.X, (int)x.BlendIndices.Y, 0, 0,
                                                                 x.BlendWeights.X, x.BlendWeights.Y, 0, 0)).ToArray();
            }
            else if (WeightCount == 0)
            {
                sourceMesh.BoneWeights = _vertexArray.Select(x => new BoneWeight(
                                                                 0, 0, 0, 0,
                                                                 0, 0, 0, 0)).ToArray();
            }

            int currentTriangleCount = sourceSubMeshIndices.Length / 3;
            int targetTriangleCount  = (int)Math.Ceiling(currentTriangleCount * quality);



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

            algorithm.Verbose = true;
            Mesh destMesh = MeshDecimation.DecimateMesh(algorithm, sourceMesh, targetTriangleCount);

            var destVertices = destMesh.Vertices;
            var destNormals  = destMesh.Normals;
            var destIndices  = destMesh.GetSubMeshIndices();

            VertexPositionNormalTextureCustom[] outputVerts = new VertexPositionNormalTextureCustom[destVertices.Length];

            for (int i = 0; i < outputVerts.Length; i++)
            {
                var pos        = destMesh.Vertices[i];
                var norm       = destMesh.Normals[i];
                var tangents   = destMesh.Tangents[i];
                var uv         = destMesh.UV1[i];
                var boneWeight = destMesh.BoneWeights[i];

                Vector3 normal   = new Vector3(norm.x, norm.y, norm.z);
                Vector3 tangent  = new Vector3(tangents.x, tangents.y, tangents.z);
                var     binormal = Vector3.Normalize(Vector3.Cross(normal, tangent));// * sign

                var vert = new VertexPositionNormalTextureCustom();
                vert.Position          = new Vector4((float)pos.x, (float)pos.y, (float)pos.z, 1);
                vert.Normal            = new Vector3(norm.x, norm.y, norm.z);
                vert.Tangent           = new Vector3(tangents.x, tangents.y, tangents.z);
                vert.BiNormal          = new Vector3(binormal.X, binormal.Y, binormal.Z);
                vert.TextureCoordinate = new Vector2(uv.x, uv.y);

                if (WeightCount == 4)
                {
                    vert.BlendIndices = new Vector4(boneWeight.boneIndex0, boneWeight.boneIndex1, boneWeight.boneIndex2, boneWeight.boneIndex3);
                    vert.BlendWeights = new Vector4(boneWeight.boneWeight0, boneWeight.boneWeight1, boneWeight.boneWeight2, boneWeight.boneWeight3);
                }
                else if (WeightCount == 2)
                {
                    vert.BlendIndices = new Vector4(boneWeight.boneIndex0, boneWeight.boneIndex1, 0, 0);
                    vert.BlendWeights = new Vector4(boneWeight.boneWeight0, boneWeight.boneWeight1, 0, 0);
                }
                else if (WeightCount == 0)
                {
                    vert.BlendIndices = new Vector4(0, 0, 0, 0);
                    vert.BlendWeights = new Vector4(0, 0, 0, 0);
                }

                if ((vert.BlendWeights.X + vert.BlendWeights.Y + vert.BlendWeights.Z + vert.BlendWeights.W) == 0)
                {
                    vert.BlendWeights.X = 1;
                }

                outputVerts[i] = vert;
            }

            var clone = Clone(false) as Rmv2Geometry;

            clone._indexList   = destIndices[0].Select(x => (ushort)x).ToArray();
            clone._vertexArray = outputVerts;

            clone.RebuildIndexBuffer();
            clone.RebuildVertexBuffer();

            return(clone);
        }