/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* * OPCODE - Optimized Collision Detection * Copyright (C) 2001 Pierre Terdiman * Homepage: http://www.codercorner.com/Opcode.htm */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Contains code for a ray collider. * \file OPC_RayCollider.cpp * \author Pierre Terdiman * \date June, 2, 2001 */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Contains a ray-vs-tree collider. * This class performs a stabbing query on an AABB tree, i.e. does a ray-mesh collision. * * HIGHER DISTANCE BOUND: * * If P0 and P1 are two 3D points, let's define: * - d = distance between P0 and P1 * - Origin = P0 * - Direction = (P1 - P0) / d = normalized direction vector * - A parameter t such as a IcePoint P on the line (P0,P1) is P = Origin + t * Direction * - t = 0 --> P = P0 * - t = d --> P = P1 * * Then we can define a general "ray" as: * * struct Ray * { * IcePoint Origin; * IcePoint Direction; * }; * * But it actually maps three different things: * - a segment, when 0 <= t <= d * - a half-line, when 0 <= t < +infinity, or -infinity < t <= d * - a line, when -infinity < t < +infinity * * In Opcode, we support segment queries, which yield half-line queries by setting d = +infinity. * We don't support line-queries. If you need them, shift the origin along the ray by an appropriate margin. * * In short, the lower bound is always 0, and you can setup the higher bound "d" with RayCollider::SetMaxDist(). * * Query |segment |half-line |line * --------|-------------------|---------------|---------------- * Usages |-shadow feelers |-raytracing |- * |-sweep tests |-in/out tests | * * FIRST CONTACT: * * - You can setup "first contact" mode or "all contacts" mode with RayCollider::SetFirstContact(). * - In "first contact" mode we return as soon as the ray hits one face. If can be useful e.g. for shadow feelers, where * you want to know whether the path to the light is free or not (a boolean answer is enough). * - In "all contacts" mode we return all faces hit by the ray. * * TEMPORAL COHERENCE: * * - You can enable or disable temporal coherence with RayCollider::SetTemporalCoherence(). * - It currently only works in "first contact" mode. * - If temporal coherence is enabled, the previously hit triangle is cached during the first query. Then, next queries * start by colliding the ray against the cached triangle. If they still collide, we return immediately. * * CLOSEST HIT: * * - You can enable or disable "closest hit" with RayCollider::SetClosestHit(). * - It currently only works in "all contacts" mode. * - If closest hit is enabled, faces are sorted by distance on-the-fly and the closest one only is reported. * * BACKFACE CULLING: * * - You can enable or disable backface culling with RayCollider::SetCulling(). * - If culling is enabled, ray will not hit back faces (only front faces). * * * * \class RayCollider * \author Pierre Terdiman * \version 1.3 * \date June, 2, 2001 */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * This class describes a face hit by a ray or segment. * This is a particular class dedicated to stabbing queries. * * \class CollisionFace * \author Pierre Terdiman * \version 1.3 * \date March, 20, 2001 */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * This class is a dedicated collection of CollisionFace. * * \class CollisionFaces * \author Pierre Terdiman * \version 1.3 * \date March, 20, 2001 */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Precompiled Header #include "Stdafx.h" using namespace Opcode; #include "OPC_RayAABBOverlap.h" #include "OPC_RayTriOverlap.h" #define SET_CONTACT(prim_index, flag) \ mNbIntersections++; \ /* Set contact status */ \ mFlags |= flag; \ /* In any case the contact has been found and recorded in mStabbedFace */ \ mStabbedFace.mFaceID = prim_index; #ifdef OPC_RAYHIT_CALLBACK #define HANDLE_CONTACT(prim_index, flag) \ SET_CONTACT(prim_index, flag) \ \ if(mHitCallback) (mHitCallback)(mStabbedFace, mUserData); #define UPDATE_CACHE \ if(cache && GetContactStatus()) \ { \ *cache = mStabbedFace.mFaceID; \ } #else #define HANDLE_CONTACT(prim_index, flag) \ SET_CONTACT(prim_index, flag) \ \ /* Now we can also record it in mStabbedFaces if available */ \ if(mStabbedFaces) \ { \ /* If we want all faces or if that's the first one we hit */ \ if(!mClosestHit || !mStabbedFaces->GetNbFaces()) \ { \ mStabbedFaces->AddFace(mStabbedFace); \ } \ else \ { \ /* We only keep closest hit */ \ CollisionFace* Current = const_cast(mStabbedFaces->GetFaces()); \ if(Current && mStabbedFace.mDistancemDistance) \ { \ *Current = mStabbedFace; \ } \ } \ } #define UPDATE_CACHE \ if(cache && GetContactStatus() && mStabbedFaces) \ { \ const CollisionFace* Current = mStabbedFaces->GetFaces(); \ if(Current) *cache = Current->mFaceID; \ else *cache = INVALID_ID; \ } #endif #define SEGMENT_PRIM(prim_index, flag) \ /* Request vertices from the app */ \ VertexPointers VP; mIMesh->GetTriangle(VP, prim_index); \ \ /* Perform ray-tri overlap test and return */ \ if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \ { \ /* Intersection IcePoint is valid if dist < segment's length */ \ /* We know dist>0 so we can use integers */ \ if(IR(mStabbedFace.mDistance)GetTriangle(VP, prim_index); \ \ /* Perform ray-tri overlap test and return */ \ if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \ { \ HANDLE_CONTACT(prim_index, flag) \ } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Constructor. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// RayCollider::RayCollider() : mNbRayBVTests (0), mNbRayPrimTests (0), mNbIntersections (0), mCulling (true), #ifdef OPC_RAYHIT_CALLBACK mHitCallback (null), mUserData (0), #else mClosestHit (false), mStabbedFaces (null), #endif mMaxDist (MAX_FLOAT) { } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Destructor. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// RayCollider::~RayCollider() { } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Validates current settings. You should call this method after all the settings and callbacks have been defined. * \return null if everything is ok, else a string describing the problem */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// const char* RayCollider::ValidateSettings() { if(mMaxDist<0.0f) return "Higher distance bound must be positive!"; if(TemporalCoherenceEnabled() && !FirstContactEnabled()) return "Temporal coherence only works with ""First contact"" mode!"; #ifndef OPC_RAYHIT_CALLBACK if(mClosestHit && FirstContactEnabled()) return "Closest hit doesn't work with ""First contact"" mode!"; if(TemporalCoherenceEnabled() && mClosestHit) return "Temporal coherence can't guarantee to report closest hit!"; #endif if(SkipPrimitiveTests()) return "SkipPrimitiveTests not possible for RayCollider ! (not implemented)"; return null; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Generic stabbing query for generic OPCODE models. After the call, access the results: * - with GetContactStatus() * - in the user-provided destination array * * \param world_ray [in] stabbing ray in world space * \param model [in] Opcode model to collide with * \param world [in] model's world matrix, or null * \param cache [in] a possibly cached face index, or null * \return true if success * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool RayCollider::Collide(const Ray& world_ray, const Model& model, const Matrix4x4* world, udword* cache) { // Checkings if(!Setup(&model)) return false; // Init collision query if(InitQuery(world_ray, world, cache)) return true; if(!model.HasLeafNodes()) { if(model.IsQuantized()) { const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree(); // Setup dequantization coeffs mCenterCoeff = Tree->mCenterCoeff; mExtentsCoeff = Tree->mExtentsCoeff; // Perform stabbing query if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes()); else _RayStab(Tree->GetNodes()); } else { const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree(); // Perform stabbing query if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes()); else _RayStab(Tree->GetNodes()); } } else { if(model.IsQuantized()) { const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree(); // Setup dequantization coeffs mCenterCoeff = Tree->mCenterCoeff; mExtentsCoeff = Tree->mExtentsCoeff; // Perform stabbing query if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes()); else _RayStab(Tree->GetNodes()); } else { const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree(); // Perform stabbing query if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes()); else _RayStab(Tree->GetNodes()); } } // Update cache if needed UPDATE_CACHE return true; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Initializes a stabbing query : * - reset stats & contact status * - compute ray in local space * - check temporal coherence * * \param world_ray [in] stabbing ray in world space * \param world [in] object's world matrix, or null * \param face_id [in] index of previously stabbed triangle * \return TRUE if we can return immediately * \warning SCALE NOT SUPPORTED. The matrix must contain rotation & translation parts only. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// BOOL RayCollider::InitQuery(const Ray& world_ray, const Matrix4x4* world, udword* face_id) { // Reset stats & contact status Collider::InitQuery(); mNbRayBVTests = 0; mNbRayPrimTests = 0; mNbIntersections = 0; #ifndef OPC_RAYHIT_CALLBACK if(mStabbedFaces) mStabbedFaces->Reset(); #endif // Compute ray in local space // The (Origin/Dir) form is needed for the ray-triangle test anyway (even for segment tests) if(world) { Matrix3x3 InvWorld = *world; mDir = InvWorld * world_ray.mDir; Matrix4x4 World; InvertPRMatrix(World, *world); mOrigin = world_ray.mOrig * World; } else { mDir = world_ray.mDir; mOrigin = world_ray.mOrig; } // 4) Special case: 1-triangle meshes [Opcode 1.3] if(mCurrentModel && mCurrentModel->HasSingleNode()) { // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0. if(!SkipPrimitiveTests()) { // Perform overlap test between the unique triangle and the ray (and set contact status if needed) SEGMENT_PRIM(udword(0), OPC_CONTACT) // Return immediately regardless of status return TRUE; } } // Check temporal coherence : // Test previously colliding primitives first if(TemporalCoherenceEnabled() && FirstContactEnabled() && face_id && *face_id!=INVALID_ID) { #ifdef OLD_CODE #ifndef OPC_RAYHIT_CALLBACK if(!mClosestHit) #endif { // Request vertices from the app VertexPointers VP; mIMesh->GetTriangle(VP, *face_id); // Perform ray-cached tri overlap test if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) { // Intersection IcePoint is valid if: // - distance is positive (else it can just be a face behind the orig IcePoint) // - distance is smaller than a given max distance (useful for shadow feelers) // if(mStabbedFace.mDistance>0.0f && mStabbedFace.mDistanceAddFace(mStabbedFace); #endif return TRUE; } } } #else // New code // We handle both Segment/ray queries with the same segment code, and a possible infinite limit SEGMENT_PRIM(*face_id, OPC_TEMPORAL_CONTACT) // Return immediately if possible if(GetContactStatus()) return TRUE; #endif } // Precompute data (moved after temporal coherence since only needed for ray-AABB) if(IR(mMaxDist)!=IEEE_MAX_FLOAT) { // For Segment-AABB overlap mData = 0.5f * mDir * mMaxDist; mData2 = mOrigin + mData; // Precompute mFDir; mFDir.x = fabsf(mData.x); mFDir.y = fabsf(mData.y); mFDir.z = fabsf(mData.z); } else { // For Ray-AABB overlap // udword x = SIR(mDir.x)-1; // udword y = SIR(mDir.y)-1; // udword z = SIR(mDir.z)-1; // mData.x = FR(x); // mData.y = FR(y); // mData.z = FR(z); // Precompute mFDir; mFDir.x = fabsf(mDir.x); mFDir.y = fabsf(mDir.y); mFDir.z = fabsf(mDir.z); } return FALSE; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Stabbing query for vanilla AABB trees. * \param world_ray [in] stabbing ray in world space * \param tree [in] AABB tree * \param box_indices [out] indices of stabbed boxes * \return true if success */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool RayCollider::Collide(const Ray& world_ray, const AABBTree* tree, Container& box_indices) { // ### bad design here // This is typically called for a scene tree, full of -AABBs-, not full of triangles. // So we don't really have "primitives" to deal with. Hence it doesn't work with // "FirstContact" + "TemporalCoherence". ASSERT( !(FirstContactEnabled() && TemporalCoherenceEnabled()) ); // Checkings if(!tree) return false; // Init collision query // Basically this is only called to initialize precomputed data if(InitQuery(world_ray)) return true; // Perform stabbing query if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(tree, box_indices); else _RayStab(tree, box_indices); return true; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Recursive stabbing query for normal AABB trees. * \param node [in] current collision node */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void RayCollider::_SegmentStab(const AABBCollisionNode* node) { // Perform Segment-AABB overlap test if(!SegmentAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; if(node->IsLeaf()) { SEGMENT_PRIM(node->GetPrimitive(), OPC_CONTACT) } else { _SegmentStab(node->GetPos()); if(ContactFound()) return; _SegmentStab(node->GetNeg()); } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Recursive stabbing query for quantized AABB trees. * \param node [in] current collision node */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void RayCollider::_SegmentStab(const AABBQuantizedNode* node) { // Dequantize box const QuantizedAABB& Box = node->mAABB; const IcePoint Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); const IcePoint Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); // Perform Segment-AABB overlap test if(!SegmentAABBOverlap(Center, Extents)) return; if(node->IsLeaf()) { SEGMENT_PRIM(node->GetPrimitive(), OPC_CONTACT) } else { _SegmentStab(node->GetPos()); if(ContactFound()) return; _SegmentStab(node->GetNeg()); } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Recursive stabbing query for no-leaf AABB trees. * \param node [in] current collision node */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void RayCollider::_SegmentStab(const AABBNoLeafNode* node) { // Perform Segment-AABB overlap test if(!SegmentAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; if(node->HasPosLeaf()) { SEGMENT_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } else _SegmentStab(node->GetPos()); if(ContactFound()) return; if(node->HasNegLeaf()) { SEGMENT_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } else _SegmentStab(node->GetNeg()); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Recursive stabbing query for quantized no-leaf AABB trees. * \param node [in] current collision node */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void RayCollider::_SegmentStab(const AABBQuantizedNoLeafNode* node) { // Dequantize box const QuantizedAABB& Box = node->mAABB; const IcePoint Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); const IcePoint Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); // Perform Segment-AABB overlap test if(!SegmentAABBOverlap(Center, Extents)) return; if(node->HasPosLeaf()) { SEGMENT_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } else _SegmentStab(node->GetPos()); if(ContactFound()) return; if(node->HasNegLeaf()) { SEGMENT_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } else _SegmentStab(node->GetNeg()); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Recursive stabbing query for vanilla AABB trees. * \param node [in] current collision node * \param box_indices [out] indices of stabbed boxes */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void RayCollider::_SegmentStab(const AABBTreeNode* node, Container& box_indices) { // Test the box against the segment IcePoint Center, Extents; node->GetAABB()->GetCenter(Center); node->GetAABB()->GetExtents(Extents); if(!SegmentAABBOverlap(Center, Extents)) return; if(node->IsLeaf()) { box_indices.Add(node->GetPrimitives(), node->GetNbPrimitives()); } else { _SegmentStab(node->GetPos(), box_indices); _SegmentStab(node->GetNeg(), box_indices); } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Recursive stabbing query for normal AABB trees. * \param node [in] current collision node */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void RayCollider::_RayStab(const AABBCollisionNode* node) { // Perform Ray-AABB overlap test if(!RayAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; if(node->IsLeaf()) { RAY_PRIM(node->GetPrimitive(), OPC_CONTACT) } else { _RayStab(node->GetPos()); if(ContactFound()) return; _RayStab(node->GetNeg()); } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Recursive stabbing query for quantized AABB trees. * \param node [in] current collision node */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void RayCollider::_RayStab(const AABBQuantizedNode* node) { // Dequantize box const QuantizedAABB& Box = node->mAABB; const IcePoint Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); const IcePoint Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); // Perform Ray-AABB overlap test if(!RayAABBOverlap(Center, Extents)) return; if(node->IsLeaf()) { RAY_PRIM(node->GetPrimitive(), OPC_CONTACT) } else { _RayStab(node->GetPos()); if(ContactFound()) return; _RayStab(node->GetNeg()); } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Recursive stabbing query for no-leaf AABB trees. * \param node [in] current collision node */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void RayCollider::_RayStab(const AABBNoLeafNode* node) { // Perform Ray-AABB overlap test if(!RayAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; if(node->HasPosLeaf()) { RAY_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } else _RayStab(node->GetPos()); if(ContactFound()) return; if(node->HasNegLeaf()) { RAY_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } else _RayStab(node->GetNeg()); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Recursive stabbing query for quantized no-leaf AABB trees. * \param node [in] current collision node */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void RayCollider::_RayStab(const AABBQuantizedNoLeafNode* node) { // Dequantize box const QuantizedAABB& Box = node->mAABB; const IcePoint Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); const IcePoint Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); // Perform Ray-AABB overlap test if(!RayAABBOverlap(Center, Extents)) return; if(node->HasPosLeaf()) { RAY_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } else _RayStab(node->GetPos()); if(ContactFound()) return; if(node->HasNegLeaf()) { RAY_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } else _RayStab(node->GetNeg()); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Recursive stabbing query for vanilla AABB trees. * \param node [in] current collision node * \param box_indices [out] indices of stabbed boxes */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void RayCollider::_RayStab(const AABBTreeNode* node, Container& box_indices) { // Test the box against the ray IcePoint Center, Extents; node->GetAABB()->GetCenter(Center); node->GetAABB()->GetExtents(Extents); if(!RayAABBOverlap(Center, Extents)) return; if(node->IsLeaf()) { mFlags |= OPC_CONTACT; box_indices.Add(node->GetPrimitives(), node->GetNbPrimitives()); } else { _RayStab(node->GetPos(), box_indices); _RayStab(node->GetNeg(), box_indices); } }