Spike submeshes

This commit is contained in:
ackwell 2024-01-05 01:03:54 +11:00
parent 79de6f1714
commit 4e8695e7a4

View file

@ -1,5 +1,6 @@
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using Lumina.Data.Parsing; using Lumina.Data.Parsing;
using OtterGui;
using OtterGui.Tasks; using OtterGui.Tasks;
using Penumbra.Collections.Manager; using Penumbra.Collections.Manager;
using Penumbra.GameData.Files; using Penumbra.GameData.Files;
@ -13,7 +14,7 @@ using SharpGLTF.Schema2;
namespace Penumbra.Import.Models; namespace Penumbra.Import.Models;
public sealed class ModelManager : SingleTaskQueue, IDisposable public sealed partial class ModelManager : SingleTaskQueue, IDisposable
{ {
private readonly IFramework _framework; private readonly IFramework _framework;
private readonly IDataManager _gameData; private readonly IDataManager _gameData;
@ -122,8 +123,12 @@ public sealed class ModelManager : SingleTaskQueue, IDisposable
} }
} }
private class ImportGltfAction : IAction private partial class ImportGltfAction : IAction
{ {
// TODO: clean this up a bit, i don't actually need all of it.
[GeneratedRegex(@".*[_ ^](?'Mesh'[0-9]+)[\\.\\-]?([0-9]+)?$", RegexOptions.Compiled)]
private static partial Regex MeshNameGroupingRegex();
public MdlFile? Out; public MdlFile? Out;
public ImportGltfAction() public ImportGltfAction()
@ -174,10 +179,25 @@ public sealed class ModelManager : SingleTaskQueue, IDisposable
var model = ModelRoot.Load("C:\\Users\\ackwell\\blender\\gltf-tests\\c0201e6180_top.gltf"); var model = ModelRoot.Load("C:\\Users\\ackwell\\blender\\gltf-tests\\c0201e6180_top.gltf");
// TODO: for grouping, should probably use `node.name ?? mesh.name`, as which are set seems to depend on the exporter. // TODO: for grouping, should probably use `node.name ?? mesh.name`, as which are set seems to depend on the exporter.
// var nodes = model.LogicalNodes
// .Where(node => node.Mesh != null)
// // TODO: I'm just grabbing the first 3, as that will contain 0.0, 0.1, and 1.0. testing, and all that.
// .Take(3);
// tt uses this
// ".*[_ ^]([0-9]+)[\\.\\-]?([0-9]+)?$"
var nodes = model.LogicalNodes var nodes = model.LogicalNodes
.Where(node => node.Mesh != null) .Where(node => node.Mesh != null)
// TODO: I'm just grabbing the first 3, as that will contain 0.0, 0.1, and 1.0. testing, and all that. .Take(6) // this model has all 3 lods in it - the first 6 are the real lod0
.Take(3); .SelectWhere(node => {
var name = node.Name ?? node.Mesh.Name;
var match = MeshNameGroupingRegex().Match(name);
return match.Success
? (true, (node, int.Parse(match.Groups["Mesh"].Value)))
: (false, (node, -1));
})
.GroupBy(pair => pair.Item2, pair => pair.node)
.OrderBy(group => group.Key);
// this is a representation of a single LoD // this is a representation of a single LoD
var vertexDeclarations = new List<MdlStructs.VertexDeclarationStruct>(); var vertexDeclarations = new List<MdlStructs.VertexDeclarationStruct>();
@ -187,7 +207,7 @@ public sealed class ModelManager : SingleTaskQueue, IDisposable
var vertexBuffer = new List<byte>(); var vertexBuffer = new List<byte>();
var indices = new List<byte>(); var indices = new List<byte>();
foreach (var node in nodes) foreach (var submeshnodes in nodes)
{ {
var boneTableOffset = boneTables.Count; var boneTableOffset = boneTables.Count;
var meshOffset = meshes.Count; var meshOffset = meshes.Count;
@ -199,10 +219,10 @@ public sealed class ModelManager : SingleTaskQueue, IDisposable
vertexDeclaration, vertexDeclaration,
boneTable, boneTable,
xivMesh, xivMesh,
xivSubmesh, xivSubmeshes,
meshVertexBuffer, meshVertexBuffer,
meshIndices meshIndices
) = MeshThing(node); ) = MeshThing(submeshnodes);
vertexDeclarations.Add(vertexDeclaration); vertexDeclarations.Add(vertexDeclaration);
boneTables.Add(boneTable); boneTables.Add(boneTable);
@ -215,12 +235,14 @@ public sealed class ModelManager : SingleTaskQueue, IDisposable
.Select(offset => (uint)(offset + vertOffset)) .Select(offset => (uint)(offset + vertOffset))
.ToArray(), .ToArray(),
}); });
submeshes.Add(xivSubmesh with { // TODO: could probably do this with linq cleaner
// TODO: this will need to keep ticking up for each submesh in the same mesh foreach (var xivSubmesh in xivSubmeshes)
IndexOffset = (uint)(xivSubmesh.IndexOffset + idxOffset / sizeof(ushort)) submeshes.Add(xivSubmesh with {
}); // TODO: this will need to keep ticking up for each submesh in the same mesh
IndexOffset = (uint)(xivSubmesh.IndexOffset + idxOffset / sizeof(ushort))
});
vertexBuffer.AddRange(meshVertexBuffer); vertexBuffer.AddRange(meshVertexBuffer);
indices.AddRange(meshIndices); indices.AddRange(meshIndices.SelectMany(index => BitConverter.GetBytes((ushort)index)));
} }
var mdl = new MdlFile() var mdl = new MdlFile()
@ -295,14 +317,99 @@ public sealed class ModelManager : SingleTaskQueue, IDisposable
} }
// this return type is an absolute meme, class that shit up. // this return type is an absolute meme, class that shit up.
public ( private (
MdlStructs.VertexDeclarationStruct, MdlStructs.VertexDeclarationStruct,
MdlStructs.BoneTableStruct, MdlStructs.BoneTableStruct,
MdlStructs.MeshStruct, MdlStructs.MeshStruct,
MdlStructs.SubmeshStruct, IEnumerable<MdlStructs.SubmeshStruct>,
IEnumerable<byte>, IEnumerable<byte>,
IEnumerable<byte> IEnumerable<ushort>
) MeshThing(Node node) ) MeshThing(IEnumerable<Node> nodes)
{
var vertexDeclaration = new MdlStructs.VertexDeclarationStruct() { VertexElements = Array.Empty<MdlStructs.VertexElement>()};
var vertexCount = (ushort)0;
// there's gotta be a better way to do this with streams or enumerables or something, surely
var streams = new List<byte>[3];
for (var i = 0; i < 3; i++)
streams[i] = new List<byte>();
var indexCount = (uint)0;
var indices = new List<ushort>();
var strides = new byte[] {0, 0, 0};
var submeshes = new List<MdlStructs.SubmeshStruct>();
// TODO: check that attrs/elems/strides match - we should be generating per-mesh stuff for sanity's sake, but we need to make sure they match if there's >1 node mesh in a mesh.
foreach (var node in nodes)
{
var vertOff = vertexCount;
var idxOff = indexCount;
var (vertDecl, newStrides, submesh, vertCount, vertStreams, idxCount, idxs) = NodeMeshThing(node);
vertexDeclaration = vertDecl; // TODO: CHECK EQUAL AFTER FIRST
strides = newStrides; // ALSO CHECK EQUAL
vertexCount += vertCount;
for (var i = 0; i < 3; i++)
streams[i].AddRange(vertStreams[i]);
indexCount += idxCount;
// we need to offset the indexes to point into the new stuff
indices.AddRange(idxs.Select(idx => (ushort)(idx + vertOff)));
submeshes.Add(submesh with {
IndexOffset = submesh.IndexOffset + idxOff
// TODO: bone stuff probably
});
}
// one of these per skinned mesh.
// TODO: check if mesh has skinning at all. (err if mixed?)
var boneTable = new MdlStructs.BoneTableStruct()
{
BoneCount = 1,
// this needs to be the full 64. this should be fine _here_ with 0s because i only have one bone, but will need to be fully populated properly. in real files.
BoneIndex = new ushort[64],
};
// mesh
var xivMesh = new MdlStructs.MeshStruct()
{
// TODO: sum across submeshes.
// TODO: would be cool to share verts on submesh boundaries but that's way out of scope for now.
VertexCount = vertexCount,
IndexCount = indexCount,
// TODO: will have to think about how to represent this - materials can be named, so maybe adjust in parent?
MaterialIndex = 0,
// TODO: this will need adjusting by parent
SubMeshIndex = 0,
SubMeshCount = (ushort)submeshes.Count,
// TODO: update in parent
BoneTableIndex = 0,
// TODO: this is relative to the lod's index buffer, and is an index, not byte offset
StartIndex = 0,
// TODO: these are relative to the lod vertex buffer. these values are accurate for a 0 offset, but lod will need to adjust
VertexBufferOffset = [0, (uint)streams[0].Count, (uint)(streams[0].Count + streams[1].Count)],
VertexBufferStride = strides,
// VertexStreamCount = /* 2 */ (byte)(attributes.Select(attribute => attribute.Element.Stream).Max() + 1),
VertexStreamCount = (byte)(vertexDeclaration.VertexElements.Select(element => element.Stream).Max() + 1)
};
return (
vertexDeclaration,
boneTable,
xivMesh,
submeshes,
streams[0].Concat(streams[1]).Concat(streams[2]),
indices
);
}
private (
MdlStructs.VertexDeclarationStruct,
byte[],
// MdlStructs.MeshStruct,
MdlStructs.SubmeshStruct,
ushort,
IEnumerable<byte>[],
uint,
IEnumerable<ushort>
) NodeMeshThing(Node node)
{ {
// BoneTable (mesh.btidx = 255 means unskinned) // BoneTable (mesh.btidx = 255 means unskinned)
// vertexdecl // vertexdecl
@ -358,10 +465,11 @@ public sealed class ModelManager : SingleTaskQueue, IDisposable
} }
// indices // indices
var indexCount = primitive.GetIndexAccessor().Count; // var indexCount = primitive.GetIndexAccessor().Count;
var indices = primitive.GetIndices() // var indices = primitive.GetIndices()
.SelectMany(index => BitConverter.GetBytes((ushort)index)) // .SelectMany(index => BitConverter.GetBytes((ushort)index))
.ToArray(); // .ToArray();
var indices = primitive.GetIndices().Select(idx => (ushort)idx).ToArray();
// one of these per mesh // one of these per mesh
var vertexDeclaration = new MdlStructs.VertexDeclarationStruct() var vertexDeclaration = new MdlStructs.VertexDeclarationStruct()
@ -369,57 +477,50 @@ public sealed class ModelManager : SingleTaskQueue, IDisposable
VertexElements = attributes.Select(attribute => attribute.Element).ToArray(), VertexElements = attributes.Select(attribute => attribute.Element).ToArray(),
}; };
// one of these per skinned mesh.
// TODO: check if mesh has skinning at all.
var boneTable = new MdlStructs.BoneTableStruct()
{
BoneCount = 1,
// this needs to be the full 64. this should be fine _here_ with 0s because i only have one bone, but will need to be fully populated properly. in real files.
BoneIndex = new ushort[64],
};
// mesh // mesh
var xivMesh = new MdlStructs.MeshStruct() // var xivMesh = new MdlStructs.MeshStruct()
{ // {
// TODO: sum across submeshes. // // TODO: sum across submeshes.
// TODO: would be cool to share verts on submesh boundaries but that's way out of scope for now. // // TODO: would be cool to share verts on submesh boundaries but that's way out of scope for now.
VertexCount = (ushort)vertexCount, // VertexCount = (ushort)vertexCount,
IndexCount = (uint)indexCount, // IndexCount = (uint)indexCount,
// TODO: will have to think about how to represent this - materials can be named, so maybe adjust in parent? // // TODO: will have to think about how to represent this - materials can be named, so maybe adjust in parent?
MaterialIndex = 0, // MaterialIndex = 0,
// TODO: this will need adjusting by parent // // TODO: this will need adjusting by parent
SubMeshIndex = 0, // SubMeshIndex = 0,
SubMeshCount = 1, // SubMeshCount = 1,
// TODO: update in parent // // TODO: update in parent
BoneTableIndex = 0, // BoneTableIndex = 0,
// TODO: this is relative to the lod's index buffer, and is an index, not byte offset // // TODO: this is relative to the lod's index buffer, and is an index, not byte offset
StartIndex = 0, // StartIndex = 0,
// TODO: these are relative to the lod vertex buffer. these values are accurate for a 0 offset, but lod will need to adjust // // TODO: these are relative to the lod vertex buffer. these values are accurate for a 0 offset, but lod will need to adjust
VertexBufferOffset = [0, (uint)streams[0].Count, (uint)(streams[0].Count + streams[1].Count)], // VertexBufferOffset = [0, (uint)streams[0].Count, (uint)(streams[0].Count + streams[1].Count)],
VertexBufferStride = strides, // VertexBufferStride = strides,
VertexStreamCount = /* 2 */ (byte)(attributes.Select(attribute => attribute.Element.Stream).Max() + 1), // VertexStreamCount = /* 2 */ (byte)(attributes.Select(attribute => attribute.Element.Stream).Max() + 1),
}; // };
// submesh // submesh
// TODO: once we have multiple submeshes, the _first_ should probably set an index offset of 0, and then further ones delta from there - and then they can be blindly adjusted by the parent that's laying out the meshes. // TODO: once we have multiple submeshes, the _first_ should probably set an index offset of 0, and then further ones delta from there - and then they can be blindly adjusted by the parent that's laying out the meshes.
var xivSubmesh = new MdlStructs.SubmeshStruct() var xivSubmesh = new MdlStructs.SubmeshStruct()
{ {
IndexOffset = 0, IndexOffset = 0,
IndexCount = (uint)indexCount, IndexCount = (uint)indices.Length,
AttributeIndexMask = 0, AttributeIndexMask = 0,
// TODO: not sure how i want to handle these ones // TODO: not sure how i want to handle these ones
BoneStartIndex = 0, BoneStartIndex = 0,
BoneCount = 1, BoneCount = 1,
}; };
var vertexBuffer = streams[0].Concat(streams[1]).Concat(streams[2]); // var vertexBuffer = streams[0].Concat(streams[1]).Concat(streams[2]);
return ( return (
vertexDeclaration, vertexDeclaration,
boneTable, strides,
xivMesh, // xivMesh,
xivSubmesh, xivSubmesh,
vertexBuffer, (ushort)vertexCount,
streams,
(uint)indices.Length,
indices indices
); );
} }