// David Eberly, Geometric Tools, Redmond WA 98052 // Copyright (c) 1998-2020 // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt // Version: 4.0.2019.08.13 #pragma once #include #include #include #include #include #include // The test-intersection query is based on the document // https://www.geometrictools.com/Documentation/MethodOfSeparatingAxes.pdf // The find-intersection query clips the triangle against the faces of // the oriented box. namespace WwiseGTE { template class TIQuery, OrientedBox3> { public: struct Result { bool intersect; }; Result operator()(Triangle3 const& triangle, OrientedBox3 const& box) { Result result; Real min0, max0, min1, max1; Vector3 D, edge[3]; // Test direction of triangle normal. edge[0] = triangle.v[1] - triangle.v[0]; edge[1] = triangle.v[2] - triangle.v[0]; D = Cross(edge[0], edge[1]); min0 = Dot(D, triangle.v[0]); max0 = min0; GetProjection(D, box, min1, max1); if (max1 < min0 || max0 < min1) { result.intersect = false; return result; } // Test direction of box faces. for (int i = 0; i < 3; ++i) { D = box.axis[i]; GetProjection(D, triangle, min0, max0); Real DdC = Dot(D, box.center); min1 = DdC - box.extent[i]; max1 = DdC + box.extent[i]; if (max1 < min0 || max0 < min1) { result.intersect = false; return result; } } // Test direction of triangle-box edge cross products. edge[2] = edge[1] - edge[0]; for (int i0 = 0; i0 < 3; ++i0) { for (int i1 = 0; i1 < 3; ++i1) { D = Cross(edge[i0], box.axis[i1]); GetProjection(D, triangle, min0, max0); GetProjection(D, box, min1, max1); if (max1 < min0 || max0 < min1) { result.intersect = false; return result; } } } result.intersect = true; return result; } private: void GetProjection(Vector3 const& axis, Triangle3 const& triangle, Real& imin, Real& imax) { Real dot[3] = { Dot(axis, triangle.v[0]), Dot(axis, triangle.v[1]), Dot(axis, triangle.v[2]) }; imin = dot[0]; imax = imin; if (dot[1] < imin) { imin = dot[1]; } else if (dot[1] > imax) { imax = dot[1]; } if (dot[2] < imin) { imin = dot[2]; } else if (dot[2] > imax) { imax = dot[2]; } } void GetProjection(Vector3 const& axis, OrientedBox3 const& box, Real& imin, Real& imax) { Real origin = Dot(axis, box.center); Real maximumExtent = std::fabs(box.extent[0] * Dot(axis, box.axis[0])) + std::fabs(box.extent[1] * Dot(axis, box.axis[1])) + std::fabs(box.extent[2] * Dot(axis, box.axis[2])); imin = origin - maximumExtent; imax = origin + maximumExtent; } }; template class FIQuery, OrientedBox3> { public: struct Result { std::vector> insidePolygon; std::vector>> outsidePolygons; }; Result operator()(Triangle3 const& triangle, OrientedBox3 const& box) { Result result; // Start with the triangle and clip it against each face of the // box. The largest number of vertices for the polygon of // intersection is 7. result.insidePolygon.resize(3); for (int i = 0; i < 3; ++i) { result.insidePolygon[i] = triangle.v[i]; } typedef FIQuery>, Hyperplane<3, Real>> PPQuery; Plane3 plane; PPQuery ppQuery; typename PPQuery::Result ppResult; for (int dir = -1; dir <= 1; dir += 2) { for (int side = 0; side < 3; ++side) { // Create a plane for the box face that points inside the box. plane.normal = ((Real)dir) * box.axis[side]; plane.constant = Dot(plane.normal, box.center) - box.extent[side]; ppResult = ppQuery(result.insidePolygon, plane); switch (ppResult.configuration) { case PPQuery::Configuration::SPLIT: result.insidePolygon = ppResult.positivePolygon; result.outsidePolygons.push_back(ppResult.negativePolygon); break; case PPQuery::Configuration::POSITIVE_SIDE_VERTEX: case PPQuery::Configuration::POSITIVE_SIDE_EDGE: case PPQuery::Configuration::POSITIVE_SIDE_STRICT: // The result.insidePolygon is already // ppResult.positivePolygon, but to make it clear, // assign it here. result.insidePolygon = ppResult.positivePolygon; break; case PPQuery::Configuration::NEGATIVE_SIDE_VERTEX: case PPQuery::Configuration::NEGATIVE_SIDE_EDGE: case PPQuery::Configuration::NEGATIVE_SIDE_STRICT: result.insidePolygon.clear(); result.outsidePolygons.push_back(ppResult.negativePolygon); return result; case PPQuery::Configuration::CONTAINED: // A triangle coplanar with a box face will be // processed as if it is inside the box. result.insidePolygon = ppResult.intersection; break; default: result.insidePolygon.clear(); result.outsidePolygons.clear(); break; } } } return result; } }; }