private Model3D LoadModel3D(string fileName, string customTexturesFolder, out Dictionary <string, Model3D> namedObjects)
        {
            if (!System.IO.File.Exists(fileName))
            {
                MessageBox.Show($"File does not exist:\r\n{fileName}");
                namedObjects = null;

                return(null);
            }


            // Therefore we need to use Assimp importer
            var assimpWpfImporter = new AssimpWpfImporter();

            // Let assimp calculate the tangents that are needed for normal mapping
            assimpWpfImporter.AssimpPostProcessSteps = PostProcessSteps.CalculateTangentSpace;

            var assimpScene = assimpWpfImporter.ReadFileToAssimpScene(fileName);

            var assimpWpfConverter = new AssimpWpfConverter();

            assimpWpfConverter.RemoveEmptyModel3DGroups = false; // Prevent removing "Helper_Extras" Model3DGroup - placholder that is used for tools

            var readModel3D = assimpWpfConverter.ConvertAssimpModel(assimpScene, System.IO.Path.GetDirectoryName(fileName));


            // Update WPF materials with PhysicallyBasedMaterial
            UpdateMaterials(fileName, customTexturesFolder, assimpScene, assimpWpfConverter, useStrictFileNameMatch: true, supportDDSTextures: true);


            // Convert ObjectNames to NamedObjects
            namedObjects = new Dictionary <string, Model3D>(assimpWpfConverter.ObjectNames.Count);
            foreach (var keyValuePair in assimpWpfConverter.ObjectNames)
            {
                var model3D = keyValuePair.Key as Model3D;

                if (model3D == null || keyValuePair.Value == null)
                {
                    continue;
                }

                namedObjects[keyValuePair.Value] = model3D;
            }


            return(readModel3D);
        }
        private void LoadFile(string fileName, string customTexturesFolder)
        {
            if (!System.IO.File.Exists(fileName))
            {
                MessageBox.Show($"File does not exist:\r\n{fileName}");
                return;
            }

            Mouse.OverrideCursor = Cursors.Wait;

            ModelPlaceholder.Content = null;
            ModelPlaceholder.Children.Clear();
            //LightsModelPlaceholder.Children.Clear();

            InfoTextBlock.Text = "";
            Log("Start opening file: " + fileName);


            try
            {
                //// ReaderObj that comes with Ab3d.PowerToys cannot read normal map textures
                //var readerObj = new Ab3d.ReaderObj();
                //readerObj.BitmapCacheOption = BitmapCacheOption.None; // Do not load textures by ReaderObj
                //Model3D readModel3D = readerObj.ReadModel3D(fileName);

                //UpdateMaterials(readModel3D);


                // Therefore we need to use Assimp importer
                var assimpWpfImporter = new AssimpWpfImporter();

                // Let assimp calculate the tangents that are needed for normal mapping
                assimpWpfImporter.AssimpPostProcessSteps |= PostProcessSteps.CalculateTangentSpace;

                //var readModel3D = assimpWpfImporter.ReadModel3D(fileName, texturesPath: null); // we can also define a textures path if the textures are located in some other directory (this is parameter can be skipped, but is defined here so you will know that you can use it)

                var assimpScene = assimpWpfImporter.ReadFileToAssimpScene(fileName);

                if (assimpScene == null)
                {
                    Log("Cannot load 3D model probably because file format is not recognized!");
                    return;
                }

                var assimpWpfConverter = new AssimpWpfConverter();
                var readModel3D        = assimpWpfConverter.ConvertAssimpModel(assimpScene, System.IO.Path.GetDirectoryName(fileName));


                _loadedFileName = fileName;

                // Convert standard WPF materials into PhysicallyBasedMaterial objects
                UpdateMaterials(fileName, customTexturesFolder, assimpScene, assimpWpfConverter, useStrictFileNameMatch: true, supportDDSTextures: true);


                ModelPlaceholder.Content = readModel3D;

                Camera1.TargetPosition = readModel3D.Bounds.GetCenterPosition();
                Camera1.Distance       = 1.5 * readModel3D.Bounds.GetDiagonalLength();

                Log(string.Format("\r\nFile '{0}' loaded\r\nCenter position: {1:0};    Size: {2:0}",
                                  System.IO.Path.GetFileName(fileName),
                                  readModel3D.Bounds.GetCenterPosition(),
                                  readModel3D.Bounds.Size));


                Camera1.ShowCameraLight = ShowCameraLightType.Always;
                Camera1.Refresh();

                // Manually call Refresh to update the scene while we are still showing Wait cursor
                MainDXViewportView.Refresh();

                this.Title = "Ab3d.DXEngine PBR Rendering - " + System.IO.Path.GetFileName(fileName);
            }
            finally
            {
                Mouse.OverrideCursor = null;
            }
        }