Bounding boxes/Spheres

Aug 10, 2010 at 3:52 PM

I've used some of the code from the tutorials to generate bounding boxes for my model, but they don't match with the model's position. How do I apply the same transforms to the boxes as my model? Also, would I be better off using spheres? I'm planning a fighting game, some of the stuff ??I've read up on suggests spheres are handier for collision detection.

Coordinator
Aug 10, 2010 at 5:18 PM
Edited Aug 10, 2010 at 5:23 PM

The bounds data stored in the model (see tutorial 12) has both spheres and boxes for each bone.

How are you debugging these boxes? Are you displaying them on screen? Are they used for cull testing? For example; You may be generating the boxes fine, but not taking the world transform into account when displaying them.

If you are using them for cull testing, try adding a culltest visualiser (shown in the advanced particles tutorial), this will visibly display all the culltests being performed.

[edit]

Yes, spheres are generally easier for collision detection. If you want to get the world space position of each sphere, then you will need to transform the position by the world matrix first (tut 12 generates a world matrix for each bone before displaying the BB - note this world matrix is multiplied with the current character matrix too). To get the absolute world position would require transforming the transformed bone position by model world matrix (This is done in the manual animation sample to pass data into the verlet solver).

Aug 10, 2010 at 5:27 PM

I used the code from tutorial 12 to generate the boxes, my understanding was that the code generates boxes for each bone
"The bounds data stored in the model (see tutorial 12) has both spheres and boxes for each bone." - should I be creating this bounds data in my 3d Modelling program?
In order to get started, I just copied the code across, I wanted to see if I could get it working with my model. I assumed that generating collision volumes would be the first step, then customizing them to my needs.

Aug 16, 2010 at 7:34 PM

I've named my bones in the modelling program, and have checked the .fbx to find them. Can I assign a volume to named bones only?For example, my model contain ik bones/fingerbones etc. that dont require collision volumes, but looping through all bones will assign them.

Aug 25, 2010 at 1:44 PM
Edited Aug 25, 2010 at 1:46 PM

I've had some success with this, I can isolate the bones, now I just need to efficiently assign the collision volumes.

I created a string array, where I name the bones I want to create volumes for

 

 //these are the bones that need collision volumes
            collisionBones = new string[] { "Head", "Forearm.l", "Forearm.r" };

 

and have been trying to compare the string elements to specific bone names using this code

 private void DrawBoundingSpheres(DrawState state)
        {
            //First, get the animated bone transforms of the model.
            //These transforms are in 'bone-space', not in world space.
            ReadOnlyArrayCollection<Transform> boneAnimationTransforms = model.GetAnimationController().GetTransformedBones(state);

            //Get a simple shader from Xen.Ex that fills a solid colour
            Xen.Ex.Shaders.FillSolidColour shader = state.GetShader<Xen.Ex.Shaders.FillSolidColour>();

            shader.FillColour = Color.SteelBlue.ToVector4();

            shader.Bind(state);

            //push the render state...
            state.PushRenderState();

            //disable back face culling
            state.RenderState.DepthColourCull.CullMode = CullMode.None;
            //set to wireframe
            state.RenderState.DepthColourCull.FillMode = FillMode.WireFrame;

            //loop through all the geometry data in the model..
            //(note, the sample model has only 1 geometry instance)


            Xen.Ex.Graphics.Content.SkeletonData modelSkeleton = model.ModelData.Skeleton;
            Matrix matrix;
            int sphereIndex = 0;
           
            foreach (Xen.Ex.Graphics.Content.MeshData meshData in model.ModelData.Meshes)
            {
                foreach (Xen.Ex.Graphics.Content.GeometryData geometry in meshData.Geometry)
                {
                    //now loop through all bones used by this geometry
                    for (int geometryBone = 0; geometryBone < geometry.BoneIndices.Length; geometryBone++)
                    {
                       foreach(string s in collisionBones)
                       {
                        //index of the bone (a piece of geometry may not use all the bones in the model)
                        int boneIndex = geometry.BoneIndices[geometryBone];
                        if (model.ModelData.Name.Equals(s))
                            {
                                //get the base transform of the bone (the transform when not animated)
                                Transform boneTransform = modelSkeleton.BoneWorldTransforms[boneIndex];

                                //multiply the transform with the animation bone-local transform

                                //it would be better to use Transform.Multiply() here to save data copying on the xbox
                                boneTransform *= boneAnimationTransforms[boneIndex];

                                //Get the transform as a matrix
                                boneTransform.GetMatrix(out matrix);
                                matrix *= worldMatrix;
                                //push the matrix
                                state.PushWorldMatrix(ref matrix);

                                //draw the sphere
                                if (boundingSpheres[sphereIndex].CullTest(state))
                                    boundingSpheres[sphereIndex].Draw(state);

                                sphereIndex++;

                                //pop the world matrix
                                state.PopWorldMatrix();
                            }
                        }
                    }
                }
            }
            //pop the render state
            state.PopRenderState();
        }

