Ejemplo n.º 1
0
        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));
        }
Ejemplo n.º 2
0
        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);
        }
Ejemplo n.º 3
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;
        }
Ejemplo n.º 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);
            }
        }
Ejemplo n.º 5
0
        MeshSimplifier(List <Vec3> verts, int?target)
        {
            Console.WriteLine("Converting to indexed form");
            ConvertToIndexed(verts);

            var nmesh = new Mesh(Vertices.Select(x => new Vector3d(x.X, x.Y, x.Z)).ToArray(),
                                 Triangles.Select(x => x.Triangulate()).SelectMany(x => x).ToArray());

            nmesh.RecalculateNormals();
            nmesh.RecalculateTangents();
            Console.WriteLine("Decimating mesh");
            nmesh = target == null
                                ? MeshDecimation.DecimateMeshLossless(nmesh)
                                : MeshDecimation.DecimateMesh(nmesh, target.Value);

            Vertices = nmesh.Vertices.Select(x => new Vec3((float)x.x, (float)x.y, (float)x.z)).ToList();
            var inds = nmesh.Indices;

            Triangles = new List <Triangle>();
            for (var i = 0; i < inds.Length; i += 3)
            {
                Triangles.Add(new Triangle(inds[i], inds[i + 1], inds[i + 2]));
            }
        }
    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();
    }
Ejemplo n.º 7
0
        /// <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));
        }
Ejemplo n.º 8
0
        /// <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));
        }
