Cosmetic: Newlines were corrected
[ode.git] / OPCODE / OPC_OBBCollider.cpp
blob730c7cca2756b94749e6afbe17f1183ab713a21c
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 an OBB collider.
12 * \file OPC_OBBCollider.cpp
13 * \author Pierre Terdiman
14 * \date January, 1st, 2002
16 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
18 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
19 /**
20 * Contains an OBB-vs-tree collider.
22 * \class OBBCollider
23 * \author Pierre Terdiman
24 * \version 1.3
25 * \date January, 1st, 2002
27 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
29 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
30 // Precompiled Header
31 #include "Stdafx.h"
33 using namespace Opcode;
35 #include "OPC_BoxBoxOverlap.h"
36 #include "OPC_TriBoxOverlap.h"
38 #define SET_CONTACT(prim_index, flag) \
39 /* Set contact status */ \
40 mFlags |= flag; \
41 mTouchedPrimitives->Add(udword(prim_index));
43 //! OBB-triangle test
44 #define OBB_PRIM(prim_index, flag) \
45 /* Request vertices from the app */ \
46 VertexPointers VP; ConversionArea VC; mIMesh->GetTriangle(VP, prim_index, VC); \
47 /* Transform them in a common space */ \
48 TransformPoint(mLeafVerts[0], *VP.Vertex[0], mRModelToBox, mTModelToBox); \
49 TransformPoint(mLeafVerts[1], *VP.Vertex[1], mRModelToBox, mTModelToBox); \
50 TransformPoint(mLeafVerts[2], *VP.Vertex[2], mRModelToBox, mTModelToBox); \
51 /* Perform triangle-box overlap test */ \
52 if(TriBoxOverlap()) \
53 { \
54 SET_CONTACT(prim_index, flag) \
57 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
58 /**
59 * Constructor.
61 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
62 OBBCollider::OBBCollider() : mFullBoxBoxTest(true)
66 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
67 /**
68 * Destructor.
70 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
71 OBBCollider::~OBBCollider()
75 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
76 /**
77 * Validates current settings. You should call this method after all the settings and callbacks have been defined.
78 * \return null if everything is ok, else a string describing the problem
80 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
81 const char* OBBCollider::ValidateSettings()
83 if(TemporalCoherenceEnabled() && !FirstContactEnabled()) return "Temporal coherence only works with ""First contact"" mode!";
85 return VolumeCollider::ValidateSettings();
88 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
89 /**
90 * Generic collision query for generic OPCODE models. After the call, access the results:
91 * - with GetContactStatus()
92 * - with GetNbTouchedPrimitives()
93 * - with GetTouchedPrimitives()
95 * \param cache [in/out] a box cache
96 * \param box [in] collision OBB in local space
97 * \param model [in] Opcode model to collide with
98 * \param worldb [in] OBB's world matrix, or null
99 * \param worldm [in] model's world matrix, or null
100 * \return true if success
101 * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
103 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
104 bool OBBCollider::Collide(OBBCache& cache, const OBB& box, const Model& model, const Matrix4x4* worldb, const Matrix4x4* worldm)
106 // Checkings
107 if(!Setup(&model)) return false;
109 // Init collision query
110 if(InitQuery(cache, box, worldb, worldm)) return true;
112 if(!model.HasLeafNodes())
114 if(model.IsQuantized())
116 const AABBQuantizedNoLeafTree* Tree = static_cast<const AABBQuantizedNoLeafTree *>(model.GetTree());
118 // Setup dequantization coeffs
119 mCenterCoeff = Tree->mCenterCoeff;
120 mExtentsCoeff = Tree->mExtentsCoeff;
122 // Perform collision query
123 if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
124 else _Collide(Tree->GetNodes());
126 else
128 const AABBNoLeafTree* Tree = static_cast<const AABBNoLeafTree *>(model.GetTree());
130 // Perform collision query
131 if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
132 else _Collide(Tree->GetNodes());
135 else
137 if(model.IsQuantized())
139 const AABBQuantizedTree* Tree = static_cast<const AABBQuantizedTree *>(model.GetTree());
141 // Setup dequantization coeffs
142 mCenterCoeff = Tree->mCenterCoeff;
143 mExtentsCoeff = Tree->mExtentsCoeff;
145 // Perform collision query
146 if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
147 else _Collide(Tree->GetNodes());
149 else
151 const AABBCollisionTree* Tree = static_cast<const AABBCollisionTree *>(model.GetTree());
153 // Perform collision query
154 if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
155 else _Collide(Tree->GetNodes());
159 return true;
162 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
164 * Initializes a collision query :
165 * - reset stats & contact status
166 * - setup matrices
167 * - check temporal coherence
169 * \param cache [in/out] a box cache
170 * \param box [in] obb in local space
171 * \param worldb [in] obb's world matrix, or null
172 * \param worldm [in] model's world matrix, or null
173 * \return TRUE if we can return immediately
174 * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
176 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
177 BOOL OBBCollider::InitQuery(OBBCache& cache, const OBB& box, const Matrix4x4* worldb, const Matrix4x4* worldm)
179 // 1) Call the base method
180 VolumeCollider::InitQuery();
182 // 2) Compute obb in world space
183 mBoxExtents = box.mExtents;
185 Matrix4x4 WorldB;
187 if(worldb)
189 WorldB = Matrix4x4( box.mRot * Matrix3x3(*worldb) );
190 WorldB.SetTrans(box.mCenter * *worldb);
192 else
194 WorldB = box.mRot;
195 WorldB.SetTrans(box.mCenter);
198 // Setup matrices
199 Matrix4x4 InvWorldB;
200 InvertPRMatrix(InvWorldB, WorldB);
202 if(worldm)
204 Matrix4x4 InvWorldM;
205 InvertPRMatrix(InvWorldM, *worldm);
207 Matrix4x4 WorldBtoM = WorldB * InvWorldM;
208 Matrix4x4 WorldMtoB = *worldm * InvWorldB;
210 mRModelToBox = WorldMtoB; WorldMtoB.GetTrans(mTModelToBox);
211 mRBoxToModel = WorldBtoM; WorldBtoM.GetTrans(mTBoxToModel);
213 else
215 mRModelToBox = InvWorldB; InvWorldB.GetTrans(mTModelToBox);
216 mRBoxToModel = WorldB; WorldB.GetTrans(mTBoxToModel);
219 // 3) Setup destination pointer
220 mTouchedPrimitives = &cache.TouchedPrimitives;
222 // 4) Special case: 1-triangle meshes [Opcode 1.3]
223 if(mCurrentModel && mCurrentModel->HasSingleNode())
225 if(!SkipPrimitiveTests())
227 // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0.
228 mTouchedPrimitives->Reset();
230 // Perform overlap test between the unique triangle and the box (and set contact status if needed)
231 OBB_PRIM(udword(0), OPC_CONTACT)
233 // Return immediately regardless of status
234 return TRUE;
238 // 5) Check temporal coherence:
239 if(TemporalCoherenceEnabled())
241 // Here we use temporal coherence
242 // => check results from previous frame before performing the collision query
243 if(FirstContactEnabled())
245 // We're only interested in the first contact found => test the unique previously touched face
246 if(mTouchedPrimitives->GetNbEntries())
248 // Get index of previously touched face = the first entry in the array
249 udword PreviouslyTouchedFace = mTouchedPrimitives->GetEntry(0);
251 // Then reset the array:
252 // - if the overlap test below is successful, the index we'll get added back anyway
253 // - if it isn't, then the array should be reset anyway for the normal query
254 mTouchedPrimitives->Reset();
256 // Perform overlap test between the cached triangle and the box (and set contact status if needed)
257 OBB_PRIM(PreviouslyTouchedFace, OPC_TEMPORAL_CONTACT)
259 // Return immediately if possible
260 if(GetContactStatus()) return TRUE;
262 // else no face has been touched during previous query
263 // => we'll have to perform a normal query
265 else
267 // ### rewrite this
268 OBB TestBox(mTBoxToModel, mBoxExtents, mRBoxToModel);
270 // We're interested in all contacts =>test the new real box N(ew) against the previous fat box P(revious):
271 if(IsCacheValid(cache) && TestBox.IsInside(cache.FatBox))
273 // - if N is included in P, return previous list
274 // => we simply leave the list (mTouchedFaces) unchanged
276 // Set contact status if needed
277 if(mTouchedPrimitives->GetNbEntries()) mFlags |= OPC_TEMPORAL_CONTACT;
279 // In any case we don't need to do a query
280 return TRUE;
282 else
284 // - else do the query using a fat N
286 // Reset cache since we'll about to perform a real query
287 mTouchedPrimitives->Reset();
289 // Make a fat box so that coherence will work for subsequent frames
290 TestBox.mExtents *= cache.FatCoeff;
291 mBoxExtents *= cache.FatCoeff;
293 // Update cache with query data (signature for cached faces)
294 cache.FatBox = TestBox;
298 else
300 // Here we don't use temporal coherence => do a normal query
301 mTouchedPrimitives->Reset();
304 // Now we can precompute box-box data
306 // Precompute absolute box-to-model rotation matrix
307 for(udword i=0;i<3;i++)
309 for(udword j=0;j<3;j++)
311 // Epsilon value prevents floating-point inaccuracies (strategy borrowed from RAPID)
312 mAR.m[i][j] = 1e-6f + fabsf(mRBoxToModel.m[i][j]);
316 // Precompute bounds for box-in-box test
317 mB0 = mBoxExtents - mTModelToBox;
318 mB1 = - mBoxExtents - mTModelToBox;
320 // Precompute box-box data - Courtesy of Erwin de Vries
321 mBBx1 = mBoxExtents.x*mAR.m[0][0] + mBoxExtents.y*mAR.m[1][0] + mBoxExtents.z*mAR.m[2][0];
322 mBBy1 = mBoxExtents.x*mAR.m[0][1] + mBoxExtents.y*mAR.m[1][1] + mBoxExtents.z*mAR.m[2][1];
323 mBBz1 = mBoxExtents.x*mAR.m[0][2] + mBoxExtents.y*mAR.m[1][2] + mBoxExtents.z*mAR.m[2][2];
325 mBB_1 = mBoxExtents.y*mAR.m[2][0] + mBoxExtents.z*mAR.m[1][0];
326 mBB_2 = mBoxExtents.x*mAR.m[2][0] + mBoxExtents.z*mAR.m[0][0];
327 mBB_3 = mBoxExtents.x*mAR.m[1][0] + mBoxExtents.y*mAR.m[0][0];
328 mBB_4 = mBoxExtents.y*mAR.m[2][1] + mBoxExtents.z*mAR.m[1][1];
329 mBB_5 = mBoxExtents.x*mAR.m[2][1] + mBoxExtents.z*mAR.m[0][1];
330 mBB_6 = mBoxExtents.x*mAR.m[1][1] + mBoxExtents.y*mAR.m[0][1];
331 mBB_7 = mBoxExtents.y*mAR.m[2][2] + mBoxExtents.z*mAR.m[1][2];
332 mBB_8 = mBoxExtents.x*mAR.m[2][2] + mBoxExtents.z*mAR.m[0][2];
333 mBB_9 = mBoxExtents.x*mAR.m[1][2] + mBoxExtents.y*mAR.m[0][2];
335 return FALSE;
338 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
340 * Checks the OBB completely contains the box. In which case we can end the query sooner.
341 * \param bc [in] box center
342 * \param be [in] box extents
343 * \return true if the OBB contains the whole box
345 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
346 inline_ BOOL OBBCollider::OBBContainsBox(const Point& bc, const Point& be)
348 // I assume if all 8 box vertices are inside the OBB, so does the whole box.
349 // Sounds ok but maybe there's a better way?
351 #define TEST_PT(a,b,c) \
352 p.x=a; p.y=b; p.z=c; p+=bc; \
353 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;\
354 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;\
355 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 Point p;
358 float f;
360 TEST_PT(be.x, be.y, be.z)
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)
369 return TRUE;
372 // Yes there is:
373 // - compute model-box's AABB in OBB space
374 // - test AABB-in-AABB
375 float NCx = bc.x * mRModelToBox.m[0][0] + bc.y * mRModelToBox.m[1][0] + bc.z * mRModelToBox.m[2][0];
376 float NEx = fabsf(mRModelToBox.m[0][0] * be.x) + fabsf(mRModelToBox.m[1][0] * be.y) + fabsf(mRModelToBox.m[2][0] * be.z);
378 if(mB0.x < NCx+NEx) return FALSE;
379 if(mB1.x > NCx-NEx) return FALSE;
381 float NCy = bc.x * mRModelToBox.m[0][1] + bc.y * mRModelToBox.m[1][1] + bc.z * mRModelToBox.m[2][1];
382 float NEy = fabsf(mRModelToBox.m[0][1] * be.x) + fabsf(mRModelToBox.m[1][1] * be.y) + fabsf(mRModelToBox.m[2][1] * be.z);
384 if(mB0.y < NCy+NEy) return FALSE;
385 if(mB1.y > NCy-NEy) return FALSE;
387 float NCz = bc.x * mRModelToBox.m[0][2] + bc.y * mRModelToBox.m[1][2] + bc.z * mRModelToBox.m[2][2];
388 float NEz = fabsf(mRModelToBox.m[0][2] * be.x) + fabsf(mRModelToBox.m[1][2] * be.y) + fabsf(mRModelToBox.m[2][2] * be.z);
390 if(mB0.z < NCz+NEz) return FALSE;
391 if(mB1.z > NCz-NEz) return FALSE;
393 return TRUE;
396 #define TEST_BOX_IN_OBB(center, extents) \
397 if(OBBContainsBox(center, extents)) \
399 /* Set contact status */ \
400 mFlags |= OPC_CONTACT; \
401 _Dump(node); \
402 return; \
405 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
407 * Recursive collision query for normal AABB trees.
408 * \param node [in] current collision node
410 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
411 void OBBCollider::_Collide(const AABBCollisionNode* node)
413 // Perform OBB-AABB overlap test
414 if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
416 TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents)
418 if(node->IsLeaf())
420 OBB_PRIM(node->GetPrimitive(), OPC_CONTACT)
422 else
424 _Collide(node->GetPos());
426 if(ContactFound()) return;
428 _Collide(node->GetNeg());
432 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
434 * Recursive collision query for normal AABB trees, without primitive tests.
435 * \param node [in] current collision node
437 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
438 void OBBCollider::_CollideNoPrimitiveTest(const AABBCollisionNode* node)
440 // Perform OBB-AABB overlap test
441 if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
443 TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents)
445 if(node->IsLeaf())
447 SET_CONTACT(node->GetPrimitive(), OPC_CONTACT)
449 else
451 _CollideNoPrimitiveTest(node->GetPos());
453 if(ContactFound()) return;
455 _CollideNoPrimitiveTest(node->GetNeg());
459 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
461 * Recursive collision query for quantized AABB trees.
462 * \param node [in] current collision node
464 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
465 void OBBCollider::_Collide(const AABBQuantizedNode* node)
467 // Dequantize box
468 const QuantizedAABB& Box = node->mAABB;
469 const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
470 const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
472 // Perform OBB-AABB overlap test
473 if(!BoxBoxOverlap(Extents, Center)) return;
475 TEST_BOX_IN_OBB(Center, Extents)
477 if(node->IsLeaf())
479 OBB_PRIM(node->GetPrimitive(), OPC_CONTACT)
481 else
483 _Collide(node->GetPos());
485 if(ContactFound()) return;
487 _Collide(node->GetNeg());
491 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
493 * Recursive collision query for quantized AABB trees, without primitive tests.
494 * \param node [in] current collision node
496 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
497 void OBBCollider::_CollideNoPrimitiveTest(const AABBQuantizedNode* node)
499 // Dequantize box
500 const QuantizedAABB& Box = node->mAABB;
501 const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
502 const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
504 // Perform OBB-AABB overlap test
505 if(!BoxBoxOverlap(Extents, Center)) return;
507 TEST_BOX_IN_OBB(Center, Extents)
509 if(node->IsLeaf())
511 SET_CONTACT(node->GetPrimitive(), OPC_CONTACT)
513 else
515 _CollideNoPrimitiveTest(node->GetPos());
517 if(ContactFound()) return;
519 _CollideNoPrimitiveTest(node->GetNeg());
523 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
525 * Recursive collision query for no-leaf AABB trees.
526 * \param node [in] current collision node
528 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
529 void OBBCollider::_Collide(const AABBNoLeafNode* node)
531 // Perform OBB-AABB overlap test
532 if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
534 TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents)
536 if(node->HasPosLeaf()) { OBB_PRIM(node->GetPosPrimitive(), OPC_CONTACT) }
537 else _Collide(node->GetPos());
539 if(ContactFound()) return;
541 if(node->HasNegLeaf()) { OBB_PRIM(node->GetNegPrimitive(), OPC_CONTACT) }
542 else _Collide(node->GetNeg());
545 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
547 * Recursive collision query for no-leaf AABB trees, without primitive tests.
548 * \param node [in] current collision node
550 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
551 void OBBCollider::_CollideNoPrimitiveTest(const AABBNoLeafNode* node)
553 // Perform OBB-AABB overlap test
554 if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
556 TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents)
558 if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) }
559 else _CollideNoPrimitiveTest(node->GetPos());
561 if(ContactFound()) return;
563 if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) }
564 else _CollideNoPrimitiveTest(node->GetNeg());
567 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
569 * Recursive collision query for quantized no-leaf AABB trees.
570 * \param node [in] current collision node
572 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
573 void OBBCollider::_Collide(const AABBQuantizedNoLeafNode* node)
575 // Dequantize box
576 const QuantizedAABB& Box = node->mAABB;
577 const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
578 const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
580 // Perform OBB-AABB overlap test
581 if(!BoxBoxOverlap(Extents, Center)) return;
583 TEST_BOX_IN_OBB(Center, Extents)
585 if(node->HasPosLeaf()) { OBB_PRIM(node->GetPosPrimitive(), OPC_CONTACT) }
586 else _Collide(node->GetPos());
588 if(ContactFound()) return;
590 if(node->HasNegLeaf()) { OBB_PRIM(node->GetNegPrimitive(), OPC_CONTACT) }
591 else _Collide(node->GetNeg());
594 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
596 * Recursive collision query for quantized no-leaf AABB trees, without primitive tests.
597 * \param node [in] current collision node
599 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
600 void OBBCollider::_CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node)
602 // Dequantize box
603 const QuantizedAABB& Box = node->mAABB;
604 const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
605 const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
607 // Perform OBB-AABB overlap test
608 if(!BoxBoxOverlap(Extents, Center)) return;
610 TEST_BOX_IN_OBB(Center, Extents)
612 if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) }
613 else _CollideNoPrimitiveTest(node->GetPos());
615 if(ContactFound()) return;
617 if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) }
618 else _CollideNoPrimitiveTest(node->GetNeg());
626 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
628 * Constructor.
630 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
631 HybridOBBCollider::HybridOBBCollider()
635 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
637 * Destructor.
639 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
640 HybridOBBCollider::~HybridOBBCollider()
644 bool HybridOBBCollider::Collide(OBBCache& cache, const OBB& box, const HybridModel& model, const Matrix4x4* worldb, const Matrix4x4* worldm)
646 // We don't want primitive tests here!
647 mFlags |= OPC_NO_PRIMITIVE_TESTS;
649 // Checkings
650 if(!Setup(&model)) return false;
652 // Init collision query
653 if(InitQuery(cache, box, worldb, worldm)) return true;
655 // Special case for 1-leaf trees
656 if(mCurrentModel && mCurrentModel->HasSingleNode())
658 // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles
659 udword Nb = mIMesh->GetNbTriangles();
661 // Loop through all triangles
662 for(udword i=0;i<Nb;i++)
664 OBB_PRIM(i, OPC_CONTACT)
666 return true;
669 // Override destination array since we're only going to get leaf boxes here
670 mTouchedBoxes.Reset();
671 mTouchedPrimitives = &mTouchedBoxes;
673 // Now, do the actual query against leaf boxes
674 if(!model.HasLeafNodes())
676 if(model.IsQuantized())
678 const AABBQuantizedNoLeafTree* Tree = static_cast<const AABBQuantizedNoLeafTree *>(model.GetTree());
680 // Setup dequantization coeffs
681 mCenterCoeff = Tree->mCenterCoeff;
682 mExtentsCoeff = Tree->mExtentsCoeff;
684 // Perform collision query - we don't want primitive tests here!
685 _CollideNoPrimitiveTest(Tree->GetNodes());
687 else
689 const AABBNoLeafTree* Tree = static_cast<const AABBNoLeafTree *>(model.GetTree());
691 // Perform collision query - we don't want primitive tests here!
692 _CollideNoPrimitiveTest(Tree->GetNodes());
695 else
697 if(model.IsQuantized())
699 const AABBQuantizedTree* Tree = static_cast<const AABBQuantizedTree *>(model.GetTree());
701 // Setup dequantization coeffs
702 mCenterCoeff = Tree->mCenterCoeff;
703 mExtentsCoeff = Tree->mExtentsCoeff;
705 // Perform collision query - we don't want primitive tests here!
706 _CollideNoPrimitiveTest(Tree->GetNodes());
708 else
710 const AABBCollisionTree* Tree = static_cast<const AABBCollisionTree *>(model.GetTree());
712 // Perform collision query - we don't want primitive tests here!
713 _CollideNoPrimitiveTest(Tree->GetNodes());
717 // We only have a list of boxes so far
718 if(GetContactStatus())
720 // Reset contact status, since it currently only reflects collisions with leaf boxes
721 Collider::InitQuery();
723 // Change dest container so that we can use built-in overlap tests and get collided primitives
724 cache.TouchedPrimitives.Reset();
725 mTouchedPrimitives = &cache.TouchedPrimitives;
727 // Read touched leaf boxes
728 udword Nb = mTouchedBoxes.GetNbEntries();
729 const udword* Touched = mTouchedBoxes.GetEntries();
731 const LeafTriangles* LT = model.GetLeafTriangles();
732 const udword* Indices = model.GetIndices();
734 // Loop through touched leaves
735 while(Nb--)
737 const LeafTriangles& CurrentLeaf = LT[*Touched++];
739 // Each leaf box has a set of triangles
740 udword NbTris = CurrentLeaf.GetNbTriangles();
741 if(Indices)
743 const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()];
745 // Loop through triangles and test each of them
746 while(NbTris--)
748 udword TriangleIndex = *T++;
749 OBB_PRIM(TriangleIndex, OPC_CONTACT)
752 else
754 udword BaseIndex = CurrentLeaf.GetTriangleIndex();
756 // Loop through triangles and test each of them
757 while(NbTris--)
759 udword TriangleIndex = BaseIndex++;
760 OBB_PRIM(TriangleIndex, OPC_CONTACT)
766 return true;