diff --git a/LegendaryExplorer/LegendaryExplorer/Misc/GltfHelper.cs b/LegendaryExplorer/LegendaryExplorer/Misc/GltfHelper.cs new file mode 100644 index 0000000000..05fec4f898 --- /dev/null +++ b/LegendaryExplorer/LegendaryExplorer/Misc/GltfHelper.cs @@ -0,0 +1,220 @@ +using LegendaryExplorer.Dialogs; +using LegendaryExplorer.SharedUI.Bases; +using LegendaryExplorer.UserControls.ExportLoaderControls; +using LegendaryExplorerCore.Helpers; +using LegendaryExplorerCore.Packages; +using LegendaryExplorerCore.Unreal; +using LegendaryExplorerCore.Unreal.ObjectInfo; +using Microsoft.Win32; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; + +namespace LegendaryExplorer.Misc +{ + /// + /// Wraps the GLTF class in LEC to make the functionality available to various parts of the user interface + /// + public static class GltfHelper + { + public static bool CanExportMeshToGltf(WPFBase owningWindow, IMEPackage package, IEntry selectedEntry) + { + // TODO is the selectedEntry a valid type? + // are experiments enabled? + return true; + } + public static void ExportMeshToGltf(WPFBase owningWindow, MeshRenderer meshRenderer, IMEPackage package, IEntry selectedEntry, GLTF.MaterialExportLevel materialExportLevel = GLTF.MaterialExportLevel.NameOnly) + { + if (package == null) + { + return; + } + if (package.Game == MEGame.ME1) + { + ShowError("This experiment does not yet support OT1; if you must do this, port it to another game first"); + return; + } + if (package.Game == MEGame.UDK) + { + ShowError("This experiment does not support UDK files;"); + return; + } + if (FilterSelectedItem(selectedEntry, ["SkeletalMesh", "StaticMesh", "SkeletalMeshComponent", "BioPawn", "SFXStuntActor", "SkeletalMeshActor"], out var export)) + { + if (export.ClassName == "StaticMesh" && !(package.Game.IsGame3() || package.Game.IsLEGame())) + { + ShowError("This experiment does not yet support OT1 or OT2 for static meshes."); + return; + } + var d = new SaveFileDialog { Filter = "glTF binary|*.glb|glTF|*.glTF", FileName = $"{selectedEntry.ObjectName.Instanced}.glb" }; + if (d.ShowDialog() == true) + { + Task.Run(() => + { + if (owningWindow != null) + { + owningWindow.BusyText = "Exporting to glTF..."; + owningWindow.IsBusy = true; + } + else + { + meshRenderer?.BusyText = "Exporting to glTF..."; + meshRenderer?.IsBusy = true; + } + GLTF.ExportMeshToGltf(export, d.FileName, materialExportLevel, $"Legendary Explorer {AppVersion.DisplayedVersion}"); + }).ContinueWithOnUIThread(x => + { + if (owningWindow != null) + { + owningWindow?.IsBusy = false; + } + else + { + meshRenderer?.IsBusy = false; + } + if (x.Exception != null) + { + ShowError(x.Exception.FlattenException()); + } + }); + } + } + else + { + ShowError("You must select a skeletal mesh, static mesh, SkeletalMeshComponent, SFXStuntActor, SkeletalMeshActor, or BioPawn"); + } + } + + public static void ReplaceFromGltf(WPFBase window, IEntry selectedEntry) + { + if (window.Pcc == null) + { + return; + } + if (window.Pcc.Game == MEGame.ME1) + { + ShowError("This experiment does not yet support OT1; if you must do this, import it into another game and port it to OT1"); + } + if (window.Pcc.Game == MEGame.UDK) + { + ShowError("This experiment does not support UDK files;"); + } + if (GetGltfFromFile(out var gltf, out string _)) + { + FilterSelectedItem(selectedEntry, ["SkeletalMesh", "StaticMesh"], out ExportEntry selectedMeshToReplace); + GLTF.QueryMeshes(gltf, out var skeletalMeshes, out var staticMeshes); + string specificMesh = null; + if (selectedMeshToReplace.ClassName == "SkeletalMesh") + { + var meshCount = skeletalMeshes.Count(); + if (meshCount == 0) + { + ShowError("You are trying to replace a skeletal mesh but the glTF file does not contain any skeletal meshes."); + return; + } + else if (meshCount > 1) + { + var prompt = new DropdownPromptDialog("Select which mesh to use to replace your mesh.", + "Select mesh", "Mesh", skeletalMeshes, window); + prompt.ShowDialog(); + if (prompt.DialogResult == true) + { + specificMesh = prompt.Response; + } + else { return; } + } + } + else if (selectedMeshToReplace.ClassName == "StaticMesh") + { + var meshCount = staticMeshes.Count(); + if (meshCount == 0) + { + ShowError("You are trying to replace a static mesh but the glTF file does not contain any static meshes."); + return; + } + else if (meshCount > 1) + { + var prompt = new DropdownPromptDialog("Select which mesh to use to replace your mesh.", + "Select mesh", "Mesh", staticMeshes, window); + prompt.ShowDialog(); + if (prompt.DialogResult == true) + { + specificMesh = prompt.Response; + } + else { return; } + } + } + GLTF.ConvertGltfToMesh(gltf, window.Pcc, selectedMeshToReplace, specificMesh); + } + } + + public static void ImportNewFromGltf(WPFBase window) + { + if (window.Pcc == null) + { + return; + } + if (window.Pcc.Game == MEGame.ME1) + { + ShowError("This experiment does not yet support OT1; if you must do this, import it into another game and port it to OT1"); + } + if (window.Pcc.Game == MEGame.UDK) + { + ShowError("This experiment does not support UDK files;"); + } + if (GetGltfFromFile(out var gltf, out string _)) + { + GLTF.QueryMeshes(gltf, out var skeletalMeshes, out var staticMeshes); + if (!skeletalMeshes.Any() && !staticMeshes.Any()) + { + ShowError("The gltf you are trying to import does not contain any meshes."); + return; + } + GLTF.ConvertGltfToMesh(gltf, window.Pcc); + } + } + + private static bool FilterSelectedItem(IEntry selectedItem, string[] expectedTypes, out ExportEntry entry) + { + entry = null; + if (selectedItem == null) + { + return false; + } + + foreach (var expectedType in expectedTypes) + { + if (selectedItem.IsA(expectedType)) + { + entry = (ExportEntry)selectedItem; + return entry != null; + } + } + return false; + } + + private static bool GetGltfFromFile(out SharpGLTF.Schema2.ModelRoot gltf, out string filePath) + { + var d = new OpenFileDialog + { + Filter = "gLTF|*.gltf;*.glb", + Title = "Select a gLTF or glb file" + }; + if (d.ShowDialog() == true) + { + filePath = d.FileName; + gltf = SharpGLTF.Schema2.ModelRoot.Load(filePath, SharpGLTF.Validation.ValidationMode.Skip); + return true; + } + + gltf = null; + filePath = null; + return false; + } + + private static void ShowError(string errMsg) + { + MessageBox.Show(errMsg, "Warning", MessageBoxButton.OK); + } + } +} diff --git a/LegendaryExplorer/LegendaryExplorer/Tools/Meshplorer/MeshplorerWindow.xaml b/LegendaryExplorer/LegendaryExplorer/Tools/Meshplorer/MeshplorerWindow.xaml index 4d42748037..eb38932409 100644 --- a/LegendaryExplorer/LegendaryExplorer/Tools/Meshplorer/MeshplorerWindow.xaml +++ b/LegendaryExplorer/LegendaryExplorer/Tools/Meshplorer/MeshplorerWindow.xaml @@ -59,6 +59,10 @@ + + + + @@ -186,6 +190,10 @@ + + + +