OPC_OBBCollider.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766
  1. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2. /*
  3. * OPCODE - Optimized Collision Detection
  4. * Copyright (C) 2001 Pierre Terdiman
  5. * Homepage: http://www.codercorner.com/Opcode.htm
  6. */
  7. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  8. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  9. /**
  10. * Contains code for an OBB collider.
  11. * \file OPC_OBBCollider.cpp
  12. * \author Pierre Terdiman
  13. * \date January, 1st, 2002
  14. */
  15. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  16. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  17. /**
  18. * Contains an OBB-vs-tree collider.
  19. *
  20. * \class OBBCollider
  21. * \author Pierre Terdiman
  22. * \version 1.3
  23. * \date January, 1st, 2002
  24. */
  25. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  27. #include "Opcode.h"
  28. using namespace Opcode;
  29. #include "OPC_BoxBoxOverlap.h"
  30. #include "OPC_TriBoxOverlap.h"
  31. #define SET_CONTACT(prim_index, flag) \
  32. /* Set contact status */ \
  33. mFlags |= flag; \
  34. mTouchedPrimitives->Add(prim_index);
  35. //! OBB-triangle test
  36. #define OBB_PRIM(prim_index, flag) \
  37. /* Request vertices from the app */ \
  38. VertexPointers VP; mIMesh->GetTriangle(VP, prim_index); \
  39. /* Transform them in a common space */ \
  40. TransformPoint(mLeafVerts[0], *VP.Vertex[0], mRModelToBox, mTModelToBox); \
  41. TransformPoint(mLeafVerts[1], *VP.Vertex[1], mRModelToBox, mTModelToBox); \
  42. TransformPoint(mLeafVerts[2], *VP.Vertex[2], mRModelToBox, mTModelToBox); \
  43. /* Perform triangle-box overlap test */ \
  44. if(TriBoxOverlap()) \
  45. { \
  46. SET_CONTACT(prim_index, flag) \
  47. }
  48. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  49. /**
  50. * Constructor.
  51. */
  52. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  53. OBBCollider::OBBCollider() : mFullBoxBoxTest(true)
  54. {
  55. }
  56. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  57. /**
  58. * Destructor.
  59. */
  60. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  61. OBBCollider::~OBBCollider()
  62. {
  63. }
  64. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  65. /**
  66. * Validates current settings. You should call this method after all the settings and callbacks have been defined.
  67. * \return null if everything is ok, else a string describing the problem
  68. */
  69. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  70. const char* OBBCollider::ValidateSettings()
  71. {
  72. if(TemporalCoherenceEnabled() && !FirstContactEnabled()) return "Temporal coherence only works with ""First contact"" mode!";
  73. return VolumeCollider::ValidateSettings();
  74. }
  75. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  76. /**
  77. * Generic collision query for generic OPCODE models. After the call, access the results:
  78. * - with GetContactStatus()
  79. * - with GetNbTouchedPrimitives()
  80. * - with GetTouchedPrimitives()
  81. *
  82. * \param cache [in/out] a box cache
  83. * \param box [in] collision OBB in local space
  84. * \param model [in] Opcode model to collide with
  85. * \param worldb [in] OBB's world matrix, or null
  86. * \param worldm [in] model's world matrix, or null
  87. * \return true if success
  88. * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
  89. */
  90. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  91. bool OBBCollider::Collide(OBBCache& cache, const OBB& box, const Model& model, const Matrix4x4* worldb, const Matrix4x4* worldm)
  92. {
  93. // Checkings
  94. if(!Setup(&model)) return false;
  95. // Init collision query
  96. if(InitQuery(cache, box, worldb, worldm)) return true;
  97. if(!model.HasLeafNodes())
  98. {
  99. if(model.IsQuantized())
  100. {
  101. const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree();
  102. // Setup dequantization coeffs
  103. mCenterCoeff = Tree->mCenterCoeff;
  104. mExtentsCoeff = Tree->mExtentsCoeff;
  105. // Perform collision query
  106. if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
  107. else _Collide(Tree->GetNodes());
  108. }
  109. else
  110. {
  111. const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
  112. // Perform collision query
  113. if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
  114. else _Collide(Tree->GetNodes());
  115. }
  116. }
  117. else
  118. {
  119. if(model.IsQuantized())
  120. {
  121. const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
  122. // Setup dequantization coeffs
  123. mCenterCoeff = Tree->mCenterCoeff;
  124. mExtentsCoeff = Tree->mExtentsCoeff;
  125. // Perform collision query
  126. if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
  127. else _Collide(Tree->GetNodes());
  128. }
  129. else
  130. {
  131. const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
  132. // Perform collision query
  133. if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes());
  134. else _Collide(Tree->GetNodes());
  135. }
  136. }
  137. return true;
  138. }
  139. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  140. /**
  141. * Initializes a collision query :
  142. * - reset stats & contact status
  143. * - setup matrices
  144. * - check temporal coherence
  145. *
  146. * \param cache [in/out] a box cache
  147. * \param box [in] obb in local space
  148. * \param worldb [in] obb's world matrix, or null
  149. * \param worldm [in] model's world matrix, or null
  150. * \return TRUE if we can return immediately
  151. * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only.
  152. */
  153. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  154. BOOL OBBCollider::InitQuery(OBBCache& cache, const OBB& box, const Matrix4x4* worldb, const Matrix4x4* worldm)
  155. {
  156. // 1) Call the base method
  157. VolumeCollider::InitQuery();
  158. // 2) Compute obb in world space
  159. mBoxExtents = box.mExtents;
  160. Matrix4x4 WorldB;
  161. if(worldb)
  162. {
  163. WorldB = Matrix4x4( box.mRot * Matrix3x3(*worldb) );
  164. WorldB.SetTrans(box.mCenter * *worldb);
  165. }
  166. else
  167. {
  168. WorldB = box.mRot;
  169. WorldB.SetTrans(box.mCenter);
  170. }
  171. // Setup matrices
  172. Matrix4x4 InvWorldB;
  173. InvertPRMatrix(InvWorldB, WorldB);
  174. if(worldm)
  175. {
  176. Matrix4x4 InvWorldM;
  177. InvertPRMatrix(InvWorldM, *worldm);
  178. Matrix4x4 WorldBtoM = WorldB * InvWorldM;
  179. Matrix4x4 WorldMtoB = *worldm * InvWorldB;
  180. mRModelToBox = WorldMtoB; WorldMtoB.GetTrans(mTModelToBox);
  181. mRBoxToModel = WorldBtoM; WorldBtoM.GetTrans(mTBoxToModel);
  182. }
  183. else
  184. {
  185. mRModelToBox = InvWorldB; InvWorldB.GetTrans(mTModelToBox);
  186. mRBoxToModel = WorldB; WorldB.GetTrans(mTBoxToModel);
  187. }
  188. // 3) Setup destination pointer
  189. mTouchedPrimitives = &cache.TouchedPrimitives;
  190. // 4) Special case: 1-triangle meshes [Opcode 1.3]
  191. if(mCurrentModel && mCurrentModel->HasSingleNode())
  192. {
  193. if(!SkipPrimitiveTests())
  194. {
  195. // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0.
  196. mTouchedPrimitives->Reset();
  197. // Perform overlap test between the unique triangle and the box (and set contact status if needed)
  198. OBB_PRIM(udword(0), OPC_CONTACT)
  199. // Return immediately regardless of status
  200. return TRUE;
  201. }
  202. }
  203. // 5) Check temporal coherence:
  204. if(TemporalCoherenceEnabled())
  205. {
  206. // Here we use temporal coherence
  207. // => check results from previous frame before performing the collision query
  208. if(FirstContactEnabled())
  209. {
  210. // We're only interested in the first contact found => test the unique previously touched face
  211. if(mTouchedPrimitives->GetNbEntries())
  212. {
  213. // Get index of previously touched face = the first entry in the array
  214. udword PreviouslyTouchedFace = mTouchedPrimitives->GetEntry(0);
  215. // Then reset the array:
  216. // - if the overlap test below is successful, the index we'll get added back anyway
  217. // - if it isn't, then the array should be reset anyway for the normal query
  218. mTouchedPrimitives->Reset();
  219. // Perform overlap test between the cached triangle and the box (and set contact status if needed)
  220. OBB_PRIM(PreviouslyTouchedFace, OPC_TEMPORAL_CONTACT)
  221. // Return immediately if possible
  222. if(GetContactStatus()) return TRUE;
  223. }
  224. // else no face has been touched during previous query
  225. // => we'll have to perform a normal query
  226. }
  227. else
  228. {
  229. // ### rewrite this
  230. OBB TestBox(mTBoxToModel, mBoxExtents, mRBoxToModel);
  231. // We're interested in all contacts =>test the new real box N(ew) against the previous fat box P(revious):
  232. if(IsCacheValid(cache) && TestBox.IsInside(cache.FatBox))
  233. {
  234. // - if N is included in P, return previous list
  235. // => we simply leave the list (mTouchedFaces) unchanged
  236. // Set contact status if needed
  237. if(mTouchedPrimitives->GetNbEntries()) mFlags |= OPC_TEMPORAL_CONTACT;
  238. // In any case we don't need to do a query
  239. return TRUE;
  240. }
  241. else
  242. {
  243. // - else do the query using a fat N
  244. // Reset cache since we'll about to perform a real query
  245. mTouchedPrimitives->Reset();
  246. // Make a fat box so that coherence will work for subsequent frames
  247. TestBox.mExtents *= cache.FatCoeff;
  248. mBoxExtents *= cache.FatCoeff;
  249. // Update cache with query data (signature for cached faces)
  250. cache.FatBox = TestBox;
  251. }
  252. }
  253. }
  254. else
  255. {
  256. // Here we don't use temporal coherence => do a normal query
  257. mTouchedPrimitives->Reset();
  258. }
  259. // Now we can precompute box-box data
  260. // Precompute absolute box-to-model rotation matrix
  261. for(udword i=0;i<3;i++)
  262. {
  263. for(udword j=0;j<3;j++)
  264. {
  265. // Epsilon value prevents floating-point inaccuracies (strategy borrowed from RAPID)
  266. mAR.m[i][j] = 1e-6f + fabsf(mRBoxToModel.m[i][j]);
  267. }
  268. }
  269. // Precompute bounds for box-in-box test
  270. mB0 = mBoxExtents - mTModelToBox;
  271. mB1 = - mBoxExtents - mTModelToBox;
  272. // Precompute box-box data - Courtesy of Erwin de Vries
  273. mBBx1 = mBoxExtents.x*mAR.m[0][0] + mBoxExtents.y*mAR.m[1][0] + mBoxExtents.z*mAR.m[2][0];
  274. mBBy1 = mBoxExtents.x*mAR.m[0][1] + mBoxExtents.y*mAR.m[1][1] + mBoxExtents.z*mAR.m[2][1];
  275. mBBz1 = mBoxExtents.x*mAR.m[0][2] + mBoxExtents.y*mAR.m[1][2] + mBoxExtents.z*mAR.m[2][2];
  276. mBB_1 = mBoxExtents.y*mAR.m[2][0] + mBoxExtents.z*mAR.m[1][0];
  277. mBB_2 = mBoxExtents.x*mAR.m[2][0] + mBoxExtents.z*mAR.m[0][0];
  278. mBB_3 = mBoxExtents.x*mAR.m[1][0] + mBoxExtents.y*mAR.m[0][0];
  279. mBB_4 = mBoxExtents.y*mAR.m[2][1] + mBoxExtents.z*mAR.m[1][1];
  280. mBB_5 = mBoxExtents.x*mAR.m[2][1] + mBoxExtents.z*mAR.m[0][1];
  281. mBB_6 = mBoxExtents.x*mAR.m[1][1] + mBoxExtents.y*mAR.m[0][1];
  282. mBB_7 = mBoxExtents.y*mAR.m[2][2] + mBoxExtents.z*mAR.m[1][2];
  283. mBB_8 = mBoxExtents.x*mAR.m[2][2] + mBoxExtents.z*mAR.m[0][2];
  284. mBB_9 = mBoxExtents.x*mAR.m[1][2] + mBoxExtents.y*mAR.m[0][2];
  285. return FALSE;
  286. }
  287. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  288. /**
  289. * Checks the OBB completely contains the box. In which case we can end the query sooner.
  290. * \param bc [in] box center
  291. * \param be [in] box extents
  292. * \return true if the OBB contains the whole box
  293. */
  294. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  295. inline_ BOOL OBBCollider::OBBContainsBox(const Point& bc, const Point& be)
  296. {
  297. // I assume if all 8 box vertices are inside the OBB, so does the whole box.
  298. // Sounds ok but maybe there's a better way?
  299. /*
  300. #define TEST_PT(a,b,c) \
  301. p.x=a; p.y=b; p.z=c; p+=bc; \
  302. 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;\
  303. 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;\
  304. 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;
  305. Point p;
  306. float f;
  307. TEST_PT(be.x, be.y, be.z)
  308. TEST_PT(-be.x, be.y, be.z)
  309. TEST_PT(be.x, -be.y, be.z)
  310. TEST_PT(-be.x, -be.y, be.z)
  311. TEST_PT(be.x, be.y, -be.z)
  312. TEST_PT(-be.x, be.y, -be.z)
  313. TEST_PT(be.x, -be.y, -be.z)
  314. TEST_PT(-be.x, -be.y, -be.z)
  315. return TRUE;
  316. */
  317. // Yes there is:
  318. // - compute model-box's AABB in OBB space
  319. // - test AABB-in-AABB
  320. float NCx = bc.x * mRModelToBox.m[0][0] + bc.y * mRModelToBox.m[1][0] + bc.z * mRModelToBox.m[2][0];
  321. float NEx = fabsf(mRModelToBox.m[0][0] * be.x) + fabsf(mRModelToBox.m[1][0] * be.y) + fabsf(mRModelToBox.m[2][0] * be.z);
  322. if(mB0.x < NCx+NEx) return FALSE;
  323. if(mB1.x > NCx-NEx) return FALSE;
  324. float NCy = bc.x * mRModelToBox.m[0][1] + bc.y * mRModelToBox.m[1][1] + bc.z * mRModelToBox.m[2][1];
  325. float NEy = fabsf(mRModelToBox.m[0][1] * be.x) + fabsf(mRModelToBox.m[1][1] * be.y) + fabsf(mRModelToBox.m[2][1] * be.z);
  326. if(mB0.y < NCy+NEy) return FALSE;
  327. if(mB1.y > NCy-NEy) return FALSE;
  328. float NCz = bc.x * mRModelToBox.m[0][2] + bc.y * mRModelToBox.m[1][2] + bc.z * mRModelToBox.m[2][2];
  329. float NEz = fabsf(mRModelToBox.m[0][2] * be.x) + fabsf(mRModelToBox.m[1][2] * be.y) + fabsf(mRModelToBox.m[2][2] * be.z);
  330. if(mB0.z < NCz+NEz) return FALSE;
  331. if(mB1.z > NCz-NEz) return FALSE;
  332. return TRUE;
  333. }
  334. #define TEST_BOX_IN_OBB(center, extents) \
  335. if(OBBContainsBox(center, extents)) \
  336. { \
  337. /* Set contact status */ \
  338. mFlags |= OPC_CONTACT; \
  339. _Dump(node); \
  340. return; \
  341. }
  342. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  343. /**
  344. * Recursive collision query for normal AABB trees.
  345. * \param node [in] current collision node
  346. */
  347. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  348. void OBBCollider::_Collide(const AABBCollisionNode* node)
  349. {
  350. // Perform OBB-AABB overlap test
  351. if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
  352. TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents)
  353. if(node->IsLeaf())
  354. {
  355. OBB_PRIM(node->GetPrimitive(), OPC_CONTACT)
  356. }
  357. else
  358. {
  359. _Collide(node->GetPos());
  360. if(ContactFound()) return;
  361. _Collide(node->GetNeg());
  362. }
  363. }
  364. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  365. /**
  366. * Recursive collision query for normal AABB trees, without primitive tests.
  367. * \param node [in] current collision node
  368. */
  369. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  370. void OBBCollider::_CollideNoPrimitiveTest(const AABBCollisionNode* node)
  371. {
  372. // Perform OBB-AABB overlap test
  373. if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
  374. TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents)
  375. if(node->IsLeaf())
  376. {
  377. SET_CONTACT(node->GetPrimitive(), OPC_CONTACT)
  378. }
  379. else
  380. {
  381. _CollideNoPrimitiveTest(node->GetPos());
  382. if(ContactFound()) return;
  383. _CollideNoPrimitiveTest(node->GetNeg());
  384. }
  385. }
  386. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  387. /**
  388. * Recursive collision query for quantized AABB trees.
  389. * \param node [in] current collision node
  390. */
  391. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  392. void OBBCollider::_Collide(const AABBQuantizedNode* node)
  393. {
  394. // Dequantize box
  395. const QuantizedAABB& Box = node->mAABB;
  396. const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
  397. const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
  398. // Perform OBB-AABB overlap test
  399. if(!BoxBoxOverlap(Extents, Center)) return;
  400. TEST_BOX_IN_OBB(Center, Extents)
  401. if(node->IsLeaf())
  402. {
  403. OBB_PRIM(node->GetPrimitive(), OPC_CONTACT)
  404. }
  405. else
  406. {
  407. _Collide(node->GetPos());
  408. if(ContactFound()) return;
  409. _Collide(node->GetNeg());
  410. }
  411. }
  412. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  413. /**
  414. * Recursive collision query for quantized AABB trees, without primitive tests.
  415. * \param node [in] current collision node
  416. */
  417. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  418. void OBBCollider::_CollideNoPrimitiveTest(const AABBQuantizedNode* node)
  419. {
  420. // Dequantize box
  421. const QuantizedAABB& Box = node->mAABB;
  422. const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
  423. const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
  424. // Perform OBB-AABB overlap test
  425. if(!BoxBoxOverlap(Extents, Center)) return;
  426. TEST_BOX_IN_OBB(Center, Extents)
  427. if(node->IsLeaf())
  428. {
  429. SET_CONTACT(node->GetPrimitive(), OPC_CONTACT)
  430. }
  431. else
  432. {
  433. _CollideNoPrimitiveTest(node->GetPos());
  434. if(ContactFound()) return;
  435. _CollideNoPrimitiveTest(node->GetNeg());
  436. }
  437. }
  438. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  439. /**
  440. * Recursive collision query for no-leaf AABB trees.
  441. * \param node [in] current collision node
  442. */
  443. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  444. void OBBCollider::_Collide(const AABBNoLeafNode* node)
  445. {
  446. // Perform OBB-AABB overlap test
  447. if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
  448. TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents)
  449. if(node->HasPosLeaf()) { OBB_PRIM(node->GetPosPrimitive(), OPC_CONTACT) }
  450. else _Collide(node->GetPos());
  451. if(ContactFound()) return;
  452. if(node->HasNegLeaf()) { OBB_PRIM(node->GetNegPrimitive(), OPC_CONTACT) }
  453. else _Collide(node->GetNeg());
  454. }
  455. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  456. /**
  457. * Recursive collision query for no-leaf AABB trees, without primitive tests.
  458. * \param node [in] current collision node
  459. */
  460. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  461. void OBBCollider::_CollideNoPrimitiveTest(const AABBNoLeafNode* node)
  462. {
  463. // Perform OBB-AABB overlap test
  464. if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return;
  465. TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents)
  466. if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) }
  467. else _CollideNoPrimitiveTest(node->GetPos());
  468. if(ContactFound()) return;
  469. if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) }
  470. else _CollideNoPrimitiveTest(node->GetNeg());
  471. }
  472. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  473. /**
  474. * Recursive collision query for quantized no-leaf AABB trees.
  475. * \param node [in] current collision node
  476. */
  477. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  478. void OBBCollider::_Collide(const AABBQuantizedNoLeafNode* node)
  479. {
  480. // Dequantize box
  481. const QuantizedAABB& Box = node->mAABB;
  482. const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
  483. const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
  484. // Perform OBB-AABB overlap test
  485. if(!BoxBoxOverlap(Extents, Center)) return;
  486. TEST_BOX_IN_OBB(Center, Extents)
  487. if(node->HasPosLeaf()) { OBB_PRIM(node->GetPosPrimitive(), OPC_CONTACT) }
  488. else _Collide(node->GetPos());
  489. if(ContactFound()) return;
  490. if(node->HasNegLeaf()) { OBB_PRIM(node->GetNegPrimitive(), OPC_CONTACT) }
  491. else _Collide(node->GetNeg());
  492. }
  493. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  494. /**
  495. * Recursive collision query for quantized no-leaf AABB trees, without primitive tests.
  496. * \param node [in] current collision node
  497. */
  498. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  499. void OBBCollider::_CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node)
  500. {
  501. // Dequantize box
  502. const QuantizedAABB& Box = node->mAABB;
  503. const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z);
  504. const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z);
  505. // Perform OBB-AABB overlap test
  506. if(!BoxBoxOverlap(Extents, Center)) return;
  507. TEST_BOX_IN_OBB(Center, Extents)
  508. if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) }
  509. else _CollideNoPrimitiveTest(node->GetPos());
  510. if(ContactFound()) return;
  511. if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) }
  512. else _CollideNoPrimitiveTest(node->GetNeg());
  513. }
  514. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  515. /**
  516. * Constructor.
  517. */
  518. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  519. HybridOBBCollider::HybridOBBCollider()
  520. {
  521. }
  522. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  523. /**
  524. * Destructor.
  525. */
  526. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  527. HybridOBBCollider::~HybridOBBCollider()
  528. {
  529. }
  530. bool HybridOBBCollider::Collide(OBBCache& cache, const OBB& box, const HybridModel& model, const Matrix4x4* worldb, const Matrix4x4* worldm)
  531. {
  532. // We don't want primitive tests here!
  533. mFlags |= OPC_NO_PRIMITIVE_TESTS;
  534. // Checkings
  535. if(!Setup(&model)) return false;
  536. // Init collision query
  537. if(InitQuery(cache, box, worldb, worldm)) return true;
  538. // Special case for 1-leaf trees
  539. if(mCurrentModel && mCurrentModel->HasSingleNode())
  540. {
  541. // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles
  542. udword Nb = mIMesh->GetNbTriangles();
  543. // Loop through all triangles
  544. for(udword i=0;i<Nb;i++)
  545. {
  546. OBB_PRIM(i, OPC_CONTACT)
  547. }
  548. return true;
  549. }
  550. // Override destination array since we're only going to get leaf boxes here
  551. mTouchedBoxes.Reset();
  552. mTouchedPrimitives = &mTouchedBoxes;
  553. // Now, do the actual query against leaf boxes
  554. if(!model.HasLeafNodes())
  555. {
  556. if(model.IsQuantized())
  557. {
  558. const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree();
  559. // Setup dequantization coeffs
  560. mCenterCoeff = Tree->mCenterCoeff;
  561. mExtentsCoeff = Tree->mExtentsCoeff;
  562. // Perform collision query - we don't want primitive tests here!
  563. _CollideNoPrimitiveTest(Tree->GetNodes());
  564. }
  565. else
  566. {
  567. const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree();
  568. // Perform collision query - we don't want primitive tests here!
  569. _CollideNoPrimitiveTest(Tree->GetNodes());
  570. }
  571. }
  572. else
  573. {
  574. if(model.IsQuantized())
  575. {
  576. const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree();
  577. // Setup dequantization coeffs
  578. mCenterCoeff = Tree->mCenterCoeff;
  579. mExtentsCoeff = Tree->mExtentsCoeff;
  580. // Perform collision query - we don't want primitive tests here!
  581. _CollideNoPrimitiveTest(Tree->GetNodes());
  582. }
  583. else
  584. {
  585. const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree();
  586. // Perform collision query - we don't want primitive tests here!
  587. _CollideNoPrimitiveTest(Tree->GetNodes());
  588. }
  589. }
  590. // We only have a list of boxes so far
  591. if(GetContactStatus())
  592. {
  593. // Reset contact status, since it currently only reflects collisions with leaf boxes
  594. Collider::InitQuery();
  595. // Change dest container so that we can use built-in overlap tests and get collided primitives
  596. cache.TouchedPrimitives.Reset();
  597. mTouchedPrimitives = &cache.TouchedPrimitives;
  598. // Read touched leaf boxes
  599. udword Nb = mTouchedBoxes.GetNbEntries();
  600. const udword* Touched = mTouchedBoxes.GetEntries();
  601. const LeafTriangles* LT = model.GetLeafTriangles();
  602. const udword* Indices = model.GetIndices();
  603. // Loop through touched leaves
  604. while(Nb--)
  605. {
  606. const LeafTriangles& CurrentLeaf = LT[*Touched++];
  607. // Each leaf box has a set of triangles
  608. udword NbTris = CurrentLeaf.GetNbTriangles();
  609. if(Indices)
  610. {
  611. const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()];
  612. // Loop through triangles and test each of them
  613. while(NbTris--)
  614. {
  615. udword TriangleIndex = *T++;
  616. OBB_PRIM(TriangleIndex, OPC_CONTACT)
  617. }
  618. }
  619. else
  620. {
  621. udword BaseIndex = CurrentLeaf.GetTriangleIndex();
  622. // Loop through triangles and test each of them
  623. while(NbTris--)
  624. {
  625. udword TriangleIndex = BaseIndex++;
  626. OBB_PRIM(TriangleIndex, OPC_CONTACT)
  627. }
  628. }
  629. }
  630. }
  631. return true;
  632. }