1

So I have this function for getting the intersection of a ray with an OBB:

std::optional<float> Ray::hitsOBB(const glm::vec3& min, const glm::vec3& max, const glm::mat4& modelMatrix) {
    float tMin = 0.0f;
    float tMax = 100000.0f;

    glm::vec3 OBBposition_worldspace(modelMatrix[3].x, modelMatrix[3].y, modelMatrix[3].z);
    glm::vec3 delta = OBBposition_worldspace - origin;

    {
        glm::vec3 xaxis(modelMatrix[0].x, modelMatrix[0].y, modelMatrix[0].z);
        float e = glm::dot(xaxis, delta);
        float f = glm::dot(direction, xaxis);

        if (fabs(f) > 0.001f) {
            float t1 = (e + min.x) / f;
            float t2 = (e + max.x) / f;

            if (t1 > t2) std::swap(t1, t2);

            if (t2 < tMax) tMax = t2;
            if (t1 > tMin) tMin = t1;
            if (tMin > tMax) return {};

        }
        else {
            if (-e + min.x > 0.0f || -e + max.x < 0.0f) return {};
        }
    }


    {
        glm::vec3 yaxis(modelMatrix[1].x, modelMatrix[1].y, modelMatrix[1].z);
        float e = glm::dot(yaxis, delta);
        float f = glm::dot(direction, yaxis);

        if (fabs(f) > 0.001f) {

            float t1 = (e + min.y) / f;
            float t2 = (e + max.y) / f;

            if (t1 > t2) std::swap(t1, t2);

            if (t2 < tMax) tMax = t2;
            if (t1 > tMin) tMin = t1;
            if (tMin > tMax) return {};

        }
        else {
            if (-e + min.y > 0.0f || -e + max.y < 0.0f) return {};
        }
    }


    {
        glm::vec3 zaxis(modelMatrix[2].x, modelMatrix[2].y, modelMatrix[2].z);
        float e = glm::dot(zaxis, delta);
        float f = glm::dot(direction, zaxis);

        if (fabs(f) > 0.001f) {

            float t1 = (e + min.z) / f;
            float t2 = (e + max.z) / f;

            if (t1 > t2) std::swap(t1, t2);

            if (t2 < tMax) tMax = t2;
            if (t1 > tMin) tMin = t1;
            if (tMin > tMax) return {};

        }
        else {
            if (-e + min.z > 0.0f || -e + max.z < 0.0f) return {};
        }
    }

    return tMin;
}

I use it to click on cubes in OpenGL. The test case is a cube with min and max of (-1, -1, -1) and (1, 1, 1). Now this works fine when rotating and translating the modelMatrix but it does not seem to take scale into account. I've tried pre-multiplying the min and max with the modelMatrix scale but to no avail. Anyone know what's going on?

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
Nico van Bentum
  • 339
  • 1
  • 13
  • 2
    The common way to do this is to transform the ray to the object space of the box. Ultimately, the problem is reduced to an intersection of a ray and an AABB (axis aligned bounding box) – Rabbid76 Jul 05 '20 at 06:54
  • 1
    scaling change the ray direction so if you do not (un)scale in the right place and all the stuff that needs to be (un)scaled your result is wrong. Also if you want just ray picking see [OpenGL 3D-raypicking with high poly meshes](https://stackoverflow.com/a/51764105/2521214) its faster, simpler and pixel perfect. if you still insist on doing this the hard way see [Cone to box collision](https://stackoverflow.com/a/62257945/2521214) and look for OBB and `convex_mesh/axis` closest point it can be transformed into intersection test ... – Spektre Jul 05 '20 at 07:36

1 Answers1

0

I had the same issue and I solved it by not scaling modelMatrix and supplying scale only for aabb_min and aabb_max:

    // cubeScale is glm::vec3(...);
    glm::vec3 aabb_min(-cubeScale);
    glm::vec3 aabb_max(cubeScale);

    glm::mat4 model(1.0f);
    model = glm::translate(model, cubePos);
    // Omit scaling of modelMatrix
    model = glm::rotate(model, glm::radians(cubeRotation.x), glm::vec3(1.0f, 0.0f, 0.0f));
    model = glm::rotate(model, glm::radians(cubeRotation.y), glm::vec3(0.0f, 1.0f, 0.0f));
    model = glm::rotate(model, glm::radians(cubeRotation.z), glm::vec3(0.0f, 0.0f, 1.0f));

    hitsOBB(...);
Dender46
  • 1
  • 2