Add submeshless support

This commit is contained in:
ackwell 2024-01-01 11:01:31 +11:00
parent dc845b766e
commit 518117b25a
2 changed files with 24 additions and 16 deletions

View file

@ -34,7 +34,6 @@ public class MeshExporter
} }
} }
// TODO: replace bonenamemap with a gltfskeleton
public static Mesh Export(MdlFile mdl, byte lod, ushort meshIndex, GltfSkeleton? skeleton) public static Mesh Export(MdlFile mdl, byte lod, ushort meshIndex, GltfSkeleton? skeleton)
{ {
var self = new MeshExporter(mdl, lod, meshIndex, skeleton?.Names); var self = new MeshExporter(mdl, lod, meshIndex, skeleton?.Names);
@ -74,7 +73,7 @@ public class MeshExporter
private Dictionary<ushort, int> BuildBoneIndexMap(Dictionary<string, int> boneNameMap) private Dictionary<ushort, int> BuildBoneIndexMap(Dictionary<string, int> boneNameMap)
{ {
// todo: BoneTableIndex of 255 means null? if so, it should probably feed into the attributes we assign... // TODO: BoneTableIndex of 255 means null? if so, it should probably feed into the attributes we assign...
var xivBoneTable = _mdl.BoneTables[XivMesh.BoneTableIndex]; var xivBoneTable = _mdl.BoneTables[XivMesh.BoneTableIndex];
var indexMap = new Dictionary<ushort, int>(); var indexMap = new Dictionary<ushort, int>();
@ -97,20 +96,25 @@ public class MeshExporter
var indices = BuildIndices(); var indices = BuildIndices();
var vertices = BuildVertices(); var vertices = BuildVertices();
// TODO: handle SubMeshCount = 0 // NOTE: Index indices are specified relative to the LOD's 0, but we're reading chunks for each mesh, so we're specifying the index base relative to the mesh's base.
if (XivMesh.SubMeshCount == 0)
return [BuildMesh(indices, vertices, 0, (int)XivMesh.IndexCount)];
return _mdl.SubMeshes return _mdl.SubMeshes
.Skip(XivMesh.SubMeshIndex) .Skip(XivMesh.SubMeshIndex)
.Take(XivMesh.SubMeshCount) .Take(XivMesh.SubMeshCount)
.Select(submesh => BuildSubMesh(submesh, indices, vertices)) .Select(submesh => BuildMesh(indices, vertices, (int)(submesh.IndexOffset - XivMesh.StartIndex), (int)submesh.IndexCount))
.ToArray(); .ToArray();
} }
private IMeshBuilder<MaterialBuilder> BuildSubMesh(MdlStructs.SubmeshStruct submesh, IReadOnlyList<ushort> indices, IReadOnlyList<IVertexBuilder> vertices) private IMeshBuilder<MaterialBuilder> BuildMesh(
IReadOnlyList<ushort> indices,
IReadOnlyList<IVertexBuilder> vertices,
int indexBase,
int indexCount
)
{ {
// Index indices are specified relative to the LOD's 0, but we're reading chunks for each mesh.
var startIndex = (int)(submesh.IndexOffset - XivMesh.StartIndex);
var meshBuilderType = typeof(MeshBuilder<,,,>).MakeGenericType( var meshBuilderType = typeof(MeshBuilder<,,,>).MakeGenericType(
typeof(MaterialBuilder), typeof(MaterialBuilder),
_geometryType, _geometryType,
@ -131,12 +135,12 @@ public class MeshExporter
var gltfIndices = new List<int>(); var gltfIndices = new List<int>();
// All XIV meshes use triangle lists. // All XIV meshes use triangle lists.
for (var indexOffset = 0; indexOffset < submesh.IndexCount; indexOffset += 3) for (var indexOffset = 0; indexOffset < indexCount; indexOffset += 3)
{ {
var (a, b, c) = primitiveBuilder.AddTriangle( var (a, b, c) = primitiveBuilder.AddTriangle(
vertices[indices[indexOffset + startIndex + 0]], vertices[indices[indexBase + indexOffset + 0]],
vertices[indices[indexOffset + startIndex + 1]], vertices[indices[indexBase + indexOffset + 1]],
vertices[indices[indexOffset + startIndex + 2]] vertices[indices[indexBase + indexOffset + 2]]
); );
gltfIndices.AddRange([a, b, c]); gltfIndices.AddRange([a, b, c]);
} }
@ -157,8 +161,8 @@ public class MeshExporter
.Take((int)shapeMesh.ShapeValueCount) .Take((int)shapeMesh.ShapeValueCount)
) )
.Where(shapeValue => .Where(shapeValue =>
shapeValue.BaseIndicesIndex >= startIndex shapeValue.BaseIndicesIndex >= indexBase
&& shapeValue.BaseIndicesIndex < startIndex + submesh.IndexCount && shapeValue.BaseIndicesIndex < indexBase + indexCount
) )
.ToList(); .ToList();
@ -169,7 +173,7 @@ public class MeshExporter
foreach (var shapeValue in shapeValues) foreach (var shapeValue in shapeValues)
morphBuilder.SetVertex( morphBuilder.SetVertex(
primitiveVertices[gltfIndices[shapeValue.BaseIndicesIndex - startIndex]].GetGeometry(), primitiveVertices[gltfIndices[shapeValue.BaseIndicesIndex - indexBase]].GetGeometry(),
vertices[shapeValue.ReplacingVertexIndex].GetGeometry() vertices[shapeValue.ReplacingVertexIndex].GetGeometry()
); );
} }

View file

@ -73,17 +73,21 @@ public sealed class ModelManager : SingleTaskQueue, IDisposable
public void Execute(CancellationToken cancel) public void Execute(CancellationToken cancel)
{ {
Penumbra.Log.Debug("Reading skeleton.");
var xivSkeleton = BuildSkeleton(cancel); var xivSkeleton = BuildSkeleton(cancel);
Penumbra.Log.Debug("Converting model.");
var model = ModelExporter.Export(_mdl, xivSkeleton); var model = ModelExporter.Export(_mdl, xivSkeleton);
Penumbra.Log.Debug("Building scene.");
var scene = new SceneBuilder(); var scene = new SceneBuilder();
model.AddToScene(scene); model.AddToScene(scene);
Penumbra.Log.Debug("Saving.");
var gltfModel = scene.ToGltf2(); var gltfModel = scene.ToGltf2();
gltfModel.SaveGLTF(_outputPath); gltfModel.SaveGLTF(_outputPath);
} }
// TODO: this should be moved to a seperate model converter or something
private XivSkeleton? BuildSkeleton(CancellationToken cancel) private XivSkeleton? BuildSkeleton(CancellationToken cancel)
{ {
if (_sklb == null) if (_sklb == null)