A helping hand for bedroom coders throughout the land.
BoundingBox’s in XNA 4.0

Blogs

RandomChaos

Syndication

As you may have seen a post on how to generate bounding box data in 4.0 was required as some of the old tutorials just don’t seem to be cutting it any more. The pipeline code in this sample will also work in 3.1 as I took it from my 3.1 instancing project.

I am also going to show you how you can extend the properties of the pipeline so you can add in a logging switch, some times it’s a pain in the back side to code for the pipeline so it’s nice to be able to add some way of pushing some debug out put.

So, first off, extending the pipeline so we can switch logging on and off per model. First we need to add two references to the pipeline project, System.Drawing and System.Drawing.Design

image

This will allow us to use the types converters we want, we then set up a bool called enableLogging and an associated field called EnableLogging

        bool enableLogging = false;

        /// <summary>
        /// Enable Logging
        /// </summary>
        [DefaultValue(false)]
        [Description("Set to true if you want logging on"), TypeConverter(typeof(BooleanConverter)), Editor(typeof(UITypeEditor), typeof(UITypeEditor))]
        [Category("Blacksun")]
        [EditorBrowsable(EditorBrowsableState.Always)]
        [DisplayName("Enable Logging")]
        public bool EnableLogging
        {
            get { return enableLogging; }
            set
            {
                enableLogging = value;
            }
        }

 

This will allow us to switch the logging on and off per model in the IDE (VS 2010) which will look like this

 

image

Now we need a way to write the logging out, you could output to the debug window if you like, but I prefer to write it out to a file, so I have created a class to do this, naturally you can do both if you like :)

And now onto the core of this post, the capture of bounds data for the model, I first create lists for the bounds data, so at the top of the pipeline ModelProcessor class I have this

    [ContentProcessor(DisplayName = "Bounds Model Processor")]
    public class BoundsModelProcessor : ModelProcessor
    {
        List<BoundingBox> boxs = new List<BoundingBox>();
        List<BoundingSphere> spheres = new List<BoundingSphere>();        

        ContentProcessorContext context;

        bool enableLogging = false;

 

 

 

So, now I can hold a bounding box and sphere per mesh in the model. In the process method I set up a dictionary that can be used to store this data and be placed in the models Tag field so it can then be accessed later in your game code. I then call the base Process method before calling my GenerateData method. I then store the bounds data in the Tag object.

        public override ModelContent Process(NodeContent input, ContentProcessorContext context)
        {
            string modelName = input.Identity.SourceFilename.Substring(input.Identity.SourceFilename.LastIndexOf("\\") + 1);
            if (EnableLogging)
                LogWriter.WriteToLog(string.Format("Process started for {0}", modelName));

            this.context = context;

            Dictionary<string, object> ModelData = new Dictionary<string, object>();

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

            GenerateData(input);

            ModelData.Add(ModelDataSlot.BoundingBoxs.ToString(), boxs);
            ModelData.Add(ModelDataSlot.BoundingSpheres.ToString(), spheres);


            baseModel.Tag = ModelData;

            if (EnableLogging)
                LogWriter.WriteToLog(string.Format("Process completed for {0}", modelName));

            return baseModel;
        }

So, the meat and potatoes of the method is in GenerateData, this method checks if the asset is indeed a mesh and for each GeometryContent object in that mesh sets up a min and max value, it then loops through all the vertices by index (this is legacy from my original processor), get’s it’s position, calculates if that is greater or smaller than the last recorded min/max and store it, once all the vertices are read it then stores the final min.max data in the bounding lists. The method then does the same for all children.

        private void GenerateData(NodeContent node)
        {
            MeshContent mesh = node as MeshContent;

            if (mesh != null)
            {
                MeshHelper.OptimizeForCache(mesh);

                // Look up the absolute transform of the mesh.
                Matrix absoluteTransform = mesh.AbsoluteTransform;

                int i = 0;

                // Loop over all the pieces of geometry in the mesh.
                foreach (GeometryContent geometry in mesh.Geometry)
                {
                    Vector3 min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
                    Vector3 max = new Vector3(float.MinValue, float.MinValue, float.MinValue);

                    // Loop over all the indices in this piece of geometry.
                    // Every group of three indices represents one triangle.
                    List<Vector3> thisVerts = new List<Vector3>();
                    List<int> ind = new List<int>();
                    
                    Vector3 vertex = Vector3.Zero;
                    
                    foreach (int index in geometry.Indices)
                    {
                        // Look up the position of this vertex.
                        vertex = Vector3.Transform(geometry.Vertices.Positions[index], absoluteTransform);

                        // Store this data.
                        min = Vector3.Min(min, vertex);
                        max = Vector3.Max(max, vertex);
                                                
                        thisVerts.Add(vertex);
                        
                        ind.Add(i++);
                    }

                    if (EnableLogging)
                    {
                        LogWriter.WriteToLog(string.Format("BoundingBox created min = {0}, max = {1}", min, max));
                    }
                    boxs.Add(new BoundingBox(min, max));
                    spheres.Add(BoundingSphere.CreateFromBoundingBox(boxs[boxs.Count - 1]));                    
                }
            }

            // Recursively scan over the children of this node.
            foreach (NodeContent child in node.Children)
            {
                GenerateData(child);
            }
        }

 

And that’s it, we can now get at this data in game. In the sample I have done it when the mesh is loaded to get the bounding box volume like this

            if (mesh == null && modelName != string.Empty)
            {
                mesh = Game.Content.Load<Model>(modelName);

                transforms = new Matrix[mesh.Bones.Count];
                mesh.CopyAbsoluteBoneTransformsTo(transforms);

                Dictionary<string, object> data = (Dictionary<string, object>)mesh.Tag;

                volume = ((List<BoundingBox>)data["BoundingBoxs"])[0];
            }

I can then, during the update set the bounding box like this

            bounds = new BoundingBox(Vector3.Transform(volume.Min, World), Vector3.Transform(volume.Max, World));

and I can now use this to check for collision.

        public bool Collided(Base3DObject bob)
        {
            bool retVal = false;

            if (bounds.Intersects(bob.bounds))
            {
                this.boxCol = Color.Pink;
                bob.boxCol = Color.Pink;
                this.DiffuseColor = Color.DarkRed;
                bob.DiffuseColor = Color.DarkRed;
                retVal = true;
            }
            else
            {
                this.boxCol = Color.Black;
                bob.boxCol = Color.Black;
                this.DiffuseColor = Color.SteelBlue;
                bob.DiffuseColor = Color.SteelBlue;
            }

            return retVal;
        }

imageimage

As ever , C&C welcome :)