This isn't working, I thought the model.ModelData.Name.Equals(s) line above would get the name of each bone as a string and compare it to the values in collisionBones. Does anyone see what I'm doing wrong?

 

Aug 27, 2010 at 10:48 PM

ok I solved this, here's my solution for anyone who might like it.
I create an array of strings containing the bones I want.

private  string[] collisionBones = new string[] { "Hand.l", "Hand.r", "Head" };

Then I add a foreach loop to the existing loop through the bones.

foreach (string s in collisionBones)

and compare the string to each bone, creating the sphere if they match,

if (boneIndex == model.ModelData.Skeleton.GetBoneIndexByName(s))
This seems to be working fine.

Here is the complete method.

 private void BuildBoundingSphereGeometry()
        {
            boundingSpheres = new List<IDraw>();
           
            foreach (string s in collisionBones)
            {
                foreach (Xen.Ex.Graphics.Content.MeshData meshData in model.ModelData.Meshes)
                {
                    foreach (Xen.Ex.Graphics.Content.GeometryData geometry in meshData.Geometry)
                    {
                        //now loop through all bones used by this geometry
                        for (int geometryBone = 0; geometryBone < geometry.BoneIndices.Length; geometryBone++)
                        {
                            //index of the bone (a peice of geometry may not use all the bones in the model)
                            int boneIndex = geometry.BoneIndices[geometryBone];

                            if (boneIndex == model.ModelData.Skeleton.GetBoneIndexByName(s))
                            {
                                //the bounds of the geometry for the given bone...
                                Xen.Ex.Graphics.Content.GeometryBounds bounds = geometry.BoneLocalBounds[geometryBone];
                                //create the vloume
                                boundingSpheres.Add(new Xen.Ex.Geometry.Sphere(new Vector3(3, 3, 3), 5));
                            }
                        }
                    }
                }
            }
        }

Sep 2, 2010 at 4:26 PM

As I stated above, I can generate the bones I want, now I need to know how to do the collision checks themselves. As I see it, the most common test would be to see if the fist of my player is intersecting with a n enemy while the player's punch animation is playing. My problem is , where do I test for this collision, in the player, enemy or game class? Once the test is performed, how do I play an animation in the Enemy class? Up to now, my animations have been contained in the player class, and activated on keyboard input.
I'd appreciate any help as I'm really stuck here.

Coordinator
Sep 4, 2010 at 9:51 PM
Edited Sep 4, 2010 at 9:56 PM

Hi. Sorry for my late reply, I'm not ignoring you, I've just had a tonne of things going on and going wrong recently.

This is typically the sort of place you'd have a 'physics manager' class, or some kind of updating structure in place to deal with this sort of thing. As you have found out, it's not an easy problem to solve. There are lots of possible solutions and it really depends on the demands of your game (I know, this isn't an answer!). Basically, if you find that you are duplicating code - or spreading code around in multiple places, then you are probably going down the wrong path. Unfortuntely it's not really something I can say 'DO THIS!' because I simply don't know enough about your game, and you generally only learn these things by screwing it up enough to learn where things go wrong (make no mistake, I've written lots of TERRIBLE code in the past :-)

...

Often times, with situations like this, resulting actions such as animation playback are not the specific result of a collision, but a result of changed state that was triggered by a collision. (Note the separation of cause and effect!)

