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_OBBCollider.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 OBB_PRIM(prim_index, flag) \
46  /* Request vertices from the app */ \
47  VertexPointers VP; mIMesh->GetTriangle(VP, prim_index); \
48  /* Transform them in a common space */ \
49  TransformPoint(mLeafVerts[0], *VP.Vertex[0], mRModelToBox, mTModelToBox); \
50  TransformPoint(mLeafVerts[1], *VP.Vertex[1], mRModelToBox, mTModelToBox); \
51  TransformPoint(mLeafVerts[2], *VP.Vertex[2], mRModelToBox, mTModelToBox); \
52  /* Perform triangle-box overlap test */ \
53  if(TriBoxOverlap()) \
54  { \
55  SET_CONTACT(prim_index, flag) \
56  }
57 
59 
62 OBBCollider::OBBCollider() : mFullBoxBoxTest(true)
64 {
65 }
66 
68 
73 {
74 }
75 
77 
83 {
84  if(TemporalCoherenceEnabled() && !FirstContactEnabled()) return "Temporal coherence only works with ""First contact"" mode!";
85 
87 }
88 
90 
104 bool OBBCollider::Collide(OBBCache& cache, const OBB& box, const Model& model, const Matrix4x4* worldb, const Matrix4x4* worldm)
106 {
107  // Checkings
108  if(!Setup(&model)) return false;
109 
110  // Init collision query
111  if(InitQuery(cache, box, worldb, worldm)) return true;
112 
113  if(!model.HasLeafNodes())
114  {
115  if(model.IsQuantized())
116  {
117  const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree();
118 
119  // Setup dequantization coeffs
120  mCenterCoeff = Tree->mCenterCoeff;
122 
123  // Perform collision query
124  if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
125  else _Collide(Tree->GetNodes());
126  }
127  else
128  {
129  const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
130 
131  // Perform collision query
132  if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
133  else _Collide(Tree->GetNodes());
134  }
135  }
136  else
137  {
138  if(model.IsQuantized())
139  {
140  const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
141 
142  // Setup dequantization coeffs
143  mCenterCoeff = Tree->mCenterCoeff;
145 
146  // Perform collision query
147  if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
148  else _Collide(Tree->GetNodes());
149  }
150  else
151  {
152  const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
153 
154  // Perform collision query
155  if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
156  else _Collide(Tree->GetNodes());
157  }
158  }
159 
160  return true;
161 }
162 
164 
177 bool OBBCollider::InitQuery(OBBCache& cache, const OBB& box, const Matrix4x4* worldb, const Matrix4x4* worldm)
179 {
180  // 1) Call the base method
182 
183  // 2) Compute obb in world space
184  mBoxExtents = box.mExtents;
185 
186  Matrix4x4 WorldB;
187 
188  if(worldb)
189  {
190  WorldB = Matrix4x4( box.mRot * Matrix3x3(*worldb) );
191  WorldB.SetTrans(box.mCenter * *worldb);
192  }
193  else
194  {
195  WorldB = box.mRot;
196  WorldB.SetTrans(box.mCenter);
197  }
198 
199  // Setup matrices
200  Matrix4x4 InvWorldB;
201  InvertPRMatrix(InvWorldB, WorldB);
202 
203  if(worldm)
204  {
205  Matrix4x4 InvWorldM;
206  InvertPRMatrix(InvWorldM, *worldm);
207 
208  Matrix4x4 WorldBtoM = WorldB * InvWorldM;
209  Matrix4x4 WorldMtoB = *worldm * InvWorldB;
210 
211  mRModelToBox = WorldMtoB; WorldMtoB.GetTrans(mTModelToBox);
212  mRBoxToModel = WorldBtoM; WorldBtoM.GetTrans(mTBoxToModel);
213  }
214  else
215  {
216  mRModelToBox = InvWorldB; InvWorldB.GetTrans(mTModelToBox);
217  mRBoxToModel = WorldB; WorldB.GetTrans(mTBoxToModel);
218  }
219 
220  // 3) Setup destination pointer
222 
223  // 4) Special case: 1-triangle meshes [Opcode 1.3]
225  {
226  if(!SkipPrimitiveTests())
227  {
228  // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0.
230 
231  // Perform overlap test between the unique triangle and the box (and set contact status if needed)
233 
234  // Return immediately regardless of status
235  return TRUE;
236  }
237  }
238 
239  // 5) Check temporal coherence:
241  {
242  // Here we use temporal coherence
243  // => check results from previous frame before performing the collision query
244  if(FirstContactEnabled())
245  {
246  // We're only interested in the first contact found => test the unique previously touched face
248  {
249  // Get index of previously touched face = the first entry in the array
250  udword PreviouslyTouchedFace = mTouchedPrimitives->GetEntry(0);
251 
252  // Then reset the array:
253  // - if the overlap test below is successful, the index we'll get added back anyway
254  // - if it isn't, then the array should be reset anyway for the normal query
256 
257  // Perform overlap test between the cached triangle and the box (and set contact status if needed)
258  OBB_PRIM(PreviouslyTouchedFace, OPC_TEMPORAL_CONTACT)
259 
260  // Return immediately if possible
261  if(GetContactStatus()) return TRUE;
262  }
263  // else no face has been touched during previous query
264  // => we'll have to perform a normal query
265  }
266  else
267  {
268  // ### rewrite this
270 
271  // We're interested in all contacts =>test the new real box N(ew) against the previous fat box P(revious):
272  if(IsCacheValid(cache) && TestBox.IsInside(cache.FatBox))
273  {
274  // - if N is included in P, return previous list
275  // => we simply leave the list (mTouchedFaces) unchanged
276 
277  // Set contact status if needed
279 
280  // In any case we don't need to do a query
281  return TRUE;
282  }
283  else
284  {
285  // - else do the query using a fat N
286 
287  // Reset cache since we'll about to perform a real query
289 
290  // Make a fat box so that coherence will work for subsequent frames
291  TestBox.mExtents *= cache.FatCoeff;
292  mBoxExtents *= cache.FatCoeff;
293 
294  // Update cache with query data (signature for cached faces)
295  cache.FatBox = TestBox;
296  }
297  }
298  }
299  else
300  {
301  // Here we don't use temporal coherence => do a normal query
303  }
304 
305  // Now we can precompute box-box data
306 
307  // Precompute absolute box-to-model rotation matrix
308  for(udword i=0;i<3;i++)
309  {
310  for(udword j=0;j<3;j++)
311  {
312  // Epsilon value prevents floating-point inaccuracies (strategy borrowed from RAPID)
313  mAR.m[i][j] = 1e-6f + fabsf(mRBoxToModel.m[i][j]);
314  }
315  }
316 
317  // Precompute bounds for box-in-box test
320 
321  // Precompute box-box data - Courtesy of Erwin de Vries
322  mBBx1 = mBoxExtents.x*mAR.m[0][0] + mBoxExtents.y*mAR.m[1][0] + mBoxExtents.z*mAR.m[2][0];
323  mBBy1 = mBoxExtents.x*mAR.m[0][1] + mBoxExtents.y*mAR.m[1][1] + mBoxExtents.z*mAR.m[2][1];
324  mBBz1 = mBoxExtents.x*mAR.m[0][2] + mBoxExtents.y*mAR.m[1][2] + mBoxExtents.z*mAR.m[2][2];
325 
326  mBB_1 = mBoxExtents.y*mAR.m[2][0] + mBoxExtents.z*mAR.m[1][0];
327  mBB_2 = mBoxExtents.x*mAR.m[2][0] + mBoxExtents.z*mAR.m[0][0];
328  mBB_3 = mBoxExtents.x*mAR.m[1][0] + mBoxExtents.y*mAR.m[0][0];
329  mBB_4 = mBoxExtents.y*mAR.m[2][1] + mBoxExtents.z*mAR.m[1][1];
330  mBB_5 = mBoxExtents.x*mAR.m[2][1] + mBoxExtents.z*mAR.m[0][1];
331  mBB_6 = mBoxExtents.x*mAR.m[1][1] + mBoxExtents.y*mAR.m[0][1];
332  mBB_7 = mBoxExtents.y*mAR.m[2][2] + mBoxExtents.z*mAR.m[1][2];
333  mBB_8 = mBoxExtents.x*mAR.m[2][2] + mBoxExtents.z*mAR.m[0][2];
334  mBB_9 = mBoxExtents.x*mAR.m[1][2] + mBoxExtents.y*mAR.m[0][2];
335 
336  return FALSE;
337 }
338 
340 
346 inline_ bool OBBCollider::OBBContainsBox(const Point& bc, const Point& be)
348 {
349  // I assume if all 8 box vertices are inside the OBB, so does the whole box.
350  // Sounds ok but maybe there's a better way?
351 /*
352 #define TEST_PT(a,b,c) \
353  p.x=a; p.y=b; p.z=c; p+=bc; \
354  f = p.x * mRModelToBox.m[0][0] + p.y * mRModelToBox.m[1][0] + p.z * mRModelToBox.m[2][0]; if(f>mB0.x || f<mB1.x) return FALSE;\
355  f = p.x * mRModelToBox.m[0][1] + p.y * mRModelToBox.m[1][1] + p.z * mRModelToBox.m[2][1]; if(f>mB0.y || f<mB1.y) return FALSE;\
356  f = p.x * mRModelToBox.m[0][2] + p.y * mRModelToBox.m[1][2] + p.z * mRModelToBox.m[2][2]; if(f>mB0.z || f<mB1.z) return FALSE;
357 
358  Point p;
359  float f;
360 
361  TEST_PT(be.x, be.y, be.z)
362  TEST_PT(-be.x, be.y, be.z)
363  TEST_PT(be.x, -be.y, be.z)
364  TEST_PT(-be.x, -be.y, be.z)
365  TEST_PT(be.x, be.y, -be.z)
366  TEST_PT(-be.x, be.y, -be.z)
367  TEST_PT(be.x, -be.y, -be.z)
368  TEST_PT(-be.x, -be.y, -be.z)
369 
370  return TRUE;
371 */
372 
373  // Yes there is:
374  // - compute model-box's AABB in OBB space
375  // - test AABB-in-AABB
376  float NCx = bc.x * mRModelToBox.m[0][0] + bc.y * mRModelToBox.m[1][0] + bc.z * mRModelToBox.m[2][0];
377  float NEx = fabsf(mRModelToBox.m[0][0] * be.x) + fabsf(mRModelToBox.m[1][0] * be.y) + fabsf(mRModelToBox.m[2][0] * be.z);
378 
379  if(mB0.x < NCx+NEx) return FALSE;
380  if(mB1.x > NCx-NEx) return FALSE;
381 
382  float NCy = bc.x * mRModelToBox.m[0][1] + bc.y * mRModelToBox.m[1][1] + bc.z * mRModelToBox.m[2][1];
383  float NEy = fabsf(mRModelToBox.m[0][1] * be.x) + fabsf(mRModelToBox.m[1][1] * be.y) + fabsf(mRModelToBox.m[2][1] * be.z);
384 
385  if(mB0.y < NCy+NEy) return FALSE;
386  if(mB1.y > NCy-NEy) return FALSE;
387 
388  float NCz = bc.x * mRModelToBox.m[0][2] + bc.y * mRModelToBox.m[1][2] + bc.z * mRModelToBox.m[2][2];
389  float NEz = fabsf(mRModelToBox.m[0][2] * be.x) + fabsf(mRModelToBox.m[1][2] * be.y) + fabsf(mRModelToBox.m[2][2] * be.z);
390 
391  if(mB0.z < NCz+NEz) return FALSE;
392  if(mB1.z > NCz-NEz) return FALSE;
393 
394  return TRUE;
395 }
396 
397 #define TEST_BOX_IN_OBB(center, extents) \
398  if(OBBContainsBox(center, extents)) \
399  { \
400  /* Set contact status */ \
401  mFlags |= OPC_CONTACT; \
402  _Dump(node); \
403  return; \
404  }
405 
407 
413 {
414  // Perform OBB-AABB overlap test
415  if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
416 
417  TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents)
418 
419  if(node->IsLeaf())
420  {
421  OBB_PRIM(node->GetPrimitive(), OPC_CONTACT)
422  }
423  else
424  {
425  _Collide(node->GetPos());
426 
427  if(ContactFound()) return;
428 
429  _Collide(node->GetNeg());
430  }
431 }
432 
434 
440 {
441  // Perform OBB-AABB overlap test
442  if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
443 
444  TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents)
445 
446  if(node->IsLeaf())
447  {
448  SET_CONTACT(node->GetPrimitive(), OPC_CONTACT)
449  }
450  else
451  {
452  _CollideNoPrimitiveTest(node->GetPos());
453 
454  if(ContactFound()) return;
455 
456  _CollideNoPrimitiveTest(node->GetNeg());
457  }
458 }
459 
461 
467 {
468  // Dequantize box
469  const QuantizedAABB& Box = node->mAABB;
470  const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
471  const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
472 
473  // Perform OBB-AABB overlap test
474  if(!BoxBoxOverlap(Extents, Center)) return;
475 
476  TEST_BOX_IN_OBB(Center, Extents)
477 
478  if(node->IsLeaf())
479  {
480  OBB_PRIM(node->GetPrimitive(), OPC_CONTACT)
481  }
482  else
483  {
484  _Collide(node->GetPos());
485 
486  if(ContactFound()) return;
487 
488  _Collide(node->GetNeg());
489  }
490 }
491 
493 
499 {
500  // Dequantize box
501  const QuantizedAABB& Box = node->mAABB;
502  const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
503  const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
504 
505  // Perform OBB-AABB overlap test
506  if(!BoxBoxOverlap(Extents, Center)) return;
507 
508  TEST_BOX_IN_OBB(Center, Extents)
509 
510  if(node->IsLeaf())
511  {
512  SET_CONTACT(node->GetPrimitive(), OPC_CONTACT)
513  }
514  else
515  {
516  _CollideNoPrimitiveTest(node->GetPos());
517 
518  if(ContactFound()) return;
519 
520  _CollideNoPrimitiveTest(node->GetNeg());
521  }
522 }
523 
525 
529 void OBBCollider::_Collide(const AABBNoLeafNode* node)
531 {
532  // Perform OBB-AABB overlap test
533  if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
534 
535  TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents)
536 
537  if(node->HasPosLeaf()) { OBB_PRIM(node->GetPosPrimitive(), OPC_CONTACT) }
538  else _Collide(node->GetPos());
539 
540  if(ContactFound()) return;
541 
542  if(node->HasNegLeaf()) { OBB_PRIM(node->GetNegPrimitive(), OPC_CONTACT) }
543  else _Collide(node->GetNeg());
544 }
545 
547 
553 {
554  // Perform OBB-AABB overlap test
555  if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
556 
557  TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents)
558 
559  if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) }
560  else _CollideNoPrimitiveTest(node->GetPos());
561 
562  if(ContactFound()) return;
563 
564  if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) }
565  else _CollideNoPrimitiveTest(node->GetNeg());
566 }
567 
569 
575 {
576  // Dequantize box
577  const QuantizedAABB& Box = node->mAABB;
578  const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
579  const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
580 
581  // Perform OBB-AABB overlap test
582  if(!BoxBoxOverlap(Extents, Center)) return;
583 
584  TEST_BOX_IN_OBB(Center, Extents)
585 
586  if(node->HasPosLeaf()) { OBB_PRIM(node->GetPosPrimitive(), OPC_CONTACT) }
587  else _Collide(node->GetPos());
588 
589  if(ContactFound()) return;
590 
591  if(node->HasNegLeaf()) { OBB_PRIM(node->GetNegPrimitive(), OPC_CONTACT) }
592  else _Collide(node->GetNeg());
593 }
594 
596 
602 {
603  // Dequantize box
604  const QuantizedAABB& Box = node->mAABB;
605  const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
606  const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
607 
608  // Perform OBB-AABB overlap test
609  if(!BoxBoxOverlap(Extents, Center)) return;
610 
611  TEST_BOX_IN_OBB(Center, Extents)
612 
613  if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) }
614  else _CollideNoPrimitiveTest(node->GetPos());
615 
616  if(ContactFound()) return;
617 
618  if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) }
619  else _CollideNoPrimitiveTest(node->GetNeg());
620 }
621 
622 
623 
624 
625 
626 
628 
633 {
634 }
635 
637 
642 {
643 }
644 
645 bool HybridOBBCollider::Collide(OBBCache& cache, const OBB& box, const HybridModel& model, const Matrix4x4* worldb, const Matrix4x4* worldm)
646 {
647  // We don't want primitive tests here!
649 
650  // Checkings
651  if(!Setup(&model)) return false;
652 
653  // Init collision query
654  if(InitQuery(cache, box, worldb, worldm)) return true;
655 
656  // Special case for 1-leaf trees
658  {
659  // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles
660  udword Nb = mIMesh->GetNbTriangles();
661 
662  // Loop through all triangles
663  for(udword i=0;i<Nb;i++)
664  {
666  }
667  return true;
668  }
669 
670  // Override destination array since we're only going to get leaf boxes here
671  mTouchedBoxes.Reset();
672  mTouchedPrimitives = &mTouchedBoxes;
673 
674  // Now, do the actual query against leaf boxes
675  if(!model.HasLeafNodes())
676  {
677  if(model.IsQuantized())
678  {
679  const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree();
680 
681  // Setup dequantization coeffs
682  mCenterCoeff = Tree->mCenterCoeff;
684 
685  // Perform collision query - we don't want primitive tests here!
686  _CollideNoPrimitiveTest(Tree->GetNodes());
687  }
688  else
689  {
690  const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
691 
692  // Perform collision query - we don't want primitive tests here!
693  _CollideNoPrimitiveTest(Tree->GetNodes());
694  }
695  }
696  else
697  {
698  if(model.IsQuantized())
699  {
700  const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
701 
702  // Setup dequantization coeffs
703  mCenterCoeff = Tree->mCenterCoeff;
705 
706  // Perform collision query - we don't want primitive tests here!
707  _CollideNoPrimitiveTest(Tree->GetNodes());
708  }
709  else
710  {
711  const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
712 
713  // Perform collision query - we don't want primitive tests here!
714  _CollideNoPrimitiveTest(Tree->GetNodes());
715  }
716  }
717 
718  // We only have a list of boxes so far
719  if(GetContactStatus())
720  {
721  // Reset contact status, since it currently only reflects collisions with leaf boxes
723 
724  // Change dest container so that we can use built-in overlap tests and get collided primitives
725  cache.TouchedPrimitives.Reset();
727 
728  // Read touched leaf boxes
729  udword Nb = mTouchedBoxes.GetNbEntries();
730  const udword* Touched = mTouchedBoxes.GetEntries();
731 
732  const LeafTriangles* LT = model.GetLeafTriangles();
733  const udword* Indices = model.GetIndices();
734 
735  // Loop through touched leaves
736  while(Nb--)
737  {
738  const LeafTriangles& CurrentLeaf = LT[*Touched++];
739 
740  // Each leaf box has a set of triangles
741  udword NbTris = CurrentLeaf.GetNbTriangles();
742  if(Indices)
743  {
744  const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()];
745 
746  // Loop through triangles and test each of them
747  while(NbTris--)
748  {
749  udword TriangleIndex = *T++;
750  OBB_PRIM(TriangleIndex, OPC_CONTACT)
751  }
752  }
753  else
754  {
755  udword BaseIndex = CurrentLeaf.GetTriangleIndex();
756 
757  // Loop through triangles and test each of them
758  while(NbTris--)
759  {
760  udword TriangleIndex = BaseIndex++;
761  OBB_PRIM(TriangleIndex, OPC_CONTACT)
762  }
763  }
764  }
765  }
766 
767  return true;
768 }
769