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
{
// input stuff
public struct Material
{
public MtrlFile Mtrl;
public Sampler[] Samplers;
public Dictionary<TextureUsage, Image<Rgba32>> Textures;
// variant?
}
public struct Sampler
{
public TextureUsage Usage;
public Image<Rgba32> 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);

View file

@ -39,8 +39,8 @@ public sealed class ModelManager(IFramework framework, ActiveCollections collect
_tasks.Clear();
}
public Task ExportToGltf(MdlFile mdl, IEnumerable<SklbFile> sklbs, string outputPath)
=> Enqueue(new ExportToGltfAction(this, mdl, sklbs, outputPath));
public Task ExportToGltf(MdlFile mdl, IEnumerable<string> sklbPaths, Func<string, byte[]> read, string outputPath)
=> Enqueue(new ExportToGltfAction(this, mdl, sklbPaths, read, outputPath));
public Task<MdlFile?> 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<SklbFile> sklbs, string outputPath)
private class ExportToGltfAction(ModelManager manager, MdlFile mdl, IEnumerable<string> sklbPaths, Func<string, byte[]> read, string outputPath)
: IAction
{
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>
private IEnumerable<XivSkeleton> 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<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;
if (pngImage == null)
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>
public void Export(string outputPath, Utf8GamePath mdlPath)
{
IEnumerable<SklbFile> skeletons;
IEnumerable<string> 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],
};
}
/// <summary> Read a .sklb from the active collection or game. </summary>
/// <param name="sklbPath"> Game path to the .sklb to load. </param>
private SklbFile ReadSklb(string sklbPath)
/// <summary> Read a file from the active collection or game. </summary>
/// <param name="path"> Game path to the file to load. </param>
// 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?");
}
/// <summary> Remove the material given by the index. </summary>