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_SphereCollider.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 
31 
34 // Precompiled Header
35 #include "Stdafx.h"
36 
37 
38 using namespace Opcode;
39 
40 #include "OPC_SphereAABBOverlap.h"
41 #include "OPC_SphereTriOverlap.h"
42 
43 #define SET_CONTACT(prim_index, flag) \
44  /* Set contact status */ \
45  mFlags |= flag; \
46  mTouchedPrimitives->Add(prim_index);
47 
49 #define SPHERE_PRIM(prim_index, flag) \
50  /* Request vertices from the app */ \
51  VertexPointers VP; mIMesh->GetTriangle(VP, prim_index); \
52  \
53  /* Perform sphere-tri overlap test */ \
54  if(SphereTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \
55  { \
56  SET_CONTACT(prim_index, flag) \
57  }
58 
60 
65 {
66  mCenter.Zero();
67  mRadius2 = 0.0f;
68 }
69 
71 
76 {
77 }
78 
80 
94 bool SphereCollider::Collide(SphereCache& cache, const Sphere& sphere, const Model& model, const Matrix4x4* worlds, const Matrix4x4* worldm)
96 {
97  // Checkings
98  if(!Setup(&model)) return false;
99 
100  // Init collision query
101  if(InitQuery(cache, sphere, worlds, worldm)) return true;
102 
103  if(!model.HasLeafNodes())
104  {
105  if(model.IsQuantized())
106  {
107  const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree();
108 
109  // Setup dequantization coeffs
110  mCenterCoeff = Tree->mCenterCoeff;
112 
113  // Perform collision query
114  if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
115  else _Collide(Tree->GetNodes());
116  }
117  else
118  {
119  const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
120 
121  // Perform collision query
122  if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
123  else _Collide(Tree->GetNodes());
124  }
125  }
126  else
127  {
128  if(model.IsQuantized())
129  {
130  const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
131 
132  // Setup dequantization coeffs
133  mCenterCoeff = Tree->mCenterCoeff;
135 
136  // Perform collision query
137  if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
138  else _Collide(Tree->GetNodes());
139  }
140  else
141  {
142  const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
143 
144  // Perform collision query
145  if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
146  else _Collide(Tree->GetNodes());
147  }
148  }
149  return true;
150 }
151 
153 
166 bool SphereCollider::InitQuery(SphereCache& cache, const Sphere& sphere, const Matrix4x4* worlds, const Matrix4x4* worldm)
168 {
169  // 1) Call the base method
171 
172  // 2) Compute sphere in model space:
173  // - Precompute R^2
174  mRadius2 = sphere.mRadius * sphere.mRadius;
175  // - Compute center position
176  mCenter = sphere.mCenter;
177  // -> to world space
178  if(worlds) mCenter *= *worlds;
179  // -> to model space
180  if(worldm)
181  {
182  // Invert model matrix
183  Matrix4x4 InvWorldM;
184  InvertPRMatrix(InvWorldM, *worldm);
185 
186  mCenter *= InvWorldM;
187  }
188 
189  // 3) Setup destination pointer
191 
192  // 4) Special case: 1-triangle meshes [Opcode 1.3]
194  {
195  if(!SkipPrimitiveTests())
196  {
197  // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0.
199 
200  // Perform overlap test between the unique triangle and the sphere (and set contact status if needed)
202 
203  // Return immediately regardless of status
204  return TRUE;
205  }
206  }
207 
208  // 5) Check temporal coherence :
210  {
211  // Here we use temporal coherence
212  // => check results from previous frame before performing the collision query
213  if(FirstContactEnabled())
214  {
215  // We're only interested in the first contact found => test the unique previously touched face
217  {
218  // Get index of previously touched face = the first entry in the array
219  udword PreviouslyTouchedFace = mTouchedPrimitives->GetEntry(0);
220 
221  // Then reset the array:
222  // - if the overlap test below is successful, the index we'll get added back anyway
223  // - if it isn't, then the array should be reset anyway for the normal query
225 
226  // Perform overlap test between the cached triangle and the sphere (and set contact status if needed)
227  SPHERE_PRIM(PreviouslyTouchedFace, OPC_TEMPORAL_CONTACT)
228 
229  // Return immediately if possible
230  if(GetContactStatus()) return TRUE;
231  }
232  // else no face has been touched during previous query
233  // => we'll have to perform a normal query
234  }
235  else
236  {
237  // We're interested in all contacts =>test the new real sphere N(ew) against the previous fat sphere P(revious):
238  float r = sqrtf(cache.FatRadius2) - sphere.mRadius;
239  if(IsCacheValid(cache) && cache.Center.SquareDistance(mCenter) < r*r)
240  {
241  // - if N is included in P, return previous list
242  // => we simply leave the list (mTouchedFaces) unchanged
243 
244  // Set contact status if needed
246 
247  // In any case we don't need to do a query
248  return TRUE;
249  }
250  else
251  {
252  // - else do the query using a fat N
253 
254  // Reset cache since we'll about to perform a real query
256 
257  // Make a fat sphere so that coherence will work for subsequent frames
258  mRadius2 *= cache.FatCoeff;
259 // mRadius2 = (sphere.mRadius * cache.FatCoeff)*(sphere.mRadius * cache.FatCoeff);
260 
261  // Update cache with query data (signature for cached faces)
262  cache.Center = mCenter;
263  cache.FatRadius2 = mRadius2;
264  }
265  }
266  }
267  else
268  {
269  // Here we don't use temporal coherence => do a normal query
271  }
272 
273  return FALSE;
274 }
275 
277 
284 bool SphereCollider::Collide(SphereCache& cache, const Sphere& sphere, const AABBTree* tree)
286 {
287  // This is typically called for a scene tree, full of -AABBs-, not full of triangles.
288  // So we don't really have "primitives" to deal with. Hence it doesn't work with
289  // "FirstContact" + "TemporalCoherence".
291 
292  // Checkings
293  if(!tree) return false;
294 
295  // Init collision query
296  if(InitQuery(cache, sphere)) return true;
297 
298  // Perform collision query
299  _Collide(tree);
300 
301  return true;
302 }
303 
305 
311 inline_ bool SphereCollider::SphereContainsBox(const Point& bc, const Point& be)
313 {
314  // I assume if all 8 box vertices are inside the sphere, so does the whole box.
315  // Sounds ok but maybe there's a better way?
316  Point p;
317  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;
318  p.x=bc.x-be.x; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE;
319  p.x=bc.x+be.x; p.y=bc.y-be.y; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE;
320  p.x=bc.x-be.x; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE;
321  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;
322  p.x=bc.x-be.x; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE;
323  p.x=bc.x+be.x; p.y=bc.y-be.y; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE;
324  p.x=bc.x-be.x; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE;
325 
326  return TRUE;
327 }
328 
329 #define TEST_BOX_IN_SPHERE(center, extents) \
330  if(SphereContainsBox(center, extents)) \
331  { \
332  /* Set contact status */ \
333  mFlags |= OPC_CONTACT; \
334  _Dump(node); \
335  return; \
336  }
337 
339 
345 {
346  // Perform Sphere-AABB overlap test
347  if(!SphereAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
348 
349  TEST_BOX_IN_SPHERE(node->mAABB.mCenter, node->mAABB.mExtents)
350 
351  if(node->IsLeaf())
352  {
353  SPHERE_PRIM(node->GetPrimitive(), OPC_CONTACT)
354  }
355  else
356  {
357  _Collide(node->GetPos());
358 
359  if(ContactFound()) return;
360 
361  _Collide(node->GetNeg());
362  }
363 }
364 
366 
372 {
373  // Perform Sphere-AABB overlap test
374  if(!SphereAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
375 
376  TEST_BOX_IN_SPHERE(node->mAABB.mCenter, node->mAABB.mExtents)
377 
378  if(node->IsLeaf())
379  {
380  SET_CONTACT(node->GetPrimitive(), OPC_CONTACT)
381  }
382  else
383  {
384  _CollideNoPrimitiveTest(node->GetPos());
385 
386  if(ContactFound()) return;
387 
388  _CollideNoPrimitiveTest(node->GetNeg());
389  }
390 }
391 
393 
399 {
400  // Dequantize box
401  const QuantizedAABB& Box = node->mAABB;
402  const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
403  const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
404 
405  // Perform Sphere-AABB overlap test
406  if(!SphereAABBOverlap(Center, Extents)) return;
407 
408  TEST_BOX_IN_SPHERE(Center, Extents)
409 
410  if(node->IsLeaf())
411  {
412  SPHERE_PRIM(node->GetPrimitive(), OPC_CONTACT)
413  }
414  else
415  {
416  _Collide(node->GetPos());
417 
418  if(ContactFound()) return;
419 
420  _Collide(node->GetNeg());
421  }
422 }
423 
425 
431 {
432  // Dequantize box
433  const QuantizedAABB& Box = node->mAABB;
434  const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
435  const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
436 
437  // Perform Sphere-AABB overlap test
438  if(!SphereAABBOverlap(Center, Extents)) return;
439 
440  TEST_BOX_IN_SPHERE(Center, Extents)
441 
442  if(node->IsLeaf())
443  {
444  SET_CONTACT(node->GetPrimitive(), OPC_CONTACT)
445  }
446  else
447  {
448  _CollideNoPrimitiveTest(node->GetPos());
449 
450  if(ContactFound()) return;
451 
452  _CollideNoPrimitiveTest(node->GetNeg());
453  }
454 }
455 
457 
463 {
464  // Perform Sphere-AABB overlap test
465  if(!SphereAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
466 
467  TEST_BOX_IN_SPHERE(node->mAABB.mCenter, node->mAABB.mExtents)
468 
469  if(node->HasPosLeaf()) { SPHERE_PRIM(node->GetPosPrimitive(), OPC_CONTACT) }
470  else _Collide(node->GetPos());
471 
472  if(ContactFound()) return;
473 
474  if(node->HasNegLeaf()) { SPHERE_PRIM(node->GetNegPrimitive(), OPC_CONTACT) }
475  else _Collide(node->GetNeg());
476 }
477 
479 
485 {
486  // Perform Sphere-AABB overlap test
487  if(!SphereAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
488 
489  TEST_BOX_IN_SPHERE(node->mAABB.mCenter, node->mAABB.mExtents)
490 
491  if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) }
492  else _CollideNoPrimitiveTest(node->GetPos());
493 
494  if(ContactFound()) return;
495 
496  if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) }
497  else _CollideNoPrimitiveTest(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 Sphere-AABB overlap test
514  if(!SphereAABBOverlap(Center, Extents)) return;
515 
516  TEST_BOX_IN_SPHERE(Center, Extents)
517 
518  if(node->HasPosLeaf()) { SPHERE_PRIM(node->GetPosPrimitive(), OPC_CONTACT) }
519  else _Collide(node->GetPos());
520 
521  if(ContactFound()) return;
522 
523  if(node->HasNegLeaf()) { SPHERE_PRIM(node->GetNegPrimitive(), OPC_CONTACT) }
524  else _Collide(node->GetNeg());
525 }
526 
528 
534 {
535  // Dequantize box
536  const QuantizedAABB& Box = node->mAABB;
537  const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
538  const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
539 
540  // Perform Sphere-AABB overlap test
541  if(!SphereAABBOverlap(Center, Extents)) return;
542 
543  TEST_BOX_IN_SPHERE(Center, Extents)
544 
545  if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) }
546  else _CollideNoPrimitiveTest(node->GetPos());
547 
548  if(ContactFound()) return;
549 
550  if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) }
551  else _CollideNoPrimitiveTest(node->GetNeg());
552 }
553 
555 
559 void SphereCollider::_Collide(const AABBTreeNode* node)
561 {
562  // Perform Sphere-AABB overlap test
563  Point Center, Extents;
564  node->GetAABB()->GetCenter(Center);
565  node->GetAABB()->GetExtents(Extents);
566  if(!SphereAABBOverlap(Center, Extents)) return;
567 
568  if(node->IsLeaf() || SphereContainsBox(Center, Extents))
569  {
570  mFlags |= OPC_CONTACT;
572  }
573  else
574  {
575  _Collide(node->GetPos());
576  _Collide(node->GetNeg());
577  }
578 }
579 
580 
581 
582 
583 
584 
585 
587 
592 {
593 }
594 
596 
601 {
602 }
603 
604 bool HybridSphereCollider::Collide(SphereCache& cache, const Sphere& sphere, const HybridModel& model, const Matrix4x4* worlds, const Matrix4x4* worldm)
605 {
606  // We don't want primitive tests here!
608 
609  // Checkings
610  if(!Setup(&model)) return false;
611 
612  // Init collision query
613  if(InitQuery(cache, sphere, worlds, worldm)) return true;
614 
615  // Special case for 1-leaf trees
617  {
618  // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles
619  udword Nb = mIMesh->GetNbTriangles();
620 
621  // Loop through all triangles
622  for(udword i=0;i<Nb;i++)
623  {
625  }
626  return true;
627  }
628 
629  // Override destination array since we're only going to get leaf boxes here
630  mTouchedBoxes.Reset();
631  mTouchedPrimitives = &mTouchedBoxes;
632 
633  // Now, do the actual query against leaf boxes
634  if(!model.HasLeafNodes())
635  {
636  if(model.IsQuantized())
637  {
638  const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree();
639 
640  // Setup dequantization coeffs
641  mCenterCoeff = Tree->mCenterCoeff;
643 
644  // Perform collision query - we don't want primitive tests here!
645  _CollideNoPrimitiveTest(Tree->GetNodes());
646  }
647  else
648  {
649  const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
650 
651  // Perform collision query - we don't want primitive tests here!
652  _CollideNoPrimitiveTest(Tree->GetNodes());
653  }
654  }
655  else
656  {
657  if(model.IsQuantized())
658  {
659  const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
660 
661  // Setup dequantization coeffs
662  mCenterCoeff = Tree->mCenterCoeff;
664 
665  // Perform collision query - we don't want primitive tests here!
666  _CollideNoPrimitiveTest(Tree->GetNodes());
667  }
668  else
669  {
670  const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
671 
672  // Perform collision query - we don't want primitive tests here!
673  _CollideNoPrimitiveTest(Tree->GetNodes());
674  }
675  }
676 
677  // We only have a list of boxes so far
678  if(GetContactStatus())
679  {
680  // Reset contact status, since it currently only reflects collisions with leaf boxes
682 
683  // Change dest container so that we can use built-in overlap tests and get collided primitives
684  cache.TouchedPrimitives.Reset();
686 
687  // Read touched leaf boxes
688  udword Nb = mTouchedBoxes.GetNbEntries();
689  const udword* Touched = mTouchedBoxes.GetEntries();
690 
691  const LeafTriangles* LT = model.GetLeafTriangles();
692  const udword* Indices = model.GetIndices();
693 
694  // Loop through touched leaves
695  while(Nb--)
696  {
697  const LeafTriangles& CurrentLeaf = LT[*Touched++];
698 
699  // Each leaf box has a set of triangles
700  udword NbTris = CurrentLeaf.GetNbTriangles();
701  if(Indices)
702  {
703  const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()];
704 
705  // Loop through triangles and test each of them
706  while(NbTris--)
707  {
708  udword TriangleIndex = *T++;
709  SPHERE_PRIM(TriangleIndex, OPC_CONTACT)
710  }
711  }
712  else
713  {
714  udword BaseIndex = CurrentLeaf.GetTriangleIndex();
715 
716  // Loop through triangles and test each of them
717  while(NbTris--)
718  {
719  udword TriangleIndex = BaseIndex++;
720  SPHERE_PRIM(TriangleIndex, OPC_CONTACT)
721  }
722  }
723  }
724  }
725 
726  return true;
727 }
728