diff --git a/Penumbra/Import/Models/Import/BoundingBox.cs b/Penumbra/Import/Models/Import/BoundingBox.cs new file mode 100644 index 00000000..34aac827 --- /dev/null +++ b/Penumbra/Import/Models/Import/BoundingBox.cs @@ -0,0 +1,33 @@ +using Lumina.Data.Parsing; + +namespace Penumbra.Import.Models.Import; + +/// Mutable representation of the bounding box surrouding a collection of vertices. +public class BoundingBox +{ + private Vector3 _minimum = Vector3.Zero; + private Vector3 _maximum = Vector3.Zero; + + /// Use the specified position to update this bounding box, expanding it if necessary. + public void Merge(Vector3 position) + { + _minimum = Vector3.Min(_minimum, position); + _maximum = Vector3.Max(_maximum, position); + } + + /// Merge the provided bounding box into this one, expanding it if necessary. + /// + public void Merge(BoundingBox other) + { + _minimum = Vector3.Min(_minimum, other._minimum); + _maximum = Vector3.Max(_maximum, other._maximum); + } + + /// Convert this bounding box to the struct format used in .mdl data structures. + public MdlStructs.BoundingBoxStruct ToStruct() + => new MdlStructs.BoundingBoxStruct + { + Min = [_minimum.X, _minimum.Y, _minimum.Z, 1], + Max = [_maximum.X, _maximum.Y, _maximum.Z, 1], + }; +} diff --git a/Penumbra/Import/Models/Import/MeshImporter.cs b/Penumbra/Import/Models/Import/MeshImporter.cs index 7da4d1d7..2a461304 100644 --- a/Penumbra/Import/Models/Import/MeshImporter.cs +++ b/Penumbra/Import/Models/Import/MeshImporter.cs @@ -19,6 +19,8 @@ public class MeshImporter(IEnumerable nodes) public List? Bones; + public BoundingBox BoundingBox; + public List MetaAttributes; public List ShapeKeys; @@ -50,6 +52,8 @@ public class MeshImporter(IEnumerable nodes) private List? _bones; + private readonly BoundingBox _boundingBox = new BoundingBox(); + private readonly List _metaAttributes = []; private readonly Dictionary> _shapeValues = []; @@ -87,6 +91,7 @@ public class MeshImporter(IEnumerable nodes) VertexBuffer = _streams[0].Concat(_streams[1]).Concat(_streams[2]), Indices = _indices, Bones = _bones, + BoundingBox = _boundingBox, MetaAttributes = _metaAttributes, ShapeKeys = _shapeValues .Select(pair => new MeshShapeKey() @@ -154,6 +159,8 @@ public class MeshImporter(IEnumerable nodes) })); } + _boundingBox.Merge(subMesh.BoundingBox); + // And finally, merge in the sub-mesh struct itself. _subMeshes.Add(subMesh.SubMeshStruct with { diff --git a/Penumbra/Import/Models/Import/ModelImporter.cs b/Penumbra/Import/Models/Import/ModelImporter.cs index 3b3d2cd0..1b7fdfa5 100644 --- a/Penumbra/Import/Models/Import/ModelImporter.cs +++ b/Penumbra/Import/Models/Import/ModelImporter.cs @@ -29,6 +29,8 @@ public partial class ModelImporter(ModelRoot model) private readonly List _bones = []; private readonly List _boneTables = []; + private readonly BoundingBox _boundingBox = new BoundingBox(); + private readonly List _metaAttributes = []; private readonly Dictionary> _shapeMeshes = []; @@ -95,9 +97,10 @@ public partial class ModelImporter(ModelRoot model) Materials = [.. materials], + BoundingBoxes = _boundingBox.ToStruct(), + // TODO: Would be good to calculate all of this up the tree. Radius = 1, - BoundingBoxes = MdlFile.EmptyBoundingBox, BoneBoundingBoxes = Enumerable.Repeat(MdlFile.EmptyBoundingBox, _bones.Count).ToArray(), RemainingData = [.._vertexBuffer, ..indexBuffer], }; @@ -156,6 +159,8 @@ public partial class ModelImporter(ModelRoot model) .ToArray(), }); + _boundingBox.Merge(mesh.BoundingBox); + _subMeshes.AddRange(mesh.SubMeshStructs.Select(m => m with { AttributeIndexMask = Utility.GetMergedAttributeMask( diff --git a/Penumbra/Import/Models/Import/SubMeshImporter.cs b/Penumbra/Import/Models/Import/SubMeshImporter.cs index bca75090..6a5d0d52 100644 --- a/Penumbra/Import/Models/Import/SubMeshImporter.cs +++ b/Penumbra/Import/Models/Import/SubMeshImporter.cs @@ -21,6 +21,8 @@ public class SubMeshImporter public ushort[] Indices; + public BoundingBox BoundingBox; + public string[] MetaAttributes; public Dictionary> ShapeValues; @@ -44,6 +46,8 @@ public class SubMeshImporter private ushort[]? _indices; + private BoundingBox _boundingBox = new BoundingBox(); + private string[]? _metaAttributes; private readonly List? _morphNames; @@ -90,9 +94,11 @@ public class SubMeshImporter private SubMesh Create() { // Build all the data we'll need. + // TODO: This structure is verging on a little silly. Reconsider. BuildIndices(); BuildVertexAttributes(); BuildVertices(); + BuildBoundingBox(); BuildMetaAttributes(); ArgumentNullException.ThrowIfNull(_indices); @@ -133,6 +139,7 @@ public class SubMeshImporter Strides = _strides, Streams = _streams, Indices = _indices, + BoundingBox = _boundingBox, MetaAttributes = _metaAttributes, ShapeValues = _shapeValues, }; @@ -255,6 +262,13 @@ public class SubMeshImporter _shapeValues = morphShapeValues; } + private void BuildBoundingBox() + { + var positions = _primitive.VertexAccessors["POSITION"].AsVector3Array(); + foreach (var position in positions) + _boundingBox.Merge(position); + } + private void BuildMetaAttributes() { // We consider any "extras" key with a boolean value set to `true` to be an attribute.