Sample code can be found here.

Multiple Mesh Bounding Box’s

As we can see from the comments below, this post skimped on the use of mesh’s that have more than one bounding volume, so a bounding volume per mesh in mesh. So to get this going you need to make a couple of modifications to the Base3DObject class.

First off, you need to make the volume and bounds fields to be List of BoundingBox’s

        List<BoundingBox> volume;
        public List<BoundingBox> bounds;

Now, in the update call, we change how we load the volumes up

                volume = new List<BoundingBox>((List<BoundingBox>)data["BoundingBoxs"]);

and also how we calculate the bounds list used for collision data like this

            bounds = new List<BoundingBox>(volume);
            for(int b = 0; b< bounds.Count;b++)
            {
                bounds[b] = new BoundingBox(Vector3.Transform(bounds[b].Min, World), Vector3.Transform(bounds[b].Max, World));
            }

And finally in the Collide call we have to check ALL the bounding volumes

            if (bounds == null || bob.bounds == null)
                return retVal;

            foreach (BoundingBox b in bounds)
            {
                foreach (BoundingBox bb in bob.bounds)
                {
                    if(b.Intersects(bb))
                    {
                        this.boxCol = Color.Pink;
                        bob.boxCol = Color.Pink;
                        this.DiffuseColor = Color.DarkRed;
                        bob.DiffuseColor = Color.DarkRed;
                        retVal = true;

                        return retVal;
                    }
                    else
                    {
                        this.boxCol = Color.Black;
                        bob.boxCol = Color.Black;
                        this.DiffuseColor = Color.SteelBlue;
                        bob.DiffuseColor = Color.SteelBlue;
                    }
                }
            }

And we should end up with something like this

image image

Hope this addition to the post has helped :D


