Improve file reading

This commit is contained in:
ackwell 2024-01-13 22:16:47 +11:00
parent db2081f14d
commit e8fd452b8f
3 changed files with 34 additions and 46 deletions

View file

@ -13,22 +13,16 @@ using ImageSharpConfiguration = SixLabors.ImageSharp.Configuration;
public class MaterialExporter public class MaterialExporter
{ {
// input stuff
public struct Material public struct Material
{ {
public MtrlFile Mtrl; public MtrlFile Mtrl;
public Sampler[] Samplers; public Dictionary<TextureUsage, Image<Rgba32>> Textures;
// variant? // variant?
} }
public struct Sampler
{
public TextureUsage Usage;
public Image<Rgba32> Texture;
}
public static MaterialBuilder Export(Material material, string name) public static MaterialBuilder Export(Material material, string name)
{ {
Penumbra.Log.Debug($"Exporting material \"{name}\".");
return material.Mtrl.ShaderPackage.Name switch return material.Mtrl.ShaderPackage.Name switch
{ {
"character.shpk" => BuildCharacter(material, name), "character.shpk" => BuildCharacter(material, name),
@ -40,11 +34,10 @@ public class MaterialExporter
{ {
// TODO: handle models with an underlying diffuse // TODO: handle models with an underlying diffuse
var table = material.Mtrl.Table; var table = material.Mtrl.Table;
// TODO: this should probably be a dict
var normal = material.Samplers // TODO: there's a few normal usages i should check, i think.
.Where(s => s.Usage == TextureUsage.SamplerNormal) // TODO: tryget
.First() var normal = material.Textures[TextureUsage.SamplerNormal];
.Texture;
var operation = new ProcessCharacterNormalOperation(normal, table); var operation = new ProcessCharacterNormalOperation(normal, table);
ParallelRowIterator.IterateRows(ImageSharpConfiguration.Default, normal.Bounds(), in operation); ParallelRowIterator.IterateRows(ImageSharpConfiguration.Default, normal.Bounds(), in operation);

View file

@ -39,8 +39,8 @@ public sealed class ModelManager(IFramework framework, ActiveCollections collect
_tasks.Clear(); _tasks.Clear();
} }
public Task ExportToGltf(MdlFile mdl, IEnumerable<SklbFile> sklbs, string outputPath) public Task ExportToGltf(MdlFile mdl, IEnumerable<string> sklbPaths, Func<string, byte[]> read, string outputPath)
=> Enqueue(new ExportToGltfAction(this, mdl, sklbs, outputPath)); => Enqueue(new ExportToGltfAction(this, mdl, sklbPaths, read, outputPath));
public Task<MdlFile?> ImportGltf(string inputPath) public Task<MdlFile?> ImportGltf(string inputPath)
{ {
@ -134,7 +134,7 @@ public sealed class ModelManager(IFramework framework, ActiveCollections collect
return task; return task;
} }
private class ExportToGltfAction(ModelManager manager, MdlFile mdl, IEnumerable<SklbFile> sklbs, string outputPath) private class ExportToGltfAction(ModelManager manager, MdlFile mdl, IEnumerable<string> sklbPaths, Func<string, byte[]> read, string outputPath)
: IAction : IAction
{ {
public void Execute(CancellationToken cancel) public void Execute(CancellationToken cancel)
@ -166,7 +166,8 @@ public sealed class ModelManager(IFramework framework, ActiveCollections collect
/// <summary> Attempt to read out the pertinent information from a .sklb. </summary> /// <summary> Attempt to read out the pertinent information from a .sklb. </summary>
private IEnumerable<XivSkeleton> BuildSkeletons(CancellationToken cancel) private IEnumerable<XivSkeleton> BuildSkeletons(CancellationToken cancel)
{ {
var havokTasks = sklbs var havokTasks = sklbPaths
.Select(path => new SklbFile(read(path)))
.WithIndex() .WithIndex()
.Select(CreateHavokTask) .Select(CreateHavokTask)
.ToArray(); .ToArray();
@ -197,29 +198,22 @@ public sealed class ModelManager(IFramework framework, ActiveCollections collect
if (absolutePath == null) if (absolutePath == null)
throw new Exception("Failed to resolve material path."); 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 mtrl = new MtrlFile(read(absolutePath));
var data = manager._gameData.GetFile(absolutePath);
if (data == null)
throw new Exception("Failed to fetch material game data.");
var mtrl = new MtrlFile(data.Data);
return new MaterialExporter.Material return new MaterialExporter.Material
{ {
Mtrl = mtrl, Mtrl = mtrl,
Samplers = mtrl.ShaderPackage.Samplers Textures = mtrl.ShaderPackage.Samplers.ToDictionary(
.Select(sampler => new MaterialExporter.Sampler sampler => (TextureUsage)sampler.SamplerId,
{ sampler => ConvertImage(mtrl.Textures[sampler.TextureIndex], cancel)
Usage = (TextureUsage)sampler.SamplerId, ),
Texture = ConvertImage(mtrl.Textures[sampler.TextureIndex], cancel),
})
.ToArray(),
}; };
} }
private Image<Rgba32> ConvertImage(MtrlFile.Texture texture, CancellationToken cancel) private Image<Rgba32> 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; var pngImage = TextureManager.ConvertToPng(image, cancel).AsPng;
if (pngImage == null) if (pngImage == null)
throw new Exception("Failed to convert texture to png."); throw new Exception("Failed to convert texture to png.");

View file

@ -116,11 +116,10 @@ public partial class ModEditWindow
/// <param name="mdlPath"> .mdl game path to resolve satellite files such as skeletons relative to. </param> /// <param name="mdlPath"> .mdl game path to resolve satellite files such as skeletons relative to. </param>
public void Export(string outputPath, Utf8GamePath mdlPath) public void Export(string outputPath, Utf8GamePath mdlPath)
{ {
IEnumerable<SklbFile> skeletons; IEnumerable<string> sklbPaths;
try try
{ {
var sklbPaths = _edit._models.ResolveSklbsForMdl(mdlPath.ToString(), GetCurrentEstManipulations()); sklbPaths = _edit._models.ResolveSklbsForMdl(mdlPath.ToString(), GetCurrentEstManipulations());
skeletons = sklbPaths.Select(ReadSklb).ToArray();
} }
catch (Exception exception) catch (Exception exception)
{ {
@ -129,7 +128,7 @@ public partial class ModEditWindow
} }
PendingIo = true; PendingIo = true;
_edit._models.ExportToGltf(Mdl, skeletons, outputPath) _edit._models.ExportToGltf(Mdl, sklbPaths, ReadFile, outputPath)
.ContinueWith(task => .ContinueWith(task =>
{ {
RecordIoExceptions(task.Exception); RecordIoExceptions(task.Exception);
@ -220,21 +219,23 @@ public partial class ModEditWindow
}; };
} }
/// <summary> Read a .sklb from the active collection or game. </summary> /// <summary> Read a file from the active collection or game. </summary>
/// <param name="sklbPath"> Game path to the .sklb to load. </param> /// <param name="path"> Game path to the file to load. </param>
private SklbFile ReadSklb(string sklbPath) // 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 // TODO: if cross-collection lookups are turned off, this conversion can be skipped
if (!Utf8GamePath.FromString(sklbPath, out var utf8SklbPath, true)) if (!Utf8GamePath.FromString(path, out var utf8SklbPath, true))
throw new Exception($"Resolved skeleton path {sklbPath} could not be converted to a game path."); throw new Exception($"Resolved path {path} could not be converted to a game path.");
var resolvedPath = _edit._activeCollections.Current.ResolvePath(utf8SklbPath); 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... // 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()); var bytes = resolvedPath == null
return bytes != null ? _edit._gameData.GetFile(path)?.Data
? new SklbFile(bytes) : File.ReadAllBytes(resolvedPath.Value.ToPath());
: throw new Exception(
$"Resolved skeleton path {sklbPath} could not be found. If modded, is it enabled in the current collection?"); return bytes ?? throw new Exception(
$"Resolved path {path} could not be found. If modded, is it enabled in the current collection?");
} }
/// <summary> Remove the material given by the index. </summary> /// <summary> Remove the material given by the index. </summary>