0
\$\begingroup\$

I have a collision method that seems like it would work, and it does, but the bounding sphere is always at 0, 0, 0. How do I fix this? Any more code and or details are available upon request.

BoundingSphere CreateBoundingSphereForModel(Model model, Matrix worldMatrix)
{
    Matrix[] boneTransforms = new Matrix[this.model.Bones.Count];
    this.model.CopyAbsoluteBoneTransformsTo(boneTransforms);

    BoundingSphere boundingSphere = new BoundingSphere();
    BoundingSphere meshSphere;

    for (int i = 0; i < model.Meshes.Count; i++)
    {
        meshSphere = model.Meshes[i].BoundingSphere.Transform(boneTransforms[i]);
        boundingSphere = BoundingSphere.CreateMerged(boundingSphere, meshSphere);
}
return boundingSphere.Transform(worldMatrix);
}

bool IsCollision2(Model model1, Matrix world1, Model model2, Matrix world2)
{
    BoundingSphere bs1 = CreateBoundingSphereForModel(model1, world1);
    BoundingSphere bs2 = CreateBoundingSphereForModel(model2, world2);

if (bs1.Intersects(bs2))
    return true;

return false;
}

private bool checkPlayerCollision(Model model1, Matrix world1)
{
    //Make player location matrix
    Vector3 playloc = new Vector3(X, Y, Z);
    //Make ship1 matrix
    Matrix ship1WorldMatrix = Matrix.CreateTranslation(ship1loc) * Matrix.CreateScale(0.1f);
//Make ship2 matrix
Matrix ship2WorldMatrix = Matrix.CreateTranslation(ship2loc) * Matrix.CreateScale(0.1f);
//Check for collision with ship1
if (IsCollision2(model1, world1, model, ship1WorldMatrix)) return true;
//Check for collision with ship2
if (IsCollision2(model1, world1, model, ship2WorldMatrix)) return true;
return false;
}
\$\endgroup\$
2
  • \$\begingroup\$ Need to see more code. For example what is the value of x, y and z ? Also the scaling looks dubious. Finally what is ship1loc and ship2loc initalised to? \$\endgroup\$
    – Peter_b_sw
    Commented Jan 2, 2014 at 23:55
  • \$\begingroup\$ X, Y, and Z are the location of the player. The only reason I am doing the scaling is because I am exporting models from Blender, and a glitch requires that. Lastly, the ship1loc is 0, -8, 5 and ship2loc is 0, -3, 0. Hope that helps! \$\endgroup\$ Commented Jan 3, 2014 at 13:18

1 Answer 1

0
\$\begingroup\$

A bit hard to see what you are doing ( will try an answer anyhow ), but if you are storing a sphere for testing collision or intersecting bounds , your bounding sphere should be initialized from the model bounding box, and store it. However keep it to object's local positioning ( usually Vector3.Zero ).

I think you may be over-complicating your collision by using translation matrices to setup world positions of bounding spheres when your ship location vectors are already in world space - when you can just use the vector3 values directly? ( also makes the code easier to read )...

I suggest not applying a world position to the bounding sphere until you do the actual hit testing, thus keeping a local space sphere for each mesh.

It sounds like ( in your comment response to Peter ) that you want to also scale it?

Assuming this , ensure you always scale the bounding-sphere independently of the positioning of the player/object's position.

