inline bool BoundingFrustum::Intersects()

in Inc/DirectXCollision.inl [3377:3561]


inline bool BoundingFrustum::Intersects(const BoundingOrientedBox& box) const noexcept
{
    static const XMVECTORU32 SelectY = { { { XM_SELECT_0, XM_SELECT_1, XM_SELECT_0, XM_SELECT_0 } } };
    static const XMVECTORU32 SelectZ = { { { XM_SELECT_0, XM_SELECT_0, XM_SELECT_1, XM_SELECT_0 } } };

    XMVECTOR Zero = XMVectorZero();

    // Build the frustum planes.
    XMVECTOR Planes[6];
    Planes[0] = XMVectorSet(0.0f, 0.0f, -1.0f, Near);
    Planes[1] = XMVectorSet(0.0f, 0.0f, 1.0f, -Far);
    Planes[2] = XMVectorSet(1.0f, 0.0f, -RightSlope, 0.0f);
    Planes[3] = XMVectorSet(-1.0f, 0.0f, LeftSlope, 0.0f);
    Planes[4] = XMVectorSet(0.0f, 1.0f, -TopSlope, 0.0f);
    Planes[5] = XMVectorSet(0.0f, -1.0f, BottomSlope, 0.0f);

    // Load origin and orientation of the frustum.
    XMVECTOR vOrigin = XMLoadFloat3(&Origin);
    XMVECTOR FrustumOrientation = XMLoadFloat4(&Orientation);

    assert(DirectX::Internal::XMQuaternionIsUnit(FrustumOrientation));

    // Load the box.
    XMVECTOR Center = XMLoadFloat3(&box.Center);
    XMVECTOR Extents = XMLoadFloat3(&box.Extents);
    XMVECTOR BoxOrientation = XMLoadFloat4(&box.Orientation);

    assert(DirectX::Internal::XMQuaternionIsUnit(BoxOrientation));

    // Transform the oriented box into the space of the frustum in order to
    // minimize the number of transforms we have to do.
    Center = XMVector3InverseRotate(XMVectorSubtract(Center, vOrigin), FrustumOrientation);
    BoxOrientation = XMQuaternionMultiply(BoxOrientation, XMQuaternionConjugate(FrustumOrientation));

    // Set w of the center to one so we can dot4 with the plane.
    Center = XMVectorInsert<0, 0, 0, 0, 1>(Center, XMVectorSplatOne());

    // Build the 3x3 rotation matrix that defines the box axes.
    XMMATRIX R = XMMatrixRotationQuaternion(BoxOrientation);

    // Check against each plane of the frustum.
    XMVECTOR Outside = XMVectorFalseInt();
    XMVECTOR InsideAll = XMVectorTrueInt();
    XMVECTOR CenterInsideAll = XMVectorTrueInt();

    for (size_t i = 0; i < 6; ++i)
    {
        // Compute the distance to the center of the box.
        XMVECTOR Dist = XMVector4Dot(Center, Planes[i]);

        // Project the axes of the box onto the normal of the plane.  Half the
        // length of the projection (sometime called the "radius") is equal to
        // h(u) * abs(n dot b(u))) + h(v) * abs(n dot b(v)) + h(w) * abs(n dot b(w))
        // where h(i) are extents of the box, n is the plane normal, and b(i) are the
        // axes of the box.
        XMVECTOR Radius = XMVector3Dot(Planes[i], R.r[0]);
        Radius = XMVectorSelect(Radius, XMVector3Dot(Planes[i], R.r[1]), SelectY);
        Radius = XMVectorSelect(Radius, XMVector3Dot(Planes[i], R.r[2]), SelectZ);
        Radius = XMVector3Dot(Extents, XMVectorAbs(Radius));

        // Outside the plane?
        Outside = XMVectorOrInt(Outside, XMVectorGreater(Dist, Radius));

        // Fully inside the plane?
        InsideAll = XMVectorAndInt(InsideAll, XMVectorLessOrEqual(Dist, XMVectorNegate(Radius)));

        // Check if the center is inside the plane.
        CenterInsideAll = XMVectorAndInt(CenterInsideAll, XMVectorLessOrEqual(Dist, Zero));
    }

    // If the box is outside any of the planes it is outside.
    if (XMVector4EqualInt(Outside, XMVectorTrueInt()))
        return false;

    // If the box is inside all planes it is fully inside.
    if (XMVector4EqualInt(InsideAll, XMVectorTrueInt()))
        return true;

    // If the center of the box is inside all planes and the box intersects
    // one or more planes then it must intersect.
    if (XMVector4EqualInt(CenterInsideAll, XMVectorTrueInt()))
        return true;

    // Build the corners of the frustum.
    XMVECTOR vRightTop = XMVectorSet(RightSlope, TopSlope, 1.0f, 0.0f);
    XMVECTOR vRightBottom = XMVectorSet(RightSlope, BottomSlope, 1.0f, 0.0f);
    XMVECTOR vLeftTop = XMVectorSet(LeftSlope, TopSlope, 1.0f, 0.0f);
    XMVECTOR vLeftBottom = XMVectorSet(LeftSlope, BottomSlope, 1.0f, 0.0f);
    XMVECTOR vNear = XMVectorReplicatePtr(&Near);
    XMVECTOR vFar = XMVectorReplicatePtr(&Far);

    XMVECTOR Corners[CORNER_COUNT];
    Corners[0] = XMVectorMultiply(vRightTop, vNear);
    Corners[1] = XMVectorMultiply(vRightBottom, vNear);
    Corners[2] = XMVectorMultiply(vLeftTop, vNear);
    Corners[3] = XMVectorMultiply(vLeftBottom, vNear);
    Corners[4] = XMVectorMultiply(vRightTop, vFar);
    Corners[5] = XMVectorMultiply(vRightBottom, vFar);
    Corners[6] = XMVectorMultiply(vLeftTop, vFar);
    Corners[7] = XMVectorMultiply(vLeftBottom, vFar);

    // Test against box axes (3)
    {
        // Find the min/max values of the projection of the frustum onto each axis.
        XMVECTOR FrustumMin, FrustumMax;

        FrustumMin = XMVector3Dot(Corners[0], R.r[0]);
        FrustumMin = XMVectorSelect(FrustumMin, XMVector3Dot(Corners[0], R.r[1]), SelectY);
        FrustumMin = XMVectorSelect(FrustumMin, XMVector3Dot(Corners[0], R.r[2]), SelectZ);
        FrustumMax = FrustumMin;

        for (size_t i = 1; i < BoundingOrientedBox::CORNER_COUNT; ++i)
        {
            XMVECTOR Temp = XMVector3Dot(Corners[i], R.r[0]);
            Temp = XMVectorSelect(Temp, XMVector3Dot(Corners[i], R.r[1]), SelectY);
            Temp = XMVectorSelect(Temp, XMVector3Dot(Corners[i], R.r[2]), SelectZ);

            FrustumMin = XMVectorMin(FrustumMin, Temp);
            FrustumMax = XMVectorMax(FrustumMax, Temp);
        }

        // Project the center of the box onto the axes.
        XMVECTOR BoxDist = XMVector3Dot(Center, R.r[0]);
        BoxDist = XMVectorSelect(BoxDist, XMVector3Dot(Center, R.r[1]), SelectY);
        BoxDist = XMVectorSelect(BoxDist, XMVector3Dot(Center, R.r[2]), SelectZ);

        // The projection of the box onto the axis is just its Center and Extents.
        // if (min > box_max || max < box_min) reject;
        XMVECTOR Result = XMVectorOrInt(XMVectorGreater(FrustumMin, XMVectorAdd(BoxDist, Extents)),
            XMVectorLess(FrustumMax, XMVectorSubtract(BoxDist, Extents)));

        if (DirectX::Internal::XMVector3AnyTrue(Result))
            return false;
    }

    // Test against edge/edge axes (3*6).
    XMVECTOR FrustumEdgeAxis[6];

    FrustumEdgeAxis[0] = vRightTop;
    FrustumEdgeAxis[1] = vRightBottom;
    FrustumEdgeAxis[2] = vLeftTop;
    FrustumEdgeAxis[3] = vLeftBottom;
    FrustumEdgeAxis[4] = XMVectorSubtract(vRightTop, vLeftTop);
    FrustumEdgeAxis[5] = XMVectorSubtract(vLeftBottom, vLeftTop);

    for (size_t i = 0; i < 3; ++i)
    {
        for (size_t j = 0; j < 6; j++)
        {
            // Compute the axis we are going to test.
            XMVECTOR Axis = XMVector3Cross(R.r[i], FrustumEdgeAxis[j]);

            // Find the min/max values of the projection of the frustum onto the axis.
            XMVECTOR FrustumMin, FrustumMax;

            FrustumMin = FrustumMax = XMVector3Dot(Axis, Corners[0]);

            for (size_t k = 1; k < CORNER_COUNT; k++)
            {
                XMVECTOR Temp = XMVector3Dot(Axis, Corners[k]);
                FrustumMin = XMVectorMin(FrustumMin, Temp);
                FrustumMax = XMVectorMax(FrustumMax, Temp);
            }

            // Project the center of the box onto the axis.
            XMVECTOR Dist = XMVector3Dot(Center, Axis);

            // Project the axes of the box onto the axis to find the "radius" of the box.
            XMVECTOR Radius = XMVector3Dot(Axis, R.r[0]);
            Radius = XMVectorSelect(Radius, XMVector3Dot(Axis, R.r[1]), SelectY);
            Radius = XMVectorSelect(Radius, XMVector3Dot(Axis, R.r[2]), SelectZ);
            Radius = XMVector3Dot(Extents, XMVectorAbs(Radius));

            // if (center > max + radius || center < min - radius) reject;
            Outside = XMVectorOrInt(Outside, XMVectorGreater(Dist, XMVectorAdd(FrustumMax, Radius)));
            Outside = XMVectorOrInt(Outside, XMVectorLess(Dist, XMVectorSubtract(FrustumMin, Radius)));
        }
    }

    if (XMVector4EqualInt(Outside, XMVectorTrueInt()))
        return false;

    // If we did not find a separating plane then the box must intersect the frustum.
    return true;
}