mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-13 12:14:17 +01:00
Improve file reading
This commit is contained in:
parent
db2081f14d
commit
e8fd452b8f
3 changed files with 34 additions and 46 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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.");
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue