summaryrefslogtreecommitdiffhomepage
path: root/contrib/Opcode/OPC_RayCollider.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/Opcode/OPC_RayCollider.cpp')
-rw-r--r--contrib/Opcode/OPC_RayCollider.cpp762
1 files changed, 762 insertions, 0 deletions
diff --git a/contrib/Opcode/OPC_RayCollider.cpp b/contrib/Opcode/OPC_RayCollider.cpp
new file mode 100644
index 0000000..d1c90a5
--- /dev/null
+++ b/contrib/Opcode/OPC_RayCollider.cpp
@@ -0,0 +1,762 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ * 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<CollisionFace*>(mStabbedFaces->GetFaces()); \
+ if(Current && mStabbedFace.mDistance<Current->mDistance) \
+ { \
+ *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)<IR(mMaxDist)) \
+ { \
+ HANDLE_CONTACT(prim_index, flag) \
+ } \
+ }
+
+#define RAY_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])) \
+ { \
+ 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.mDistance<mMaxDist)
+ if(IR(mStabbedFace.mDistance)<IR(mMaxDist)) // The other test is already performed in RayTriOverlap
+ {
+ // Set contact status
+ mFlags |= OPC_TEMPORAL_CONTACT;
+
+ mStabbedFace.mFaceID = *face_id;
+
+#ifndef OPC_RAYHIT_CALLBACK
+ if(mStabbedFaces) mStabbedFaces->AddFace(mStabbedFace);
+#endif
+ return TRUE;
+ }
+ }
+ }
+#else
+ // New code
+ // We handle both IceSegment/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 IceSegment-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 IceSegment-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 IceSegment-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 IceSegment-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 IceSegment-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);
+ }
+}