This commit is contained in:
Ottermandias 2024-01-23 23:23:03 +01:00
parent b2bd31a166
commit a159ef28c3
5 changed files with 47 additions and 39 deletions

View file

@ -2,11 +2,11 @@ using Lumina.Data.Parsing;
namespace Penumbra.Import.Models.Import; namespace Penumbra.Import.Models.Import;
/// <summary> Mutable representation of the bounding box surrouding a collection of vertices. </summary> /// <summary> Mutable representation of the bounding box surrounding a collection of vertices. </summary>
public class BoundingBox public class BoundingBox
{ {
private Vector3 _minimum = new Vector3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity); private Vector3 _minimum = new(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
private Vector3 _maximum = new Vector3(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity); private Vector3 _maximum = new(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity);
/// <summary> Use the specified position to update this bounding box, expanding it if necessary. </summary> /// <summary> Use the specified position to update this bounding box, expanding it if necessary. </summary>
public void Merge(Vector3 position) public void Merge(Vector3 position)
@ -25,7 +25,7 @@ public class BoundingBox
/// <summary> Convert this bounding box to the struct format used in .mdl data structures. </summary> /// <summary> Convert this bounding box to the struct format used in .mdl data structures. </summary>
public MdlStructs.BoundingBoxStruct ToStruct() public MdlStructs.BoundingBoxStruct ToStruct()
=> new MdlStructs.BoundingBoxStruct => new()
{ {
Min = [_minimum.X, _minimum.Y, _minimum.Z, 1], Min = [_minimum.X, _minimum.Y, _minimum.Z, 1],
Max = [_maximum.X, _maximum.Y, _maximum.Z, 1], Max = [_maximum.X, _maximum.Y, _maximum.Z, 1],

View file

@ -53,7 +53,7 @@ public class MeshImporter(IEnumerable<Node> nodes, IoNotifier notifier)
private List<string>? _bones; private List<string>? _bones;
private readonly BoundingBox _boundingBox = new BoundingBox(); private readonly BoundingBox _boundingBox = new();
private readonly List<string> _metaAttributes = []; private readonly List<string> _metaAttributes = [];
@ -124,7 +124,8 @@ public class MeshImporter(IEnumerable<Node> nodes, IoNotifier notifier)
_material ??= subMesh.Material; _material ??= subMesh.Material;
if (subMesh.Material != null && _material != subMesh.Material) if (subMesh.Material != null && _material != subMesh.Material)
notifier.Warning($"Meshes may only reference one material. Sub-mesh {subMeshName} material \"{subMesh.Material}\" has been ignored."); notifier.Warning(
$"Meshes may only reference one material. Sub-mesh {subMeshName} material \"{subMesh.Material}\" has been ignored.");
// Check that vertex declarations match - we need to combine the buffers, so a mismatch would take a whole load of resolution. // Check that vertex declarations match - we need to combine the buffers, so a mismatch would take a whole load of resolution.
if (_vertexDeclaration == null) if (_vertexDeclaration == null)

View file

@ -42,14 +42,14 @@ public class PrimitiveImporter
private byte[] _strides = [0, 0, 0]; private byte[] _strides = [0, 0, 0];
private readonly List<byte>[] _streams = [[], [], []]; private readonly List<byte>[] _streams = [[], [], []];
private BoundingBox _boundingBox = new BoundingBox(); private readonly BoundingBox _boundingBox = new();
private List<List<MdlStructs.ShapeValueStruct>>? _shapeValues; private List<List<MdlStructs.ShapeValueStruct>>? _shapeValues;
private PrimitiveImporter(MeshPrimitive primitive, IDictionary<ushort, ushort>? nodeBoneMap, IoNotifier notifier) private PrimitiveImporter(MeshPrimitive primitive, IDictionary<ushort, ushort>? nodeBoneMap, IoNotifier notifier)
{ {
_notifier = notifier; _notifier = notifier;
_primitive = primitive; _primitive = primitive;
_nodeBoneMap = nodeBoneMap; _nodeBoneMap = nodeBoneMap;
} }
@ -154,9 +154,10 @@ public class PrimitiveImporter
_streams[attribute.Stream].AddRange(attribute.Build(vertexIndex)); _streams[attribute.Stream].AddRange(attribute.Build(vertexIndex));
// Record which morph targets have values for this vertex, if any. // Record which morph targets have values for this vertex, if any.
var index = vertexIndex;
var changedMorphs = morphModifiedVertices var changedMorphs = morphModifiedVertices
.WithIndex() .WithIndex()
.Where(pair => _vertexAttributes.Any(attribute => attribute.HasMorph(pair.Index, vertexIndex))) .Where(pair => _vertexAttributes.Any(attribute => attribute.HasMorph(pair.Index, index)))
.Select(pair => pair.Value); .Select(pair => pair.Value);
foreach (var modifiedVertices in changedMorphs) foreach (var modifiedVertices in changedMorphs)
modifiedVertices.Add(vertexIndex); modifiedVertices.Add(vertexIndex);

View file

@ -46,12 +46,12 @@ public class SubMeshImporter
private byte[]? _strides; private byte[]? _strides;
private readonly List<byte>[] _streams = [[], [], []]; private readonly List<byte>[] _streams = [[], [], []];
private List<ushort> _indices = []; private readonly List<ushort> _indices = [];
private BoundingBox _boundingBox = new BoundingBox(); private readonly BoundingBox _boundingBox = new();
private readonly List<string>? _morphNames; private readonly List<string>? _morphNames;
private Dictionary<string, List<MdlStructs.ShapeValueStruct>> _shapeValues = []; private readonly Dictionary<string, List<MdlStructs.ShapeValueStruct>> _shapeValues = [];
private SubMeshImporter(Node node, IDictionary<ushort, ushort>? nodeBoneMap, IoNotifier notifier) private SubMeshImporter(Node node, IDictionary<ushort, ushort>? nodeBoneMap, IoNotifier notifier)
{ {
@ -86,7 +86,7 @@ public class SubMeshImporter
var attributeMask = metaAttributes.Length switch var attributeMask = metaAttributes.Length switch
{ {
< 32 => (1u << metaAttributes.Length) - 1, < 32 => (1u << metaAttributes.Length) - 1,
32 => uint.MaxValue, 32 => uint.MaxValue,
> 32 => throw _notifier.Exception("Models may utilise a maximum of 32 attributes."), > 32 => throw _notifier.Exception("Models may utilise a maximum of 32 attributes."),
}; };
@ -102,22 +102,22 @@ public class SubMeshImporter
BoneStartIndex = 0, BoneStartIndex = 0,
BoneCount = 0, BoneCount = 0,
}, },
Material = _material, Material = _material,
VertexDeclaration = _vertexDeclaration.Value, VertexDeclaration = _vertexDeclaration.Value,
VertexCount = _vertexCount, VertexCount = _vertexCount,
Strides = _strides, Strides = _strides,
Streams = _streams, Streams = _streams,
Indices = _indices, Indices = _indices,
BoundingBox = _boundingBox, BoundingBox = _boundingBox,
MetaAttributes = metaAttributes, MetaAttributes = metaAttributes,
ShapeValues = _shapeValues, ShapeValues = _shapeValues,
}; };
} }
private void BuildPrimitive(MeshPrimitive meshPrimitive, int index) private void BuildPrimitive(MeshPrimitive meshPrimitive, int index)
{ {
var vertexOffset = _vertexCount; var vertexOffset = _vertexCount;
var indexOffset = _indices.Count; var indexOffset = _indices.Count;
var primitive = PrimitiveImporter.Import(meshPrimitive, _nodeBoneMap, _notifier.WithContext($"Primitive {index}")); var primitive = PrimitiveImporter.Import(meshPrimitive, _nodeBoneMap, _notifier.WithContext($"Primitive {index}"));
@ -141,7 +141,7 @@ public class SubMeshImporter
stream.AddRange(primitiveStream); stream.AddRange(primitiveStream);
// Indices // Indices
_indices.AddRange(primitive.Indices.Select(index => (ushort)(index + vertexOffset))); _indices.AddRange(primitive.Indices.Select(i => (ushort)(i + vertexOffset)));
// Shape values // Shape values
foreach (var (primitiveShapeValues, morphIndex) in primitive.ShapeValues.WithIndex()) foreach (var (primitiveShapeValues, morphIndex) in primitive.ShapeValues.WithIndex())
@ -181,8 +181,9 @@ public class SubMeshImporter
// We consider any "extras" key with a boolean value set to `true` to be an attribute. // We consider any "extras" key with a boolean value set to `true` to be an attribute.
return nodeExtras? return nodeExtras?
.Where(pair => pair.Value.ValueKind == JsonValueKind.True) .Where(pair => pair.Value.ValueKind == JsonValueKind.True)
.Select(pair => pair.Key) .Select(pair => pair.Key)
.ToArray() ?? []; .ToArray()
?? [];
} }
} }

