Vegastrike 0.5.1 rc1  1.0
Original sources for Vegastrike Evolved
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
OPC_AABBCollider.cpp
Go to the documentation of this file.
1 /*
3  * OPCODE - Optimized Collision Detection
4  * Copyright (C) 2001 Pierre Terdiman
5  * Homepage: http://www.codercorner.com/Opcode.htm
6  */
8 
10 
16 
19 
27 
30 // Precompiled Header
31 #include "Stdafx.h"
32 
33 
34 using namespace Opcode;
35 
36 #include "OPC_BoxBoxOverlap.h"
37 #include "OPC_TriBoxOverlap.h"
38 
39 #define SET_CONTACT(prim_index, flag) \
40  /* Set contact status */ \
41  mFlags |= flag; \
42  mTouchedPrimitives->Add(prim_index);
43 
45 #define AABB_PRIM(prim_index, flag) \
46  /* Request vertices from the app */ \
47  VertexPointers VP; mIMesh->GetTriangle(VP, prim_index);\
48  mLeafVerts[0] = *VP.Vertex[0]; \
49  mLeafVerts[1] = *VP.Vertex[1]; \
50  mLeafVerts[2] = *VP.Vertex[2]; \
51  /* Perform triangle-box overlap test */ \
52  if(TriBoxOverlap()) \
53  { \
54  SET_CONTACT(prim_index, flag) \
55  }
56 
58 
63 {
64 }
65 
67 
72 {
73 }
74 
76 
88 bool AABBCollider::Collide(AABBCache& cache, const CollisionAABB& box, const Model& model)
90 {
91  // Checkings
92  if(!Setup(&model)) return false;
93 
94  // Init collision query
95  if(InitQuery(cache, box)) return true;
96 
97  if(!model.HasLeafNodes())
98  {
99  if(model.IsQuantized())
100  {
101  const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree();
102 
103  // Setup dequantization coeffs
104  mCenterCoeff = Tree->mCenterCoeff;
106 
107  // Perform collision query
108  if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
109  else _Collide(Tree->GetNodes());
110  }
111  else
112  {
113  const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
114 
115  // Perform collision query
116  if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
117  else _Collide(Tree->GetNodes());
118  }
119  }
120  else
121  {
122  if(model.IsQuantized())
123  {
124  const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
125 
126  // Setup dequantization coeffs
127  mCenterCoeff = Tree->mCenterCoeff;
129 
130  // Perform collision query
131  if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
132  else _Collide(Tree->GetNodes());
133  }
134  else
135  {
136  const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
137 
138  // Perform collision query
139  if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
140  else _Collide(Tree->GetNodes());
141  }
142  }
143  return true;
144 }
145 
147 
156 bool AABBCollider::InitQuery(AABBCache& cache, const CollisionAABB& box)
158 {
159  // 1) Call the base method
161 
162  // 2) Keep track of the query box
163  mBox = box;
164 
165  // 3) Setup destination pointer
167 
168  // 4) Special case: 1-triangle meshes [Opcode 1.3]
170  {
171  if(!SkipPrimitiveTests())
172  {
173  // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0.
175 
176  // Perform overlap test between the unique triangle and the box (and set contact status if needed)
178 
179  // Return immediately regardless of status
180  return TRUE;
181  }
182  }
183 
184  // 5) Check temporal coherence :
186  {
187  // Here we use temporal coherence
188  // => check results from previous frame before performing the collision query
189  if(FirstContactEnabled())
190  {
191  // We're only interested in the first contact found => test the unique previously touched face
193  {
194  // Get index of previously touched face = the first entry in the array
195  udword PreviouslyTouchedFace = mTouchedPrimitives->GetEntry(0);
196 
197  // Then reset the array:
198  // - if the overlap test below is successful, the index we'll get added back anyway
199  // - if it isn't, then the array should be reset anyway for the normal query
201 
202  // Perform overlap test between the cached triangle and the box (and set contact status if needed)
203  AABB_PRIM(PreviouslyTouchedFace, OPC_TEMPORAL_CONTACT)
204 
205  // Return immediately if possible
206  if(GetContactStatus()) return TRUE;
207  }
208  // else no face has been touched during previous query
209  // => we'll have to perform a normal query
210  }
211  else
212  {
213  // We're interested in all contacts =>test the new real box N(ew) against the previous fat box P(revious):
214  if(IsCacheValid(cache) && mBox.IsInside(cache.FatBox))
215  {
216  // - if N is included in P, return previous list
217  // => we simply leave the list (mTouchedFaces) unchanged
218 
219  // Set contact status if needed
221 
222  // In any case we don't need to do a query
223  return TRUE;
224  }
225  else
226  {
227  // - else do the query using a fat N
228 
229  // Reset cache since we'll about to perform a real query
231 
232  // Make a fat box so that coherence will work for subsequent frames
233  mBox.mExtents *= cache.FatCoeff;
234 
235  // Update cache with query data (signature for cached faces)
236  cache.FatBox = mBox;
237  }
238  }
239  }
240  else
241  {
242  // Here we don't use temporal coherence => do a normal query
244  }
245 
246  // 5) Precompute min & max bounds if needed
247  mMin = box.mCenter - box.mExtents;
248  mMax = box.mCenter + box.mExtents;
249 
250  return FALSE;
251 }
252 
254 
261 bool AABBCollider::Collide(AABBCache& cache, const CollisionAABB& box, const AABBTree* tree)
263 {
264  // This is typically called for a scene tree, full of -AABBs-, not full of triangles.
265  // So we don't really have "primitives" to deal with. Hence it doesn't work with
266  // "FirstContact" + "TemporalCoherence".
268 
269  // Checkings
270  if(!tree) return false;
271 
272  // Init collision query
273  if(InitQuery(cache, box)) return true;
274 
275  // Perform collision query
276  _Collide(tree);
277 
278  return true;
279 }
280 
282 
288 inline_ bool AABBCollider::AABBContainsBox(const Point& bc, const Point& be)
290 {
291  if(mMin.x > bc.x - be.x) return FALSE;
292  if(mMin.y > bc.y - be.y) return FALSE;
293  if(mMin.z > bc.z - be.z) return FALSE;
294 
295  if(mMax.x < bc.x + be.x) return FALSE;
296  if(mMax.y < bc.y + be.y) return FALSE;
297  if(mMax.z < bc.z + be.z) return FALSE;
298 
299  return TRUE;
300 }
301 
302 #define TEST_BOX_IN_AABB(center, extents) \
303  if(AABBContainsBox(center, extents)) \
304  { \
305  /* Set contact status */ \
306  mFlags |= OPC_CONTACT; \
307  _Dump(node); \
308  return; \
309  }
310 
312 
318 {
319  // Perform AABB-AABB overlap test
320  if(!AABBAABBOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
321 
322  TEST_BOX_IN_AABB(node->mAABB.mCenter, node->mAABB.mExtents)
323 
324  if(node->IsLeaf())
325  {
326  AABB_PRIM(node->GetPrimitive(), OPC_CONTACT)
327  }
328  else
329  {
330  _Collide(node->GetPos());
331 
332  if(ContactFound()) return;
333 
334  _Collide(node->GetNeg());
335  }
336 }
337 
339 
345 {
346  // Perform AABB-AABB overlap test
347  if(!AABBAABBOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
348 
349  TEST_BOX_IN_AABB(node->mAABB.mCenter, node->mAABB.mExtents)
350 
351  if(node->IsLeaf())
352  {
353  SET_CONTACT(node->GetPrimitive(), OPC_CONTACT)
354  }
355  else
356  {
357  _CollideNoPrimitiveTest(node->GetPos());
358 
359  if(ContactFound()) return;
360 
361  _CollideNoPrimitiveTest(node->GetNeg());
362  }
363 }
364 
366 
372 {
373  // Dequantize box
374  const QuantizedAABB& Box = node->mAABB;
375  const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
376  const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
377 
378  // Perform AABB-AABB overlap test
379  if(!AABBAABBOverlap(Extents, Center)) return;
380 
381  TEST_BOX_IN_AABB(Center, Extents)
382 
383  if(node->IsLeaf())
384  {
385  AABB_PRIM(node->GetPrimitive(), OPC_CONTACT)
386  }
387  else
388  {
389  _Collide(node->GetPos());
390 
391  if(ContactFound()) return;
392 
393  _Collide(node->GetNeg());
394  }
395 }
396 
398 
404 {
405  // Dequantize box
406  const QuantizedAABB& Box = node->mAABB;
407  const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
408  const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
409 
410  // Perform AABB-AABB overlap test
411  if(!AABBAABBOverlap(Extents, Center)) return;
412 
413  TEST_BOX_IN_AABB(Center, Extents)
414 
415  if(node->IsLeaf())
416  {
417  SET_CONTACT(node->GetPrimitive(), OPC_CONTACT)
418  }
419  else
420  {
421  _CollideNoPrimitiveTest(node->GetPos());
422 
423  if(ContactFound()) return;
424 
425  _CollideNoPrimitiveTest(node->GetNeg());
426  }
427 }
428 
430 
434 void AABBCollider::_Collide(const AABBNoLeafNode* node)
436 {
437  // Perform AABB-AABB overlap test
438  if(!AABBAABBOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
439 
440  TEST_BOX_IN_AABB(node->mAABB.mCenter, node->mAABB.mExtents)
441 
442  if(node->HasPosLeaf()) { AABB_PRIM(node->GetPosPrimitive(), OPC_CONTACT) }
443  else _Collide(node->GetPos());
444 
445  if(ContactFound()) return;
446 
447  if(node->HasNegLeaf()) { AABB_PRIM(node->GetNegPrimitive(), OPC_CONTACT) }
448  else _Collide(node->GetNeg());
449 }
450 
452 
458 {
459  // Perform AABB-AABB overlap test
460  if(!AABBAABBOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
461 
462  TEST_BOX_IN_AABB(node->mAABB.mCenter, node->mAABB.mExtents)
463 
464  if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) }
465  else _CollideNoPrimitiveTest(node->GetPos());
466 
467  if(ContactFound()) return;
468 
469  if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) }
470  else _CollideNoPrimitiveTest(node->GetNeg());
471 }
472 
474 
480 {
481  // Dequantize box
482  const QuantizedAABB& Box = node->mAABB;
483  const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
484  const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
485 
486  // Perform AABB-AABB overlap test
487  if(!AABBAABBOverlap(Extents, Center)) return;
488 
489  TEST_BOX_IN_AABB(Center, Extents)
490 
491  if(node->HasPosLeaf()) { AABB_PRIM(node->GetPosPrimitive(), OPC_CONTACT) }
492  else _Collide(node->GetPos());
493 
494  if(ContactFound()) return;
495 
496  if(node->HasNegLeaf()) { AABB_PRIM(node->GetNegPrimitive(), OPC_CONTACT) }
497  else _Collide(node->GetNeg());
498 }
499 
501 
507 {
508  // Dequantize box
509  const QuantizedAABB& Box = node->mAABB;
510  const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
511  const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
512 
513  // Perform AABB-AABB overlap test
514  if(!AABBAABBOverlap(Extents, Center)) return;
515 
516  TEST_BOX_IN_AABB(Center, Extents)
517 
518  if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) }
519  else _CollideNoPrimitiveTest(node->GetPos());
520 
521  if(ContactFound()) return;
522 
523  if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) }
524  else _CollideNoPrimitiveTest(node->GetNeg());
525 }
526 
528 
532 void AABBCollider::_Collide(const AABBTreeNode* node)
534 {
535  // Perform AABB-AABB overlap test
536  Point Center, Extents;
537  node->GetAABB()->GetCenter(Center);
538  node->GetAABB()->GetExtents(Extents);
539  if(!AABBAABBOverlap(Center, Extents)) return;
540 
541  if(node->IsLeaf() || AABBContainsBox(Center, Extents))
542  {
543  mFlags |= OPC_CONTACT;
545  }
546  else
547  {
548  _Collide(node->GetPos());
549  _Collide(node->GetNeg());
550  }
551 }
552 
553 
554 
555 
557 
562 {
563 }
564 
566 
571 {
572 }
573 
574 bool HybridAABBCollider::Collide(AABBCache& cache, const CollisionAABB& box, const HybridModel& model)
575 {
576  // We don't want primitive tests here!
578 
579  // Checkings
580  if(!Setup(&model)) return false;
581 
582  // Init collision query
583  if(InitQuery(cache, box)) return true;
584 
585  // Special case for 1-leaf trees
587  {
588  // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles
589  udword Nb = mIMesh->GetNbTriangles();
590 
591  // Loop through all triangles
592  for(udword i=0;i<Nb;i++)
593  {
595  }
596  return true;
597  }
598 
599  // Override destination array since we're only going to get leaf boxes here
600  mTouchedBoxes.Reset();
601  mTouchedPrimitives = &mTouchedBoxes;
602 
603  // Now, do the actual query against leaf boxes
604  if(!model.HasLeafNodes())
605  {
606  if(model.IsQuantized())
607  {
608  const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree();
609 
610  // Setup dequantization coeffs
611  mCenterCoeff = Tree->mCenterCoeff;
613 
614  // Perform collision query - we don't want primitive tests here!
615  _CollideNoPrimitiveTest(Tree->GetNodes());
616  }
617  else
618  {
619  const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
620 
621  // Perform collision query - we don't want primitive tests here!
622  _CollideNoPrimitiveTest(Tree->GetNodes());
623  }
624  }
625  else
626  {
627  if(model.IsQuantized())
628  {
629  const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
630 
631  // Setup dequantization coeffs
632  mCenterCoeff = Tree->mCenterCoeff;
634 
635  // Perform collision query - we don't want primitive tests here!
636  _CollideNoPrimitiveTest(Tree->GetNodes());
637  }
638  else
639  {
640  const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
641 
642  // Perform collision query - we don't want primitive tests here!
643  _CollideNoPrimitiveTest(Tree->GetNodes());
644  }
645  }
646 
647  // We only have a list of boxes so far
648  if(GetContactStatus())
649  {
650  // Reset contact status, since it currently only reflects collisions with leaf boxes
652 
653  // Change dest container so that we can use built-in overlap tests and get collided primitives
654  cache.TouchedPrimitives.Reset();
656 
657  // Read touched leaf boxes
658  udword Nb = mTouchedBoxes.GetNbEntries();
659  const udword* Touched = mTouchedBoxes.GetEntries();
660 
661  const LeafTriangles* LT = model.GetLeafTriangles();
662  const udword* Indices = model.GetIndices();
663 
664  // Loop through touched leaves
665  while(Nb--)
666  {
667  const LeafTriangles& CurrentLeaf = LT[*Touched++];
668 
669  // Each leaf box has a set of triangles
670  udword NbTris = CurrentLeaf.GetNbTriangles();
671  if(Indices)
672  {
673  const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()];
674 
675  // Loop through triangles and test each of them
676  while(NbTris--)
677  {
678  udword TriangleIndex = *T++;
679  AABB_PRIM(TriangleIndex, OPC_CONTACT)
680  }
681  }
682  else
683  {
684  udword BaseIndex = CurrentLeaf.GetTriangleIndex();
685 
686  // Loop through triangles and test each of them
687  while(NbTris--)
688  {
689  udword TriangleIndex = BaseIndex++;
690  AABB_PRIM(TriangleIndex, OPC_CONTACT)
691  }
692  }
693  }
694  }
695 
696  return true;
697 }
698