diff --git a/Penumbra/Import/Models/Export/MeshExporter.cs b/Penumbra/Import/Models/Export/MeshExporter.cs index 6e6169ee..8127f348 100644 --- a/Penumbra/Import/Models/Export/MeshExporter.cs +++ b/Penumbra/Import/Models/Export/MeshExporter.cs @@ -13,20 +13,31 @@ namespace Penumbra.Import.Models.Export; public class MeshExporter { - public class Mesh(IEnumerable> meshes, NodeBuilder[]? joints) + public class Mesh(IEnumerable meshes, NodeBuilder[]? joints) { public void AddToScene(SceneBuilder scene) { - foreach (var mesh in meshes) + foreach (var data in meshes) { - if (joints == null) - scene.AddRigidMesh(mesh, Matrix4x4.Identity); - else - scene.AddSkinnedMesh(mesh, Matrix4x4.Identity, joints); + var instance = joints != null + ? scene.AddSkinnedMesh(data.Mesh, Matrix4x4.Identity, joints) + : scene.AddRigidMesh(data.Mesh, Matrix4x4.Identity); + + var extras = new Dictionary(); + foreach (var attribute in data.Attributes) + extras.Add(attribute, true); + + instance.WithExtras(JsonContent.CreateFrom(extras)); } } } + public struct MeshData + { + public IMeshBuilder Mesh; + public string[] Attributes; + } + public static Mesh Export(MdlFile mdl, byte lod, ushort meshIndex, MaterialBuilder[] materials, GltfSkeleton? skeleton) { var self = new MeshExporter(mdl, lod, meshIndex, materials, skeleton?.Names); @@ -103,31 +114,33 @@ public class MeshExporter } /// Build glTF meshes for this XIV mesh. - private IMeshBuilder[] BuildMeshes() + private MeshData[] BuildMeshes() { var indices = BuildIndices(); var vertices = BuildVertices(); // 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($"mesh {_meshIndex}", indices, vertices, 0, (int)XivMesh.IndexCount)]; + return [BuildMesh($"mesh {_meshIndex}", indices, vertices, 0, (int)XivMesh.IndexCount, 0)]; return _mdl.SubMeshes .Skip(XivMesh.SubMeshIndex) .Take(XivMesh.SubMeshCount) .WithIndex() .Select(subMesh => BuildMesh($"mesh {_meshIndex}.{subMesh.Index}", indices, vertices, - (int)(subMesh.Value.IndexOffset - XivMesh.StartIndex), (int)subMesh.Value.IndexCount)) + (int)(subMesh.Value.IndexOffset - XivMesh.StartIndex), (int)subMesh.Value.IndexCount, + subMesh.Value.AttributeIndexMask)) .ToArray(); } /// Build a mesh from the provided indices and vertices. A subset of the full indices may be built by providing an index base and count. - private IMeshBuilder BuildMesh( + private MeshData BuildMesh( string name, IReadOnlyList indices, IReadOnlyList vertices, int indexBase, - int indexCount + int indexCount, + uint attributeMask ) { var meshBuilderType = typeof(MeshBuilder<,,,>).MakeGenericType( @@ -190,12 +203,23 @@ public class MeshExporter } } + // Named morph targets aren't part of the specification, however `MESH.extras.targetNames` + // is a commonly-accepted means of providing the data. meshBuilder.Extras = JsonContent.CreateFrom(new Dictionary() { { "targetNames", shapeNames }, }); - return meshBuilder; + var attributes = Enumerable.Range(0, 32) + .Where(index => ((attributeMask >> index) & 1) == 1) + .Select(index => _mdl.Attributes[index]) + .ToArray(); + + return new MeshData + { + Mesh = meshBuilder, + Attributes = attributes, + }; } /// Read in the indices for this mesh.