Posted Mon, Aug 9 2010 2:21 PM by Charles Humphrey

Comments

Community Blogs wrote Windows Client Developer Roundup 036 for 8/9/2010
on Tue, Aug 10 2010 12:00 AM

This is Windows Client Developer roundup #36. The Windows Client Developer Roundup aggregates information

mike wrote re: BoundingBox’s in XNA 4.0
on Fri, Jan 14 2011 8:57 AM

this will sound noob, but when i load a model that contains multiple meshes, only 1 bounding box is created from the model, why is that? (supposed to create one for all no?)

Charles Humphrey wrote re: BoundingBox’s in XNA 4.0
on Fri, Jan 14 2011 9:20 AM

You can, but you will need to alter the code.

In the project, you will have to modify the code in Base3DObject, as it stands now we have one volume value and one bounds (transformed bb) value, you will need to create a list of each and when the volume data is read from the mesh i.e.

volume = ((List<BoundingBox>)data["BoundingBoxs"])[0];

Put that in a loop to get ALL the bounding box data from the mesh.

Then you will need to put the transformed bounds into a loop to, replacing

bounds = new BoundingBox(Vector3.Transform(volume.Min, World), Vector3.Transform(volume.Max, World));

with a loop to populate the new list.

Then in the draw call you will have to make a DrawBox(bounds[b]); call rather than this

       public override void Draw(GameTime gameTime)

       {

           Draw(gameTime, Effect);

           DrawBox(bounds);

       }

mike wrote re: BoundingBox’s in XNA 4.0
on Sat, Jan 15 2011 12:25 PM

Thanks for the help, i'm still confused about one part. How do i know how many meshes there are within the model file? If i create a forloop for volume (assuming volume is a list), e.g.

for (int i = 0; i < volume.Count; i++)

               {

                   volume[i] = ((List<BoundingBox>)data["BoundingBoxs"])[i];

               }

volume.count will return no elements since the list will be empty no? (i have tried it with arrays and it works but i need to know how many meshes there are at all times which is rather troublesome .. ).

Charles Humphrey wrote re: BoundingBox’s in XNA 4.0
on Sun, Jan 16 2011 10:49 AM

You would use ((List<BoundingBox>)data["BoundingBoxs"]).Count instead as that would be your source.

Also you have mixed syntax there, you would want to do a volume.Add

If both are lists you can avoid the loop and do

List<BoundingBax> volumes = new List<BoundingBox>(((List<BoundingBox>)data["BoundingBoxs"]));

mike wrote re: BoundingBox’s in XNA 4.0
on Sun, Jan 16 2011 12:39 PM

When i try to loop and add the bounding boxes :

           for (int i = 0; i < volume.Count; i++)

           {

              bounds[i] = new BoundingBox(Vector3.Transform(volume[i].Min, World), Vector3.Transform(volume[i].Max, World));

           }

