1 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
3 * OPCODE - Optimized Collision Detection
4 * Copyright (C) 2001 Pierre Terdiman
5 * Homepage: http://www.codercorner.com/Opcode.htm
7 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
9 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
11 * Contains code for an OBB collider.
12 * \file OPC_OBBCollider.cpp
13 * \author Pierre Terdiman
14 * \date January, 1st, 2002
16 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
18 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
20 * Contains an OBB-vs-tree collider.
23 * \author Pierre Terdiman
25 * \date January, 1st, 2002
27 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
29 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
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 */ \
41 mTouchedPrimitives->Add(udword(prim_index));
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 */ \
54 SET_CONTACT(prim_index, flag) \
57 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
61 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
62 OBBCollider::OBBCollider() : mFullBoxBoxTest(true)
66 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
70 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
71 OBBCollider::~OBBCollider()
75 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
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 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
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
)
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());
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());
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());
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());
162 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
164 * Initializes a collision query :
165 * - reset stats & contact status
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
;
189 WorldB
= Matrix4x4( box
.mRot
* Matrix3x3(*worldb
) );
190 WorldB
.SetTrans(box
.mCenter
* *worldb
);
195 WorldB
.SetTrans(box
.mCenter
);
200 InvertPRMatrix(InvWorldB
, WorldB
);
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
);
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
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
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
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
;
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];
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;
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)
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
;
396 #define TEST_BOX_IN_OBB(center, extents) \
397 if(OBBContainsBox(center, extents)) \
399 /* Set contact status */ \
400 mFlags |= OPC_CONTACT; \
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
)
420 OBB_PRIM(node
->GetPrimitive(), OPC_CONTACT
)
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
)
447 SET_CONTACT(node
->GetPrimitive(), OPC_CONTACT
)
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
)
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
)
479 OBB_PRIM(node
->GetPrimitive(), OPC_CONTACT
)
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
)
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
)
511 SET_CONTACT(node
->GetPrimitive(), OPC_CONTACT
)
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
)
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
)
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 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
630 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
631 HybridOBBCollider::HybridOBBCollider()
635 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
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
;
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
)
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());
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());
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());
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
737 const LeafTriangles
& CurrentLeaf
= LT
[*Touched
++];
739 // Each leaf box has a set of triangles
740 udword NbTris
= CurrentLeaf
.GetNbTriangles();
743 const udword
* T
= &Indices
[CurrentLeaf
.GetTriangleIndex()];
745 // Loop through triangles and test each of them
748 udword TriangleIndex
= *T
++;
749 OBB_PRIM(TriangleIndex
, OPC_CONTACT
)
754 udword BaseIndex
= CurrentLeaf
.GetTriangleIndex();
756 // Loop through triangles and test each of them
759 udword TriangleIndex
= BaseIndex
++;
760 OBB_PRIM(TriangleIndex
, OPC_CONTACT
)