2 * OPCODE - Optimized Collision Detection
3 * http://www.codercorner.com/Opcode.htm
5 * Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com
7 This software is provided 'as-is', without any express or implied warranty.
8 In no event will the authors be held liable for any damages arising from the use of this software.
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it freely,
11 subject to the following restrictions:
13 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
14 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
15 3. This notice may not be removed or altered from any source distribution.
18 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
20 * Contains code for a sphere collider.
21 * \file OPC_SphereCollider.cpp
22 * \author Pierre Terdiman
25 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
27 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
29 * Contains a sphere-vs-tree collider.
30 * This class performs a collision test between a sphere and an AABB tree. You can use this to do a standard player vs world collision,
31 * in a Nettle/Telemachos way. It doesn't suffer from all reported bugs in those two classic codes - the "new" one by Paul Nettle is a
32 * debuggued version I think. Collision response can be driven by reported collision data - it works extremely well for me. In sake of
33 * efficiency, all meshes (that is, all AABB trees) should of course also be kept in an extra hierarchical structure (octree, whatever).
35 * \class SphereCollider
36 * \author Pierre Terdiman
40 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
42 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
46 using namespace Opcode;
48 #include "OPC_SphereAABBOverlap.h"
49 #include "OPC_SphereTriOverlap.h"
51 #define SET_CONTACT(prim_index, flag) \
52 /* Set contact status */ \
54 mTouchedPrimitives->Add(prim_index);
56 //! Sphere-triangle overlap test
57 #define SPHERE_PRIM(prim_index, flag) \
58 /* Request vertices from the app */ \
59 VertexPointers VP; mIMesh->GetTriangle(VP, prim_index); \
61 /* Perform sphere-tri overlap test */ \
62 if(SphereTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \
64 SET_CONTACT(prim_index, flag) \
67 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
71 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
72 SphereCollider::SphereCollider()
78 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
82 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
83 SphereCollider::~SphereCollider()
87 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
89 * Generic collision query for generic OPCODE models. After the call, access the results:
90 * - with GetContactStatus()
91 * - with GetNbTouchedPrimitives()
92 * - with GetTouchedPrimitives()
94 * \param cache [in/out] a sphere cache
95 * \param sphere [in] collision sphere in local space
96 * \param model [in] Opcode model to collide with
97 * \param worlds [in] sphere's world matrix, or null
98 * \param worldm [in] model's world matrix, or null
99 * \return true if success
100 * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
102 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
103 bool SphereCollider::Collide(SphereCache& cache, const Sphere& sphere, const Model& model, const Matrix4x4* worlds, const Matrix4x4* worldm)
106 if(!Setup(&model)) return false;
108 // Init collision query
109 if(InitQuery(cache, sphere, worlds, worldm)) return true;
111 if(!model.HasLeafNodes())
113 if(model.IsQuantized())
115 const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree();
117 // Setup dequantization coeffs
118 mCenterCoeff = Tree->mCenterCoeff;
119 mExtentsCoeff = Tree->mExtentsCoeff;
121 // Perform collision query
122 if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
123 else _Collide(Tree->GetNodes());
127 const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
129 // Perform collision query
130 if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
131 else _Collide(Tree->GetNodes());
136 if(model.IsQuantized())
138 const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
140 // Setup dequantization coeffs
141 mCenterCoeff = Tree->mCenterCoeff;
142 mExtentsCoeff = Tree->mExtentsCoeff;
144 // Perform collision query
145 if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
146 else _Collide(Tree->GetNodes());
150 const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
152 // Perform collision query
153 if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
154 else _Collide(Tree->GetNodes());
160 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
162 * Initializes a collision query :
163 * - reset stats & contact status
165 * - check temporal coherence
167 * \param cache [in/out] a sphere cache
168 * \param sphere [in] sphere in local space
169 * \param worlds [in] sphere's world matrix, or null
170 * \param worldm [in] model's world matrix, or null
171 * \return TRUE if we can return immediately
172 * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
174 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
175 BOOL SphereCollider::InitQuery(SphereCache& cache, const Sphere& sphere, const Matrix4x4* worlds, const Matrix4x4* worldm)
177 // 1) Call the base method
178 VolumeCollider::InitQuery();
180 // 2) Compute sphere in model space:
182 mRadius2 = sphere.mRadius * sphere.mRadius;
183 // - Compute center position
184 mCenter = sphere.mCenter;
186 if(worlds) mCenter *= *worlds;
190 // Invert model matrix
192 InvertPRMatrix(InvWorldM, *worldm);
194 mCenter *= InvWorldM;
197 // 3) Setup destination pointer
198 mTouchedPrimitives = &cache.TouchedPrimitives;
200 // 4) Special case: 1-triangle meshes [Opcode 1.3]
201 if(mCurrentModel && mCurrentModel->HasSingleNode())
203 if(!SkipPrimitiveTests())
205 // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0.
206 mTouchedPrimitives->Reset();
208 // Perform overlap test between the unique triangle and the sphere (and set contact status if needed)
209 SPHERE_PRIM(udword(0), OPC_CONTACT)
211 // Return immediately regardless of status
216 // 5) Check temporal coherence :
217 if(TemporalCoherenceEnabled())
219 // Here we use temporal coherence
220 // => check results from previous frame before performing the collision query
221 if(FirstContactEnabled())
223 // We're only interested in the first contact found => test the unique previously touched face
224 if(mTouchedPrimitives->GetNbEntries())
226 // Get index of previously touched face = the first entry in the array
227 udword PreviouslyTouchedFace = mTouchedPrimitives->GetEntry(0);
229 // Then reset the array:
230 // - if the overlap test below is successful, the index we'll get added back anyway
231 // - if it isn't, then the array should be reset anyway for the normal query
232 mTouchedPrimitives->Reset();
234 // Perform overlap test between the cached triangle and the sphere (and set contact status if needed)
235 SPHERE_PRIM(PreviouslyTouchedFace, OPC_TEMPORAL_CONTACT)
237 // Return immediately if possible
238 if(GetContactStatus()) return TRUE;
240 // else no face has been touched during previous query
241 // => we'll have to perform a normal query
245 // We're interested in all contacts =>test the new real sphere N(ew) against the previous fat sphere P(revious):
246 float r = sqrtf(cache.FatRadius2) - sphere.mRadius;
247 if(IsCacheValid(cache) && cache.Center.SquareDistance(mCenter) < r*r)
249 // - if N is included in P, return previous list
250 // => we simply leave the list (mTouchedFaces) unchanged
252 // Set contact status if needed
253 if(mTouchedPrimitives->GetNbEntries()) mFlags |= OPC_TEMPORAL_CONTACT;
255 // In any case we don't need to do a query
260 // - else do the query using a fat N
262 // Reset cache since we'll about to perform a real query
263 mTouchedPrimitives->Reset();
265 // Make a fat sphere so that coherence will work for subsequent frames
266 mRadius2 *= cache.FatCoeff;
267 // mRadius2 = (sphere.mRadius * cache.FatCoeff)*(sphere.mRadius * cache.FatCoeff);
269 // Update cache with query data (signature for cached faces)
270 cache.Center = mCenter;
271 cache.FatRadius2 = mRadius2;
277 // Here we don't use temporal coherence => do a normal query
278 mTouchedPrimitives->Reset();
284 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
286 * Collision query for vanilla AABB trees.
287 * \param cache [in/out] a sphere cache
288 * \param sphere [in] collision sphere in world space
289 * \param tree [in] AABB tree
290 * \return true if success
292 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
293 bool SphereCollider::Collide(SphereCache& cache, const Sphere& sphere, const AABBTree* tree)
295 // This is typically called for a scene tree, full of -AABBs-, not full of triangles.
296 // So we don't really have "primitives" to deal with. Hence it doesn't work with
297 // "FirstContact" + "TemporalCoherence".
298 ASSERT( !(FirstContactEnabled() && TemporalCoherenceEnabled()) );
301 if(!tree) return false;
303 // Init collision query
304 if(InitQuery(cache, sphere)) return true;
306 // Perform collision query
312 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
314 * Checks the sphere completely contains the box. In which case we can end the query sooner.
315 * \param bc [in] box center
316 * \param be [in] box extents
317 * \return true if the sphere contains the whole box
319 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
320 inline_ BOOL SphereCollider::SphereContainsBox(const Point& bc, const Point& be)
322 // I assume if all 8 box vertices are inside the sphere, so does the whole box.
323 // Sounds ok but maybe there's a better way?
325 p.x=bc.x+be.x; p.y=bc.y+be.y; p.z=bc.z+be.z; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE;
326 p.x=bc.x-be.x; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE;
327 p.x=bc.x+be.x; p.y=bc.y-be.y; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE;
328 p.x=bc.x-be.x; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE;
329 p.x=bc.x+be.x; p.y=bc.y+be.y; p.z=bc.z-be.z; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE;
330 p.x=bc.x-be.x; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE;
331 p.x=bc.x+be.x; p.y=bc.y-be.y; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE;
332 p.x=bc.x-be.x; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE;
337 #define TEST_BOX_IN_SPHERE(center, extents) \
338 if(SphereContainsBox(center, extents)) \
340 /* Set contact status */ \
341 mFlags |= OPC_CONTACT; \
346 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
348 * Recursive collision query for normal AABB trees.
349 * \param node [in] current collision node
351 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
352 void SphereCollider::_Collide(const AABBCollisionNode* node)
354 // Perform Sphere-AABB overlap test
355 if(!SphereAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
357 TEST_BOX_IN_SPHERE(node->mAABB.mCenter, node->mAABB.mExtents)
361 SPHERE_PRIM(node->GetPrimitive(), OPC_CONTACT)
365 _Collide(node->GetPos());
367 if(ContactFound()) return;
369 _Collide(node->GetNeg());
373 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
375 * Recursive collision query for normal AABB trees, without primitive tests.
376 * \param node [in] current collision node
378 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
379 void SphereCollider::_CollideNoPrimitiveTest(const AABBCollisionNode* node)
381 // Perform Sphere-AABB overlap test
382 if(!SphereAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
384 TEST_BOX_IN_SPHERE(node->mAABB.mCenter, node->mAABB.mExtents)
388 SET_CONTACT(node->GetPrimitive(), OPC_CONTACT)
392 _CollideNoPrimitiveTest(node->GetPos());
394 if(ContactFound()) return;
396 _CollideNoPrimitiveTest(node->GetNeg());
400 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
402 * Recursive collision query for quantized AABB trees.
403 * \param node [in] current collision node
405 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
406 void SphereCollider::_Collide(const AABBQuantizedNode* node)
409 const QuantizedAABB& Box = node->mAABB;
410 const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
411 const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
413 // Perform Sphere-AABB overlap test
414 if(!SphereAABBOverlap(Center, Extents)) return;
416 TEST_BOX_IN_SPHERE(Center, Extents)
420 SPHERE_PRIM(node->GetPrimitive(), OPC_CONTACT)
424 _Collide(node->GetPos());
426 if(ContactFound()) return;
428 _Collide(node->GetNeg());
432 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
434 * Recursive collision query for quantized AABB trees, without primitive tests.
435 * \param node [in] current collision node
437 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
438 void SphereCollider::_CollideNoPrimitiveTest(const AABBQuantizedNode* node)
441 const QuantizedAABB& Box = node->mAABB;
442 const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
443 const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
445 // Perform Sphere-AABB overlap test
446 if(!SphereAABBOverlap(Center, Extents)) return;
448 TEST_BOX_IN_SPHERE(Center, Extents)
452 SET_CONTACT(node->GetPrimitive(), OPC_CONTACT)
456 _CollideNoPrimitiveTest(node->GetPos());
458 if(ContactFound()) return;
460 _CollideNoPrimitiveTest(node->GetNeg());
464 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
466 * Recursive collision query for no-leaf AABB trees.
467 * \param node [in] current collision node
469 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
470 void SphereCollider::_Collide(const AABBNoLeafNode* node)
472 // Perform Sphere-AABB overlap test
473 if(!SphereAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
475 TEST_BOX_IN_SPHERE(node->mAABB.mCenter, node->mAABB.mExtents)
477 if(node->HasPosLeaf()) { SPHERE_PRIM(node->GetPosPrimitive(), OPC_CONTACT) }
478 else _Collide(node->GetPos());
480 if(ContactFound()) return;
482 if(node->HasNegLeaf()) { SPHERE_PRIM(node->GetNegPrimitive(), OPC_CONTACT) }
483 else _Collide(node->GetNeg());
486 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
488 * Recursive collision query for no-leaf AABB trees, without primitive tests.
489 * \param node [in] current collision node
491 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
492 void SphereCollider::_CollideNoPrimitiveTest(const AABBNoLeafNode* node)
494 // Perform Sphere-AABB overlap test
495 if(!SphereAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
497 TEST_BOX_IN_SPHERE(node->mAABB.mCenter, node->mAABB.mExtents)
499 if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) }
500 else _CollideNoPrimitiveTest(node->GetPos());
502 if(ContactFound()) return;
504 if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) }
505 else _CollideNoPrimitiveTest(node->GetNeg());
508 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
510 * Recursive collision query for quantized no-leaf AABB trees.
511 * \param node [in] current collision node
513 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
514 void SphereCollider::_Collide(const AABBQuantizedNoLeafNode* node)
517 const QuantizedAABB& Box = node->mAABB;
518 const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
519 const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
521 // Perform Sphere-AABB overlap test
522 if(!SphereAABBOverlap(Center, Extents)) return;
524 TEST_BOX_IN_SPHERE(Center, Extents)
526 if(node->HasPosLeaf()) { SPHERE_PRIM(node->GetPosPrimitive(), OPC_CONTACT) }
527 else _Collide(node->GetPos());
529 if(ContactFound()) return;
531 if(node->HasNegLeaf()) { SPHERE_PRIM(node->GetNegPrimitive(), OPC_CONTACT) }
532 else _Collide(node->GetNeg());
535 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
537 * Recursive collision query for quantized no-leaf AABB trees, without primitive tests.
538 * \param node [in] current collision node
540 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
541 void SphereCollider::_CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node)
544 const QuantizedAABB& Box = node->mAABB;
545 const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
546 const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
548 // Perform Sphere-AABB overlap test
549 if(!SphereAABBOverlap(Center, Extents)) return;
551 TEST_BOX_IN_SPHERE(Center, Extents)
553 if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) }
554 else _CollideNoPrimitiveTest(node->GetPos());
556 if(ContactFound()) return;
558 if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) }
559 else _CollideNoPrimitiveTest(node->GetNeg());
562 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
564 * Recursive collision query for vanilla AABB trees.
565 * \param node [in] current collision node
567 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
568 void SphereCollider::_Collide(const AABBTreeNode* node)
570 // Perform Sphere-AABB overlap test
571 Point Center, Extents;
572 node->GetAABB()->GetCenter(Center);
573 node->GetAABB()->GetExtents(Extents);
574 if(!SphereAABBOverlap(Center, Extents)) return;
576 if(node->IsLeaf() || SphereContainsBox(Center, Extents))
578 mFlags |= OPC_CONTACT;
579 mTouchedPrimitives->Add(node->GetPrimitives(), node->GetNbPrimitives());
583 _Collide(node->GetPos());
584 _Collide(node->GetNeg());
594 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
598 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
599 HybridSphereCollider::HybridSphereCollider()
603 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
607 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
608 HybridSphereCollider::~HybridSphereCollider()
612 bool HybridSphereCollider::Collide(SphereCache& cache, const Sphere& sphere, const HybridModel& model, const Matrix4x4* worlds, const Matrix4x4* worldm)
614 // We don't want primitive tests here!
615 mFlags |= OPC_NO_PRIMITIVE_TESTS;
618 if(!Setup(&model)) return false;
620 // Init collision query
621 if(InitQuery(cache, sphere, worlds, worldm)) return true;
623 // Special case for 1-leaf trees
624 if(mCurrentModel && mCurrentModel->HasSingleNode())
626 // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles
627 udword Nb = mIMesh->GetNbTriangles();
629 // Loop through all triangles
630 for(udword i=0;i<Nb;i++)
632 SPHERE_PRIM(i, OPC_CONTACT)
637 // Override destination array since we're only going to get leaf boxes here
638 mTouchedBoxes.Reset();
639 mTouchedPrimitives = &mTouchedBoxes;
641 // Now, do the actual query against leaf boxes
642 if(!model.HasLeafNodes())
644 if(model.IsQuantized())
646 const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree();
648 // Setup dequantization coeffs
649 mCenterCoeff = Tree->mCenterCoeff;
650 mExtentsCoeff = Tree->mExtentsCoeff;
652 // Perform collision query - we don't want primitive tests here!
653 _CollideNoPrimitiveTest(Tree->GetNodes());
657 const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
659 // Perform collision query - we don't want primitive tests here!
660 _CollideNoPrimitiveTest(Tree->GetNodes());
665 if(model.IsQuantized())
667 const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
669 // Setup dequantization coeffs
670 mCenterCoeff = Tree->mCenterCoeff;
671 mExtentsCoeff = Tree->mExtentsCoeff;
673 // Perform collision query - we don't want primitive tests here!
674 _CollideNoPrimitiveTest(Tree->GetNodes());
678 const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
680 // Perform collision query - we don't want primitive tests here!
681 _CollideNoPrimitiveTest(Tree->GetNodes());
685 // We only have a list of boxes so far
686 if(GetContactStatus())
688 // Reset contact status, since it currently only reflects collisions with leaf boxes
689 Collider::InitQuery();
691 // Change dest container so that we can use built-in overlap tests and get collided primitives
692 cache.TouchedPrimitives.Reset();
693 mTouchedPrimitives = &cache.TouchedPrimitives;
695 // Read touched leaf boxes
696 udword Nb = mTouchedBoxes.GetNbEntries();
697 const udword* Touched = mTouchedBoxes.GetEntries();
699 const LeafTriangles* LT = model.GetLeafTriangles();
700 const udword* Indices = model.GetIndices();
702 // Loop through touched leaves
705 const LeafTriangles& CurrentLeaf = LT[*Touched++];
707 // Each leaf box has a set of triangles
708 udword NbTris = CurrentLeaf.GetNbTriangles();
711 const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()];
713 // Loop through triangles and test each of them
716 udword TriangleIndex = *T++;
717 SPHERE_PRIM(TriangleIndex, OPC_CONTACT)
722 udword BaseIndex = CurrentLeaf.GetTriangleIndex();
724 // Loop through triangles and test each of them
727 udword TriangleIndex = BaseIndex++;
728 SPHERE_PRIM(TriangleIndex, OPC_CONTACT)