XNA - 3D AABB collision detection and response
- by fastinvsqrt
I've been fiddling around with 3D AABB collision in my voxel engine for the last couple of days, and every method I've come up with thus far has been almost correct, but each one never quite worked exactly the way I hoped it would.
Currently what I do is get two bounding boxes for my entity, one modified by the X translation component and the other by the Z component, and check if each collides with any of the surrounding chunks (chunks have their own octrees that are populated only with blocks that support collision). If there is a collision, then I cast out rays into that chunk to get the shortest collision distance, and set the translation component to that distance if the component is greater than the distance.
The problem is that sometimes collisions aren't even registered. Here's a video on YouTube that I created showing what I mean. I suspect the problem may be with the rays that I cast to get the collision distance not being where I think they are, but I'm not entirely sure what would be wrong with them if they are indeed the problem.
Here is my code for collision detection and response in the X direction (the Z direction is basically the same):
// create the XZ offset vector
Vector3 offsXZ = new Vector3(
( _translation.X > 0.0f ) ? SizeX / 2.0f : ( _translation.X < 0.0f ) ? -SizeX / 2.0f : 0.0f,
0.0f,
( _translation.Z > 0.0f ) ? SizeZ / 2.0f : ( _translation.Z < 0.0f ) ? -SizeZ / 2.0f : 0.0f
);
// X physics
BoundingBox boxx = GetBounds( _translation.X, 0.0f, 0.0f );
if ( _translation.X > 0.0f )
{
foreach ( Chunk chunk in surrounding )
{
if ( chunk.Collides( boxx ) )
{
float dist = GetShortestCollisionDistance( chunk, Vector3.Right, offsXZ ) - 0.0001f;
if ( dist < _translation.X )
{
_translation.X = dist;
}
}
}
}
else if ( _translation.X < 0.0f )
{
foreach ( Chunk chunk in surrounding )
{
if ( chunk.Collides( boxx ) )
{
float dist = GetShortestCollisionDistance( chunk, Vector3.Left, offsXZ ) - 0.0001f;
if ( dist < -_translation.X )
{
_translation.X = -dist;
}
}
}
}
And here is my implementation for GetShortestCollisionDistance:
private float GetShortestCollisionDistance( Chunk chunk, Vector3 rayDir, Vector3 offs )
{
int startY = (int)( -SizeY / 2.0f );
int endY = (int)( SizeY / 2.0f );
int incY = (int)Cube.Size;
float dist = Chunk.Size;
for ( int y = startY; y <= endY; y += incY )
{
// Position is the center of the entity's bounding box
Ray ray = new Ray(
new Vector3(
Position.X + offs.X,
Position.Y + offs.Y + y,
Position.Z + offs.Z
),
rayDir
);
// Chunk.GetIntersections(Ray) returns Dictionary<Block, float?>
foreach ( var pair in chunk.GetIntersections( ray ) )
{
if ( pair.Value.HasValue && pair.Value.Value < dist )
{
dist = pair.Value.Value;
}
}
}
return dist;
}
I realize some of this code can be consolidated to help with speed, but my main concern right now is to get this bit of physics programming to actually work.