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_RayCollider.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 
88 
91 
100 
103 
111 
114 // Precompiled Header
115 #include "Stdafx.h"
116 
117 
118 using namespace Opcode;
119 
120 #include "OPC_RayAABBOverlap.h"
121 #include "OPC_RayTriOverlap.h"
122 
123 #define SET_CONTACT(prim_index, flag) \
124  mNbIntersections++; \
125  /* Set contact status */ \
126  mFlags |= flag; \
127  /* In any case the contact has been found and recorded in mStabbedFace */ \
128  mStabbedFace.mFaceID = prim_index;
129 
130 #ifdef OPC_RAYHIT_CALLBACK
131 
132  #define HANDLE_CONTACT(prim_index, flag) \
133  SET_CONTACT(prim_index, flag) \
134  \
135  if(mHitCallback) (mHitCallback)(mStabbedFace, mUserData);else{}
136 
137  #define UPDATE_CACHE \
138  if(cache && GetContactStatus()) \
139  { \
140  *cache = mStabbedFace.mFaceID; \
141  }else{}
142 #else
143 
144  #define HANDLE_CONTACT(prim_index, flag) \
145  SET_CONTACT(prim_index, flag) \
146  \
147  /* Now we can also record it in mStabbedFaces if available */ \
148  if(mStabbedFaces) \
149  { \
150  /* If we want all faces or if that's the first one we hit */ \
151  if(!mClosestHit || !mStabbedFaces->GetNbFaces()) \
152  { \
153  mStabbedFaces->AddFace(mStabbedFace); \
154  } \
155  else \
156  { \
157  /* We only keep closest hit */ \
158  CollisionFace* Current = const_cast<CollisionFace*>(mStabbedFaces->GetFaces()); \
159  if(Current && mStabbedFace.mDistance<Current->mDistance) \
160  { \
161  *Current = mStabbedFace; \
162  } \
163  } \
164  }else{}
165 
166  #define UPDATE_CACHE \
167  if(cache && GetContactStatus() && mStabbedFaces) \
168  { \
169  const CollisionFace* Current = mStabbedFaces->GetFaces(); \
170  if(Current) *cache = Current->mFaceID; \
171  else *cache = INVALID_ID; \
172  }else{}
173 #endif
174 
175 #define SEGMENT_PRIM(prim_index, flag) \
176  /* Request vertices from the app */ \
177  VertexPointers VP; mIMesh->GetTriangle(VP, prim_index); \
178  \
179  /* Perform ray-tri overlap test and return */ \
180  if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \
181  { \
182  /* Intersection point is valid if dist < segment's length */ \
183  /* We know dist>0 so we can use integers */ \
184  if(IR(mStabbedFace.mDistance)<IR(mMaxDist)) \
185  { \
186  HANDLE_CONTACT(prim_index, flag) \
187  } \
188  }else{}
189 
190 #define RAY_PRIM(prim_index, flag) \
191  /* Request vertices from the app */ \
192  VertexPointers VP; mIMesh->GetTriangle(VP, prim_index); \
193  \
194  /* Perform ray-tri overlap test and return */ \
195  if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \
196  { \
197  HANDLE_CONTACT(prim_index, flag) \
198  }else{}
199 
200 
202 
207 #ifdef OPC_RAYHIT_CALLBACK
208  mHitCallback (null),
209  mUserData (0),
210 #else
211  mStabbedFaces (null),
212 #endif
213  mNbRayBVTests (0),
214  mNbRayPrimTests (0),
215  mNbIntersections (0),
216  mMaxDist (MAX_FLOAT),
217 #ifndef OPC_RAYHIT_CALLBACK
218  mClosestHit (false),
219 #endif
220  mCulling (true)
221 {
222 }
223 
225 
230 {
231 }
232 
234 
238 const char* RayCollider::ValidateSettings()
240 {
241  if(mMaxDist<0.0f) return "Higher distance bound must be positive!";
242  if(TemporalCoherenceEnabled() && !FirstContactEnabled()) return "Temporal coherence only works with ""First contact"" mode!";
243 #ifndef OPC_RAYHIT_CALLBACK
244  if(mClosestHit && FirstContactEnabled()) return "Closest hit doesn't work with ""First contact"" mode!";
245  if(TemporalCoherenceEnabled() && mClosestHit) return "Temporal coherence can't guarantee to report closest hit!";
246 #endif
247  if(SkipPrimitiveTests()) return "SkipPrimitiveTests not possible for RayCollider ! (not implemented)";
248  return null;
249 }
250 
252 
264 bool RayCollider::Collide(const Ray& world_ray, const Model& model, const Matrix4x4* world, udword* cache)
266 {
267  // Checkings
268  if(!Setup(&model)) return false;
269 
270  // Init collision query
271  if(InitQuery(world_ray, world, cache)) return true;
272 
273  if(!model.HasLeafNodes())
274  {
275  if(model.IsQuantized())
276  {
277  const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree();
278 
279  // Setup dequantization coeffs
280  mCenterCoeff = Tree->mCenterCoeff;
282 
283  // Perform stabbing query
284  if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes());
285  else _RayStab(Tree->GetNodes());
286  }
287  else
288  {
289  const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
290 
291  // Perform stabbing query
292  if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes());
293  else _RayStab(Tree->GetNodes());
294  }
295  }
296  else
297  {
298  if(model.IsQuantized())
299  {
300  const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
301 
302  // Setup dequantization coeffs
303  mCenterCoeff = Tree->mCenterCoeff;
305 
306  // Perform stabbing query
307  if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes());
308  else _RayStab(Tree->GetNodes());
309  }
310  else
311  {
312  const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
313 
314  // Perform stabbing query
315  if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes());
316  else _RayStab(Tree->GetNodes());
317  }
318  }
319 
320  // Update cache if needed
322  return true;
323 }
324 
325 
327 
339 bool RayCollider::InitQuery(const Ray& world_ray, const Matrix4x4* world, udword* face_id)
341 {
342  // Reset stats & contact status
344  mNbRayBVTests = 0;
345  mNbRayPrimTests = 0;
346  mNbIntersections = 0;
347 #ifndef OPC_RAYHIT_CALLBACK
348  if(mStabbedFaces) mStabbedFaces->Reset();
349 #endif
350 
351  // Compute ray in local space
352  // The (Origin/Dir) form is needed for the ray-triangle test anyway (even for segment tests)
353  if(world)
354  {
355  Matrix3x3 InvWorld = *world;
356  mDir = InvWorld * world_ray.mDir;
357 
358  Matrix4x4 World;
359  InvertPRMatrix(World, *world);
360  mOrigin = world_ray.mOrig * World;
361  }
362  else
363  {
364  mDir = world_ray.mDir;
365  mOrigin = world_ray.mOrig;
366  }
367 
368  // 4) Special case: 1-triangle meshes [Opcode 1.3]
370  {
371  // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0.
372  if(!SkipPrimitiveTests())
373  {
374  // Perform overlap test between the unique triangle and the ray (and set contact status if needed)
376 
377  // Return immediately regardless of status
378  return TRUE;
379  }
380  }
381 
382  // Check temporal coherence :
383 
384  // Test previously colliding primitives first
385  if(TemporalCoherenceEnabled() && FirstContactEnabled() && face_id && *face_id!=INVALID_ID)
386  {
387 #ifdef OLD_CODE
388 #ifndef OPC_RAYHIT_CALLBACK
389  if(!mClosestHit)
390 #endif
391  {
392  // Request vertices from the app
393  VertexPointers VP;
394  mIMesh->GetTriangle(VP, *face_id);
395  // Perform ray-cached tri overlap test
396  if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2]))
397  {
398  // Intersection point is valid if:
399  // - distance is positive (else it can just be a face behind the orig point)
400  // - distance is smaller than a given max distance (useful for shadow feelers)
401 // if(mStabbedFace.mDistance>0.0f && mStabbedFace.mDistance<mMaxDist)
402  if(IR(mStabbedFace.mDistance)<IR(mMaxDist)) // The other test is already performed in RayTriOverlap
403  {
404  // Set contact status
406 
407  mStabbedFace.mFaceID = *face_id;
408 
409 #ifndef OPC_RAYHIT_CALLBACK
410  if(mStabbedFaces) mStabbedFaces->AddFace(mStabbedFace);
411 #endif
412  return TRUE;
413  }
414  }
415  }
416 #else
417  // New code
418  // We handle both Segment/ray queries with the same segment code, and a possible infinite limit
420 
421  // Return immediately if possible
422  if(GetContactStatus()) return TRUE;
423 #endif
424  }
425 
426  // Precompute data (moved after temporal coherence since only needed for ray-AABB)
428  {
429  // For Segment-AABB overlap
430  mData = 0.5f * mDir * mMaxDist;
431  mData2 = mOrigin + mData;
432 
433  // Precompute mFDir;
434  mFDir.x = fabsf(mData.x);
435  mFDir.y = fabsf(mData.y);
436  mFDir.z = fabsf(mData.z);
437  }
438  else
439  {
440  // For Ray-AABB overlap
441 // udword x = SIR(mDir.x)-1;
442 // udword y = SIR(mDir.y)-1;
443 // udword z = SIR(mDir.z)-1;
444 // mData.x = FR(x);
445 // mData.y = FR(y);
446 // mData.z = FR(z);
447 
448  // Precompute mFDir;
449  mFDir.x = fabsf(mDir.x);
450  mFDir.y = fabsf(mDir.y);
451  mFDir.z = fabsf(mDir.z);
452  }
453 
454  return FALSE;
455 }
456 
458 
465 bool RayCollider::Collide(const Ray& world_ray, const AABBTree* tree, Container& box_indices)
467 {
468  // ### bad design here
469 
470  // This is typically called for a scene tree, full of -AABBs-, not full of triangles.
471  // So we don't really have "primitives" to deal with. Hence it doesn't work with
472  // "FirstContact" + "TemporalCoherence".
474 
475  // Checkings
476  if(!tree) return false;
477 
478  // Init collision query
479  // Basically this is only called to initialize precomputed data
480  if(InitQuery(world_ray)) return true;
481 
482  // Perform stabbing query
483  if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(tree, box_indices);
484  else _RayStab(tree, box_indices);
485 
486  return true;
487 }
488 
489 
491 
497 {
498  // Perform Segment-AABB overlap test
499  if(!SegmentAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
500 
501  if(node->IsLeaf())
502  {
503  SEGMENT_PRIM(node->GetPrimitive(), OPC_CONTACT)
504  }
505  else
506  {
507  _SegmentStab(node->GetPos());
508 
509  if(ContactFound()) return;
510 
511  _SegmentStab(node->GetNeg());
512  }
513 }
514 
516 
522 {
523  // Dequantize box
524  const QuantizedAABB& Box = node->mAABB;
525  const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
526  const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
527 
528  // Perform Segment-AABB overlap test
529  if(!SegmentAABBOverlap(Center, Extents)) return;
530 
531  if(node->IsLeaf())
532  {
533  SEGMENT_PRIM(node->GetPrimitive(), OPC_CONTACT)
534  }
535  else
536  {
537  _SegmentStab(node->GetPos());
538 
539  if(ContactFound()) return;
540 
541  _SegmentStab(node->GetNeg());
542  }
543 }
544 
546 
552 {
553  // Perform Segment-AABB overlap test
554  if(!SegmentAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
555 
556  if(node->HasPosLeaf())
557  {
558  SEGMENT_PRIM(node->GetPosPrimitive(), OPC_CONTACT)
559  }
560  else _SegmentStab(node->GetPos());
561 
562  if(ContactFound()) return;
563 
564  if(node->HasNegLeaf())
565  {
566  SEGMENT_PRIM(node->GetNegPrimitive(), OPC_CONTACT)
567  }
568  else _SegmentStab(node->GetNeg());
569 }
570 
572 
578 {
579  // Dequantize box
580  const QuantizedAABB& Box = node->mAABB;
581  const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
582  const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
583 
584  // Perform Segment-AABB overlap test
585  if(!SegmentAABBOverlap(Center, Extents)) return;
586 
587  if(node->HasPosLeaf())
588  {
589  SEGMENT_PRIM(node->GetPosPrimitive(), OPC_CONTACT)
590  }
591  else _SegmentStab(node->GetPos());
592 
593  if(ContactFound()) return;
594 
595  if(node->HasNegLeaf())
596  {
597  SEGMENT_PRIM(node->GetNegPrimitive(), OPC_CONTACT)
598  }
599  else _SegmentStab(node->GetNeg());
600 }
601 
603 
608 void RayCollider::_SegmentStab(const AABBTreeNode* node, Container& box_indices)
610 {
611  // Test the box against the segment
612  Point Center, Extents;
613  node->GetAABB()->GetCenter(Center);
614  node->GetAABB()->GetExtents(Extents);
615  if(!SegmentAABBOverlap(Center, Extents)) return;
616 
617  if(node->IsLeaf())
618  {
619  box_indices.Add(node->GetPrimitives(), node->GetNbPrimitives());
620  }
621  else
622  {
623  _SegmentStab(node->GetPos(), box_indices);
624  _SegmentStab(node->GetNeg(), box_indices);
625  }
626 }
627 
629 
635 {
636  // Perform Ray-AABB overlap test
637  if(!RayAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
638 
639  if(node->IsLeaf())
640  {
641  RAY_PRIM(node->GetPrimitive(), OPC_CONTACT)
642  }
643  else
644  {
645  _RayStab(node->GetPos());
646 
647  if(ContactFound()) return;
648 
649  _RayStab(node->GetNeg());
650  }
651 }
652 
654 
660 {
661  // Dequantize box
662  const QuantizedAABB& Box = node->mAABB;
663  const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
664  const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
665 
666  // Perform Ray-AABB overlap test
667  if(!RayAABBOverlap(Center, Extents)) return;
668 
669  if(node->IsLeaf())
670  {
671  RAY_PRIM(node->GetPrimitive(), OPC_CONTACT)
672  }
673  else
674  {
675  _RayStab(node->GetPos());
676 
677  if(ContactFound()) return;
678 
679  _RayStab(node->GetNeg());
680  }
681 }
682 
684 
688 void RayCollider::_RayStab(const AABBNoLeafNode* node)
690 {
691  // Perform Ray-AABB overlap test
692  if(!RayAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return;
693 
694  if(node->HasPosLeaf())
695  {
696  RAY_PRIM(node->GetPosPrimitive(), OPC_CONTACT)
697  }
698  else _RayStab(node->GetPos());
699 
700  if(ContactFound()) return;
701 
702  if(node->HasNegLeaf())
703  {
704  RAY_PRIM(node->GetNegPrimitive(), OPC_CONTACT)
705  }
706  else _RayStab(node->GetNeg());
707 }
708 
710 
716 {
717  // Dequantize box
718  const QuantizedAABB& Box = node->mAABB;
719  const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
720  const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
721 
722  // Perform Ray-AABB overlap test
723  if(!RayAABBOverlap(Center, Extents)) return;
724 
725  if(node->HasPosLeaf())
726  {
727  RAY_PRIM(node->GetPosPrimitive(), OPC_CONTACT)
728  }
729  else _RayStab(node->GetPos());
730 
731  if(ContactFound()) return;
732 
733  if(node->HasNegLeaf())
734  {
735  RAY_PRIM(node->GetNegPrimitive(), OPC_CONTACT)
736  }
737  else _RayStab(node->GetNeg());
738 }
739 
741 
746 void RayCollider::_RayStab(const AABBTreeNode* node, Container& box_indices)
748 {
749  // Test the box against the ray
750  Point Center, Extents;
751  node->GetAABB()->GetCenter(Center);
752  node->GetAABB()->GetExtents(Extents);
753  if(!RayAABBOverlap(Center, Extents)) return;
754  if(node->IsLeaf())
755  {
756  mFlags |= OPC_CONTACT;
757  box_indices.Add(node->GetPrimitives(), node->GetNbPrimitives());
758  }
759  else
760  {
761  _RayStab(node->GetPos(), box_indices);
762  _RayStab(node->GetNeg(), box_indices);
763  }
764 }
765