Fixed: Some incorrect changes from #2114 were reverted and full reorder of constraint...
[ode.git] / OPCODE / OPC_SphereCollider.cpp
blobe2406ec05d048106c7135a1dbef7052f3f072e40
1 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2 /*
3 * OPCODE - Optimized Collision Detection
4 * Copyright (C) 2001 Pierre Terdiman
5 * Homepage: http://www.codercorner.com/Opcode.htm
6 */
7 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
9 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
10 /**
11 * Contains code for a sphere collider.
12 * \file OPC_SphereCollider.cpp
13 * \author Pierre Terdiman
14 * \date June, 2, 2001
16 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
18 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
19 /**
20 * Contains a sphere-vs-tree collider.
21 * 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,
22 * 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
23 * debuggued version I think. Collision response can be driven by reported collision data - it works extremely well for me. In sake of
24 * efficiency, all meshes (that is, all AABB trees) should of course also be kept in an extra hierarchical structure (octree, whatever).
26 * \class SphereCollider
27 * \author Pierre Terdiman
28 * \version 1.3
29 * \date June, 2, 2001
31 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
33 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
34 // Precompiled Header
35 #include "Stdafx.h"
37 using namespace Opcode;
39 #include "OPC_SphereAABBOverlap.h"
40 #include "OPC_SphereTriOverlap.h"
42 #define SET_CONTACT(prim_index, flag) \
43 /* Set contact status */ \
44 mFlags |= flag; \
45 mTouchedPrimitives->Add(udword(prim_index));
47 //! Sphere-triangle overlap test
48 #define SPHERE_PRIM(prim_index, flag) \
49 /* Request vertices from the app */ \
50 VertexPointers VP; ConversionArea VC; mIMesh->GetTriangle(VP, prim_index, VC); \
52 /* Perform sphere-tri overlap test */ \
53 if(SphereTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \
54 { \
55 SET_CONTACT(prim_index, flag) \
58 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
59 /**
60 * Constructor.
62 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
63 SphereCollider::SphereCollider()
65 mCenter.Zero();
66 mRadius2 = 0.0f;
69 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
70 /**
71 * Destructor.
73 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
74 SphereCollider::~SphereCollider()
78 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
79 /**
80 * Generic collision query for generic OPCODE models. After the call, access the results:
81 * - with GetContactStatus()
82 * - with GetNbTouchedPrimitives()
83 * - with GetTouchedPrimitives()
85 * \param cache [in/out] a sphere cache
86 * \param sphere [in] collision sphere in local space
87 * \param model [in] Opcode model to collide with
88 * \param worlds [in] sphere's world matrix, or null
89 * \param worldm [in] model's world matrix, or null
90 * \return true if success
91 * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
93 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
94 bool SphereCollider::Collide(SphereCache& cache, const Sphere& sphere, const Model& model, const Matrix4x4* worlds, const Matrix4x4* worldm)
96 // Checkings
97 if(!Setup(&model)) return false;
99 // Init collision query
100 if(InitQuery(cache, sphere, worlds, worldm)) return true;
102 // Special case for 1-leaf trees
103 if(mCurrentModel && mCurrentModel->HasSingleNode())
105 // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles
106 udword Nb = mIMesh->GetNbTriangles();
107 // Loop through all triangles
108 for(udword i=0;i<Nb;i++)
110 SPHERE_PRIM(i, OPC_CONTACT)
112 return true;
115 if(!model.HasLeafNodes())
117 if(model.IsQuantized())
119 const AABBQuantizedNoLeafTree* Tree = static_cast<const AABBQuantizedNoLeafTree *>(model.GetTree());
121 // Setup dequantization coeffs
122 mCenterCoeff = Tree->mCenterCoeff;
123 mExtentsCoeff = Tree->mExtentsCoeff;
125 // Perform collision query
126 if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
127 else _Collide(Tree->GetNodes());
129 else
131 const AABBNoLeafTree* Tree = static_cast<const AABBNoLeafTree *>(model.GetTree());
133 // Perform collision query
134 if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
135 else _Collide(Tree->GetNodes());
138 else
140 if(model.IsQuantized())
142 const AABBQuantizedTree* Tree = static_cast<const AABBQuantizedTree *>(model.GetTree());
144 // Setup dequantization coeffs
145 mCenterCoeff = Tree->mCenterCoeff;
146 mExtentsCoeff = Tree->mExtentsCoeff;
148 // Perform collision query
149 if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
150 else _Collide(Tree->GetNodes());
152 else
154 const AABBCollisionTree* Tree = static_cast<const AABBCollisionTree *>(model.GetTree());
156 // Perform collision query
157 if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
158 else _Collide(Tree->GetNodes());
161 return true;
164 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
166 * Initializes a collision query :
167 * - reset stats & contact status
168 * - setup matrices
169 * - check temporal coherence
171 * \param cache [in/out] a sphere cache
172 * \param sphere [in] sphere in local space
173 * \param worlds [in] sphere's world matrix, or null
174 * \param worldm [in] model's world matrix, or null
175 * \return TRUE if we can return immediately
176 * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
178 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
179 BOOL SphereCollider::InitQuery(SphereCache& cache, const Sphere& sphere, const Matrix4x4* worlds, const Matrix4x4* worldm)
181 // 1) Call the base method
182 VolumeCollider::InitQuery();
184 // 2) Compute sphere in model space:
185 // - Precompute R^2
186 mRadius2 = sphere.mRadius * sphere.mRadius;
187 // - Compute center position
188 mCenter = sphere.mCenter;
189 // -> to world space
190 if(worlds) mCenter *= *worlds;
191 // -> to model space
192 if(worldm)
194 // Invert model matrix
195 Matrix4x4 InvWorldM;
196 InvertPRMatrix(InvWorldM, *worldm);
198 mCenter *= InvWorldM;
201 // 3) Setup destination pointer
202 mTouchedPrimitives = &cache.TouchedPrimitives;
204 // 4) Special case: 1-triangle meshes [Opcode 1.3]
205 if(mCurrentModel && mCurrentModel->HasSingleNode())
207 if(!SkipPrimitiveTests())
209 // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0.
210 mTouchedPrimitives->Reset();
212 // Perform overlap test between the unique triangle and the sphere (and set contact status if needed)
213 SPHERE_PRIM(udword(0), OPC_CONTACT)
215 // Return immediately regardless of status
216 return TRUE;
220 // 5) Check temporal coherence :
221 if(TemporalCoherenceEnabled())
223 // Here we use temporal coherence
224 // => check results from previous frame before performing the collision query
225 if(FirstContactEnabled())
227 // We're only interested in the first contact found => test the unique previously touched face
228 if(mTouchedPrimitives->GetNbEntries())
230 // Get index of previously touched face = the first entry in the array
231 udword PreviouslyTouchedFace = mTouchedPrimitives->GetEntry(0);
233 // Then reset the array:
234 // - if the overlap test below is successful, the index we'll get added back anyway
235 // - if it isn't, then the array should be reset anyway for the normal query
236 mTouchedPrimitives->Reset();
238 // Perform overlap test between the cached triangle and the sphere (and set contact status if needed)
239 SPHERE_PRIM(PreviouslyTouchedFace, OPC_TEMPORAL_CONTACT)
241 // Return immediately if possible
242 if(GetContactStatus()) return TRUE;
244 // else no face has been touched during previous query
245 // => we'll have to perform a normal query
247 else
249 // We're interested in all contacts =>test the new real sphere N(ew) against the previous fat sphere P(revious):
250 float r = sqrtf(cache.FatRadius2) - sphere.mRadius;
251 if(IsCacheValid(cache) && cache.Center.SquareDistance(mCenter) < r*r)
253 // - if N is included in P, return previous list
254 // => we simply leave the list (mTouchedFaces) unchanged
256 // Set contact status if needed
257 if(mTouchedPrimitives->GetNbEntries()) mFlags |= OPC_TEMPORAL_CONTACT;
259 // In any case we don't need to do a query
260 return TRUE;
262 else
264 // - else do the query using a fat N
266 // Reset cache since we'll about to perform a real query
267 mTouchedPrimitives->Reset();
269 // Make a fat sphere so that coherence will work for subsequent frames
270 mRadius2 *= cache.FatCoeff;
271 // mRadius2 = (sphere.mRadius * cache.FatCoeff)*(sphere.mRadius * cache.FatCoeff);
273 // Update cache with query data (signature for cached faces)
274 cache.Center = mCenter;
275 cache.FatRadius2 = mRadius2;
279 else
281 // Here we don't use temporal coherence => do a normal query
282 mTouchedPrimitives->Reset();
285 return FALSE;
288 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
290 * Collision query for vanilla AABB trees.
291 * \param cache [in/out] a sphere cache
292 * \param sphere [in] collision sphere in world space
293 * \param tree [in] AABB tree
294 * \return true if success
296 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
297 bool SphereCollider::Collide(SphereCache& cache, const Sphere& sphere, const AABBTree* tree)
299 // This is typically called for a scene tree, full of -AABBs-, not full of triangles.
300 // So we don't really have "primitives" to deal with. Hence it doesn't work with
301 // "FirstContact" + "TemporalCoherence".
302 ASSERT( !(FirstContactEnabled() && TemporalCoherenceEnabled()) );
304 // Checkings
305 if(!tree) return false;
307 // Init collision query
308 if(InitQuery(cache, sphere)) return true;
310 // Perform collision query
311 _Collide(tree);
313 return true;
316 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
318 * Checks the sphere completely contains the box. In which case we can end the query sooner.
319 * \param bc [in] box center
320 * \param be [in] box extents
321 * \return true if the sphere contains the whole box
323 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
324 inline_ BOOL SphereCollider::SphereContainsBox(const Point& bc, const Point& be)
326 // I assume if all 8 box vertices are inside the sphere, so does the whole box.
327 // Sounds ok but maybe there's a better way?
328 Point p;
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;
333 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;
334 p.x=bc.x-be.x; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE;
335 p.x=bc.x+be.x; p.y=bc.y-be.y; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE;
336 p.x=bc.x-be.x; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE;
338 return TRUE;
341 #define TEST_BOX_IN_SPHERE(center, extents) \
342 if(SphereContainsBox(center, extents)) \
344 /* Set contact status */ \
345 mFlags |= OPC_CONTACT; \
346 _Dump(node); \
347 return; \
350 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
352 * Recursive collision query for normal AABB trees.
353 * \param node [in] current collision node
355 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
356 void SphereCollider::_Collide(const AABBCollisionNode* node)
358 // Perform Sphere-AABB overlap test
359 if(!SphereAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
361 TEST_BOX_IN_SPHERE(node->mAABB.mCenter, node->mAABB.mExtents)
363 if(node->IsLeaf())
365 SPHERE_PRIM(node->GetPrimitive(), OPC_CONTACT)
367 else
369 _Collide(node->GetPos());
371 if(ContactFound()) return;
373 _Collide(node->GetNeg());
377 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
379 * Recursive collision query for normal AABB trees, without primitive tests.
380 * \param node [in] current collision node
382 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
383 void SphereCollider::_CollideNoPrimitiveTest(const AABBCollisionNode* node)
385 // Perform Sphere-AABB overlap test
386 if(!SphereAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
388 TEST_BOX_IN_SPHERE(node->mAABB.mCenter, node->mAABB.mExtents)
390 if(node->IsLeaf())
392 SET_CONTACT(node->GetPrimitive(), OPC_CONTACT)
394 else
396 _CollideNoPrimitiveTest(node->GetPos());
398 if(ContactFound()) return;
400 _CollideNoPrimitiveTest(node->GetNeg());
404 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
406 * Recursive collision query for quantized AABB trees.
407 * \param node [in] current collision node
409 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
410 void SphereCollider::_Collide(const AABBQuantizedNode* node)
412 // Dequantize box
413 const QuantizedAABB& Box = node->mAABB;
414 const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
415 const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
417 // Perform Sphere-AABB overlap test
418 if(!SphereAABBOverlap(Center, Extents)) return;
420 TEST_BOX_IN_SPHERE(Center, Extents)
422 if(node->IsLeaf())
424 SPHERE_PRIM(node->GetPrimitive(), OPC_CONTACT)
426 else
428 _Collide(node->GetPos());
430 if(ContactFound()) return;
432 _Collide(node->GetNeg());
436 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
438 * Recursive collision query for quantized AABB trees, without primitive tests.
439 * \param node [in] current collision node
441 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
442 void SphereCollider::_CollideNoPrimitiveTest(const AABBQuantizedNode* node)
444 // Dequantize box
445 const QuantizedAABB& Box = node->mAABB;
446 const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
447 const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
449 // Perform Sphere-AABB overlap test
450 if(!SphereAABBOverlap(Center, Extents)) return;
452 TEST_BOX_IN_SPHERE(Center, Extents)
454 if(node->IsLeaf())
456 SET_CONTACT(node->GetPrimitive(), OPC_CONTACT)
458 else
460 _CollideNoPrimitiveTest(node->GetPos());
462 if(ContactFound()) return;
464 _CollideNoPrimitiveTest(node->GetNeg());
468 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
470 * Recursive collision query for no-leaf AABB trees.
471 * \param node [in] current collision node
473 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
474 void SphereCollider::_Collide(const AABBNoLeafNode* node)
476 // Perform Sphere-AABB overlap test
477 if(!SphereAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
479 TEST_BOX_IN_SPHERE(node->mAABB.mCenter, node->mAABB.mExtents)
481 if(node->HasPosLeaf()) { SPHERE_PRIM(node->GetPosPrimitive(), OPC_CONTACT) }
482 else _Collide(node->GetPos());
484 if(ContactFound()) return;
486 if(node->HasNegLeaf()) { SPHERE_PRIM(node->GetNegPrimitive(), OPC_CONTACT) }
487 else _Collide(node->GetNeg());
490 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
492 * Recursive collision query for no-leaf AABB trees, without primitive tests.
493 * \param node [in] current collision node
495 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
496 void SphereCollider::_CollideNoPrimitiveTest(const AABBNoLeafNode* node)
498 // Perform Sphere-AABB overlap test
499 if(!SphereAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
501 TEST_BOX_IN_SPHERE(node->mAABB.mCenter, node->mAABB.mExtents)
503 if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) }
504 else _CollideNoPrimitiveTest(node->GetPos());
506 if(ContactFound()) return;
508 if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) }
509 else _CollideNoPrimitiveTest(node->GetNeg());
512 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
514 * Recursive collision query for quantized no-leaf AABB trees.
515 * \param node [in] current collision node
517 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
518 void SphereCollider::_Collide(const AABBQuantizedNoLeafNode* node)
520 // Dequantize box
521 const QuantizedAABB& Box = node->mAABB;
522 const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
523 const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
525 // Perform Sphere-AABB overlap test
526 if(!SphereAABBOverlap(Center, Extents)) return;
528 TEST_BOX_IN_SPHERE(Center, Extents)
530 if(node->HasPosLeaf()) { SPHERE_PRIM(node->GetPosPrimitive(), OPC_CONTACT) }
531 else _Collide(node->GetPos());
533 if(ContactFound()) return;
535 if(node->HasNegLeaf()) { SPHERE_PRIM(node->GetNegPrimitive(), OPC_CONTACT) }
536 else _Collide(node->GetNeg());
539 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
541 * Recursive collision query for quantized no-leaf AABB trees, without primitive tests.
542 * \param node [in] current collision node
544 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
545 void SphereCollider::_CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node)
547 // Dequantize box
548 const QuantizedAABB& Box = node->mAABB;
549 const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
550 const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
552 // Perform Sphere-AABB overlap test
553 if(!SphereAABBOverlap(Center, Extents)) return;
555 TEST_BOX_IN_SPHERE(Center, Extents)
557 if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) }
558 else _CollideNoPrimitiveTest(node->GetPos());
560 if(ContactFound()) return;
562 if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) }
563 else _CollideNoPrimitiveTest(node->GetNeg());
566 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
568 * Recursive collision query for vanilla AABB trees.
569 * \param node [in] current collision node
571 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
572 void SphereCollider::_Collide(const AABBTreeNode* node)
574 // Perform Sphere-AABB overlap test
575 Point Center, Extents;
576 node->GetAABB()->GetCenter(Center);
577 node->GetAABB()->GetExtents(Extents);
578 if(!SphereAABBOverlap(Center, Extents)) return;
580 if(node->IsLeaf() || SphereContainsBox(Center, Extents))
582 mFlags |= OPC_CONTACT;
583 mTouchedPrimitives->Add(node->GetPrimitives(), node->GetNbPrimitives());
585 else
587 _Collide(node->GetPos());
588 _Collide(node->GetNeg());
598 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
600 * Constructor.
602 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
603 HybridSphereCollider::HybridSphereCollider()
607 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
609 * Destructor.
611 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
612 HybridSphereCollider::~HybridSphereCollider()
616 bool HybridSphereCollider::Collide(SphereCache& cache, const Sphere& sphere, const HybridModel& model, const Matrix4x4* worlds, const Matrix4x4* worldm)
618 // We don't want primitive tests here!
619 mFlags |= OPC_NO_PRIMITIVE_TESTS;
621 // Checkings
622 if(!Setup(&model)) return false;
624 // Init collision query
625 if(InitQuery(cache, sphere, worlds, worldm)) return true;
627 // Special case for 1-leaf trees
628 if(mCurrentModel && mCurrentModel->HasSingleNode())
630 // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles
631 udword Nb = mIMesh->GetNbTriangles();
633 // Loop through all triangles
634 for(udword i=0;i<Nb;i++)
636 SPHERE_PRIM(i, OPC_CONTACT)
638 return true;
641 // Override destination array since we're only going to get leaf boxes here
642 mTouchedBoxes.Reset();
643 mTouchedPrimitives = &mTouchedBoxes;
645 // Now, do the actual query against leaf boxes
646 if(!model.HasLeafNodes())
648 if(model.IsQuantized())
650 const AABBQuantizedNoLeafTree* Tree = static_cast<const AABBQuantizedNoLeafTree *>(model.GetTree());
652 // Setup dequantization coeffs
653 mCenterCoeff = Tree->mCenterCoeff;
654 mExtentsCoeff = Tree->mExtentsCoeff;
656 // Perform collision query - we don't want primitive tests here!
657 _CollideNoPrimitiveTest(Tree->GetNodes());
659 else
661 const AABBNoLeafTree* Tree = static_cast<const AABBNoLeafTree *>(model.GetTree());
663 // Perform collision query - we don't want primitive tests here!
664 _CollideNoPrimitiveTest(Tree->GetNodes());
667 else
669 if(model.IsQuantized())
671 const AABBQuantizedTree* Tree = static_cast<const AABBQuantizedTree *>(model.GetTree());
673 // Setup dequantization coeffs
674 mCenterCoeff = Tree->mCenterCoeff;
675 mExtentsCoeff = Tree->mExtentsCoeff;
677 // Perform collision query - we don't want primitive tests here!
678 _CollideNoPrimitiveTest(Tree->GetNodes());
680 else
682 const AABBCollisionTree* Tree = static_cast<const AABBCollisionTree *>(model.GetTree());
684 // Perform collision query - we don't want primitive tests here!
685 _CollideNoPrimitiveTest(Tree->GetNodes());
689 // We only have a list of boxes so far
690 if(GetContactStatus())
692 // Reset contact status, since it currently only reflects collisions with leaf boxes
693 Collider::InitQuery();
695 // Change dest container so that we can use built-in overlap tests and get collided primitives
696 cache.TouchedPrimitives.Reset();
697 mTouchedPrimitives = &cache.TouchedPrimitives;
699 // Read touched leaf boxes
700 udword Nb = mTouchedBoxes.GetNbEntries();
701 const udword* Touched = mTouchedBoxes.GetEntries();
703 const LeafTriangles* LT = model.GetLeafTriangles();
704 const udword* Indices = model.GetIndices();
706 // Loop through touched leaves
707 while(Nb--)
709 const LeafTriangles& CurrentLeaf = LT[*Touched++];
711 // Each leaf box has a set of triangles
712 udword NbTris = CurrentLeaf.GetNbTriangles();
713 if(Indices)
715 const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()];
717 // Loop through triangles and test each of them
718 while(NbTris--)
720 udword TriangleIndex = *T++;
721 SPHERE_PRIM(TriangleIndex, OPC_CONTACT)
724 else
726 udword BaseIndex = CurrentLeaf.GetTriangleIndex();
728 // Loop through triangles and test each of them
729 while(NbTris--)
731 udword TriangleIndex = BaseIndex++;
732 SPHERE_PRIM(TriangleIndex, OPC_CONTACT)
738 return true;