I don't know what I'm doing

This commit is contained in:
Ottermandias 2024-04-19 15:40:12 +02:00
parent 1641166d6e
commit ef1bbb6d9d
9 changed files with 65 additions and 59 deletions

@ -1 +1 @@
Subproject commit fe9d563d9845630673cf098f7a6bfbd26e600fb4
Subproject commit 845d1f99a752f4d23288a316e42d4bfa32fa987f

View file

@ -1,5 +1,6 @@
using Lumina.Data.Parsing;
using Penumbra.GameData.Files;
using Penumbra.GameData.Files.MaterialStructs;
using SharpGLTF.Materials;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Advanced;
@ -102,7 +103,7 @@ public class MaterialExporter
// TODO: It feels a little silly to request the entire normal here when extracting the normal only needs some of the components.
// As a future refactor, it would be neat to accept a single-channel field here, and then do composition of other stuff later.
private readonly struct ProcessCharacterNormalOperation(Image<Rgba32> normal, MtrlFile.ColorTable table) : IRowOperation
private readonly struct ProcessCharacterNormalOperation(Image<Rgba32> normal, ColorTable table) : IRowOperation
{
public Image<Rgba32> Normal { get; } = normal.Clone();
public Image<Rgba32> BaseColor { get; } = new(normal.Width, normal.Height);

View file

@ -3,6 +3,7 @@ using Lumina.Data.Parsing;
using Lumina.Extensions;
using OtterGui;
using Penumbra.GameData.Files;
using Penumbra.GameData.Files.ModelStructs;
using SharpGLTF.Geometry;
using SharpGLTF.Geometry.VertexTypes;
using SharpGLTF.IO;
@ -55,7 +56,7 @@ public class MeshExporter
private readonly byte _lod;
private readonly ushort _meshIndex;
private MdlStructs.MeshStruct XivMesh
private MeshStruct XivMesh
=> _mdl.Meshes[_meshIndex];
private readonly MaterialBuilder _material;
@ -109,8 +110,8 @@ public class MeshExporter
var xivBoneTable = _mdl.BoneTables[XivMesh.BoneTableIndex];
var indexMap = new Dictionary<ushort, int>();
foreach (var (xivBoneIndex, tableIndex) in xivBoneTable.BoneIndex.Take(xivBoneTable.BoneCount).WithIndex())
// #TODO @ackwell maybe fix for V6 Models, I think this works fine.
foreach (var (xivBoneIndex, tableIndex) in xivBoneTable.BoneIndex.Take((int)xivBoneTable.BoneCount).WithIndex())
{
var boneName = _mdl.Bones[xivBoneIndex];
if (!skeleton.Names.TryGetValue(boneName, out var gltfBoneIndex))
@ -238,19 +239,15 @@ public class MeshExporter
{ "targetNames", shapeNames },
});
string[] attributes = [];
var maxAttribute = 31 - BitOperations.LeadingZeroCount(attributeMask);
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
{
@ -278,7 +275,7 @@ public class MeshExporter
for (var streamIndex = 0; streamIndex < MaximumMeshBufferStreams; streamIndex++)
{
streams[streamIndex] = new BinaryReader(new MemoryStream(_mdl.RemainingData));
streams[streamIndex].Seek(_mdl.VertexOffset[_lod] + XivMesh.VertexBufferOffset[streamIndex]);
streams[streamIndex].Seek(_mdl.VertexOffset[_lod] + XivMesh.VertexBufferOffset(streamIndex));
}
var sortedElements = _mdl.VertexDeclarations[_meshIndex].VertexElements
@ -315,7 +312,7 @@ public class MeshExporter
MdlFile.VertexType.Single3 => new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()),
MdlFile.VertexType.Single4 => new Vector4(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()),
MdlFile.VertexType.UByte4 => reader.ReadBytes(4),
MdlFile.VertexType.NByte4 => new Vector4(reader.ReadByte() / 255f, reader.ReadByte() / 255f, reader.ReadByte() / 255f,
MdlFile.VertexType.NByte4 => new Vector4(reader.ReadByte() / 255f, reader.ReadByte() / 255f, reader.ReadByte() / 255f,
reader.ReadByte() / 255f),
MdlFile.VertexType.Half2 => new Vector2((float)reader.ReadHalf(), (float)reader.ReadHalf()),
MdlFile.VertexType.Half4 => new Vector4((float)reader.ReadHalf(), (float)reader.ReadHalf(), (float)reader.ReadHalf(),

View file

@ -1,5 +1,6 @@
using Lumina.Data.Parsing;
using OtterGui;
using Penumbra.GameData.Files.ModelStructs;
using SharpGLTF.Schema2;
namespace Penumbra.Import.Models.Import;
@ -8,7 +9,7 @@ public class MeshImporter(IEnumerable<Node> nodes, IoNotifier notifier)
{
public struct Mesh
{
public MdlStructs.MeshStruct MeshStruct;
public MeshStruct MeshStruct;
public List<MdlStructs.SubmeshStruct> SubMeshStructs;
public string? Material;
@ -69,10 +70,14 @@ public class MeshImporter(IEnumerable<Node> nodes, IoNotifier notifier)
return new Mesh
{
MeshStruct = new MdlStructs.MeshStruct
MeshStruct = new MeshStruct
{
VertexBufferOffset = [0, (uint)_streams[0].Count, (uint)(_streams[0].Count + _streams[1].Count)],
VertexBufferStride = _strides,
VertexBufferOffset1 = 0,
VertexBufferOffset2 = (uint)_streams[0].Count,
VertexBufferOffset3 = (uint)(_streams[0].Count + _streams[1].Count),
VertexBufferStride1 = _strides[0],
VertexBufferStride2 = _strides[1],
VertexBufferStride3 = _strides[2],
VertexCount = _vertexCount,
VertexStreamCount = (byte)_vertexDeclaration.Value.VertexElements
.Select(element => element.Stream + 1)

View file

@ -1,6 +1,7 @@
using Lumina.Data.Parsing;
using OtterGui;
using Penumbra.GameData.Files;
using Penumbra.GameData.Files.ModelStructs;
using SharpGLTF.Schema2;
namespace Penumbra.Import.Models.Import;
@ -14,10 +15,11 @@ public partial class ModelImporter(ModelRoot model, IoNotifier notifier)
}
// NOTE: This is intended to match TexTool's grouping regex, ".*[_ ^]([0-9]+)[\\.\\-]?([0-9]+)?$"
[GeneratedRegex(@"[_ ^](?'Mesh'[0-9]+)[.-]?(?'SubMesh'[0-9]+)?$", RegexOptions.Compiled | RegexOptions.NonBacktracking | RegexOptions.ExplicitCapture)]
[GeneratedRegex(@"[_ ^](?'Mesh'[0-9]+)[.-]?(?'SubMesh'[0-9]+)?$",
RegexOptions.Compiled | RegexOptions.NonBacktracking | RegexOptions.ExplicitCapture)]
private static partial Regex MeshNameGroupingRegex();
private readonly List<MdlStructs.MeshStruct> _meshes = [];
private readonly List<MeshStruct> _meshes = [];
private readonly List<MdlStructs.SubmeshStruct> _subMeshes = [];
private readonly List<string> _materials = [];
@ -27,10 +29,10 @@ public partial class ModelImporter(ModelRoot model, IoNotifier notifier)
private readonly List<ushort> _indices = [];
private readonly List<string> _bones = [];
private readonly List<MdlStructs.BoneTableStruct> _boneTables = [];
private readonly List<string> _bones = [];
private readonly List<BoneTableStruct> _boneTables = [];
private readonly BoundingBox _boundingBox = new BoundingBox();
private readonly BoundingBox _boundingBox = new();
private readonly List<string> _metaAttributes = [];
@ -95,9 +97,7 @@ public partial class ModelImporter(ModelRoot model, IoNotifier notifier)
IndexBufferSize = (uint)indexBuffer.Length,
},
],
Materials = [.. materials],
Materials = [.. materials],
BoundingBoxes = _boundingBox.ToStruct(),
// TODO: Would be good to calculate all of this up the tree.
@ -132,9 +132,9 @@ public partial class ModelImporter(ModelRoot model, IoNotifier notifier)
private void BuildMeshForGroup(IEnumerable<Node> subMeshNodes, int index)
{
// Record some offsets we'll be using later, before they get mutated with mesh values.
var subMeshOffset = _subMeshes.Count;
var vertexOffset = _vertexBuffer.Count;
var indexOffset = _indices.Count;
var subMeshOffset = _subMeshes.Count;
var vertexOffset = _vertexBuffer.Count;
var indexOffset = _indices.Count;
var mesh = MeshImporter.Import(subMeshNodes, notifier.WithContext($"Mesh {index}"));
var meshStartIndex = (uint)(mesh.MeshStruct.StartIndex + indexOffset);
@ -154,9 +154,9 @@ public partial class ModelImporter(ModelRoot model, IoNotifier notifier)
SubMeshIndex = (ushort)(mesh.MeshStruct.SubMeshIndex + subMeshOffset),
BoneTableIndex = boneTableIndex,
StartIndex = meshStartIndex,
VertexBufferOffset = mesh.MeshStruct.VertexBufferOffset
.Select(offset => (uint)(offset + vertexOffset))
.ToArray(),
VertexBufferOffset1 = (uint)(mesh.MeshStruct.VertexBufferOffset1 + vertexOffset),
VertexBufferOffset2 = (uint)(mesh.MeshStruct.VertexBufferOffset2 + vertexOffset),
VertexBufferOffset3 = (uint)(mesh.MeshStruct.VertexBufferOffset3 + vertexOffset),
});
_boundingBox.Merge(mesh.BoundingBox);
@ -196,7 +196,8 @@ public partial class ModelImporter(ModelRoot model, IoNotifier notifier)
// arrays, values is practically guaranteed to be the highest of the
// group, so a failure on any of them will be a failure on it.
if (_shapeValues.Count > ushort.MaxValue)
throw notifier.Exception($"Importing this file would require more than the maximum of {ushort.MaxValue} shape values.\nTry removing or applying shape keys that do not need to be changed at runtime in-game.");
throw notifier.Exception(
$"Importing this file would require more than the maximum of {ushort.MaxValue} shape values.\nTry removing or applying shape keys that do not need to be changed at runtime in-game.");
}
private ushort GetMaterialIndex(string materialName)
@ -216,6 +217,7 @@ public partial class ModelImporter(ModelRoot model, IoNotifier notifier)
return (ushort)count;
}
// #TODO @ackwell fix for V6 Models
private ushort BuildBoneTable(List<string> boneNames)
{
var boneIndices = new List<ushort>();
@ -238,7 +240,7 @@ public partial class ModelImporter(ModelRoot model, IoNotifier notifier)
Array.Copy(boneIndices.ToArray(), boneIndicesArray, boneIndices.Count);
var boneTableIndex = _boneTables.Count;
_boneTables.Add(new MdlStructs.BoneTableStruct()
_boneTables.Add(new BoneTableStruct()
{
BoneIndex = boneIndicesArray,
BoneCount = (byte)boneIndices.Count,

View file

@ -1,6 +1,5 @@
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
using Penumbra.GameData.Files;
using Penumbra.GameData.Interop;
using Penumbra.Interop.SafeHandles;
@ -9,7 +8,7 @@ namespace Penumbra.Interop.MaterialPreview;
public sealed unsafe class LiveColorTablePreviewer : LiveMaterialPreviewerBase
{
public const int TextureWidth = 4;
public const int TextureHeight = MtrlFile.ColorTable.NumRows;
public const int TextureHeight = GameData.Files.MaterialStructs.ColorTable.NumUsedRows;
public const int TextureLength = TextureWidth * TextureHeight * 4;
private readonly IFramework _framework;
@ -17,7 +16,7 @@ public sealed unsafe class LiveColorTablePreviewer : LiveMaterialPreviewerBase
private readonly Texture** _colorTableTexture;
private readonly SafeTextureHandle _originalColorTableTexture;
private bool _updatePending;
private bool _updatePending;
public Half[] ColorTable { get; }
@ -40,7 +39,7 @@ public sealed unsafe class LiveColorTablePreviewer : LiveMaterialPreviewerBase
if (_originalColorTableTexture == null)
throw new InvalidOperationException("Material doesn't have a color table");
ColorTable = new Half[TextureLength];
ColorTable = new Half[TextureLength];
_updatePending = true;
framework.Update += OnFrameworkUpdate;

View file

@ -4,6 +4,7 @@ using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
using Penumbra.GameData.Files;
using Penumbra.GameData.Files.MaterialStructs;
using Penumbra.String.Functions;
namespace Penumbra.UI.AdvancedWindow;
@ -74,7 +75,7 @@ public partial class ModEditWindow
ImGui.TableHeader("Dye Preview");
}
for (var i = 0; i < MtrlFile.ColorTable.NumRows; ++i)
for (var i = 0; i < ColorTable.NumUsedRows; ++i)
{
ret |= DrawColorTableRow(tab, i, disabled);
ImGui.TableNextRow();
@ -115,8 +116,8 @@ public partial class ModEditWindow
{
var ret = false;
if (tab.Mtrl.HasDyeTable)
for (var i = 0; i < MtrlFile.ColorTable.NumRows; ++i)
ret |= tab.Mtrl.ApplyDyeTemplate(_stainService.StmFile, i, dyeId);
for (var i = 0; i < ColorTable.NumUsedRows; ++i)
ret |= tab.Mtrl.ApplyDyeTemplate(_stainService.StmFile, i, dyeId, 0);
tab.UpdateColorTablePreview();
@ -140,21 +141,21 @@ public partial class ModEditWindow
{
var text = ImGui.GetClipboardText();
var data = Convert.FromBase64String(text);
if (data.Length < Marshal.SizeOf<MtrlFile.ColorTable>())
if (data.Length < Marshal.SizeOf<ColorTable>())
return false;
ref var rows = ref tab.Mtrl.Table;
fixed (void* ptr = data, output = &rows)
{
MemoryUtility.MemCpyUnchecked(output, ptr, Marshal.SizeOf<MtrlFile.ColorTable>());
if (data.Length >= Marshal.SizeOf<MtrlFile.ColorTable>() + Marshal.SizeOf<MtrlFile.ColorDyeTable>()
MemoryUtility.MemCpyUnchecked(output, ptr, Marshal.SizeOf<ColorTable>());
if (data.Length >= Marshal.SizeOf<ColorTable>() + Marshal.SizeOf<ColorDyeTable>()
&& tab.Mtrl.HasDyeTable)
{
ref var dyeRows = ref tab.Mtrl.DyeTable;
fixed (void* output2 = &dyeRows)
{
MemoryUtility.MemCpyUnchecked(output2, (byte*)ptr + Marshal.SizeOf<MtrlFile.ColorTable>(),
Marshal.SizeOf<MtrlFile.ColorDyeTable>());
MemoryUtility.MemCpyUnchecked(output2, (byte*)ptr + Marshal.SizeOf<ColorTable>(),
Marshal.SizeOf<ColorDyeTable>());
}
}
}
@ -169,7 +170,7 @@ public partial class ModEditWindow
}
}
private static unsafe void ColorTableCopyClipboardButton(MtrlFile.ColorTable.Row row, MtrlFile.ColorDyeTable.Row dye)
private static unsafe void ColorTableCopyClipboardButton(ColorTable.Row row, ColorDyeTable.Row dye)
{
if (!ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Clipboard.ToIconString(), ImGui.GetFrameHeight() * Vector2.One,
"Export this row to your clipboard.", false, true))
@ -177,11 +178,11 @@ public partial class ModEditWindow
try
{
var data = new byte[MtrlFile.ColorTable.Row.Size + 2];
var data = new byte[ColorTable.Row.Size + 2];
fixed (byte* ptr = data)
{
MemoryUtility.MemCpyUnchecked(ptr, &row, MtrlFile.ColorTable.Row.Size);
MemoryUtility.MemCpyUnchecked(ptr + MtrlFile.ColorTable.Row.Size, &dye, 2);
MemoryUtility.MemCpyUnchecked(ptr, &row, ColorTable.Row.Size);
MemoryUtility.MemCpyUnchecked(ptr + ColorTable.Row.Size, &dye, 2);
}
var text = Convert.ToBase64String(data);
@ -217,15 +218,15 @@ public partial class ModEditWindow
{
var text = ImGui.GetClipboardText();
var data = Convert.FromBase64String(text);
if (data.Length != MtrlFile.ColorTable.Row.Size + 2
if (data.Length != ColorTable.Row.Size + 2
|| !tab.Mtrl.HasTable)
return false;
fixed (byte* ptr = data)
{
tab.Mtrl.Table[rowIdx] = *(MtrlFile.ColorTable.Row*)ptr;
tab.Mtrl.Table[rowIdx] = *(ColorTable.Row*)ptr;
if (tab.Mtrl.HasDyeTable)
tab.Mtrl.DyeTable[rowIdx] = *(MtrlFile.ColorDyeTable.Row*)(ptr + MtrlFile.ColorTable.Row.Size);
tab.Mtrl.DyeTable[rowIdx] = *(ColorDyeTable.Row*)(ptr + ColorTable.Row.Size);
}
tab.UpdateColorTableRowPreview(rowIdx);
@ -451,7 +452,7 @@ public partial class ModEditWindow
return ret;
}
private bool DrawDyePreview(MtrlTab tab, int rowIdx, bool disabled, MtrlFile.ColorDyeTable.Row dye, float floatSize)
private bool DrawDyePreview(MtrlTab tab, int rowIdx, bool disabled, ColorDyeTable.Row dye, float floatSize)
{
var stain = _stainService.StainCombo.CurrentSelection.Key;
if (stain == 0 || !_stainService.StmFile.Entries.TryGetValue(dye.Template, out var entry))
@ -463,7 +464,7 @@ public partial class ModEditWindow
var ret = ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.PaintBrush.ToIconString(), new Vector2(ImGui.GetFrameHeight()),
"Apply the selected dye to this row.", disabled, true);
ret = ret && tab.Mtrl.ApplyDyeTemplate(_stainService.StmFile, rowIdx, stain);
ret = ret && tab.Mtrl.ApplyDyeTemplate(_stainService.StmFile, rowIdx, stain, 0);
if (ret)
tab.UpdateColorTableRowPreview(rowIdx);

View file

@ -8,6 +8,7 @@ using OtterGui.Classes;
using OtterGui.Raii;
using Penumbra.GameData.Data;
using Penumbra.GameData.Files;
using Penumbra.GameData.Files.MaterialStructs;
using Penumbra.GameData.Structs;
using Penumbra.Interop.Hooks.Objects;
using Penumbra.Interop.MaterialPreview;
@ -601,7 +602,7 @@ public partial class ModEditWindow
var stm = _edit._stainService.StmFile;
var dye = Mtrl.DyeTable[rowIdx];
if (stm.TryGetValue(dye.Template, _edit._stainService.StainCombo.CurrentSelection.Key, out var dyes))
row.ApplyDyeTemplate(dye, dyes);
row.ApplyDyeTemplate(dye, dyes, default);
}
if (HighlightedColorTableRow == rowIdx)
@ -628,12 +629,12 @@ public partial class ModEditWindow
{
var stm = _edit._stainService.StmFile;
var stainId = (StainId)_edit._stainService.StainCombo.CurrentSelection.Key;
for (var i = 0; i < MtrlFile.ColorTable.NumRows; ++i)
for (var i = 0; i < ColorTable.NumUsedRows; ++i)
{
ref var row = ref rows[i];
var dye = Mtrl.DyeTable[i];
if (stm.TryGetValue(dye.Template, stainId, out var dyes))
row.ApplyDyeTemplate(dye, dyes);
row.ApplyDyeTemplate(dye, dyes, default);
}
}
@ -647,7 +648,7 @@ public partial class ModEditWindow
}
}
private static void ApplyHighlight(ref MtrlFile.ColorTable.Row row, float time)
private static void ApplyHighlight(ref ColorTable.Row row, float time)
{
var level = (MathF.Sin(time * 2.0f * MathF.PI) + 2.0f) / 3.0f / 255.0f;
var baseColor = ColorId.InGameHighlight.Value();

View file

@ -483,7 +483,7 @@ public partial class ModEditWindow
if (table)
{
ImGuiUtil.DrawTableColumn("Version");
ImGuiUtil.DrawTableColumn(_lastFile.Version.ToString());
ImGuiUtil.DrawTableColumn($"0x{_lastFile.Version:X}");
ImGuiUtil.DrawTableColumn("Radius");
ImGuiUtil.DrawTableColumn(_lastFile.Radius.ToString(CultureInfo.InvariantCulture));
ImGuiUtil.DrawTableColumn("Model Clip Out Distance");