2 * OPCODE - Optimized Collision Detection
3 * http://www.codercorner.com/Opcode.htm
5 * Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com
7 This software is provided 'as-is', without any express or implied warranty.
8 In no event will the authors be held liable for any damages arising from the use of this software.
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it freely,
11 subject to the following restrictions:
13 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
14 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
15 3. This notice may not be removed or altered from any source distribution.
18 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
20 * Contains code for an OBB collider.
21 * \file OPC_OBBCollider.cpp
22 * \author Pierre Terdiman
23 * \date January, 1st, 2002
25 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
27 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
29 * Contains an OBB-vs-tree collider.
32 * \author Pierre Terdiman
34 * \date January, 1st, 2002
36 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
38 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
42 using namespace Opcode;
44 #include "OPC_BoxBoxOverlap.h"
45 #include "OPC_TriBoxOverlap.h"
47 #define SET_CONTACT(prim_index, flag) \
48 /* Set contact status */ \
50 mTouchedPrimitives->Add(prim_index);
53 #define OBB_PRIM(prim_index, flag) \
54 /* Request vertices from the app */ \
55 VertexPointers VP; mIMesh->GetTriangle(VP, prim_index); \
56 /* Transform them in a common space */ \
57 TransformPoint(mLeafVerts[0], *VP.Vertex[0], mRModelToBox, mTModelToBox); \
58 TransformPoint(mLeafVerts[1], *VP.Vertex[1], mRModelToBox, mTModelToBox); \
59 TransformPoint(mLeafVerts[2], *VP.Vertex[2], mRModelToBox, mTModelToBox); \
60 /* Perform triangle-box overlap test */ \
63 SET_CONTACT(prim_index, flag) \
66 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
70 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
71 OBBCollider::OBBCollider() : mFullBoxBoxTest(true)
75 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
79 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
80 OBBCollider::~OBBCollider()
84 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
86 * Validates current settings. You should call this method after all the settings and callbacks have been defined.
87 * \return null if everything is ok, else a string describing the problem
89 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
90 const char* OBBCollider::ValidateSettings()
92 if(TemporalCoherenceEnabled() && !FirstContactEnabled()) return "Temporal coherence only works with ""First contact"" mode!";
94 return VolumeCollider::ValidateSettings();
97 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
99 * Generic collision query for generic OPCODE models. After the call, access the results:
100 * - with GetContactStatus()
101 * - with GetNbTouchedPrimitives()
102 * - with GetTouchedPrimitives()
104 * \param cache [in/out] a box cache
105 * \param box [in] collision OBB in local space
106 * \param model [in] Opcode model to collide with
107 * \param worldb [in] OBB's world matrix, or null
108 * \param worldm [in] model's world matrix, or null
109 * \return true if success
110 * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
112 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
113 bool OBBCollider::Collide(OBBCache& cache, const OBB& box, const Model& model, const Matrix4x4* worldb, const Matrix4x4* worldm)
116 if(!Setup(&model)) return false;
118 // Init collision query
119 if(InitQuery(cache, box, worldb, worldm)) return true;
121 if(!model.HasLeafNodes())
123 if(model.IsQuantized())
125 const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree();
127 // Setup dequantization coeffs
128 mCenterCoeff = Tree->mCenterCoeff;
129 mExtentsCoeff = Tree->mExtentsCoeff;
131 // Perform collision query
132 if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
133 else _Collide(Tree->GetNodes());
137 const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
139 // Perform collision query
140 if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
141 else _Collide(Tree->GetNodes());
146 if(model.IsQuantized())
148 const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
150 // Setup dequantization coeffs
151 mCenterCoeff = Tree->mCenterCoeff;
152 mExtentsCoeff = Tree->mExtentsCoeff;
154 // Perform collision query
155 if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
156 else _Collide(Tree->GetNodes());
160 const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
162 // Perform collision query
163 if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
164 else _Collide(Tree->GetNodes());
171 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
173 * Initializes a collision query :
174 * - reset stats & contact status
176 * - check temporal coherence
178 * \param cache [in/out] a box cache
179 * \param box [in] obb in local space
180 * \param worldb [in] obb's world matrix, or null
181 * \param worldm [in] model's world matrix, or null
182 * \return TRUE if we can return immediately
183 * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
185 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
186 BOOL OBBCollider::InitQuery(OBBCache& cache, const OBB& box, const Matrix4x4* worldb, const Matrix4x4* worldm)
188 // 1) Call the base method
189 VolumeCollider::InitQuery();
191 // 2) Compute obb in world space
192 mBoxExtents = box.mExtents;
198 WorldB = Matrix4x4( box.mRot * Matrix3x3(*worldb) );
199 WorldB.SetTrans(box.mCenter * *worldb);
204 WorldB.SetTrans(box.mCenter);
209 InvertPRMatrix(InvWorldB, WorldB);
214 InvertPRMatrix(InvWorldM, *worldm);
216 Matrix4x4 WorldBtoM = WorldB * InvWorldM;
217 Matrix4x4 WorldMtoB = *worldm * InvWorldB;
219 mRModelToBox = WorldMtoB; WorldMtoB.GetTrans(mTModelToBox);
220 mRBoxToModel = WorldBtoM; WorldBtoM.GetTrans(mTBoxToModel);
224 mRModelToBox = InvWorldB; InvWorldB.GetTrans(mTModelToBox);
225 mRBoxToModel = WorldB; WorldB.GetTrans(mTBoxToModel);
228 // 3) Setup destination pointer
229 mTouchedPrimitives = &cache.TouchedPrimitives;
231 // 4) Special case: 1-triangle meshes [Opcode 1.3]
232 if(mCurrentModel && mCurrentModel->HasSingleNode())
234 if(!SkipPrimitiveTests())
236 // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0.
237 mTouchedPrimitives->Reset();
239 // Perform overlap test between the unique triangle and the box (and set contact status if needed)
240 OBB_PRIM(udword(0), OPC_CONTACT)
242 // Return immediately regardless of status
247 // 5) Check temporal coherence:
248 if(TemporalCoherenceEnabled())
250 // Here we use temporal coherence
251 // => check results from previous frame before performing the collision query
252 if(FirstContactEnabled())
254 // We're only interested in the first contact found => test the unique previously touched face
255 if(mTouchedPrimitives->GetNbEntries())
257 // Get index of previously touched face = the first entry in the array
258 udword PreviouslyTouchedFace = mTouchedPrimitives->GetEntry(0);
260 // Then reset the array:
261 // - if the overlap test below is successful, the index we'll get added back anyway
262 // - if it isn't, then the array should be reset anyway for the normal query
263 mTouchedPrimitives->Reset();
265 // Perform overlap test between the cached triangle and the box (and set contact status if needed)
266 OBB_PRIM(PreviouslyTouchedFace, OPC_TEMPORAL_CONTACT)
268 // Return immediately if possible
269 if(GetContactStatus()) return TRUE;
271 // else no face has been touched during previous query
272 // => we'll have to perform a normal query
277 OBB TestBox(mTBoxToModel, mBoxExtents, mRBoxToModel);
279 // We're interested in all contacts =>test the new real box N(ew) against the previous fat box P(revious):
280 if(IsCacheValid(cache) && TestBox.IsInside(cache.FatBox))
282 // - if N is included in P, return previous list
283 // => we simply leave the list (mTouchedFaces) unchanged
285 // Set contact status if needed
286 if(mTouchedPrimitives->GetNbEntries()) mFlags |= OPC_TEMPORAL_CONTACT;
288 // In any case we don't need to do a query
293 // - else do the query using a fat N
295 // Reset cache since we'll about to perform a real query
296 mTouchedPrimitives->Reset();
298 // Make a fat box so that coherence will work for subsequent frames
299 TestBox.mExtents *= cache.FatCoeff;
300 mBoxExtents *= cache.FatCoeff;
302 // Update cache with query data (signature for cached faces)
303 cache.FatBox = TestBox;
309 // Here we don't use temporal coherence => do a normal query
310 mTouchedPrimitives->Reset();
313 // Now we can precompute box-box data
315 // Precompute absolute box-to-model rotation matrix
316 for(udword i=0;i<3;i++)
318 for(udword j=0;j<3;j++)
320 // Epsilon value prevents floating-point inaccuracies (strategy borrowed from RAPID)
321 mAR.m[i][j] = 1e-6f + fabsf(mRBoxToModel.m[i][j]);
325 // Precompute bounds for box-in-box test
326 mB0 = mBoxExtents - mTModelToBox;
327 mB1 = - mBoxExtents - mTModelToBox;
329 // Precompute box-box data - Courtesy of Erwin de Vries
330 mBBx1 = mBoxExtents.x*mAR.m[0][0] + mBoxExtents.y*mAR.m[1][0] + mBoxExtents.z*mAR.m[2][0];
331 mBBy1 = mBoxExtents.x*mAR.m[0][1] + mBoxExtents.y*mAR.m[1][1] + mBoxExtents.z*mAR.m[2][1];
332 mBBz1 = mBoxExtents.x*mAR.m[0][2] + mBoxExtents.y*mAR.m[1][2] + mBoxExtents.z*mAR.m[2][2];
334 mBB_1 = mBoxExtents.y*mAR.m[2][0] + mBoxExtents.z*mAR.m[1][0];
335 mBB_2 = mBoxExtents.x*mAR.m[2][0] + mBoxExtents.z*mAR.m[0][0];
336 mBB_3 = mBoxExtents.x*mAR.m[1][0] + mBoxExtents.y*mAR.m[0][0];
337 mBB_4 = mBoxExtents.y*mAR.m[2][1] + mBoxExtents.z*mAR.m[1][1];
338 mBB_5 = mBoxExtents.x*mAR.m[2][1] + mBoxExtents.z*mAR.m[0][1];
339 mBB_6 = mBoxExtents.x*mAR.m[1][1] + mBoxExtents.y*mAR.m[0][1];
340 mBB_7 = mBoxExtents.y*mAR.m[2][2] + mBoxExtents.z*mAR.m[1][2];
341 mBB_8 = mBoxExtents.x*mAR.m[2][2] + mBoxExtents.z*mAR.m[0][2];
342 mBB_9 = mBoxExtents.x*mAR.m[1][2] + mBoxExtents.y*mAR.m[0][2];
347 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
349 * Checks the OBB completely contains the box. In which case we can end the query sooner.
350 * \param bc [in] box center
351 * \param be [in] box extents
352 * \return true if the OBB contains the whole box
354 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
355 inline_ BOOL OBBCollider::OBBContainsBox(const Point& bc, const Point& be)
357 // I assume if all 8 box vertices are inside the OBB, so does the whole box.
358 // Sounds ok but maybe there's a better way?
360 #define TEST_PT(a,b,c) \
361 p.x=a; p.y=b; p.z=c; p+=bc; \
362 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;\
363 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;\
364 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;
369 TEST_PT(be.x, be.y, be.z)
370 TEST_PT(-be.x, be.y, be.z)
371 TEST_PT(be.x, -be.y, be.z)
372 TEST_PT(-be.x, -be.y, be.z)
373 TEST_PT(be.x, be.y, -be.z)
374 TEST_PT(-be.x, be.y, -be.z)
375 TEST_PT(be.x, -be.y, -be.z)
376 TEST_PT(-be.x, -be.y, -be.z)
382 // - compute model-box's AABB in OBB space
383 // - test AABB-in-AABB
384 float NCx = bc.x * mRModelToBox.m[0][0] + bc.y * mRModelToBox.m[1][0] + bc.z * mRModelToBox.m[2][0];
385 float NEx = fabsf(mRModelToBox.m[0][0] * be.x) + fabsf(mRModelToBox.m[1][0] * be.y) + fabsf(mRModelToBox.m[2][0] * be.z);
387 if(mB0.x < NCx+NEx) return FALSE;
388 if(mB1.x > NCx-NEx) return FALSE;
390 float NCy = bc.x * mRModelToBox.m[0][1] + bc.y * mRModelToBox.m[1][1] + bc.z * mRModelToBox.m[2][1];
391 float NEy = fabsf(mRModelToBox.m[0][1] * be.x) + fabsf(mRModelToBox.m[1][1] * be.y) + fabsf(mRModelToBox.m[2][1] * be.z);
393 if(mB0.y < NCy+NEy) return FALSE;
394 if(mB1.y > NCy-NEy) return FALSE;
396 float NCz = bc.x * mRModelToBox.m[0][2] + bc.y * mRModelToBox.m[1][2] + bc.z * mRModelToBox.m[2][2];
397 float NEz = fabsf(mRModelToBox.m[0][2] * be.x) + fabsf(mRModelToBox.m[1][2] * be.y) + fabsf(mRModelToBox.m[2][2] * be.z);
399 if(mB0.z < NCz+NEz) return FALSE;
400 if(mB1.z > NCz-NEz) return FALSE;
405 #define TEST_BOX_IN_OBB(center, extents) \
406 if(OBBContainsBox(center, extents)) \
408 /* Set contact status */ \
409 mFlags |= OPC_CONTACT; \
414 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
416 * Recursive collision query for normal AABB trees.
417 * \param node [in] current collision node
419 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
420 void OBBCollider::_Collide(const AABBCollisionNode* node)
422 // Perform OBB-AABB overlap test
423 if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
425 TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents)
429 OBB_PRIM(node->GetPrimitive(), OPC_CONTACT)
433 _Collide(node->GetPos());
435 if(ContactFound()) return;
437 _Collide(node->GetNeg());
441 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
443 * Recursive collision query for normal AABB trees, without primitive tests.
444 * \param node [in] current collision node
446 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
447 void OBBCollider::_CollideNoPrimitiveTest(const AABBCollisionNode* node)
449 // Perform OBB-AABB overlap test
450 if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
452 TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents)
456 SET_CONTACT(node->GetPrimitive(), OPC_CONTACT)
460 _CollideNoPrimitiveTest(node->GetPos());
462 if(ContactFound()) return;
464 _CollideNoPrimitiveTest(node->GetNeg());
468 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
470 * Recursive collision query for quantized AABB trees.
471 * \param node [in] current collision node
473 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
474 void OBBCollider::_Collide(const AABBQuantizedNode* node)
477 const QuantizedAABB& Box = node->mAABB;
478 const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
479 const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
481 // Perform OBB-AABB overlap test
482 if(!BoxBoxOverlap(Extents, Center)) return;
484 TEST_BOX_IN_OBB(Center, Extents)
488 OBB_PRIM(node->GetPrimitive(), OPC_CONTACT)
492 _Collide(node->GetPos());
494 if(ContactFound()) return;
496 _Collide(node->GetNeg());
500 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
502 * Recursive collision query for quantized AABB trees, without primitive tests.
503 * \param node [in] current collision node
505 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
506 void OBBCollider::_CollideNoPrimitiveTest(const AABBQuantizedNode* node)
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);
513 // Perform OBB-AABB overlap test
514 if(!BoxBoxOverlap(Extents, Center)) return;
516 TEST_BOX_IN_OBB(Center, Extents)
520 SET_CONTACT(node->GetPrimitive(), OPC_CONTACT)
524 _CollideNoPrimitiveTest(node->GetPos());
526 if(ContactFound()) return;
528 _CollideNoPrimitiveTest(node->GetNeg());
532 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
534 * Recursive collision query for no-leaf AABB trees.
535 * \param node [in] current collision node
537 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
538 void OBBCollider::_Collide(const AABBNoLeafNode* node)
540 // Perform OBB-AABB overlap test
541 if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
543 TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents)
545 if(node->HasPosLeaf()) { OBB_PRIM(node->GetPosPrimitive(), OPC_CONTACT) }
546 else _Collide(node->GetPos());
548 if(ContactFound()) return;
550 if(node->HasNegLeaf()) { OBB_PRIM(node->GetNegPrimitive(), OPC_CONTACT) }
551 else _Collide(node->GetNeg());
554 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
556 * Recursive collision query for no-leaf AABB trees, without primitive tests.
557 * \param node [in] current collision node
559 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
560 void OBBCollider::_CollideNoPrimitiveTest(const AABBNoLeafNode* node)
562 // Perform OBB-AABB overlap test
563 if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
565 TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents)
567 if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) }
568 else _CollideNoPrimitiveTest(node->GetPos());
570 if(ContactFound()) return;
572 if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) }
573 else _CollideNoPrimitiveTest(node->GetNeg());
576 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
578 * Recursive collision query for quantized no-leaf AABB trees.
579 * \param node [in] current collision node
581 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
582 void OBBCollider::_Collide(const AABBQuantizedNoLeafNode* node)
585 const QuantizedAABB& Box = node->mAABB;
586 const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
587 const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
589 // Perform OBB-AABB overlap test
590 if(!BoxBoxOverlap(Extents, Center)) return;
592 TEST_BOX_IN_OBB(Center, Extents)
594 if(node->HasPosLeaf()) { OBB_PRIM(node->GetPosPrimitive(), OPC_CONTACT) }
595 else _Collide(node->GetPos());
597 if(ContactFound()) return;
599 if(node->HasNegLeaf()) { OBB_PRIM(node->GetNegPrimitive(), OPC_CONTACT) }
600 else _Collide(node->GetNeg());
603 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
605 * Recursive collision query for quantized no-leaf AABB trees, without primitive tests.
606 * \param node [in] current collision node
608 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
609 void OBBCollider::_CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node)
612 const QuantizedAABB& Box = node->mAABB;
613 const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
614 const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
616 // Perform OBB-AABB overlap test
617 if(!BoxBoxOverlap(Extents, Center)) return;
619 TEST_BOX_IN_OBB(Center, Extents)
621 if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) }
622 else _CollideNoPrimitiveTest(node->GetPos());
624 if(ContactFound()) return;
626 if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) }
627 else _CollideNoPrimitiveTest(node->GetNeg());
635 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
639 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
640 HybridOBBCollider::HybridOBBCollider()
644 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
648 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
649 HybridOBBCollider::~HybridOBBCollider()
653 bool HybridOBBCollider::Collide(OBBCache& cache, const OBB& box, const HybridModel& model, const Matrix4x4* worldb, const Matrix4x4* worldm)
655 // We don't want primitive tests here!
656 mFlags |= OPC_NO_PRIMITIVE_TESTS;
659 if(!Setup(&model)) return false;
661 // Init collision query
662 if(InitQuery(cache, box, worldb, worldm)) return true;
664 // Special case for 1-leaf trees
665 if(mCurrentModel && mCurrentModel->HasSingleNode())
667 // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles
668 udword Nb = mIMesh->GetNbTriangles();
670 // Loop through all triangles
671 for(udword i=0;i<Nb;i++)
673 OBB_PRIM(i, OPC_CONTACT)
678 // Override destination array since we're only going to get leaf boxes here
679 mTouchedBoxes.Reset();
680 mTouchedPrimitives = &mTouchedBoxes;
682 // Now, do the actual query against leaf boxes
683 if(!model.HasLeafNodes())
685 if(model.IsQuantized())
687 const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree();
689 // Setup dequantization coeffs
690 mCenterCoeff = Tree->mCenterCoeff;
691 mExtentsCoeff = Tree->mExtentsCoeff;
693 // Perform collision query - we don't want primitive tests here!
694 _CollideNoPrimitiveTest(Tree->GetNodes());
698 const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
700 // Perform collision query - we don't want primitive tests here!
701 _CollideNoPrimitiveTest(Tree->GetNodes());
706 if(model.IsQuantized())
708 const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
710 // Setup dequantization coeffs
711 mCenterCoeff = Tree->mCenterCoeff;
712 mExtentsCoeff = Tree->mExtentsCoeff;
714 // Perform collision query - we don't want primitive tests here!
715 _CollideNoPrimitiveTest(Tree->GetNodes());
719 const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
721 // Perform collision query - we don't want primitive tests here!
722 _CollideNoPrimitiveTest(Tree->GetNodes());
726 // We only have a list of boxes so far
727 if(GetContactStatus())
729 // Reset contact status, since it currently only reflects collisions with leaf boxes
730 Collider::InitQuery();
732 // Change dest container so that we can use built-in overlap tests and get collided primitives
733 cache.TouchedPrimitives.Reset();
734 mTouchedPrimitives = &cache.TouchedPrimitives;
736 // Read touched leaf boxes
737 udword Nb = mTouchedBoxes.GetNbEntries();
738 const udword* Touched = mTouchedBoxes.GetEntries();
740 const LeafTriangles* LT = model.GetLeafTriangles();
741 const udword* Indices = model.GetIndices();
743 // Loop through touched leaves
746 const LeafTriangles& CurrentLeaf = LT[*Touched++];
748 // Each leaf box has a set of triangles
749 udword NbTris = CurrentLeaf.GetNbTriangles();
752 const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()];
754 // Loop through triangles and test each of them
757 udword TriangleIndex = *T++;
758 OBB_PRIM(TriangleIndex, OPC_CONTACT)
763 udword BaseIndex = CurrentLeaf.GetTriangleIndex();
765 // Loop through triangles and test each of them
768 udword TriangleIndex = BaseIndex++;
769 OBB_PRIM(TriangleIndex, OPC_CONTACT)