mirror of
https://github.com/xivdev/Penumbra.git
synced 2026-01-02 05:43:42 +01:00
Merge branch 'mdl-io-triage-4'
This commit is contained in:
commit
8a0d217977
4 changed files with 79 additions and 45 deletions
|
|
@ -89,11 +89,15 @@ public class MaterialExporter
|
|||
// TODO: handle other textures stored in the mask?
|
||||
}
|
||||
|
||||
// Specular extension puts colour on RGB and factor on A. We're already packing like that, so we can reuse the texture.
|
||||
var specularImage = BuildImage(specular, name, "specular");
|
||||
|
||||
return BuildSharedBase(material, name)
|
||||
.WithBaseColor(BuildImage(baseColor, name, "basecolor"))
|
||||
.WithNormal(BuildImage(operation.Normal, name, "normal"))
|
||||
.WithSpecularColor(BuildImage(specular, name, "specular"))
|
||||
.WithEmissive(BuildImage(operation.Emissive, name, "emissive"), Vector3.One, 1);
|
||||
.WithEmissive(BuildImage(operation.Emissive, name, "emissive"), Vector3.One, 1)
|
||||
.WithSpecularFactor(specularImage, 1)
|
||||
.WithSpecularColor(specularImage);
|
||||
}
|
||||
|
||||
// TODO: It feels a little silly to request the entire normal here when extracting the normal only needs some of the components.
|
||||
|
|
@ -102,7 +106,7 @@ public class MaterialExporter
|
|||
{
|
||||
public Image<Rgba32> Normal { get; } = normal.Clone();
|
||||
public Image<Rgba32> BaseColor { get; } = new(normal.Width, normal.Height);
|
||||
public Image<Rgb24> Specular { get; } = new(normal.Width, normal.Height);
|
||||
public Image<Rgba32> Specular { get; } = new(normal.Width, normal.Height);
|
||||
public Image<Rgb24> Emissive { get; } = new(normal.Width, normal.Height);
|
||||
|
||||
private Buffer2D<Rgba32> NormalBuffer
|
||||
|
|
@ -111,7 +115,7 @@ public class MaterialExporter
|
|||
private Buffer2D<Rgba32> BaseColorBuffer
|
||||
=> BaseColor.Frames.RootFrame.PixelBuffer;
|
||||
|
||||
private Buffer2D<Rgb24> SpecularBuffer
|
||||
private Buffer2D<Rgba32> SpecularBuffer
|
||||
=> Specular.Frames.RootFrame.PixelBuffer;
|
||||
|
||||
private Buffer2D<Rgb24> EmissiveBuffer
|
||||
|
|
@ -140,7 +144,9 @@ public class MaterialExporter
|
|||
|
||||
// Specular (table)
|
||||
var lerpedSpecularColor = Vector3.Lerp(prevRow.Specular, nextRow.Specular, tableRow.Weight);
|
||||
specularSpan[x].FromVector4(new Vector4(lerpedSpecularColor, 1));
|
||||
// float.Lerp is .NET8 ;-; #TODO
|
||||
var lerpedSpecularFactor = prevRow.SpecularStrength * (1.0f - tableRow.Weight) + nextRow.SpecularStrength * tableRow.Weight;
|
||||
specularSpan[x].FromVector4(new Vector4(lerpedSpecularColor, lerpedSpecularFactor));
|
||||
|
||||
// Emissive (table)
|
||||
var lerpedEmissive = Vector3.Lerp(prevRow.Emissive, nextRow.Emissive, tableRow.Weight);
|
||||
|
|
|
|||
|
|
@ -230,10 +230,19 @@ public class MeshExporter
|
|||
{ "targetNames", shapeNames },
|
||||
});
|
||||
|
||||
var attributes = Enumerable.Range(0, 32)
|
||||
.Where(index => ((attributeMask >> index) & 1) == 1)
|
||||
.Select(index => _mdl.Attributes[index])
|
||||
.ToArray();
|
||||
string[] attributes = [];
|
||||
var maxAttribute = 31 - BitOperations.LeadingZeroCount(attributeMask);
|
||||
if (maxAttribute < _mdl.Attributes.Length)
|
||||
{
|
||||
attributes = Enumerable.Range(0, 32)
|
||||
.Where(index => ((attributeMask >> index) & 1) == 1)
|
||||
.Select(index => _mdl.Attributes[index])
|
||||
.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
_notifier.Warning("Invalid attribute data, ignoring.");
|
||||
}
|
||||
|
||||
return new MeshData
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ and there's reason to overhaul the export pipeline.
|
|||
|
||||
public struct VertexColorFfxiv : IVertexCustom
|
||||
{
|
||||
[VertexAttribute("_FFXIV_COLOR", EncodingType.UNSIGNED_BYTE, false)]
|
||||
// NOTE: We only realistically require UNSIGNED_BYTE for this, however Blender 3.6 errors on that (fixed in 4.0).
|
||||
[VertexAttribute("_FFXIV_COLOR", EncodingType.UNSIGNED_SHORT, false)]
|
||||
public Vector4 FfxivColor;
|
||||
|
||||
public int MaxColors => 0;
|
||||
|
|
@ -80,7 +81,7 @@ public struct VertexTexture1ColorFfxiv : IVertexCustom
|
|||
[VertexAttribute("TEXCOORD_0")]
|
||||
public Vector2 TexCoord0;
|
||||
|
||||
[VertexAttribute("_FFXIV_COLOR", EncodingType.UNSIGNED_BYTE, false)]
|
||||
[VertexAttribute("_FFXIV_COLOR", EncodingType.UNSIGNED_SHORT, false)]
|
||||
public Vector4 FfxivColor;
|
||||
|
||||
public int MaxColors => 0;
|
||||
|
|
@ -162,7 +163,7 @@ public struct VertexTexture2ColorFfxiv : IVertexCustom
|
|||
[VertexAttribute("TEXCOORD_1")]
|
||||
public Vector2 TexCoord1;
|
||||
|
||||
[VertexAttribute("_FFXIV_COLOR", EncodingType.UNSIGNED_BYTE, false)]
|
||||
[VertexAttribute("_FFXIV_COLOR", EncodingType.UNSIGNED_SHORT, false)]
|
||||
public Vector4 FfxivColor;
|
||||
|
||||
public int MaxColors => 0;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using Lumina.Data.Parsing;
|
|||
using OtterGui;
|
||||
using Penumbra.GameData;
|
||||
using Penumbra.GameData.Files;
|
||||
using Penumbra.Import.Models;
|
||||
using Penumbra.Import.Models.Export;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.String.Classes;
|
||||
|
|
@ -27,8 +28,8 @@ public partial class ModEditWindow
|
|||
|
||||
private bool _dirty;
|
||||
public bool PendingIo { get; private set; }
|
||||
public List<Exception> IoExceptions { get; private set; } = [];
|
||||
public List<string> IoWarnings { get; private set; } = [];
|
||||
public List<Exception> IoExceptions { get; } = [];
|
||||
public List<string> IoWarnings { get; } = [];
|
||||
|
||||
public MdlTab(ModEditWindow edit, byte[] bytes, string path)
|
||||
{
|
||||
|
|
@ -79,7 +80,7 @@ public partial class ModEditWindow
|
|||
return;
|
||||
}
|
||||
|
||||
PendingIo = true;
|
||||
BeginIo();
|
||||
var task = Task.Run(() =>
|
||||
{
|
||||
// TODO: Is it worth trying to order results based on option priorities for cases where more than one match is found?
|
||||
|
|
@ -91,12 +92,7 @@ public partial class ModEditWindow
|
|||
.ToList();
|
||||
});
|
||||
|
||||
task.ContinueWith(t =>
|
||||
{
|
||||
RecordIoExceptions(t.Exception);
|
||||
GamePaths = t.Result;
|
||||
PendingIo = false;
|
||||
});
|
||||
task.ContinueWith(t => { GamePaths = FinalizeIo(t); });
|
||||
}
|
||||
|
||||
private EstManipulation[] GetCurrentEstManipulations()
|
||||
|
|
@ -132,33 +128,22 @@ public partial class ModEditWindow
|
|||
return;
|
||||
}
|
||||
|
||||
PendingIo = true;
|
||||
BeginIo();
|
||||
_edit._models.ExportToGltf(ExportConfig, Mdl, sklbPaths, ReadFile, outputPath)
|
||||
.ContinueWith(task =>
|
||||
{
|
||||
RecordIoExceptions(task.Exception);
|
||||
if (task is { IsCompletedSuccessfully: true, Result: not null })
|
||||
IoWarnings = task.Result.GetWarnings().ToList();
|
||||
PendingIo = false;
|
||||
});
|
||||
.ContinueWith(FinalizeIo);
|
||||
}
|
||||
|
||||
/// <summary> Import a model from an interchange format. </summary>
|
||||
/// <param name="inputPath"> Disk path to load model data from. </param>
|
||||
public void Import(string inputPath)
|
||||
{
|
||||
PendingIo = true;
|
||||
BeginIo();
|
||||
_edit._models.ImportGltf(inputPath)
|
||||
.ContinueWith(task =>
|
||||
{
|
||||
RecordIoExceptions(task.Exception);
|
||||
if (task is { IsCompletedSuccessfully: true, Result: (not null, _) })
|
||||
{
|
||||
IoWarnings = task.Result.Item2.GetWarnings().ToList();
|
||||
FinalizeImport(task.Result.Item1);
|
||||
}
|
||||
|
||||
PendingIo = false;
|
||||
var mdlFile = FinalizeIo(task, result => result.Item1, result => result.Item2);
|
||||
if (mdlFile != null)
|
||||
FinalizeImport(mdlFile);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -186,7 +171,7 @@ public partial class ModEditWindow
|
|||
/// <summary> Merge material configuration from the source onto the target. </summary>
|
||||
/// <param name="target"> Model that will be updated. </param>
|
||||
/// <param name="source"> Model to copy material configuration from. </param>
|
||||
public void MergeMaterials(MdlFile target, MdlFile source)
|
||||
private static void MergeMaterials(MdlFile target, MdlFile source)
|
||||
{
|
||||
target.Materials = source.Materials;
|
||||
|
||||
|
|
@ -201,7 +186,7 @@ public partial class ModEditWindow
|
|||
/// <summary> Merge attribute configuration from the source onto the target. </summary>
|
||||
/// <param name="target"> Model that will be updated. ></param>
|
||||
/// <param name="source"> Model to copy attribute configuration from. </param>
|
||||
public static void MergeAttributes(MdlFile target, MdlFile source)
|
||||
private static void MergeAttributes(MdlFile target, MdlFile source)
|
||||
{
|
||||
target.Attributes = source.Attributes;
|
||||
|
||||
|
|
@ -255,14 +240,47 @@ public partial class ModEditWindow
|
|||
target.ElementIds = [.. elementIds];
|
||||
}
|
||||
|
||||
private void BeginIo()
|
||||
{
|
||||
PendingIo = true;
|
||||
IoWarnings.Clear();
|
||||
IoExceptions.Clear();
|
||||
}
|
||||
|
||||
private void FinalizeIo(Task<IoNotifier> task)
|
||||
=> FinalizeIo<IoNotifier, object?>(task, _ => null, notifier => notifier);
|
||||
|
||||
private TResult? FinalizeIo<TResult>(Task<TResult> task)
|
||||
=> FinalizeIo(task, result => result, null);
|
||||
|
||||
private TResult? FinalizeIo<TTask, TResult>(Task<TTask> task, Func<TTask, TResult> getResult, Func<TTask, IoNotifier>? getNotifier)
|
||||
{
|
||||
TResult? result = default;
|
||||
RecordIoExceptions(task.Exception);
|
||||
if (task is { IsCompletedSuccessfully: true, Result: not null })
|
||||
{
|
||||
result = getResult(task.Result);
|
||||
if (getNotifier != null)
|
||||
IoWarnings.AddRange(getNotifier(task.Result).GetWarnings());
|
||||
}
|
||||
|
||||
PendingIo = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void RecordIoExceptions(Exception? exception)
|
||||
{
|
||||
IoExceptions = exception switch
|
||||
switch (exception)
|
||||
{
|
||||
null => [],
|
||||
AggregateException ae => [.. ae.Flatten().InnerExceptions],
|
||||
_ => [exception],
|
||||
};
|
||||
case null: break;
|
||||
case AggregateException ae:
|
||||
IoExceptions.AddRange(ae.Flatten().InnerExceptions);
|
||||
break;
|
||||
default:
|
||||
IoExceptions.Add(exception);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Read a file from the active collection or game. </summary>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue