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;
}