2
0

OPC_SphereCollider.cpp 28 KB

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