Making bounding boxes for models

 

The XNA Mesh class does provide a method that returns a BoundingSphere.  A BoundingSphere is not useful for our purposes.  For our game it would be more helpful to have a BoundingBox for the entire Model.

 

How Models are loaded:

content.Load<Model>("contents\\models\\brick")

 

We use the Content manager to load Models at runtime.  The Models that are loaded have been “pre-compiled” by the content pipeline at compile time.  This process compiles the content into a format that can be loading directly into the memory of an object of a specified type, in this case a Model. 

 

Content Processors

The content compilers are known as “content processors”.

 

The XNA game studio environment allows the programmer to add content processors.  To do this pre programmer must create a dll that contains classes that extend the functionality of the built in content processors.

 

Built in content processor classes that can be extended.  These are found in the namespace Microsoft.Xna.Framework.Content.Pipeline.Processors

·         EffectProcessor

·         MaterialProcessor

·         ModelProcessor

·         ModelTextureProcessor

·         PassThroughProcessor

·         SpriteTextureProcessor

 

Our Model Processor

Since we are trying to make a bounding box for a Model we will extend the ModelProcessor.   Our goal is to find the minimum and maximum values of z, y, and z for each vertex in the model.

 

We only need to override one method, it name is “Process”.  It takes two arguments.  The one we will need to use is the NodeContent.

NodeContent - Provides a base class for graphics types that define local coordinate systems. (MSND Library)

 

The node Content contains a list of “Children” who are them selves of type NodeContent.  When we run across a NodeContent object that is also a MeshContent object we should examine its x, y, and z values to see if they are less than the known minimum or greater than the known maximum.

 

Using the DLL, Content Processor, & BoundingBox

Open the properties of the project in which you would like to use your new content processor.  Select the Content Pipeline tab.  Click “add” and then brows to the dll you created.  Close the properties.

 

Click on the model file in a solution explorer.  View its properties.  For the “Content Processor” select the name you gave to the class that inherited from Model Processor.

 

Given a Model object in your code access the BoundingBox

BoundingBox modelBox = (BoundingBox)model.Tag;

 


using System;

using System.IO;

using System.Collections.Generic;

using Microsoft.Xna.Framework;

using Microsoft.Xna.Framework.Audio;

using Microsoft.Xna.Framework.Graphics;

using Microsoft.Xna.Framework.Input;

using Microsoft.Xna.Framework.Storage;

using Microsoft.Xna.Framework.Content;

using Microsoft.Xna.Framework.Content.Pipeline;

using Microsoft.Xna.Framework.Content.Pipeline.Graphics;

using Microsoft.Xna.Framework.Content.Pipeline.Processors;

 

//This program is a slightly modified version of the one found here:

//http://andyq.no-ip.com/blog/?p=16

 

namespace ModelBoundingBoxProcessor

{

    [ContentProcessor]

    public class ModelBoundingBoxProcessor : ModelProcessor

    {

        float minX = float.MaxValue;

        float minY = float.MaxValue;

        float minZ = float.MaxValue;

        float maxX = float.MinValue;

        float maxY = float.MinValue;

        float maxZ = float.MinValue;

 

        public override ModelContent Process(NodeContent input, ContentProcessorContext context)

        {

            NodeContentCollection nodeContentCollection = input.Children;

 

            //This is a recursive function in case the input's children have children.

            parseChildren(nodeContentCollection);

 

            ModelContent modelContent = base.Process(input, context);

 

            Vector3 min = new Vector3(minX, minY, minZ);

            Vector3 max = new Vector3(maxX, maxY, maxZ);

 

            modelContent.Tag = new BoundingBox(min, max);

 

            return modelContent;

        }

 

        private void parseChildren(NodeContentCollection nodeContentCollection)

        {

            foreach (NodeContent nodeContent in nodeContentCollection)

            {

                if (nodeContent is MeshContent)

                {

                    MeshContent meshContent = (MeshContent)nodeContent;

                    foreach (Vector3 vector in meshContent.Positions)

                    {

                        if (vector.X < minX)

                            minX = vector.X;

 

                        if (vector.Y < minY)

                            minY = vector.Y;

 

                        if (vector.Z < minZ)

                            minZ = vector.Z;

 

                        if (vector.X > maxX)

                            maxX = vector.X;

 

                        if (vector.Y > maxY)

                            maxY = vector.Y;

 

                        if (vector.Z > maxZ)

                            maxZ = vector.Z;

                    }

                }

                else

                {

                    parseChildren(nodeContent.Children);

                }

            }

        }

    }

}