1 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
3 * OPCODE - Optimized Collision Detection
4 * Copyright (C) 2001 Pierre Terdiman
5 * Homepage: http://www.codercorner.com/Opcode.htm
7 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
9 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
11 * Contains code for a ray collider.
12 * \file OPC_RayCollider.cpp
13 * \author Pierre Terdiman
16 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
18 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
20 * Contains a ray-vs-tree collider.
21 * This class performs a stabbing query on an AABB tree, i.e. does a ray-mesh collision.
23 * HIGHER DISTANCE BOUND:
25 * If P0 and P1 are two 3D points, let's define:
26 * - d = distance between P0 and P1
28 * - Direction = (P1 - P0) / d = normalized direction vector
29 * - A parameter t such as a point P on the line (P0,P1) is P = Origin + t * Direction
33 * Then we can define a general "ray" as:
41 * But it actually maps three different things:
42 * - a segment, when 0 <= t <= d
43 * - a half-line, when 0 <= t < +infinity, or -infinity < t <= d
44 * - a line, when -infinity < t < +infinity
46 * In Opcode, we support segment queries, which yield half-line queries by setting d = +infinity.
47 * We don't support line-queries. If you need them, shift the origin along the ray by an appropriate margin.
49 * In short, the lower bound is always 0, and you can setup the higher bound "d" with RayCollider::SetMaxDist().
51 * Query |segment |half-line |line
52 * --------|-------------------|---------------|----------------
53 * Usages |-shadow feelers |-raytracing |-
54 * |-sweep tests |-in/out tests |
58 * - You can setup "first contact" mode or "all contacts" mode with RayCollider::SetFirstContact().
59 * - In "first contact" mode we return as soon as the ray hits one face. If can be useful e.g. for shadow feelers, where
60 * you want to know whether the path to the light is free or not (a boolean answer is enough).
61 * - In "all contacts" mode we return all faces hit by the ray.
65 * - You can enable or disable temporal coherence with RayCollider::SetTemporalCoherence().
66 * - It currently only works in "first contact" mode.
67 * - If temporal coherence is enabled, the previously hit triangle is cached during the first query. Then, next queries
68 * start by colliding the ray against the cached triangle. If they still collide, we return immediately.
72 * - You can enable or disable "closest hit" with RayCollider::SetClosestHit().
73 * - It currently only works in "all contacts" mode.
74 * - If closest hit is enabled, faces are sorted by distance on-the-fly and the closest one only is reported.
78 * - You can enable or disable backface culling with RayCollider::SetCulling().
79 * - If culling is enabled, ray will not hit back faces (only front faces).
84 * \author Pierre Terdiman
88 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
90 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
92 * This class describes a face hit by a ray or segment.
93 * This is a particular class dedicated to stabbing queries.
95 * \class CollisionFace
96 * \author Pierre Terdiman
98 * \date March, 20, 2001
100 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
102 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
104 * This class is a dedicated collection of CollisionFace.
106 * \class CollisionFaces
107 * \author Pierre Terdiman
109 * \date March, 20, 2001
111 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
113 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
114 // Precompiled Header
117 using namespace Opcode
;
119 #include "OPC_RayAABBOverlap.h"
120 #include "OPC_RayTriOverlap.h"
122 #define SET_CONTACT(prim_index, flag) \
123 mNbIntersections++; \
124 /* Set contact status */ \
126 /* In any case the contact has been found and recorded in mStabbedFace */ \
127 mStabbedFace.mFaceID = prim_index;
129 #ifdef OPC_RAYHIT_CALLBACK
131 #define HANDLE_CONTACT(prim_index, flag) \
132 SET_CONTACT(prim_index, flag) \
134 if(mHitCallback) (mHitCallback)(mStabbedFace, mUserData);
136 #define UPDATE_CACHE \
137 if(cache && GetContactStatus()) \
139 *cache = mStabbedFace.mFaceID; \
143 #define HANDLE_CONTACT(prim_index, flag) \
144 SET_CONTACT(prim_index, flag) \
146 /* Now we can also record it in mStabbedFaces if available */ \
149 /* If we want all faces or if that's the first one we hit */ \
150 if(!mClosestHit || !mStabbedFaces->GetNbFaces()) \
152 mStabbedFaces->AddFace(mStabbedFace); \
156 /* We only keep closest hit */ \
157 CollisionFace* Current = const_cast<CollisionFace*>(mStabbedFaces->GetFaces()); \
158 if(Current && mStabbedFace.mDistance<Current->mDistance) \
160 *Current = mStabbedFace; \
165 #define UPDATE_CACHE \
166 if(cache && GetContactStatus() && mStabbedFaces) \
168 const CollisionFace* Current = mStabbedFaces->GetFaces(); \
169 if(Current) *cache = Current->mFaceID; \
170 else *cache = INVALID_ID; \
174 #define SEGMENT_PRIM(prim_index, flag) \
175 /* Request vertices from the app */ \
176 VertexPointers VP; ConversionArea VC; mIMesh->GetTriangle(VP, prim_index, VC); \
178 /* Perform ray-tri overlap test and return */ \
179 if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \
181 /* Intersection point is valid if dist < segment's length */ \
182 /* We know dist>0 so we can use integers */ \
183 if(IR(mStabbedFace.mDistance)<IR(mMaxDist)) \
185 HANDLE_CONTACT(prim_index, flag) \
189 #define RAY_PRIM(prim_index, flag) \
190 /* Request vertices from the app */ \
191 VertexPointers VP; ConversionArea VC; mIMesh->GetTriangle(VP, prim_index, VC); \
193 /* Perform ray-tri overlap test and return */ \
194 if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \
196 HANDLE_CONTACT(prim_index, flag) \
200 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
204 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
205 RayCollider::RayCollider() :
206 #ifdef OPC_RAYHIT_CALLBACK
210 mStabbedFaces (null
),
215 mNbIntersections (0),
216 mMaxDist (MAX_FLOAT
),
222 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
226 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
227 RayCollider::~RayCollider()
231 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
233 * Validates current settings. You should call this method after all the settings and callbacks have been defined.
234 * \return null if everything is ok, else a string describing the problem
236 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
237 const char* RayCollider::ValidateSettings()
239 if(mMaxDist
<0.0f
) return "Higher distance bound must be positive!";
240 if(TemporalCoherenceEnabled() && !FirstContactEnabled()) return "Temporal coherence only works with ""First contact"" mode!";
241 #ifndef OPC_RAYHIT_CALLBACK
242 if(mClosestHit
&& FirstContactEnabled()) return "Closest hit doesn't work with ""First contact"" mode!";
243 if(TemporalCoherenceEnabled() && mClosestHit
) return "Temporal coherence can't guarantee to report closest hit!";
245 if(SkipPrimitiveTests()) return "SkipPrimitiveTests not possible for RayCollider ! (not implemented)";
249 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
251 * Generic stabbing query for generic OPCODE models. After the call, access the results:
252 * - with GetContactStatus()
253 * - in the user-provided destination array
255 * \param world_ray [in] stabbing ray in world space
256 * \param model [in] Opcode model to collide with
257 * \param world [in] model's world matrix, or null
258 * \param cache [in] a possibly cached face index, or null
259 * \return true if success
260 * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
262 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
263 bool RayCollider::Collide(const Ray
& world_ray
, const Model
& model
, const Matrix4x4
* world
, udword
* cache
)
266 if(!Setup(&model
)) return false;
268 // Init collision query
269 if(InitQuery(world_ray
, world
, cache
)) return true;
271 if(!model
.HasLeafNodes())
273 if(model
.IsQuantized())
275 const AABBQuantizedNoLeafTree
* Tree
= static_cast<const AABBQuantizedNoLeafTree
*>(model
.GetTree());
277 // Setup dequantization coeffs
278 mCenterCoeff
= Tree
->mCenterCoeff
;
279 mExtentsCoeff
= Tree
->mExtentsCoeff
;
281 // Perform stabbing query
282 if(IR(mMaxDist
)!=IEEE_MAX_FLOAT
) _SegmentStab(Tree
->GetNodes());
283 else _RayStab(Tree
->GetNodes());
287 const AABBNoLeafTree
* Tree
= static_cast<const AABBNoLeafTree
*>(model
.GetTree());
289 // Perform stabbing query
290 if(IR(mMaxDist
)!=IEEE_MAX_FLOAT
) _SegmentStab(Tree
->GetNodes());
291 else _RayStab(Tree
->GetNodes());
296 if(model
.IsQuantized())
298 const AABBQuantizedTree
* Tree
= static_cast<const AABBQuantizedTree
*>(model
.GetTree());
300 // Setup dequantization coeffs
301 mCenterCoeff
= Tree
->mCenterCoeff
;
302 mExtentsCoeff
= Tree
->mExtentsCoeff
;
304 // Perform stabbing query
305 if(IR(mMaxDist
)!=IEEE_MAX_FLOAT
) _SegmentStab(Tree
->GetNodes());
306 else _RayStab(Tree
->GetNodes());
310 const AABBCollisionTree
* Tree
= static_cast<const AABBCollisionTree
*>(model
.GetTree());
312 // Perform stabbing query
313 if(IR(mMaxDist
)!=IEEE_MAX_FLOAT
) _SegmentStab(Tree
->GetNodes());
314 else _RayStab(Tree
->GetNodes());
318 // Update cache if needed
324 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
326 * Initializes a stabbing query :
327 * - reset stats & contact status
328 * - compute ray in local space
329 * - check temporal coherence
331 * \param world_ray [in] stabbing ray in world space
332 * \param world [in] object's world matrix, or null
333 * \param face_id [in] index of previously stabbed triangle
334 * \return TRUE if we can return immediately
335 * \warning SCALE NOT SUPPORTED. The matrix must contain rotation & translation parts only.
337 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
338 BOOL
RayCollider::InitQuery(const Ray
& world_ray
, const Matrix4x4
* world
, udword
* face_id
)
340 // Reset stats & contact status
341 Collider::InitQuery();
344 mNbIntersections
= 0;
345 #ifndef OPC_RAYHIT_CALLBACK
346 if(mStabbedFaces
) mStabbedFaces
->Reset();
349 // Compute ray in local space
350 // The (Origin/Dir) form is needed for the ray-triangle test anyway (even for segment tests)
353 Matrix3x3 InvWorld
= *world
;
354 mDir
= InvWorld
* world_ray
.mDir
;
357 InvertPRMatrix(World
, *world
);
358 mOrigin
= world_ray
.mOrig
* World
;
362 mDir
= world_ray
.mDir
;
363 mOrigin
= world_ray
.mOrig
;
366 // 4) Special case: 1-triangle meshes [Opcode 1.3]
367 if(mCurrentModel
&& mCurrentModel
->HasSingleNode())
369 // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0.
370 if(!SkipPrimitiveTests())
372 // Perform overlap test between the unique triangle and the ray (and set contact status if needed)
373 SEGMENT_PRIM(udword(0), OPC_CONTACT
)
375 // Return immediately regardless of status
380 // Check temporal coherence :
382 // Test previously colliding primitives first
383 if(TemporalCoherenceEnabled() && FirstContactEnabled() && face_id
&& *face_id
!=INVALID_ID
)
386 #ifndef OPC_RAYHIT_CALLBACK
390 // Request vertices from the app
393 mIMesh
->GetTriangle(VP
, *face_id
, VC
);
394 // Perform ray-cached tri overlap test
395 if(RayTriOverlap(*VP
.Vertex
[0], *VP
.Vertex
[1], *VP
.Vertex
[2]))
397 // Intersection point is valid if:
398 // - distance is positive (else it can just be a face behind the orig point)
399 // - distance is smaller than a given max distance (useful for shadow feelers)
400 // if(mStabbedFace.mDistance>0.0f && mStabbedFace.mDistance<mMaxDist)
401 if(IR(mStabbedFace
.mDistance
)<IR(mMaxDist
)) // The other test is already performed in RayTriOverlap
403 // Set contact status
404 mFlags
|= OPC_TEMPORAL_CONTACT
;
406 mStabbedFace
.mFaceID
= *face_id
;
408 #ifndef OPC_RAYHIT_CALLBACK
409 if(mStabbedFaces
) mStabbedFaces
->AddFace(mStabbedFace
);
417 // We handle both Segment/ray queries with the same segment code, and a possible infinite limit
418 SEGMENT_PRIM(*face_id
, OPC_TEMPORAL_CONTACT
)
420 // Return immediately if possible
421 if(GetContactStatus()) return TRUE
;
425 // Precompute data (moved after temporal coherence since only needed for ray-AABB)
426 if(IR(mMaxDist
)!=IEEE_MAX_FLOAT
)
428 // For Segment-AABB overlap
429 mData
= 0.5f
* mDir
* mMaxDist
;
430 mData2
= mOrigin
+ mData
;
433 mFDir
.x
= fabsf(mData
.x
);
434 mFDir
.y
= fabsf(mData
.y
);
435 mFDir
.z
= fabsf(mData
.z
);
439 // For Ray-AABB overlap
440 // udword x = SIR(mDir.x)-1;
441 // udword y = SIR(mDir.y)-1;
442 // udword z = SIR(mDir.z)-1;
448 mFDir
.x
= fabsf(mDir
.x
);
449 mFDir
.y
= fabsf(mDir
.y
);
450 mFDir
.z
= fabsf(mDir
.z
);
456 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
458 * Stabbing query for vanilla AABB trees.
459 * \param world_ray [in] stabbing ray in world space
460 * \param tree [in] AABB tree
461 * \param box_indices [out] indices of stabbed boxes
462 * \return true if success
464 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
465 bool RayCollider::Collide(const Ray
& world_ray
, const AABBTree
* tree
, Container
& box_indices
)
467 // ### bad design here
469 // This is typically called for a scene tree, full of -AABBs-, not full of triangles.
470 // So we don't really have "primitives" to deal with. Hence it doesn't work with
471 // "FirstContact" + "TemporalCoherence".
472 ASSERT( !(FirstContactEnabled() && TemporalCoherenceEnabled()) );
475 if(!tree
) return false;
477 // Init collision query
478 // Basically this is only called to initialize precomputed data
479 if(InitQuery(world_ray
)) return true;
481 // Perform stabbing query
482 if(IR(mMaxDist
)!=IEEE_MAX_FLOAT
) _SegmentStab(tree
, box_indices
);
483 else _RayStab(tree
, box_indices
);
489 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
491 * Recursive stabbing query for normal AABB trees.
492 * \param node [in] current collision node
494 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
495 void RayCollider::_SegmentStab(const AABBCollisionNode
* node
)
497 // Perform Segment-AABB overlap test
498 if(!SegmentAABBOverlap(node
->mAABB
.mCenter
, node
->mAABB
.mExtents
)) return;
502 SEGMENT_PRIM(node
->GetPrimitive(), OPC_CONTACT
)
506 _SegmentStab(node
->GetPos());
508 if(ContactFound()) return;
510 _SegmentStab(node
->GetNeg());
514 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
516 * Recursive stabbing query for quantized AABB trees.
517 * \param node [in] current collision node
519 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
520 void RayCollider::_SegmentStab(const AABBQuantizedNode
* node
)
523 const QuantizedAABB
& Box
= node
->mAABB
;
524 const Point
Center(float(Box
.mCenter
[0]) * mCenterCoeff
.x
, float(Box
.mCenter
[1]) * mCenterCoeff
.y
, float(Box
.mCenter
[2]) * mCenterCoeff
.z
);
525 const Point
Extents(float(Box
.mExtents
[0]) * mExtentsCoeff
.x
, float(Box
.mExtents
[1]) * mExtentsCoeff
.y
, float(Box
.mExtents
[2]) * mExtentsCoeff
.z
);
527 // Perform Segment-AABB overlap test
528 if(!SegmentAABBOverlap(Center
, Extents
)) return;
532 SEGMENT_PRIM(node
->GetPrimitive(), OPC_CONTACT
)
536 _SegmentStab(node
->GetPos());
538 if(ContactFound()) return;
540 _SegmentStab(node
->GetNeg());
544 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
546 * Recursive stabbing query for no-leaf AABB trees.
547 * \param node [in] current collision node
549 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
550 void RayCollider::_SegmentStab(const AABBNoLeafNode
* node
)
552 // Perform Segment-AABB overlap test
553 if(!SegmentAABBOverlap(node
->mAABB
.mCenter
, node
->mAABB
.mExtents
)) return;
555 if(node
->HasPosLeaf())
557 SEGMENT_PRIM(node
->GetPosPrimitive(), OPC_CONTACT
)
559 else _SegmentStab(node
->GetPos());
561 if(ContactFound()) return;
563 if(node
->HasNegLeaf())
565 SEGMENT_PRIM(node
->GetNegPrimitive(), OPC_CONTACT
)
567 else _SegmentStab(node
->GetNeg());
570 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
572 * Recursive stabbing query for quantized no-leaf AABB trees.
573 * \param node [in] current collision node
575 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
576 void RayCollider::_SegmentStab(const AABBQuantizedNoLeafNode
* node
)
579 const QuantizedAABB
& Box
= node
->mAABB
;
580 const Point
Center(float(Box
.mCenter
[0]) * mCenterCoeff
.x
, float(Box
.mCenter
[1]) * mCenterCoeff
.y
, float(Box
.mCenter
[2]) * mCenterCoeff
.z
);
581 const Point
Extents(float(Box
.mExtents
[0]) * mExtentsCoeff
.x
, float(Box
.mExtents
[1]) * mExtentsCoeff
.y
, float(Box
.mExtents
[2]) * mExtentsCoeff
.z
);
583 // Perform Segment-AABB overlap test
584 if(!SegmentAABBOverlap(Center
, Extents
)) return;
586 if(node
->HasPosLeaf())
588 SEGMENT_PRIM(node
->GetPosPrimitive(), OPC_CONTACT
)
590 else _SegmentStab(node
->GetPos());
592 if(ContactFound()) return;
594 if(node
->HasNegLeaf())
596 SEGMENT_PRIM(node
->GetNegPrimitive(), OPC_CONTACT
)
598 else _SegmentStab(node
->GetNeg());
601 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
603 * Recursive stabbing query for vanilla AABB trees.
604 * \param node [in] current collision node
605 * \param box_indices [out] indices of stabbed boxes
607 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
608 void RayCollider::_SegmentStab(const AABBTreeNode
* node
, Container
& box_indices
)
610 // Test the box against the segment
611 Point Center
, Extents
;
612 node
->GetAABB()->GetCenter(Center
);
613 node
->GetAABB()->GetExtents(Extents
);
614 if(!SegmentAABBOverlap(Center
, Extents
)) return;
618 box_indices
.Add(node
->GetPrimitives(), node
->GetNbPrimitives());
622 _SegmentStab(node
->GetPos(), box_indices
);
623 _SegmentStab(node
->GetNeg(), box_indices
);
627 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
629 * Recursive stabbing query for normal AABB trees.
630 * \param node [in] current collision node
632 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
633 void RayCollider::_RayStab(const AABBCollisionNode
* node
)
635 // Perform Ray-AABB overlap test
636 if(!RayAABBOverlap(node
->mAABB
.mCenter
, node
->mAABB
.mExtents
)) return;
640 RAY_PRIM(node
->GetPrimitive(), OPC_CONTACT
)
644 _RayStab(node
->GetPos());
646 if(ContactFound()) return;
648 _RayStab(node
->GetNeg());
652 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
654 * Recursive stabbing query for quantized AABB trees.
655 * \param node [in] current collision node
657 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
658 void RayCollider::_RayStab(const AABBQuantizedNode
* node
)
661 const QuantizedAABB
& Box
= node
->mAABB
;
662 const Point
Center(float(Box
.mCenter
[0]) * mCenterCoeff
.x
, float(Box
.mCenter
[1]) * mCenterCoeff
.y
, float(Box
.mCenter
[2]) * mCenterCoeff
.z
);
663 const Point
Extents(float(Box
.mExtents
[0]) * mExtentsCoeff
.x
, float(Box
.mExtents
[1]) * mExtentsCoeff
.y
, float(Box
.mExtents
[2]) * mExtentsCoeff
.z
);
665 // Perform Ray-AABB overlap test
666 if(!RayAABBOverlap(Center
, Extents
)) return;
670 RAY_PRIM(node
->GetPrimitive(), OPC_CONTACT
)
674 _RayStab(node
->GetPos());
676 if(ContactFound()) return;
678 _RayStab(node
->GetNeg());
682 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
684 * Recursive stabbing query for no-leaf AABB trees.
685 * \param node [in] current collision node
687 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
688 void RayCollider::_RayStab(const AABBNoLeafNode
* node
)
690 // Perform Ray-AABB overlap test
691 if(!RayAABBOverlap(node
->mAABB
.mCenter
, node
->mAABB
.mExtents
)) return;
693 if(node
->HasPosLeaf())
695 RAY_PRIM(node
->GetPosPrimitive(), OPC_CONTACT
)
697 else _RayStab(node
->GetPos());
699 if(ContactFound()) return;
701 if(node
->HasNegLeaf())
703 RAY_PRIM(node
->GetNegPrimitive(), OPC_CONTACT
)
705 else _RayStab(node
->GetNeg());
708 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
710 * Recursive stabbing query for quantized no-leaf AABB trees.
711 * \param node [in] current collision node
713 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
714 void RayCollider::_RayStab(const AABBQuantizedNoLeafNode
* node
)
717 const QuantizedAABB
& Box
= node
->mAABB
;
718 const Point
Center(float(Box
.mCenter
[0]) * mCenterCoeff
.x
, float(Box
.mCenter
[1]) * mCenterCoeff
.y
, float(Box
.mCenter
[2]) * mCenterCoeff
.z
);
719 const Point
Extents(float(Box
.mExtents
[0]) * mExtentsCoeff
.x
, float(Box
.mExtents
[1]) * mExtentsCoeff
.y
, float(Box
.mExtents
[2]) * mExtentsCoeff
.z
);
721 // Perform Ray-AABB overlap test
722 if(!RayAABBOverlap(Center
, Extents
)) return;
724 if(node
->HasPosLeaf())
726 RAY_PRIM(node
->GetPosPrimitive(), OPC_CONTACT
)
728 else _RayStab(node
->GetPos());
730 if(ContactFound()) return;
732 if(node
->HasNegLeaf())
734 RAY_PRIM(node
->GetNegPrimitive(), OPC_CONTACT
)
736 else _RayStab(node
->GetNeg());
739 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
741 * Recursive stabbing query for vanilla AABB trees.
742 * \param node [in] current collision node
743 * \param box_indices [out] indices of stabbed boxes
745 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
746 void RayCollider::_RayStab(const AABBTreeNode
* node
, Container
& box_indices
)
748 // Test the box against the ray
749 Point Center
, Extents
;
750 node
->GetAABB()->GetCenter(Center
);
751 node
->GetAABB()->GetExtents(Extents
);
752 if(!RayAABBOverlap(Center
, Extents
)) return;
756 mFlags
|= OPC_CONTACT
;
757 box_indices
.Add(node
->GetPrimitives(), node
->GetNbPrimitives());
761 _RayStab(node
->GetPos(), box_indices
);
762 _RayStab(node
->GetNeg(), box_indices
);