Drawing an XNA Model bounding box
2013-12-18 10:45
453 查看
The source code for a working version is
here.
If you're making a level editor, or perhaps a
model viewer, it can sometimes be useful to see a visualisation of the 3D space a model sits within. It might also be handy if you use bounding boxes for physics, and you want to check that the bounding box is really where you think it is.
Creating the bounding box
First (and hopefully not surprisingly), you'll need the bounding box itself. XNA has a nice structure for this, helpfully namedBoundingBox. If you already have access to one of these for your model, then you can skip to the next section. If you only have a
Modelobject, and want to creating a bounding box, then you can use this code. Note that the
ModelMeshclass has a
BoundingSphereproperty. XNA allows us to create a
BoundingBoxfrom a
BoundingSphere, but that wouldn't be a tight fit - it would be larger than necessary. So I prefer to go back to the original vertices and use them to create the bounding box. Step 1, then, is to extract the vertex
positions from the
Model:
public static class VertexElementExtractor { public static Vector3[] GetVertexElement(ModelMeshPart meshPart, VertexElementUsage usage) { VertexDeclaration vd = meshPart.VertexBuffer.VertexDeclaration; VertexElement[] elements = vd.GetVertexElements(); Func<VertexElement, bool> elementPredicate = ve => ve.VertexElementUsage == usage && ve.VertexElementFormat == VertexElementFormat.Vector3; if (!elements.Any(elementPredicate)) return null; VertexElement element = elements.First(elementPredicate); Vector3[] vertexData = new Vector3[meshPart.NumVertices]; meshPart.VertexBuffer.GetData((meshPart.VertexOffset * vd.VertexStride) + element.Offset, vertexData, 0, vertexData.Length, vd.VertexStride); return vertexData; } }
We can now write the code to create a bounding box for a
ModelMeshPart:
private static BoundingBox? GetBoundingBox(ModelMeshPart meshPart, Matrix transform) { if (meshPart.VertexBuffer == null) return null; Vector3[] positions = VertexElementExtractor.GetVertexElement(meshPart, VertexElementUsage.Position); if (positions == null) return null; Vector3[] transformedPositions = new Vector3[positions.Length]; Vector3.Transform(positions, ref transform, transformedPositions); return BoundingBox.CreateFromPoints(transformedPositions); }
Now we can loop through each
ModelMeshand
ModelMeshPartwithin the
Model, and create the merged bounding box for the whole model (making sure to transform the positions based on the bone transforms):
private static BoundingBox CreateBoundingBox(Model model) { Matrix[] boneTransforms = new Matrix[model.Bones.Count]; model.CopyAbsoluteBoneTransformsTo(boneTransforms); BoundingBox result = new BoundingBox(); foreach (ModelMesh mesh in model.Meshes) foreach (ModelMeshPart meshPart in mesh.MeshParts) { BoundingBox? meshPartBoundingBox = GetBoundingBox(meshPart, boneTransforms[mesh.ParentBone.Index]); if (meshPartBoundingBox != null) result = BoundingBox.CreateMerged(result, meshPartBoundingBox.Value); } return result; }
In XBuilder, I actually create a bounding box for each
ModelMesh, but the general principle is the same.
We now have a
BoundingBoxfor our
Model, so we can get ready to draw it.
Preparing to draw the bounding box
You could draw an actual box, with solid lines. I prefer the approach taken by most 3D modelling packages of just drawing the corners. This is a bit more involved, and sorry for the long code snippet. If anybody knows a cleverer way of doing this, pleaselet me know! First we need a class to hold the vertex and index buffers we're going to create:
public class BoundingBoxBuffers { public VertexBuffer Vertices; public int VertexCount; public IndexBuffer Indices; public int PrimitiveCount; }
Now we can build this object:
private BoundingBoxBuffers CreateBoundingBoxBuffers(BoundingBox boundingBox, GraphicsDevice graphicsDevice) { BoundingBoxBuffers boundingBoxBuffers = new BoundingBoxBuffers(); boundingBoxBuffers.PrimitiveCount = 24; boundingBoxBuffers.VertexCount = 48; VertexBuffer vertexBuffer = new VertexBuffer(graphicsDevice, typeof(VertexPositionColor), boundingBoxBuffers.VertexCount, BufferUsage.WriteOnly); List<VertexPositionColor> vertices = new List<VertexPositionColor>(); const float ratio = 5.0f; Vector3 xOffset = new Vector3((boundingBox.Max.X - boundingBox.Min.X) / ratio, 0, 0); Vector3 yOffset = new Vector3(0, (boundingBox.Max.Y - boundingBox.Min.Y) / ratio, 0); Vector3 zOffset = new Vector3(0, 0, (boundingBox.Max.Z - boundingBox.Min.Z) / ratio); Vector3[] corners = boundingBox.GetCorners(); // Corner 1. AddVertex(vertices, corners[0]); AddVertex(vertices, corners[0] + xOffset); AddVertex(vertices, corners[0]); AddVertex(vertices, corners[0] - yOffset); AddVertex(vertices, corners[0]); AddVertex(vertices, corners[0] - zOffset); // Corner 2. AddVertex(vertices, corners[1]); AddVertex(vertices, corners[1] - xOffset); AddVertex(vertices, corners[1]); AddVertex(vertices, corners[1] - yOffset); AddVertex(vertices, corners[1]); AddVertex(vertices, corners[1] - zOffset); // Corner 3. AddVertex(vertices, corners[2]); AddVertex(vertices, corners[2] - xOffset); AddVertex(vertices, corners[2]); AddVertex(vertices, corners[2] + yOffset); AddVertex(vertices, corners[2]); AddVertex(vertices, corners[2] - zOffset); // Corner 4. AddVertex(vertices, corners[3]); AddVertex(vertices, corners[3] + xOffset); AddVertex(vertices, corners[3]); AddVertex(vertices, corners[3] + yOffset); AddVertex(vertices, corners[3]); AddVertex(vertices, corners[3] - zOffset); // Corner 5. AddVertex(vertices, corners[4]); AddVertex(vertices, corners[4] + xOffset); AddVertex(vertices, corners[4]); AddVertex(vertices, corners[4] - yOffset); AddVertex(vertices, corners[4]); AddVertex(vertices, corners[4] + zOffset); // Corner 6. AddVertex(vertices, corners[5]); AddVertex(vertices, corners[5] - xOffset); AddVertex(vertices, corners[5]); AddVertex(vertices, corners[5] - yOffset); AddVertex(vertices, corners[5]); AddVertex(vertices, corners[5] + zOffset); // Corner 7. AddVertex(vertices, corners[6]); AddVertex(vertices, corners[6] - xOffset); AddVertex(vertices, corners[6]); AddVertex(vertices, corners[6] + yOffset); AddVertex(vertices, corners[6]); AddVertex(vertices, corners[6] + zOffset); // Corner 8. AddVertex(vertices, corners[7]); AddVertex(vertices, corners[7] + xOffset); AddVertex(vertices, corners[7]); AddVertex(vertices, corners[7] + yOffset); AddVertex(vertices, corners[7]); AddVertex(vertices, corners[7] + zOffset); vertexBuffer.SetData(vertices.ToArray()); boundingBoxBuffers.Vertices = vertexBuffer; IndexBuffer indexBuffer = new IndexBuffer(graphicsDevice, IndexElementSize.SixteenBits, boundingBoxBuffers.VertexCount, BufferUsage.WriteOnly); indexBuffer.SetData(Enumerable.Range(0, boundingBoxBuffers.VertexCount).Select(i => (short)i).ToArray()); boundingBoxBuffers.Indices = indexBuffer; return boundingBoxBuffers; } private static void AddVertex(List<VertexPositionColor> vertices, Vector3 position) { vertices.Add(new VertexPositionColor(position, Color.White)); }
Drawing the bounding box
By now you should have an instance ofBoundingBoxBuffers. To get it on to the screen, you'll need an
Effect- I keep it simple and use
BasicEffect:
BasicEffect lineEffect = new BasicEffect(graphicsDevice); lineEffect.LightingEnabled = false; lineEffect.TextureEnabled = false; lineEffect.VertexColorEnabled = true;
Finally, we're ready to draw the bounding box:
private void DrawBoundingBox(BoundingBoxBuffers buffers, BasicEffect effect, GraphicsDevice graphicsDevice, Matrix view, Matrix projection) { graphicsDevice.SetVertexBuffer(buffers.Vertices); graphicsDevice.Indices = buffers.Indices; effect.World = Matrix.Identity; effect.View = view; effect.Projection = projection; foreach (EffectPass pass in effect.CurrentTechnique.Passes) { pass.Apply(); graphicsDevice.DrawIndexedPrimitives(PrimitiveType.LineList, 0, 0, buffers.VertexCount, 0, buffers.PrimitiveCount); } }
If everything went right, you should have something that looks like this on your screen:
If you don't have that, or if I didn't explain it properly, maybe looking at
my version will help. I hope this is useful - let me know if you have any comments or questions.
相关文章推荐
- An Axis-Aligned Bounding Box (AABB) Sweep Test
- python利用opencv标注bounding box
- xna中使用自定义的shader来绘制model
- Model:parse+toBean (接口原始数据Model, Model提供各种toBean()的接口)
- Box2D Get Bounding Box of a Body
- unity spine boundingbox 碰撞检测
- ASP.NET MVC- Model- An Introduction to Entity Framework for Absolute Beginners
- 5 CSS BOX Model with example
- 踩着坑画bounding-box
- About CSS Box-Model and Flow
- Notes:An Ontology-based Information Retrieval Model--基于本体的信息检索模型
- XUL Tutorial Study Notes - The Box Model
- Python+OpenCV图像标注矩形框bounding box
- 最小体积包围盒Minimum Volumn Bounding Box (MVB)-begtostudy UG/NX免费外挂欢迎公测(2015.10.9更新)
- Defining an Interaction Model: The Cornerstone of Application Design
- 制作自己的数据集 打标签bounding box 之 Windows 10 下python环境安装(PyQt4)
- Can I use an image for the sidebox header?
- 《An Introduction to Ray Tracing》—— 2.4 Ray/Box Intersection
- Bounding box regression详细解答
- Box2D Get Bounding Box of a Body