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.