Ejemplo n.º 9
0
        private void SetModelTask(ModelFileData modelData)
        {
            if (CurrentModelVisual != null)
            {
                this.Viewport.Children.Remove(CurrentModelVisual);
                CurrentModelVisual = null;
                GC.Collect(0, GCCollectionMode.Forced);
            }

            if (!this.Viewport.IsVisible)
            {
                this.ModelDataToLoadWhenVisible = modelData;
                return;
            }

            Model3DGroup  modelGroup  = new Model3DGroup();
            ModelVisual3D modelVisual = new ModelVisual3D();

            try
            {
                if (modelData != null)
                {
                    ModelFileData newModelData = new ModelFileData(modelData.FileFullPath);
                    this.ModelData    = newModelData;
                    Viewport.SubTitle = modelData.FileName;

                    newModelData.LoadBasicFileData();

                    if (userSettings.GetSettingBool(UserSettingEnum.EnableMaxSizeMBToLoadMeshInView) && modelData.FileSizeMB > userSettings.GetSettingInt(UserSettingEnum.MaxSizeMBToLoadMeshInView))
                    {
                        // TODO: Load generic model.
                        Viewport.SubTitle  = Loc.GetTextFormatted("FileSizeTooBigToLoadMB", modelData.FileSizeMB, userSettings.GetSettingInt(UserSettingEnum.MaxSizeMBToLoadMeshInView));
                        CurrentModelVisual = null;
                        return;
                    }

                    if (!newModelData.HasBytes())
                    {
                        newModelData.LoadFileBytes(newModelData.FileSizeMB < 50f);
                    }
                    if (newModelData.Mesh == null)
                    {
                        newModelData.ParseFile();
                    }
                    newModelData.ReleaseData(true, false);

                    if (newModelData.Mesh == null)
                    {
                        newModelData.ReleaseData(true, false);
                        SetModel(null);
                        return;
                    }

                    LoadModelInfoAvailableEvent?.Invoke(newModelData.FileName, newModelData.Mesh.TriangleCount, newModelData.Mesh.Vertices.Length, (int)newModelData.FileSizeKB);

                    float modelScale         = newModelData.Mesh.Scale / 4f;
                    float modelScaleMultiply = (newModelData.Mesh.Scale < 0.001f ? 0.1f : (newModelData.Mesh.Scale > 0.1 ? 10f : 1));


                    var transformTranslate           = new TranslateTransform3D(-newModelData.Mesh.OffsetX, -newModelData.Mesh.OffsetY, -newModelData.Mesh.OffsetZ);
                    AxisAngleRotation3D axisRotation = new AxisAngleRotation3D(new Vector3D(0, 0, 1), 0);
                    transformObjectRotation = new RotateTransform3D(axisRotation, new Point3D(newModelData.Mesh.OffsetX, newModelData.Mesh.OffsetY, 0));
                    ScaleTransform3D transformScale = new ScaleTransform3D(modelScaleMultiply, modelScaleMultiply, modelScaleMultiply);

                    Transform3DGroup transforms = new Transform3DGroup();
                    transforms.Children.Add(transformObjectRotation);
                    transforms.Children.Add(transformTranslate);
                    transforms.Children.Add(transformScale);

                    Mesh3D mesh;

                    // Mesh decimation if enabled
                    if (userSettings.GetSettingBool(UserSettingEnum.EnableMeshDecimation) && newModelData.Mesh.TriangleCount > userSettings.GetSettingInt(UserSettingEnum.MinTrianglesForMeshDecimation))
                    {
                        MeshDecimator.Math.Vector3d[] vectors3D = newModelData.Mesh.Vertices.Select(v => new MeshDecimator.Math.Vector3d(v.x, v.y, v.z)).ToArray();
                        Mesh decimatorMesh = new Mesh(vectors3D, newModelData.Mesh.Triangles.ToArray());

                        Mesh decimatedMesh = MeshDecimation.DecimateMeshLossless(decimatorMesh);
                        mesh = new Mesh3D(decimatedMesh.Vertices.Select(v => new Point3D(v.x, v.y, v.z)), decimatedMesh.Indices);

                        // TODO: Possibly cache the decimated models to avoid re-processing.
                    }
                    else
                    {
                        mesh = new Mesh3D(Point3DFromLinearCoordinates(newModelData.Mesh.Vertices), newModelData.Mesh.Triangles);
                    }
                    GeometryModel3D geometryModel = new GeometryModel3D(mesh.ToMeshGeometry3D(), GetMaterial());
                    geometryModel.Freeze();

                    modelGroup.Children.Add(geometryModel);
                    modelGroup.Freeze();

                    _modelHeightPosition = newModelData.Mesh.Height * modelScaleMultiply;
                    newModelData.ReleaseData(true, true);

                    // Animation
                    if (ModelAutoRotationEnabled)
                    {
                        DoubleAnimation animation1 = new DoubleAnimation(-90, 395d, TimeSpan.FromMilliseconds(1000));
                        animation1.EasingFunction = new ExponentialEase();

                        DoubleAnimation animation2 = new DoubleAnimation(36d, 395d, TimeSpan.FromMilliseconds(7000));
                        animation2.RepeatBehavior = RepeatBehavior.Forever;

                        animation1.Completed += (o, e) => axisRotation.BeginAnimation(AxisAngleRotation3D.AngleProperty, animation2);
                        axisRotation.BeginAnimation(AxisAngleRotation3D.AngleProperty, animation1);
                    }

                    // Camera animation
                    var nomalizedOriginalPosition = _originalCameraPosition.ToVector3D();
                    nomalizedOriginalPosition.Normalize();
                    _modelCameraPosition = new Point3D(nomalizedOriginalPosition.X / modelScale * modelScaleMultiply, nomalizedOriginalPosition.Y / modelScale * modelScaleMultiply, nomalizedOriginalPosition.Z / modelScale * modelScaleMultiply);

                    Point3D targetCameraPosition = _modelCameraPosition;
                    if (_CurrentCameraPosition == CameraPositionEnum.Default)
                    {
                        var normalizedPosition = Viewport.Camera.Position.ToVector3D();
                        normalizedPosition.Normalize();
                        targetCameraPosition = new Point3D(normalizedPosition.X / modelScale * modelScaleMultiply, normalizedPosition.Y / modelScale * modelScaleMultiply, normalizedPosition.Z / modelScale * modelScaleMultiply);

                        Viewport.Camera.AnimateTo(targetCameraPosition, Viewport.Camera.LookDirection, Viewport.Camera.UpDirection, 500d);
                    }
                    else
                    {
                        ResetCamera(CameraPositionEnum.Current);
                    }

                    minZoom = targetCameraPosition.Multiply(0.5d).DistanceTo(new Point3D());
                    maxZoom = minZoom * 4d;

                    this.CurrentAxisRotation = axisRotation;
                    modelVisual.Transform    = transforms;
                }
                modelVisual.Content = modelGroup;
                this.Viewport.Children.Add(modelVisual);

                this.CurrentModelVisual = modelVisual;
            }
            catch (Exception ex)
            {
                if (CurrentModelVisual != null)
                {
                    this.Viewport.Children.Add(CurrentModelVisual);
                    this.CurrentModelVisual = null;
                }
            }
            if (modelData == null)
            {
                _modelCameraPosition = _originalCameraPosition;
                ResetCamera(CameraPositionEnum.Default, true);
                LoadModelInfoAvailableEvent?.Invoke("", 0, 0, 0);
                _modelHeightPosition = (float)(_originalCameraPosition.Z / 2d);
                Viewport.SubTitle    = string.Empty;
            }
            GC.Collect();
        }
        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);
        }