To give a simple example: When do you play a 'player hurt' animation in a first person shooter?. The immediate answer is 'when the player is hit!' however this actually is potentially a very bad answer - by 'hard coding' in the reaction to an event, then you effectivly introduce a special case. Therefore, all other events that could trigger, stop or otherwise affect that speceial case need to take it into account. You can quickly get exponential complexity growth. Basically, your code explodes.
You then get truly horrible maintenence nightmares along the lines of "bug 51345: shooting player that has died from a fall but hasn't stopped the falling down dead animation makes them play the hit by bullet in the left leg animation standing up, and when it finishes the player is in the T-pose". It's not uncommon for multi-million dollar games to be cancelled because the project turns into giant 'deal with special case bugs' nightmares. In fact, I'd bet this is the cause for the majority of failed games.

The end solution is you often need to look at the rules that govern when these things happen, how they happen, and what priority they should happen. (Think about the animation question thread).
As long as you can spearate this logic out from other game logic, then you are on the right track. So after the punch lands, you can adjust the state of the enemy and call 'UpdateAnimations()' and know that the correct animation is playing (even if it's NOT the 'hit by punch' animation!). UpdateAnimations may be some very complex logic, but as long as it's in one place, and not duplicated, that's fine!

FInally, I will say that this sort of question is best asked in the main XNA forums. There are WAY more people there who will be ready and to answer any questions, and you'll get a responce much quicker too :-)

Good luck!

Sep 4, 2010 at 10:04 PM

Interesting post, I have noticed some duplication in my code, and have been considering how to deal with it. for instance, I have a player and enemy class that both have more or less identical function, just that the enemy isn't controlled by keyboard input. I may just go with a base Actor class, and extend from there. I've already pulled the collision code out, with the intent to put it in a separate class. I asked on XNA forums and one solution was to have a method that accepts the classes as arguments. I have it in my Game2 class at the moment, but as it will groo, I expect it'll need a class of it's own.

Sep 7, 2010 at 2:31 PM
Edited Sep 14, 2010 at 6:05 PM

Do the Xen.Ex.Geometry.Cube or Xen.Ex.Geometry.Sphere types have anything similar to the .intersects method? I've been hunting around for the source code for the XNA method, I wondered if it might be possible to incorporate it to the Xen Sphere object.

I'd prefer to use the Xen type as they're easier to draw with my current code, but I can't figure out how to use them for collision tests.
I'm having trouble updating the sphere's during runtime, how do I apply the transforms for each bone in my model to the relevant  Spheres? At the moment, the spheres positions don't update.

Sep 21, 2010 at 1:52 PM

Due to the lack of an intersects method, I've gone back to using BoundingSphere. I've also been trying to get the spheres to render using the bounding sphere rendering class from the XNAWiki http://xnawiki.com/index.php?title=Rendering_Bounding_Spheres .
The thing is, I'm not sure how to use this with Xen, specifically, how do I use the method

Render( BoundingSphere sphere,
GraphicsDevice graphicsDevice,
 Matrix view,
 Matrix projection,
 Color color)

What do I use for the parameters here? I have created my spheres, so the first one's ok. How does Xen deal with graphics device? The first tutorial mentions 'The vast majority of applications shouldn't need to directly access the graphics device.' But it seems that I need to here. How do I do that?

Sep 21, 2010 at 4:50 PM

I've tried this

 GraphicsDevice graphDev =  state.BeginGetGraphicsDevice(StateFlag.None);
            foreach (BoundingSphere s in boundingSpheres)
            {
                BoundingSphereRenderer.Render(s, graphDev, view, projection, Color.AliceBlue);
            }
But the spheres aren't visible yet. Is this the correct way to go about it? I couldn't see how to use the existing tutorial code to draw bounding boxes, as that seems to be specific to the Xen type geometry.

Sep 23, 2010 at 3:54 PM

As well as the question above, what should I use for the 'view' and 'projection' fields?

Sep 25, 2010 at 10:40 PM

This is the code I'm using at the moment to build my collision spheres.

 private void BuildBoundingSphereGeometry(DrawState state)
        {
            //These transforms are in 'bone-space', not in world space.
            ReadOnlyArrayCollection<Transform> boneAnimationTransforms = model.GetAnimationController().GetTransformedBones(state);

            boundingSpheres = new List<BoundingSphere>();
            Xen.Ex.Graphics.Content.SkeletonData modelSkeleton = model.ModelData.Skeleton;
            Matrix matrix;
           
            foreach (string s in collisionBones)
            {
                foreach (Xen.Ex.Graphics.Content.MeshData meshData in model.ModelData.Meshes)
                {
                    foreach (Xen.Ex.Graphics.Content.GeometryData geometry in meshData.Geometry)
                    {
                        //now loop through all bones used by this geometry
                        for (int geometryBone = 0; geometryBone < geometry.BoneIndices.Length; geometryBone++)
                        {
                            //index of the bone (a peice of geometry may not use all the bones in the model)
                            int boneIndex = geometry.BoneIndices[geometryBone];

                            if (boneIndex == model.ModelData.Skeleton.GetBoneIndexByName("Hand.l"))
                            {
                                Transform boneTransform = modelSkeleton.BoneWorldTransforms[boneIndex];
                               
                                //multiply the transform with the animation bone-local transform

                                //it would be better to use Transform.Multiply() here to save data copying on the xbox
                                boneTransform *= boneAnimationTransforms[boneIndex];

                                //the bounds of the geometry for the given bone...
                                Xen.Ex.Graphics.Content.GeometryBounds bounds = geometry.BoneLocalBounds[geometryBone];
                                
                                boneTransform.GetMatrix(out matrix);
                                state.PushWorldMatrix(ref matrix);   
                            
                                boundingSpheres.Add(handSphereL = new BoundingSphere(matrix.Translation, 3));

                                state.PopWorldMatrix();
                            }
                        }
                    }
                }
            }
        }

I don't think that the spheres are updating properly , and that's part of the reason I'd like to get the XNA BoundingSphere to render. It seems I'm stuck between a Xen sphere with no intersect method, and BoundingSphere with difficulty getting it working with Xen. Can anyone help?

Coordinator
Sep 26, 2010 at 12:52 PM

Hi.

Sorry about my slow reply, I've been on holiday for a while :-)

Looks like you have a tonne of questions. I'll try and answer them as best I can:
The code/sphere rendering classes don't have any built in collision code. The best you can do is probably the existing intersect code in XNA. Intersecting cubes get's really fiddly (separating axis theorem, etc) when they are not axis-aligned (axis aligned cubes are incredibly easy to test). If you are interested in collision tests, then check out the intersections page on realtimerendering.com.

I think it's best to ignore that XNA class. The view/projection matrices are all internally dealt with by xen - you can get them through the camera (ICamera interface has GetProjection, etc). However it's basically doing everything the Xen.Ex.Geometry.Sphere class does - just in a tonne more code.

The Sphere object in Xen simply creates a vertex buffer and index buffer to draw a sphere - it doesn't store position, etc. It's just a 'dumb' drawing object, representing the geometry of a sphere.

So in your case, I'd suggest simply create a xen sphere (just one) then in your loop you have now, replace:

                                boneTransform.GetMatrix(out matrix);
                                state.PushWorldMatrix(ref matrix);   
                            
                                boundingSpheres.Add(handSphereL = new BoundingSphere(matrix.Translation, 3));

                                state.PopWorldMatrix();

with something more like:

                                boneTransform.GetMatrix(out matrix);
                                state.PushWorldMatrixMultiply(ref matrix);
                                Matrix.CreateScale(bounds.Radius, out matrix);
                                state.WorldMatrixMultiply(ref matrix);
                           
                                sphereGeometry.Draw(state);

                                state.PopWorldMatrix();

 

And change the method from 'BuildBoundingSpheres' to 'DrawBoundingSpheres' and call it every frame. That way you'd have one object for sphere geometry, and you'd be reusing it to draw all the bounding spheres.

The only tricky bit is getting the scale correct (which I think would be correct like that)

Sep 26, 2010 at 2:46 PM

'The best you can do is probably the existing intersect code in XNA'
So I should probably try to take the code from the XNA class and add it to a custom class for Xen?
My understanding of the basics for sphere collision is that I would need the center and radius of the sphere, updated at runtime, to get a collision test, the Xen sphere doesn't store position ,as you mention, so do I use the bone transform for the position/center?

Sep 30, 2010 at 7:08 PM

I'm having trouble understanding the tip above, where is the method "state.WorldMatrixMultiply(ref matrix);" ? I get the error saying that state contains no definition for this method. Is this pseudo code? From what I understand, the idea with your suggestion is to create a method that takes a name (i.e handSphereL), as an argument, allowing me to call it for the spheres I need? If the sphere doesn't store position, how do I get it to move with the bone? Is it stored in the state variable with "state.PushWorldMatrixMultiply(ref matrix);" ?

Oct 1, 2010 at 3:05 PM

I cant get this class working, the spheres are offset from the bone positions, hey seem to be floating below the model, they move with the animations, but don't move with the model(translations). And all this is before I even have to code the intersections XP

Oct 3, 2010 at 11:11 PM
Edited Oct 7, 2010 at 10:24 PM

I had been forgetting to multiply the bone transform by the world matrix :o I now have the spheres appearing and moving with the model, and can create spheres for specific bones. The next step will be to try adding collision tests to the spheres.

Oct 12, 2010 at 11:51 PM

I still don't understand how I can use a Xen sphere  for collision detection, I'm trying to fill in this class to do the checks

namespace myXen
{
    class CollisionHandling
    {
        public void collisionCheck(Enemy enemy, Actor actor)
        {
            /* if (Intersects(actor.handSphereL,enemy.chestSphere, Sphere Center1, Sphere Center2))
             {
                 enemy.HeadImpact();
             }*/
        }
        /* private bool Intersects(Sphere x, Sphere y, Transform xCenter, Transform yCenter) 
         {
             Matrix xMatrix;
             Matrix yMatrix;
             xCenter.GetMatrix(out xMatrix);
             xCenter.GetMatrix(out yMatrix);
         * 
             if()
             {
                 return true;
             }
          
             else
             {
                 return false;
             }
         }*/
    }   
}

But I cant see how I can get the center of the Xen spheres needed to perform the collision test. radius is no problem, the Sphere class exposes that, so it's just getting the center to fulfill this fomula I found on Gamedev "The simplest possible way to do bounding-sphere test is to measure the (squared) distance between the two objects in question and to compare the result with the (squared again) sum of their radii. "

Oct 13, 2010 at 6:41 AM

I hope I won't steer you wrong because I've only skimmed the last few posts in this thread, but if I'm reading right you're already drawing the collisions spheres.  Tthe centre of each sphere will be the bottom row of the World matrix you used to draw it.

i.e.  after this:

                                boneTransform.GetMatrix(out matrix);
                                state.PushWorldMatrixMultiply(ref matrix);

Vector3 sphereCentre = new Vector3(matrix.M41, matrix.M42, matrix.M43);

 

Oct 16, 2010 at 4:32 PM

That looks right, but the problem is in getting the sphereCentre value and passing it to the CollisionHandling class.  I need each sphere, (i.e. handSphereL, HeadSphere, etc), to have expose their centres to the collision handler. Using your advice, I got the centre, but I can't use it outside the class, and as it builds each sphere in turn, doesn't it get overwritten?

Oct 16, 2010 at 5:09 PM
Edited Oct 17, 2010 at 10:43 PM

This is my current collision class.

 class CollisionHandling
    {
         public void collisionCheck(Enemy enemy, Actor actor)
         {
             if (Intersects(actor.handSphereL, enemy.chestSphere, actor.handSphereCentre, enemy.chestSphereCentre))
             {
                 enemy.HeadImpact();
             }
         }
         private bool Intersects(Sphere x, Sphere y, Vector3 xCentre, Vector3 yCentre) 
         {
             float sumRadii = ((x.Radius + y.Radius) * (x.Radius + y.Radius));
             double distance = (((xCentre.X - yCentre.X) 
* (xCentre.X - yCentre.X)) + ((xCentre.Y - yCentre.Y)
 * (xCentre.Y - yCentre.Y)) + ((xCentre.Z - yCentre.Z) * (xCentre.Z - yCentre.Z))); if(distance < sumRadii) { return true; } return false; }

I added this public Vector3 handSphereCentre; to the actor and enemy classes, so that I can access the centre of the spheres in the collision class. Now I'm wondering if there's another problem, as the collision is returning as always true, making the headHit animation play non-stop. It's tough to track down, it could be that the sphere's matrices aren't being updated, or that the animation code is wrong. Any help would be appreciated.

Oct 19, 2010 at 2:42 AM

Your code looks ok.  If you're using some variant of the code I previously posted, I think I *did* steer you wrong.  (Sorry)

state.PushWorldMatrixMultiply(ref matrix); probably doesn't update the matrix you pass in.  Check that out to be sure: I suspect it's always be in bone local space.  So to get the center of the each sphere in World space you need to do the pushmultiply call and the use something like state.WorldMatrix.M12 etc. to get the position of each sphere in world space.  In fact you'd probably be better off with a physics manager and do the matrix operations yourself instead of relying on the rendering negine to create them, but I understand the appeal of using Xen's working animation system while figuring things out and then refactor code into your own physics system afterwards.

I think you're using Xen 1.8 right?  Unfortunately I'm exclusively on Xen 2.0 now, so I can't easily double check what "state" is doing, but it should be pretty straightforward to check the interface to see how to get the World matrix back out from it.  (In Xen2, "WorldMatrix" is a property:  hopefully the same is true of Xen 1.8).  Reality checking the centers of the spheres should be straightfoward.  Put your character quite a long way away from the origin, and set a breakpoint at the state.pushmultiply call.  The boneTransforms.Translation should all be close to zero compared to the location of the character in world space.  After the pushmultiply, I suspect you'll see the same matrix as was passed in, and then once you get the world matrices back from the state object, you should see their translation vector being close to the vector you used to position your character in the world.

 

Oct 19, 2010 at 4:17 PM

I am mostly using 1.8, I've been trying to get it to port to 2.0, but I'm getting an error with the animations,

if (animationIndex == -1)
                throw new ArgumentException();

as well as some texturing problems. Ideally, I'd prefer to work on 2.0, because of the XNA 4.0 compatibility.

The main reason I've been doing the collision in Xen, is because it seemed easier at the time than importing a physics package, and trying to tie them together. Also, my needs seem relatively simple, compared to a physics-heavy game.
If you don't mind me asking, what physics system do you use?

Oct 21, 2010 at 6:05 AM

2.0 is still a little unstable in places.  I think StatusUnknown mentioned somewhere that the Xbox causing him problems in the animation system and the instancing system in Xen 2.0 (aside from being sick and moving and starting a new job and generally having more to do in a day than day to do it in.)  So for what you're doing it sounds like you'd want to stay on 1.8 for now.

I wrote the physics system for my current project. a racing sim, beacuse I wanted to experiment with a pretty involved dynamics model that only one library I've seen can support in real time (and that package is commercial and runs $11 000 US).  

But I did retrofit my game with JigLibX in an evening.  I can't comment on how accurate/fast/robust it is because I just needed something quick to test something out, but I found it was flexible and has a decent number of sample programs to demonstrate what it can do and get you into the code smoothly.  When I was looking around I saw three or four good looking libraries with C# source available and picked JigLibX mostly because of the demo programs.  IIRC the collision system and the integrator are separate entities, so you ought to be able to make use of the collision system on its own and synch the collision primitives to your animation in a similar way to what you've described in this thread.  The nice thing is that then you just say: ProcessCollisions (or something like that) and you'll get callbacks telling you what's colliding with what, so you don't have to write or test any of that code and can concentrate on reacting to collision events with animation changes and scoring updates etc:  i.e. game stuff instead of engine stuff.

Nov 2, 2010 at 5:19 PM

I'm back to 1.8, but no joy. I'm trying to implement JigLibx, but the sample on their wiki are tough to interpret. It's a little hard to see what should go in which class to get the collision I need.