Exemple #1
0
        public static void ExportFile(HSD_JOBJ rootJOBJ, Dictionary <int, string> boneLabels = null)
        {
            StringBuilder sup = new StringBuilder();

            AssimpContext importer = new AssimpContext();
            var           length   = importer.GetSupportedExportFormats().Length;
            var           index    = 0;

            foreach (var v in importer.GetSupportedExportFormats())
            {
                sup.Append($"{v.Description} (*.{v.FileExtension})|*.{v.FileExtension};");
                index++;
                if (index != length)
                {
                    sup.Append("|");
                }
            }

            var f = Tools.FileIO.SaveFile(sup.ToString());

            if (f != null)
            {
                var settings = new ModelExportSettings();
                using (PropertyDialog d = new PropertyDialog("Model Import Options", settings))
                {
                    if (d.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                    {
                        ExportFile(f, rootJOBJ, settings, boneLabels);
                    }
                }
            }
        }
Exemple #2
0
        public void Export(IFile outputFile, IModel model)
        {
            var outputPath      = outputFile.FullName;
            var outputExtension = outputFile.Extension;

            var inputFile      = outputFile.CloneWithExtension(".glb");
            var inputPath      = inputFile.FullName;
            var inputExtension = inputFile.Extension;

            var ctx = new AssimpContext();

            string exportFormatId;
            {
                var supportedImportFormatExtensions = ctx.GetSupportedImportFormats();
                Asserts.True(supportedImportFormatExtensions.Contains(inputExtension),
                             $"'{inputExtension}' is not a supported import format!");

                var supportedExportFormats = ctx.GetSupportedExportFormats();
                var exportFormatIds        =
                    supportedExportFormats
                    .Where(exportFormat
                           => outputExtension == $".{exportFormat.FileExtension}")
                    .Select(exportFormat => exportFormat.FormatId);
                Asserts.True(exportFormatIds.Any(),
                             $"'{outputExtension}' is not a supported export format!");

                exportFormatId = exportFormatIds.First();
            }

            var sc      = ctx.ImportFile(inputPath);
            var success = ctx.ExportFile(sc, outputPath, exportFormatId);

            Asserts.True(success, "Failed to export model.");
        }
            static Exporter()
            {
                using (var temp = new AssimpContext())
                {
                    SupportedFormats = temp.GetSupportedExportFormats().ToArray();
                }
                var builder = new StringBuilder();

                foreach (var s in SupportedFormats)
                {
                    builder.Append($"{s.Description} (*.{s.FileExtension})|*.{ s.FileExtension }|");
                }
                SupportedFormatsString = builder.ToString(0, builder.Length - 1);
            }
        public void TestSupportedFormats()
        {
            AssimpContext importer = new AssimpContext();

            ExportFormatDescription[] exportDescs = importer.GetSupportedExportFormats();

            String[] importFormats = importer.GetSupportedImportFormats();

            Assert.IsNotNull(exportDescs);
            Assert.IsNotNull(importFormats);
            Assert.IsTrue(exportDescs.Length >= 1);
            Assert.IsTrue(importFormats.Length >= 1);

            Assert.IsTrue(importer.IsExportFormatSupported(exportDescs[0].FileExtension));
            Assert.IsTrue(importer.IsImportFormatSupported(importFormats[0]));
        }
Exemple #5
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="filePath"></param>
        /// <param name="rootJOBJ"></param>
        public static void ExportFile(string filePath, HSD_JOBJ rootJOBJ, ModelExportSettings settings = null, Dictionary <int, string> boneLabels = null)
        {
            ModelExporter mex      = new ModelExporter();
            AssimpContext importer = new AssimpContext();

            Dictionary <string, string> extToId = new Dictionary <string, string>();

            foreach (var v in importer.GetSupportedExportFormats())
            {
                if (!extToId.ContainsKey("." + v.FileExtension))
                {
                    extToId.Add("." + v.FileExtension, v.FormatId);
                }
            }

            PostProcessSteps postProcess = PostProcessSteps.FlipWindingOrder;

            if (settings.Optimize)
            {
                postProcess |= PostProcessSteps.JoinIdenticalVertices;
            }

            if (settings.FlipUVs)
            {
                postProcess |= PostProcessSteps.FlipUVs;
            }

            settings.Directory = System.IO.Path.GetDirectoryName(filePath) + "\\";

            if (System.IO.Path.GetExtension(filePath).ToLower() == ".dae")
            {
                var sc = mex.WriteRootNode(rootJOBJ, settings, boneLabels);

                /*var scn = Scene.ToUnmanagedScene(sc);
                 * scn = AssimpLibrary.Instance.ApplyPostProcessing(scn, postProcess);
                 * var scene = Scene.FromUnmanagedScene(scn);
                 * Scene.FreeUnmanagedScene(scn);*/
                ExportCustomDAE(filePath, sc, settings);
            }
            else
            {
                importer.ExportFile(mex.WriteRootNode(rootJOBJ, settings, boneLabels), filePath, extToId[System.IO.Path.GetExtension(filePath)], postProcess);
            }

            importer.Dispose();
        }
        public static void ExportModel(SrdFile srd, string exportName)
        {
            // Setup the scene for the root of our model
            Scene scene = new Scene();

            // Get all our various data
            scene.Materials.AddRange(GetMaterials(srd));
            scene.Meshes.AddRange(GetMeshes(srd, scene.Materials));
            scene.RootNode = GetNodeTree(srd, scene.Materials, scene.Meshes);

            AssimpContext exportContext = new AssimpContext();
            var           exportFormats = exportContext.GetSupportedExportFormats();

            foreach (var format in exportFormats)
            {
                if (format.FileExtension == "gltf")
                {
                    exportContext.ExportFile(scene, exportName + '.' + format.FileExtension, format.FormatId);
                    break;
                }
            }
        }
Exemple #7
0
        public ExportDialog(MainWindow main)
        {
            _main = main;
            InitializeComponent();

            using (var v = new AssimpContext())
            {
                _formats = v.GetSupportedExportFormats();
                foreach (var format in _formats)
                {
                    comboBoxExportFormats.Items.Add(format.Description + "  (" + format.FileExtension + ")");
                }
                comboBoxExportFormats.SelectedIndex         = ExportSettings.Default.ExportFormatIndex;
                comboBoxExportFormats.SelectedIndexChanged += (object s, EventArgs e) =>
                {
                    ExportSettings.Default.ExportFormatIndex = comboBoxExportFormats.SelectedIndex;
                    UpdateFileName(true);
                };
            }

            textBoxFileName.KeyPress += (object s, KeyPressEventArgs e) =>
            {
                _changedText = true;
            };

            // Respond to updates in the main window - the export dialog is non-modal and
            // always takes the currently selected file at the time the export button
            // is pressed.
            _main.SelectedTabChanged += (Tab tab) =>
            {
                UpdateFileName();
                UpdateCaption();
            };

            UpdateFileName();
            UpdateCaption();
        }
        public void TestSupportedFormats()
        {
            AssimpContext importer = new AssimpContext();
            ExportFormatDescription[] exportDescs = importer.GetSupportedExportFormats();

            String[] importFormats = importer.GetSupportedImportFormats();

            Assert.IsNotNull(exportDescs);
            Assert.IsNotNull(importFormats);
            Assert.IsTrue(exportDescs.Length >= 1);
            Assert.IsTrue(importFormats.Length >= 1);

            Assert.IsTrue(importer.IsExportFormatSupported(exportDescs[0].FileExtension));
            Assert.IsTrue(importer.IsImportFormatSupported(importFormats[0]));
        }
Exemple #9
0
        private static bool PackModels(ProcessorInfo info, BinPacking <int> texturePacker)
        {
            using (AssimpContext importer = new AssimpContext())
            {
                ExportFormatDescription[] exportFormatDescriptions = importer.GetSupportedExportFormats();
                ExportFormatDescription   exportFormat             = exportFormatDescriptions.FirstOrDefault(x =>
                                                                                                             x.FormatId == info.modelExportFormatId);

                if (exportFormat == null)
                {
                    Log.Line(LogType.Error, "Model export format {0} is not supported!",
                             info.modelExportFormatId);
                    Log.Line(LogType.Info, "Supported formats are:");
                    foreach (ExportFormatDescription e in exportFormatDescriptions)
                    {
                        Log.Line(LogType.Info, "{0} ({1})", e.FormatId, e.Description);
                    }

                    return(false);
                }

                Log.Line(LogType.Info, "Shifting uv(w) positions of {0} models to their new location",
                         info.models.Length);

                if (info.mergeModels)
                {
                    Log.Line(LogType.Info, "Going to merge models");
                }
                Scene mergedScene = new Scene
                {
                    RootNode  = new Assimp.Node(),
                    Materials = { new Material() }
                };

                for (int i = 0; i < info.models.Length; i++)
                {
                    string modelPath = info.models[i];
                    Scene  model     = importer.ImportFile(modelPath);
                    if (!model.HasMeshes)
                    {
                        Log.Line(LogType.Warning, "There are no meshes in model file '{0}'", modelPath);
                        continue;
                    }

                    Log.Line(LogType.Info, "Processing {0} meshes from model file '{1}'", model.MeshCount, modelPath);
                    foreach (Mesh mesh in model.Meshes)
                    {
                        Log.Line(LogType.Debug, "Found {0} uv(w) channels in mesh '{1}'",
                                 mesh.TextureCoordinateChannelCount, mesh.Name);

                        // TODO: Add support for offsetting uvs per mesh instead of per model file
                        // Thus having 1 texture per mesh instead of having 1 texture per model file
                        Block <int> block = texturePacker.blocks.First(x => x.data == i);
                        Log.Line(LogType.Debug, "Using texture at index {0} for this model",
                                 Array.IndexOf(texturePacker.blocks, block));
                        float scaleX  = (block.w - info.padding * 2) / (float)texturePacker.root.w;
                        float scaleY  = (block.h - info.padding * 2) / (float)texturePacker.root.h;
                        float offsetX = (block.fit.x + info.padding) / (float)texturePacker.root.w;
                        float offsetY = (block.fit.y + info.padding) / (float)texturePacker.root.h;
                        offsetY = 1 - offsetY - scaleY; // This is because the uv 0,0 is in the bottom left

                        Log.Line(LogType.Debug, "Calculated scaling multipliers: x: {0}; y: {1}", scaleX, scaleY);
                        Log.Line(LogType.Debug, "Calculated offsets: x: {0}; y: {1}", offsetX, offsetY);

                        for (int uvwChannel = 0; uvwChannel < mesh.TextureCoordinateChannelCount; uvwChannel++)
                        {
                            List <Vector3D> uvw = mesh.TextureCoordinateChannels[uvwChannel];
                            if (uvw == null || uvw.Count == 0)
                            {
                                continue;
                            }

                            for (int n = 0; n < uvw.Count; n++)
                            {
                                uvw[n] = new Vector3D(
                                    uvw[n].X * scaleX + offsetX,
                                    uvw[n].Y * scaleY + offsetY,
                                    uvw[n].Z);
                            }

                            mesh.TextureCoordinateChannels[uvwChannel] = uvw;
                        }

                        if (info.mergeModels)
                        {
                            mergedScene.RootNode.MeshIndices.Add(mergedScene.MeshCount);
                            mesh.MaterialIndex = 0;
                            mergedScene.Meshes.Add(mesh);
                            mergedScene.Animations.AddRange(model.Animations);
                            Log.Line(LogType.Info, "Merged mesh {0} into output model", mesh.Name);
                        }
                    }

                    if (!info.mergeModels)
                    {
                        string savePath;
                        if (!string.IsNullOrEmpty(info.outputFilesPrefix.Trim()))
                        {
                            savePath = Path.Combine(info.outputDir,
                                                    string.Format("{0}-model-{1}.{2}",
                                                                  info.outputFilesPrefix,
                                                                  Path.GetFileNameWithoutExtension(modelPath),
                                                                  exportFormat.FileExtension));
                        }
                        else
                        {
                            savePath = Path.Combine(info.outputDir,
                                                    string.Format("model-{0}.{1}",
                                                                  Path.GetFileNameWithoutExtension(modelPath),
                                                                  exportFormat.FileExtension));
                        }

                        Log.Line(LogType.Info, "Saving edited model to '{0}'", savePath);
                        importer.ExportFile(model, savePath, exportFormat.FormatId);
                    }
                }

                if (info.mergeModels)
                {
                    string savePath;
                    if (!string.IsNullOrEmpty(info.outputFilesPrefix.Trim()))
                    {
                        savePath = Path.Combine(info.outputDir,
                                                string.Format("{0}-models-merged.{1}",
                                                              info.outputFilesPrefix,
                                                              exportFormat.FileExtension));
                    }
                    else
                    {
                        savePath = Path.Combine(info.outputDir,
                                                string.Format("models-merged.{0}",
                                                              exportFormat.FileExtension));
                    }

                    Log.Line(LogType.Info, "Saving merged model to '{0}'", savePath);
                    importer.ExportFile(mergedScene, savePath, exportFormat.FormatId);
                }
            }

            return(true);
        }
        // You can bet your ass I'm gonna prefix everything with ass.

        public void Export(IFile outputFile, IModel model)
        {
            var outputPath      = outputFile.FullName;
            var outputExtension = outputFile.Extension;

            using var ctx = new AssimpContext();

            string exportFormatId;
            {
                var supportedExportFormats = ctx.GetSupportedExportFormats();
                var exportFormatIds        =
                    supportedExportFormats
                    .Where(exportFormat
                           => outputExtension ==
                           $".{exportFormat.FileExtension}")
                    .Select(exportFormat => exportFormat.FormatId);
                Asserts.True(exportFormatIds.Any(),
                             $"'{outputExtension}' is not a supported export format!");

                exportFormatId = exportFormatIds.First();
            }

            var assScene = new Scene();

            assScene.RootNode = new Node("ROOT");

            var inputFile = outputFile.CloneWithExtension(".glb");
            var inputPath = inputFile.FullName;

            new GltfExporter().Export(inputFile, model);
            var sc = ctx.ImportFile(inputPath);

            ;

            // [ ]: Get skeleton working
            // [ ]: Get mesh working
            // [ ]: Get UVs working
            // [ ]: Get materials working
            // [ ]: Get multi-bone-binding working

            new AssimpSkeletonBuilder().BuildAndBindSkeleton(assScene, model);
            new AssimpMeshBuilder().BuildAndBindMesh(assScene, model);

            /*var postProcessSteps = PostProcessSteps.FindInvalidData |
             *                     PostProcessSteps.ValidateDataStructure |
             *                     PostProcessSteps.GenerateBoundingBoxes |
             *                     PostProcessSteps.JoinIdenticalVertices |
             *                     PostProcessSteps.OptimizeMeshes |
             *                     PostProcessSteps.PreTransformVertices;*/

            LogStream.IsVerboseLoggingEnabled = true;
            var logStream = new FinLogStream();

            logStream.Attach();

            bool wasSuccessful = false;

            try {
                wasSuccessful =
                    ctx.ExportFile(assScene,
                                   outputPath,
                                   exportFormatId);
            } catch (Exception e) {}
            var error = AssimpLibrary.Instance.GetErrorString();

            Asserts.True(wasSuccessful, "Failed to export model: " + error);

            return;

/*      // Importing the pre-generated GLTF file does most of the hard work off
 *    // the bat: generating the mesh with properly weighted bones.
 *
 *    // Bone orientation is already correct, you just need to enable
 *    // "Automatic Bone Orientation" if importing in Blender.
 *
 *    // Fix animations.
 *    var finAnimations = model.AnimationManager.Animations;
 *    var assAnimations = sc.Animations;
 *    for (var a = 0; a < assAnimations.Count; ++a) {
 *      var assAnimation = assAnimations[a];
 *      var finAnimation = finAnimations[a];
 *
 *      // Animations are SUPER slow, we need to speed them way up!
 *      {
 *        // Not entirely sure why this is right...
 *        var animationSpeedup = 2 / assAnimation.DurationInTicks;
 *
 *        // TODO: Include tangents from the animation file.
 *        foreach (var channel in assAnimation.NodeAnimationChannels) {
 *          this.ScaleKeyTimes_(channel.PositionKeys, animationSpeedup);
 *          this.ScaleKeyTimes_(channel.ScalingKeys, animationSpeedup);
 *          this.ScaleKeyTimes_(channel.RotationKeys, animationSpeedup);
 *        }
 *      }
 *
 *      var assFps = assAnimation.TicksPerSecond;
 *      var finFps = finAnimation.Fps;
 *
 *      assAnimation.TicksPerSecond = finFps;
 *      assAnimation.DurationInTicks *= finFps / assFps;
 *
 *      // TODO: Include animation looping behavior here.
 *    }
 *
 *    // Export materials.
 *    {
 *      /*var finTextures = new HashSet<string>();
 *      foreach (var finMaterial in model.MaterialManager.All) {
 *        foreach (var finTexture in finMaterial.Textures) {
 *          finTextures.Add(finTexture);
 *        }
 *      }
 *
 *      foreach (var finTexture in finTextures) {
 *        var imageData = finTexture.ImageData;
 *
 *        var imageBytes = new MemoryStream();
 *        imageData.Save(imageBytes, ImageFormat.Png);
 *
 *        var assTexture =
 *            new EmbeddedTexture("png",
 *                                imageBytes.ToArray(),
 *                                finTexture.Name);
 *        assTexture.Filename = finTexture.Name + ".png";
 *
 *        finTextureToAssTexture[finTexture] = assTexture;
 *        sc.Textures.Add(assTexture);
 *      }
 *
 *      // TODO: Need to update the UVs...
 *
 *      // Fix the UVs.
 *      var finVertices = model.Skin.Vertices;
 *      var assMeshes = sc.Meshes;
 *
 *      var animations = model.AnimationManager.Animations;
 *      var firstAnimation =
 *          (animations?.Count ?? 0) > 0 ? animations[0] : null;
 *
 *      var boneTransformManager = new BoneTransformManager();
 *      /*boneTransformManager.CalculateMatrices(
 *          model.Skeleton.Root,
 *          firstAnimation != null ? (firstAnimation, 0) : null);
 *      boneTransformManager.CalculateMatrices(model.Skeleton.Root, null);
 *
 *      var finVerticesByMaterial =
 *          model.Skin.Primitives
 *               .GroupBy(primitive => primitive.Material)
 *               .Select(grouping => {
 *                 var positionsAndNormals = grouping
 *                     .SelectMany(primitive => primitive.Vertices)
 *                     .ToHashSet()
 *                     .Select(finVertex => {
 *                       IPosition position =
 *                           new ModelImpl.PositionImpl();
 *                       INormal normal = new ModelImpl.NormalImpl();
 *                       boneTransformManager.ProjectVertex(
 *                           finVertex,
 *                           position,
 *                           normal);
 *                       return (position, normal);
 *                     });
 *
 *                 //var uniquePositions = positionsAndNormals.
 *
 *                 return positionsAndNormals;
 *               })
 *               .ToList();
 *
 *      var worldFinVerticesAndNormals =
 *          finVertices.Select(finVertex => {
 *                       IPosition position =
 *                           new ModelImpl.PositionImpl();
 *                       INormal normal = new ModelImpl.NormalImpl();
 *                       boneTransformManager.ProjectVertex(
 *                           finVertex,
 *                           position,
 *                           normal);
 *                       return (position, normal);
 *                     })
 *                     .ToArray();
 *
 *      foreach (var assMesh in assMeshes) {
 *        var assLocations = assMesh.Vertices;
 *        var assNormals = assMesh.Normals;
 *
 *        var assUvs = assMesh.TextureCoordinateChannels;
 *
 *        var oldAssUvs = new List<Vector3D>[8];
 *        for (var t = 0; t < 8; ++t) {
 *          oldAssUvs[t] = assUvs[t].Select(x => x).ToList();
 *          assUvs[t].Clear();
 *        }
 *
 *
 *        var hadUv = new bool[8];
 *        for (var i = 0; i < assLocations.Count; ++i) {
 *          var assLocation = assLocations[i];
 *          var assNormal = assNormals[i];
 *
 *          // TODO: How to find this more efficiently???
 *          var minIndex = -1;
 *          for (var v = 0; v < worldFinVerticesAndNormals.Length; ++v) {
 *            var (position, normal) = worldFinVerticesAndNormals[v];
 *            var inNormal = finVertices[v].LocalNormal;
 *
 *            var tolerance = .005;
 *
 *            if (i == 131) {
 *              var finUv = finVertices[v].GetUv(0);
 *
 *              if (Math.Abs(finUv.U - .9375) < tolerance ||
 *                  Math.Abs((1 - finUv.U) - .9375) < tolerance) {
 *                var u0 = finUv.U;
 *                var v0 = 1 - finUv.V;
 *                ;
 *              }
 *            }
 *
 *            if (Math.Abs(position.X - assLocation.X) < tolerance &&
 *                Math.Abs(position.Y - assLocation.Y) < tolerance &&
 *                Math.Abs(position.Z - assLocation.Z) < tolerance) {
 *              if (Math.Abs(normal.X - assNormal.X) < tolerance &&
 *                  Math.Abs(normal.Y - assNormal.Y) < tolerance &&
 *                  Math.Abs(normal.Z - assNormal.Z) < tolerance) {
 *                minIndex = v;
 *                break;
 *              } else {
 *                ;
 *              }
 *            }
 *          }
 *
 *          var minLocation = worldFinVerticesAndNormals[minIndex];
 *          var minVertex = finVertices[minIndex];
 *
 *          for (var t = 0; t < 8; ++t) {
 *            var uv = minVertex.GetUv(t);
 *
 *            if (uv != null) {
 *              hadUv[t] = true;
 *              assUvs[t].Add(new Vector3D(uv.U, 1 - uv.V, 0));
 *            } else {
 *              assUvs[t].Add(default);
 *            }
 *          }
 *        }
 *
 *        for (var t = 0; t < 8; ++t) {
 *          if (!hadUv[t]) {
 *            assUvs[t].Clear();
 *          }
 *        }
 *
 *        var lhs = oldAssUvs[0];
 *        var rhs = assUvs[0];
 *        for (var i = 0; i < lhs.Count; ++i) {
 *          var oldAssUv = lhs[i];
 *          var assUv = rhs[i];
 *
 *          if (Math.Abs(oldAssUv.X - assUv.X) > .01 ||
 *              Math.Abs(oldAssUv.Y - assUv.Y) > .01) {
 *            var assWorld = assLocations[i];
 *            ;
 *          }
 *        }
 *
 *        ;
 *      }
 *
 *      // Re-add the textures.
 *
 *      sc.Textures.Clear();
 *      sc.Materials.Clear();
 *
 *      foreach (var finMaterial in model.MaterialManager.All) {
 *        var assMaterial = new Material();
 *
 *        assMaterial.Shaders.ShaderLanguageType = "HLSL";
 *        assMaterial.Shaders.FragmentShader =
 *            @"
 * struct a2v {
 * float4 Position : POSITION;
 * float4 Color    : COLOR0;
 * };
 *
 * struct v2p {
 * float4 Position : POSITION;
 * float4 Color    : COLOR0;
 * };
 *
 * void main(in a2v IN, out v2p OUT, uniform float4x4 ModelViewMatrix) {
 * OUT.Position = mul(IN.Position, ModelViewMatrix);
 * OUT.Color    = float4(255, 255, 0, 0);
 * }
 * ";
 *
 *        assMaterial.Name = finMaterial.Name;
 *        // TODO: Set shader
 *
 *        if (finMaterial is ILayerMaterial layerMaterial) {
 *          var addLayers =
 *              layerMaterial
 *                  .Layers
 *                  .Where(layer => layer.BlendMode == FinBlendMode.ADD)
 *                  .ToArray();
 *          var multiplyLayers =
 *              layerMaterial
 *                  .Layers
 *                  .Where(layer => layer.BlendMode == FinBlendMode.MULTIPLY)
 *                  .ToArray();
 *
 *          if (addLayers.Length == 0) {
 *            throw new NotSupportedException("Expected to find an add layer!");
 *          }
 *          if (addLayers.Length > 1) {
 *            ;
 *          }
 *          if (addLayers.Length > 2) {
 *            throw new NotSupportedException("Too many add layers for GLTF!");
 *          }
 *
 *          for (var i = 0; i < addLayers.Length; ++i) {
 *            var layer = addLayers[i];
 *
 *            // TODO: Support flat color layers by generating a 1x1 clamped texture of that color.
 *            if (layer.ColorSource is ITexture finTexture) {
 *              var assTextureSlot = new TextureSlot();
 *              assTextureSlot.FilePath = finTexture.Name + ".png";
 *
 *              // TODO: FBX doesn't support mirror. Blegh
 *              assTextureSlot.WrapModeU =
 *                  this.ConvertWrapMode_(finTexture.WrapModeU);
 *              assTextureSlot.WrapModeV =
 *                  this.ConvertWrapMode_(finTexture.WrapModeV);
 *
 *              if (i == 0) {
 *                assTextureSlot.TextureType = TextureType.Diffuse;
 *              } else {
 *                assTextureSlot.TextureType = TextureType.Emissive;
 *              }
 *
 *              // TODO: Set blend mode
 *              //assTextureSlot.Operation =
 *
 *              assTextureSlot.UVIndex = layer.TexCoordIndex;
 *
 *              // TODO: Set texture coord type
 *
 *              assMaterial.AddMaterialTexture(assTextureSlot);
 *            }
 *          }
 *
 *          /*foreach (var layer in layerMaterial.Layers) {
 *            // TODO: Support flat color layers by generating a 1x1 clamped texture of that color.
 *
 *            if (layer.ColorSource is ITexture finTexture) {
 *              var assTextureSlot = new TextureSlot();
 *              assTextureSlot.FilePath = finTexture.Name + ".png";
 *              assTextureSlot.TextureType = TextureType.Diffuse;
 *
 *              // TODO: FBX doesn't support mirror. Blegh
 *              assTextureSlot.WrapModeU =
 *                  this.ConvertWrapMode_(finTexture.WrapModeU);
 *              assTextureSlot.WrapModeV =
 *                  this.ConvertWrapMode_(finTexture.WrapModeV);
 *
 *              // TODO: Set blend mode
 *              //assTextureSlot.Operation =
 *
 *              // TODO: Set texture coord type
 *
 *              assMaterial.AddMaterialTexture(assTextureSlot);
 *            }
 *          }*/
        }
        public void Export(IFile outputFile, IModel model)
        {
            var outputPath      = outputFile.FullName;
            var outputExtension = outputFile.Extension;

            using var ctx = new AssimpContext();

            string exportFormatId;
            {
                var supportedExportFormats = ctx.GetSupportedExportFormats();
                var exportFormatIds        =
                    supportedExportFormats
                    .Where(exportFormat
                           => outputExtension ==
                           $".{exportFormat.FileExtension}")
                    .Select(exportFormat => exportFormat.FormatId);
                Asserts.True(exportFormatIds.Any(),
                             $"'{outputExtension}' is not a supported export format!");

                exportFormatId = exportFormatIds.First();
            }

            var inputFile = !this.LowLevel
                          ? outputFile.CloneWithExtension(".glb")
                          : outputFile.CloneWithExtension(".gltf");
            var inputPath = inputFile.FullName;

            IGltfExporter gltfExporter = !this.LowLevel
                                       ? new GltfExporter()
                                       : new LowLevelGltfExporter();

            Scene?assScene = null;

            if (!this.LowLevel)
            {
                gltfExporter.UvIndices = true;
                gltfExporter.Embedded  = true;
                gltfExporter.Export(inputFile, model);

                assScene = ctx.ImportFile(inputPath);
                File.Delete(inputPath);

                // Importing the pre-generated GLTF file does most of the hard work off
                // the bat: generating the mesh with properly weighted bones.

                // Bone orientation is already correct, you just need to enable
                // "Automatic Bone Orientation" if importing in Blender.

                new AssimpIndirectAnimationFixer().Fix(model, assScene);
                new AssimpIndirectUvFixer().Fix(model, assScene);
                new AssimpIndirectTextureFixer().Fix(model, assScene);
            }

            // Reexports the GLTF version in case the FBX version is screwed up.
            gltfExporter.UvIndices = false;
            gltfExporter.Embedded  = false;
            gltfExporter.Export(
                new FinFile(inputFile.FullName.Replace(".glb", "_gltf.glb")),
                model);

            if (assScene != null)
            {
                // Finally exports the fbx version.
                // TODO: Are these all safe to include?
                var preProcessing =
                    PostProcessSteps.FindInvalidData |
                    PostProcessSteps.JoinIdenticalVertices;

                var success =
                    ctx.ExportFile(assScene, outputPath, exportFormatId, preProcessing);
                Asserts.True(success, "Failed to export model.");
            }
        }