1: - Create Bounding Sphere from loaded model in local space ( of the mesh itself ) - usually Vector3.Zero ( because most models are usually centered on it's center point ), or relative to the center of the overall mesh in local space ( averaged position of all mesh vertices )

2: - Scale the bounding sphere as needed . If all models of the same source are scaled to the same value, then you can keep one bounding sphere for all instances of the same model. If you scaled the mesh vertices in local space using matrix( scale* bonetransform ) directly, then generate the bounding sphere from the mesh itself only after scaling the mesh, but might be easier to creat the bounding box from the mesh first, then initialize a bounding sphere from that bounding box, as the bounding box will fit to the mesh itself.

3: - Apply object/player position to the bounding sphere only when testing collision or intersections ( not when the mesh is loaded or generated ). Use a local space copy to create a world space equivalent of the same bounding sphere ( and they can be cached ).

For example:

//
// the bounding sphere passed to the method, and is relative to the mesh's local space ( usually vector3.Zero, or averaged to mesh center )
// 
// ObjectPosition represent the position of the player or game object
//

//
// the iCamera object contains a view frustum to test intersection against if we are testing against the camera's view frustum. This does not apply when hit testing two bounding spheres though.
// 
public static bool g_IsInViewOfCamera(Vector3 ObjectPosition, BoundingSphere Sphere, iCamera Camera)
{

    //
    // This is the World Space bounding sphere used to test collisions or intersections:
    // 
    BoundingSphere S = new BoundingSphere(ObjectPositionInfo.Position + Sphere.Center, Sphere.Radius);

    // logic here... such as hit testing or frustum checks

    // If you are testing View Frustum only:
    return Camera.ViewFrustum.Contains(S) == ContainmentType.Contains ? Camera.ViewFrustum.Contains(S) == ContainmentType.Intersects : false;


}

And if you want to test only collisions between two bounding spheres (if they are both from the same model):

/// <summary>
/// Tests colition between two player positions, if both objects use the same bounding sphere
/// </summary>
/// <param name="ObjectPosition1">World Space position of a player or object</param>
/// <param name="ObjectPosition2">World Space position of the other player or object</param>
/// <param name="BoundingSphere">Local Space bounding sphere realitive to zero, generated from the mesh.</param>
/// <returns>True if they collide or intersect</returns>
/// <remarks></remarks>
public bool g_IsCollision(Vector3 ObjectPosition1, Vector3 ObjectPosition2, BoundingSphere BoundingSphere)
{
    //
    // World Space bounding sphere of player1 / object1
    //
    BoundingSphere S1 = new BoundingSphere(ObjectPosition1 + BoundingSphere.Center, BoundingSphere.Radius);


    //
    // World Space bounding sphere of player2 / object2
    //
    BoundingSphere S2 = new BoundingSphere(ObjectPosition2 + BoundingSphere.Center, BoundingSphere.Radius);


    if (S1.Contains(S2) == ContainmentType.Intersects)
        return true;
    if (S1.Contains(S2) == ContainmentType.Contains)
        return true;

    return false;
}

Or if you want to test using two local space spheres generated from two different meshes:

//
// Sphere 1 and 2 are local space bounding spheres
// object positions are in world space
//  
public bool g_IsCollision(Vector3 ObjectPosition1, Vector3 ObjectPosition2, BoundingSphere Sphere1, BoundingSphere Sphere2)
{
    //
    // World Space bounding sphere of player1 / object1
    //
    BoundingSphere S1 = new BoundingSphere(ObjectPosition1 + Sphere1.Center, Sphere1.Radius);

    //
    // World Space bounding sphere of player2 / object2
    //
    BoundingSphere S2 = new BoundingSphere(ObjectPosition2 + Sphere2.Center, Sphere2.Radius);

    if (S1.Contains(S2) == ContainmentType.Intersects)
    return true;
    if (S1.Contains(S2) == ContainmentType.Contains)
    return true;

    return false;
}

Just my input though. I don't see any need to create world matrices to test sphere intersections when the when the vector used to create the world translation matrix is already in world space. Seems like a bit of unnecessary processing to me...

...Probably just my own opinion though...

If you want scale, then you could simply add a scale argument to the collision test routines of my above examples

public bool g_IsCollision(Vector3 ObjectPosition1, Vector3 ObjectPosition2, BoundingSphere Sphere1, BoundingSphere Sphere2, float Scale)

and then ...

BoundingSphere S1 = new BoundingSphere(ObjectPosition1 + BoundingSphere.Center, BoundingSphere.Radius * Scale);

Note: - above examples intend that the bounding spheres are ONLY in local space relative to mesh before being passed as arguments, and can have bone transformations applied ( as bone transformations are generally relative to local space? ) , world transformation should not be applied to bounding spheres before passing to the above examples.

  • Another note in your code, the following looks to me like you are scaling the entire world matrix, and not the scale of the mesh itself... ( so if scale changes, the positions of ALL objects using this algorithm would scale closer or farther away from World Center instead of Mesh Centers ) - is this intended?

    Matrix ship1WorldMatrix = Matrix.CreateTranslation(ship1loc) * Matrix.CreateScale(0.1f);

Update

Example Usage of the above methods, without bone transforms:

vector3 ShipLocation1 = new vector3( /* whatever value here */);
vector3 ShipLocation2 = new vector3( /* whatever value here */);

/*
    Lets assume both players have the same ship:
*/
Model ShipModel = Content.Load<Model>("Models\MyShipModel"); /* etc... */

/*

*/
BoundingSphere LocalSphere = ShipModel.Meshes[0].BoundingSphere;

/*
    if you want to scale the sphere independently of model.
*/
// BoundingSphere.Radius = BoundingSphere.Radius * 0.1f;

/*
   Will be true if colliding ( unscaled )
*/ 
bool IsColliding = g_IsCollision(ShipLocation1, ShipLocation2 , LocalSphere );

You can apply the bonetransform ( or should ) from the Mesh's ParentBone - but do not multiply a world matrix against it.

The only time you really need to apply world transformation matrices is when you draw the mesh itself.

\$\endgroup\$
8
  • \$\begingroup\$ Trying it now... \$\endgroup\$ Commented Jan 3, 2014 at 21:38
  • \$\begingroup\$ I have implemented most of it, but how would I create a local space bounding sphere? If I have that, then I believe it will work. \$\endgroup\$ Commented Jan 3, 2014 at 21:59
  • \$\begingroup\$ Bounding sphere would be generated from the mesh bounding box ( or vertices ) - without World position applied. So it's center is either Vector3.Zero, or averaged from the vertices of the mesh. I think for starters - just try initializing it directly from the bounding box of a mesh object - then pass that sphere directly to the example routines. \$\endgroup\$ Commented Jan 3, 2014 at 22:06
  • \$\begingroup\$ What do you mean "Directly from the bounding box of a mesh object"? How do I get that bounding box. \$\endgroup\$ Commented Jan 3, 2014 at 22:17
  • \$\begingroup\$ If you are loading models using XNA Content Manager - you will have a Model object. Model hierarchy is Model->ModelMesh->ModelMeshPart. You can actually just use the bounding sphere supplied by the Microsoft.Xna.Framework.Graphics.ModelMeshPart() directly. So if you have a model loaded from Content.Load<model>(), it will be acessable as BoundingSphere myBoundingSphere = MyModel.Meshes[index].BoundingSphere;. The BoundingSphere you collect from the MeshPart will be in local space. \$\endgroup\$ Commented Jan 3, 2014 at 22:35

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .