Understanding Plane Mathematics and AABB Intersection Testing in Game Engines
Plane Representation Methods
A plane can be represented in two equivalent forms: the vector-based point-normal form and the scalar plane equation.
Point-Normal Form
$$\mathbf{n} \cdot (\mathbf{p} - \mathbf{p_0}) = 0$$
Scalar Form
$$Ax + By + Cz + D = 0$$
The conversion between these forms follows:
$$A = \mathbf{n}.x, ; B = \mathbf{n}.y, ; C = \mathbf{n}.z, ; D = \mathbf{n} \cdot -\mathbf{p_0}$$
The plane equation calculates the perpendicular distance from any point to the plane, measured along the normal direction.
Interpreting the Point-Normal Form
The point-normal form has two equivalent interpretations:
- The distance from point p projected onto the normal, minus the distance from point p₀ projected onto the normal.
- The distance from the vector (p - p₀) projected onto the normal.
Think of the normal vector n as a one-dimensional number line. The projected distance can be either positive or negative, depending on which side of the plane the point lies.
Application: Physics Collision Detection
Testing whether an axis-aligned bounding box intersects a plane rqeuires careful handling of the box's extent. The absolute value operation is necessary because we must consider wich vertices of the bounding box fall on which side of the plane.
// Determine if AABB b intersects plane p
int AABBIntersectsPlane(AABB b, Plane p) {
// Calculate AABB center point
Vector3 center = (b.max + b.min) * 0.5f;
// Calculate half-extents along each axis
Vector3 halfExtents = b.max - center;
// Weighted projection of extents onto plane normal
float weightedExtent = halfExtents.x * Abs(p.n.x) +
halfExtents.y * Abs(p.n.y) +
halfExtents.z * Abs(p.n.z);
// Signed distance from box center to plane
float signedDist = Dot(p.n, center) - p.d;
// Intersection occurs when the distance falls within the box's thickness
return Abs(signedDist) <= weightedExtent;
}
Unreal Engine Implementation: Plane-AABB Spatial Relationship
Unreal Engine determines the relative position of a bounding box with respect to a plane by selecting the appropriate box corners based on the plane's normal direction.
// Calculate relative position of AABB to plane
int32 DeterminePlaneAABBPosition(const FPlane& P, const FBox& AABB)
{
FVector cornerMin, cornerMax;
// Direct pointer access for performance optimization
float* RESTRICT minPtr = &cornerMin.x;
float* RESTRICT maxPtr = &cornerMax.x;
const float* RESTRICT aabbMinPtr = &AABB.Min.x;
const float* RESTRICT aabbMaxPtr = &AABB.Max.x;
const float* RESTRICT planePtr = &P.x;
for (int32 axis = 0; axis < 3; ++axis)
{
if (planePtr[axis] >= 0.0f)
{
minPtr[axis] = aabbMinPtr[axis];
maxPtr[axis] = aabbMaxPtr[axis];
}
else
{
minPtr[axis] = aabbMaxPtr[axis];
maxPtr[axis] = aabbMinPtr[axis];
}
}
float distMax = P.PlaneDot(cornerMax);
float distMin = P.PlaneDot(cornerMin);
if (distMax < 0.0f)
{
return -1; // Box is entirely behind the plane
}
else if (distMin > 0.0f)
{
return 1; // Box is entirely in front of the plane
}
return 0; // Box straddles the plane
}
// Determine if intersection exists between plane and AABB
bool DetectPlaneAABBIntersection(const FPlane& P, const FBox& AABB)
{
return DeterminePlaneAABBPosition(P, AABB) == 0;
}
The algorithm examines each axis of the plane normal. If the normal component is positive, it pairs the minimum corner with the AABB minimum and maximum corner with the AABB maximum. For negative components, this pairing is reversed. The plane-dot products of these two selected corners determine whether the box lies completely on one side or spans across the plane.
References
- https://en.wikipedia.org/wiki/Line–plane_intersection
- https://gdbooks.gitbooks.io/3dcollisions/
- Games Physicks Cookbook