bool GfFrustum::Intersects(const GfBBox3d &bbox) const { if (bbox.GetBox().IsEmpty()) return false; // Recalculate frustum planes if necessary _CalculateFrustumPlanes(); // Get the bbox in its local space and the matrix that converts // world space to that local space. const GfRange3d &localBBox = bbox.GetRange(); const GfMatrix4d &worldToLocal = bbox.GetInverseMatrix(); // Test the bbox against each of the frustum planes, transforming // the plane by the inverse of the matrix to bring it into the // bbox's local space. for (size_t i = 0; i < _planes.size(); i++) { GfPlane localPlane = _planes[i]; localPlane.Transform(worldToLocal); if (! localPlane.IntersectsPositiveHalfSpace(localBBox)) return false; } return true; }
bool GfFrustum::IntersectsViewVolume(const GfBBox3d &bbox, const GfMatrix4d &viewProjMat) { // This implementation is a standard technique employed in frustum // culling during rendering. It correctly culls the box even from // view volumes that are not representable by a GfFrustum because of // skewed near/far planes, such as the ones produced by // presto shadowmap cameras. // // Its principle of operation: If all 8 points of // the box, when transformed into clip coordinates, // are on one side or the other of each dimension's // clipping interval, then the entire // box volume must lie outside the view volume. // Compute the 8 points of the bbox in // bbox local space. GfVec4d points[8]; const GfVec3d &localMin = bbox.GetRange().GetMin(); const GfVec3d &localMax = bbox.GetRange().GetMax(); points[0] = GfVec4d(localMin[0], localMin[1], localMin[2], 1); points[1] = GfVec4d(localMin[0], localMin[1], localMax[2], 1); points[2] = GfVec4d(localMin[0], localMax[1], localMin[2], 1); points[3] = GfVec4d(localMin[0], localMax[1], localMax[2], 1); points[4] = GfVec4d(localMax[0], localMin[1], localMin[2], 1); points[5] = GfVec4d(localMax[0], localMin[1], localMax[2], 1); points[6] = GfVec4d(localMax[0], localMax[1], localMin[2], 1); points[7] = GfVec4d(localMax[0], localMax[1], localMax[2], 1); // Transform bbox local space points into clip space for (int i = 0; i < 8; ++i) { points[i] = points[i] * bbox.GetMatrix() * viewProjMat; } // clipFlags is a 6-bit field with one bit per +/- per x,y,z, // or one per frustum plane. If the points overlap the // clip volume in any axis, then clipFlags will be 0x3f (0b111111). int clipFlags = 0; for (int i = 0; i < 8; ++i) { GfVec4d clipPos = points[i]; // flag is used as a 6-bit shift register, as we append // results of plane-side testing. OR-ing all the flags // combines all the records of what plane-side the points // have been on. int flag = 0; for (int j = 0; j < 3; ++j) { // We use +/-clipPos[3] as the interval bound instead of // 1,-1 because these coordinates are not normalized. flag = (flag << 1) | (clipPos[j] < clipPos[3]); flag = (flag << 1) | (clipPos[j] > -clipPos[3]); } clipFlags |= flag; } return clipFlags == 0x3f; }
bool GusdBoundsCache::_ComputeBound( const UsdPrim &prim, UsdTimeCode time, const TfTokenVector &includedPurposes, ComputeFunc boundFunc, UT_BoundingBox &bounds ) { if( !prim.IsValid() ) return false; TfToken stageId( prim.GetStage()->GetRootLayer()->GetRealPath() ); MapType::accessor accessor; if( !m_map.find( accessor, Key( stageId, includedPurposes ))) { m_map.insert( accessor, Key( stageId, includedPurposes ) ); accessor->second = new Item( time, includedPurposes ); } std::lock_guard<std::mutex> lock(accessor->second->lock); UsdGeomBBoxCache& cache = accessor->second->bboxCache; cache.SetTime( time ); // boundFunc is either ComputeWorldBound or ComputeLocalBound GfBBox3d primBBox = (cache.*boundFunc)(prim); if( !primBBox.GetRange().IsEmpty() ) { const GfRange3d rng = primBBox.ComputeAlignedRange(); bounds = UT_BoundingBox( rng.GetMin()[0], rng.GetMin()[1], rng.GetMin()[2], rng.GetMax()[0], rng.GetMax()[1], rng.GetMax()[2]); return true; } return false; }