diff --git a/Penumbra/Import/Models/Export/MaterialExporter.cs b/Penumbra/Import/Models/Export/MaterialExporter.cs index de2f1425..dee386df 100644 --- a/Penumbra/Import/Models/Export/MaterialExporter.cs +++ b/Penumbra/Import/Models/Export/MaterialExporter.cs @@ -13,22 +13,16 @@ using ImageSharpConfiguration = SixLabors.ImageSharp.Configuration; public class MaterialExporter { - // input stuff public struct Material { public MtrlFile Mtrl; - public Sampler[] Samplers; + public Dictionary> Textures; // variant? } - public struct Sampler - { - public TextureUsage Usage; - public Image Texture; - } - public static MaterialBuilder Export(Material material, string name) { + Penumbra.Log.Debug($"Exporting material \"{name}\"."); return material.Mtrl.ShaderPackage.Name switch { "character.shpk" => BuildCharacter(material, name), @@ -40,11 +34,10 @@ public class MaterialExporter { // TODO: handle models with an underlying diffuse var table = material.Mtrl.Table; - // TODO: this should probably be a dict - var normal = material.Samplers - .Where(s => s.Usage == TextureUsage.SamplerNormal) - .First() - .Texture; + + // TODO: there's a few normal usages i should check, i think. + // TODO: tryget + var normal = material.Textures[TextureUsage.SamplerNormal]; var operation = new ProcessCharacterNormalOperation(normal, table); ParallelRowIterator.IterateRows(ImageSharpConfiguration.Default, normal.Bounds(), in operation); diff --git a/Penumbra/Import/Models/ModelManager.cs b/Penumbra/Import/Models/ModelManager.cs index ccf56fe8..4f652436 100644 --- a/Penumbra/Import/Models/ModelManager.cs +++ b/Penumbra/Import/Models/ModelManager.cs @@ -39,8 +39,8 @@ public sealed class ModelManager(IFramework framework, ActiveCollections collect _tasks.Clear(); } - public Task ExportToGltf(MdlFile mdl, IEnumerable sklbs, string outputPath) - => Enqueue(new ExportToGltfAction(this, mdl, sklbs, outputPath)); + public Task ExportToGltf(MdlFile mdl, IEnumerable sklbPaths, Func read, string outputPath) + => Enqueue(new ExportToGltfAction(this, mdl, sklbPaths, read, outputPath)); public Task ImportGltf(string inputPath) { @@ -134,7 +134,7 @@ public sealed class ModelManager(IFramework framework, ActiveCollections collect return task; } - private class ExportToGltfAction(ModelManager manager, MdlFile mdl, IEnumerable sklbs, string outputPath) + private class ExportToGltfAction(ModelManager manager, MdlFile mdl, IEnumerable sklbPaths, Func read, string outputPath) : IAction { public void Execute(CancellationToken cancel) @@ -166,7 +166,8 @@ public sealed class ModelManager(IFramework framework, ActiveCollections collect /// Attempt to read out the pertinent information from a .sklb. private IEnumerable BuildSkeletons(CancellationToken cancel) { - var havokTasks = sklbs + var havokTasks = sklbPaths + .Select(path => new SklbFile(read(path))) .WithIndex() .Select(CreateHavokTask) .ToArray(); @@ -197,29 +198,22 @@ public sealed class ModelManager(IFramework framework, ActiveCollections collect if (absolutePath == null) throw new Exception("Failed to resolve material path."); - // TODO: collection lookup and such. this is currently in mdltab (readsklb), and should be wholesale moved in here. - var data = manager._gameData.GetFile(absolutePath); - if (data == null) - throw new Exception("Failed to fetch material game data."); - - var mtrl = new MtrlFile(data.Data); + var mtrl = new MtrlFile(read(absolutePath)); return new MaterialExporter.Material { Mtrl = mtrl, - Samplers = mtrl.ShaderPackage.Samplers - .Select(sampler => new MaterialExporter.Sampler - { - Usage = (TextureUsage)sampler.SamplerId, - Texture = ConvertImage(mtrl.Textures[sampler.TextureIndex], cancel), - }) - .ToArray(), + Textures = mtrl.ShaderPackage.Samplers.ToDictionary( + sampler => (TextureUsage)sampler.SamplerId, + sampler => ConvertImage(mtrl.Textures[sampler.TextureIndex], cancel) + ), }; } private Image ConvertImage(MtrlFile.Texture texture, CancellationToken cancel) { - var (image, _) = manager._textureManager.Load(texture.Path); + using var textureData = new MemoryStream(read(texture.Path)); + var image = TexFileParser.Parse(textureData); var pngImage = TextureManager.ConvertToPng(image, cancel).AsPng; if (pngImage == null) throw new Exception("Failed to convert texture to png."); diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.MdlTab.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.MdlTab.cs index cdaf399f..f79d161e 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.MdlTab.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.MdlTab.cs @@ -116,11 +116,10 @@ public partial class ModEditWindow /// .mdl game path to resolve satellite files such as skeletons relative to. public void Export(string outputPath, Utf8GamePath mdlPath) { - IEnumerable skeletons; + IEnumerable sklbPaths; try { - var sklbPaths = _edit._models.ResolveSklbsForMdl(mdlPath.ToString(), GetCurrentEstManipulations()); - skeletons = sklbPaths.Select(ReadSklb).ToArray(); + sklbPaths = _edit._models.ResolveSklbsForMdl(mdlPath.ToString(), GetCurrentEstManipulations()); } catch (Exception exception) { @@ -129,7 +128,7 @@ public partial class ModEditWindow } PendingIo = true; - _edit._models.ExportToGltf(Mdl, skeletons, outputPath) + _edit._models.ExportToGltf(Mdl, sklbPaths, ReadFile, outputPath) .ContinueWith(task => { RecordIoExceptions(task.Exception); @@ -219,22 +218,24 @@ public partial class ModEditWindow Exception other => [other], }; } - - /// Read a .sklb from the active collection or game. - /// Game path to the .sklb to load. - private SklbFile ReadSklb(string sklbPath) + + /// Read a file from the active collection or game. + /// Game path to the file to load. + // TODO: Also look up files within the current mod regardless of mod state? + private byte[] ReadFile(string path) { // TODO: if cross-collection lookups are turned off, this conversion can be skipped - if (!Utf8GamePath.FromString(sklbPath, out var utf8SklbPath, true)) - throw new Exception($"Resolved skeleton path {sklbPath} could not be converted to a game path."); + if (!Utf8GamePath.FromString(path, out var utf8SklbPath, true)) + throw new Exception($"Resolved path {path} could not be converted to a game path."); var resolvedPath = _edit._activeCollections.Current.ResolvePath(utf8SklbPath); // TODO: is it worth trying to use streams for these instead? I'll need to do this for mtrl/tex too, so might be a good idea. that said, the mtrl reader doesn't accept streams, so... - var bytes = resolvedPath == null ? _edit._gameData.GetFile(sklbPath)?.Data : File.ReadAllBytes(resolvedPath.Value.ToPath()); - return bytes != null - ? new SklbFile(bytes) - : throw new Exception( - $"Resolved skeleton path {sklbPath} could not be found. If modded, is it enabled in the current collection?"); + var bytes = resolvedPath == null + ? _edit._gameData.GetFile(path)?.Data + : File.ReadAllBytes(resolvedPath.Value.ToPath()); + + return bytes ?? throw new Exception( + $"Resolved path {path} could not be found. If modded, is it enabled in the current collection?"); } /// Remove the material given by the index.