OPC_SphereCollider.cpp 28 KB

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