What am i doing wrong? sorry for being hard headed, trying to learn :(

Charles Humphrey wrote re: BoundingBox’s in XNA 4.0
on Sun, Jan 16 2011 1:57 PM

I'll alter the post later and show you how to add and render all the box's for your mesh's.

Don't apologies, I should have included it in the sample really :S

mike wrote re: BoundingBox’s in XNA 4.0
on Sun, Jan 16 2011 4:48 PM

thanks mate , really appreciate it^^.

Charles Humphrey wrote re: BoundingBox’s in XNA 4.0
on Sun, Jan 16 2011 8:28 PM

Done. Hope it helps.

mike wrote re: BoundingBox’s in XNA 4.0
on Tue, Jan 18 2011 3:14 PM

thanks, it does help. I still want to ask you, is it possible to access the position of each mesh? where does the position of the mesh get stored when the object when its run though the content processor?

Charles Humphrey wrote re: BoundingBox’s in XNA 4.0
on Wed, Jan 19 2011 9:04 AM

The transforms array will give you the transform matrix for each mesh in the mesh, from the matrix you can get the translation.

i.e.

transforms = new Matrix[mesh.Bones.Count];                mesh.CopyAbsoluteBoneTransformsTo(transforms);

So you could do

Vector3 MeshPos = transforms[n].Translation;

Or you could use the min/max values of each bounding box to calculate the centre of the mesh.

In the custom content processor we use each mesh's transform to set up each vertex

Matrix absoluteTransform = mesh.AbsoluteTransform;

again, you could get the model position from the translation of the matrix like this

Vector3 MeshPos = absoluteTransform.Translation;

mike wrote re: BoundingBox’s in XNA 4.0
on Sun, Jan 23 2011 2:41 AM

Thanks. Assuming that you are translating the mesh object, why doesn't the bounding box get updated? is it because the content processor runs only once when the game is initialized? if so how do u overcome this issue?

Charles Humphrey wrote re: BoundingBox’s in XNA 4.0
on Sun, Jan 23 2011 9:24 AM

The bounds data and the mesh data are separate transforming one will not transform the other and so, you can see in the Update that each box gets transformed with the world matrix, just as we do when transforming the mesh.

I don't think I am understanding your question too well, can you describe the issue a bit more?

mike wrote re: BoundingBox’s in XNA 4.0
on Sun, Jan 23 2011 4:18 PM

For example, if you have 50 meshes within 1 file and you create a bounding box for each one. You want to translate mesh[25] forward, how would you do this?.

The way i am thinking is, copying transform[i].translate into a list<vector3> and then update it in the world matrix Matrix.Translate(position[i]).But it seems a bit long winded, is there a easier approach to this?

Charles Humphrey wrote re: BoundingBox’s in XNA 4.0
on Sun, Jan 23 2011 7:38 PM

Well in that case you will have a separate world matrix for each mesh so you apply the same world matrix for bounds[25].

All you have to do is apply the same world matrix to bounds[25] as you did mesh[25]...

What are you doing? Animating a model on the CPU?

mike wrote re: BoundingBox’s in XNA 4.0
on Mon, Jan 24 2011 8:56 PM

I am animating however, not in this instance. The way i have it at present is :  I created a general class that is able to render a object, giving its coordinate vector 3 "position" on screen. Now, when i render a model, its able to create bounding boxes for every mesh however the whole file is relative to its vector3 position that i initialize at the start. My world matrix is as follows :  world = Matrix.CreateScale(Scale) * Matrix.CreateTranslation(position); . At present i am a bit confused on how i can transform the whole mesh including the bounding box (for mesh i now know its transforms[i].Translate) . If that makes any sense?

Charles Humphrey wrote re: BoundingBox’s in XNA 4.0
on Tue, Jan 25 2011 11:04 AM

Well the world matrix will do that. Changing position value will result in a change to the World matrix and so the rendered position of the model.

mike wrote re: BoundingBox’s in XNA 4.0
on Tue, Feb 1 2011 2:01 PM

Thanks :D. I still have another question. If you have 1 mesh within one file (lets call it A) , and 100 meshes within another file (B). How do you retrieve the mesh that its colliding with? I put it in a forloop and made a return function, it does work but for some reason its returning the wrong mesh

Charles Humphrey wrote re: BoundingBox’s in XNA 4.0
on Tue, Feb 1 2011 2:09 PM

OK, you need to look at space partitioning and scene management, this post does not cover that, but I have an old post on a GGR I did, I have since found that that implementation has lots of issues. I have fixed them but not posted the code yet.

Google BSP and KD trees too

mike wrote re: BoundingBox’s in XNA 4.0
on Sun, Feb 6 2011 3:42 PM

thanks, i started reading into it, will try to implement octrees first then work my way up to bsp (find that a bit harder to understand). One thing im still a bit confused about, if a mesh is rotated, lets assume its a box. When the game loads the bounding box is disproportionate/does not fit well at all.Why is this?i do know the dimensions of the box will be dependant on the min/max values, but in 3D space, its essentially the still a box with equal dimensions (lenght/width). If the box is not rotated, it fits perfectly ..

Charles Humphrey wrote re: BoundingBox’s in XNA 4.0
on Sun, Feb 6 2011 4:15 PM

We are using Axis Aligned Bounding Box's (AABB) not Object Orientated Bounding Box's (OOBB)

XNA does not have an intrinsic OOBB, but does have a AABB. You will need to write your own OOBB class to do this.

mike wrote re: BoundingBox’s in XNA 4.0
on Sun, Feb 6 2011 5:07 PM

yeah figured that out, doing that now ty ^^