View file

@ -4,11 +4,11 @@ namespace Penumbra.Import.Models.Import;
public static class Utility public static class Utility
{ {
/// <summary> Merge attributes into an existing attribute array, providing an updated submesh mask. </summary> /// <summary> Merge attributes into an existing attribute array, providing an updated sub mesh mask. </summary>
/// <param name="oldMask"> Old submesh attribute mask. </param> /// <param name="oldMask"> Old sub mesh attribute mask. </param>
/// <param name="oldAttributes"> Old attribute array that should be merged. </param> /// <param name="oldAttributes"> Old attribute array that should be merged. </param>
/// <param name="newAttributes"> New attribute array. Will be mutated. </param> /// <param name="newAttributes"> New attribute array. Will be mutated. </param>
/// <returns> New submesh attribute mask, updated to match the merged attribute array. </returns> /// <returns> New sub mesh attribute mask, updated to match the merged attribute array. </returns>
public static uint GetMergedAttributeMask(uint oldMask, IList<string> oldAttributes, List<string> newAttributes) public static uint GetMergedAttributeMask(uint oldMask, IList<string> oldAttributes, List<string> newAttributes)
{ {
var metaAttributes = Enumerable.Range(0, 32) var metaAttributes = Enumerable.Range(0, 32)
@ -28,6 +28,7 @@ public static class Utility
newAttributes.Add(metaAttribute); newAttributes.Add(metaAttribute);
attributeIndex = newAttributes.Count - 1; attributeIndex = newAttributes.Count - 1;
} }
newMask |= 1u << attributeIndex; newMask |= 1u << attributeIndex;
} }
@ -35,18 +36,22 @@ public static class Utility
} }
/// <summary> Ensures that the two vertex declarations provided are equal, throwing if not. </summary> /// <summary> Ensures that the two vertex declarations provided are equal, throwing if not. </summary>
public static void EnsureVertexDeclarationMatch(MdlStructs.VertexDeclarationStruct current, MdlStructs.VertexDeclarationStruct @new, IoNotifier notifier) public static void EnsureVertexDeclarationMatch(MdlStructs.VertexDeclarationStruct current, MdlStructs.VertexDeclarationStruct @new,
IoNotifier notifier)
{ {
if (VertexDeclarationMismatch(current, @new)) if (VertexDeclarationMismatch(current, @new))
throw notifier.Exception( throw notifier.Exception(
$@"All sub-meshes of a mesh must have equivalent vertex declarations. $"""
Current: {FormatVertexDeclaration(current)} All sub-meshes of a mesh must have equivalent vertex declarations.
New: {FormatVertexDeclaration(@new)}" Current: {FormatVertexDeclaration(current)}
New: {FormatVertexDeclaration(@new)}
"""
); );
} }
private static string FormatVertexDeclaration(MdlStructs.VertexDeclarationStruct vertexDeclaration) private static string FormatVertexDeclaration(MdlStructs.VertexDeclarationStruct vertexDeclaration)
=> string.Join(", ", vertexDeclaration.VertexElements.Select(element => $"{element.Usage} ({element.Type}@{element.Stream}:{element.Offset})")); => string.Join(", ",
vertexDeclaration.VertexElements.Select(element => $"{element.Usage} ({element.Type}@{element.Stream}:{element.Offset})"));
private static bool VertexDeclarationMismatch(MdlStructs.VertexDeclarationStruct a, MdlStructs.VertexDeclarationStruct b) private static bool VertexDeclarationMismatch(MdlStructs.VertexDeclarationStruct a, MdlStructs.VertexDeclarationStruct b)
{ {