Search code examples
luacollision-detectionlinear-algebravectormath

Selecting the relevant plane of a bounding box prior to ray plane intersection


I am tracking objects for which I need to test if they are located inside or outside a cube / bounding box. If they are outside, I do a ray-plane intersection to calculate a point on one of the planes of the cube. The ray starts at the center of the box, and points towards the object. The plane is one of the 6 which make up the cube.

What I would like to avoid is that I am testing the ray-plane intersection on every one of the 6 planes all the time. So I thought I'd be clever and calculate first the dot product between each of the plane normals and the ray. Then select the one which has the smallest angle (closest to 1).

This works only halfway. There are locations when the object is aligned with one of the planes, where my function selects the wrong plane. Most of the time it works though, and I wonder why that is. I figure there must be something fundamentally wrong with my approach.

Here are my defined planes, which each have a label. The 0,0,0 of the coordinate system is one corner of the cube.

planes = {
    xnear = { normal = {1, 0, 0}, d = 0 },
    xfar = { normal = {-1, 0, 0}, d = cubeSize.x },
    ynear = { normal = {0, 1, 0}, d = 0 },
    yfar = { normal = {0, -1, 0}, d = cubeSize.y },
    znear = { normal = {0, 0, 1}, d = 0 },
    zfar = { normal = {0, 0, -1}, d = cubeSize.z }, 
}

Then I use the following function:

-- Determine what plane to use for collision testing. The angle is calculated
-- between the plane normal and the direction of the ray
function whatPlane(pos)
local direction = vec3.sub(cubeCenter, pos)
local result
local max = -1
for label, plane in pairs(planes) do
    local dotproduct = vec3.dot(plane.normal, direction)
    if dotproduct > max then
        max = dotproduct
        result = label
    end
end
return result
end

What I'm I missing here?

I guess I could just do the collision test on every plane and then choose the point which is closest to the center of the cube, but that seems like a waste.


Solution

  • Your argument would be fine if the cube was indeed a cube, i.e. all dimensions are the same (and only for the case of being in the center of a regular polyhedron). But as it looks like you have different dimension for each of the axes.

    If one length is rather small (think of a very thin box) no matter what direction you look in almost any case you'll hit those big planes and almost never the thin sides.

    You can compensate this if you scale your direction by the exact lengths of the box. I.e. instead of direction you use direction/(cubeSize.x,cubeSize.y,cubeSize.z) where the divison is done by coordinate.

    Another remark: note that the comparison works fine, also with unnormalized direction but you may run into problems in other if you keep your dot-product unnormalized.