2
0

BroadPhaseQuadTree.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #include <Jolt/Jolt.h>
  5. #include <Jolt/Physics/Collision/BroadPhase/BroadPhaseQuadTree.h>
  6. #include <Jolt/Physics/Collision/RayCast.h>
  7. #include <Jolt/Physics/Collision/AABoxCast.h>
  8. #include <Jolt/Physics/Collision/CastResult.h>
  9. #include <Jolt/Core/QuickSort.h>
  10. JPH_NAMESPACE_BEGIN
  11. BroadPhaseQuadTree::~BroadPhaseQuadTree()
  12. {
  13. delete [] mLayers;
  14. }
  15. void BroadPhaseQuadTree::Init(BodyManager *inBodyManager, const BroadPhaseLayerInterface &inLayerInterface)
  16. {
  17. BroadPhase::Init(inBodyManager, inLayerInterface);
  18. // Store input parameters
  19. mBroadPhaseLayerInterface = &inLayerInterface;
  20. mNumLayers = inLayerInterface.GetNumBroadPhaseLayers();
  21. JPH_ASSERT(mNumLayers < (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid);
  22. #ifdef JPH_ENABLE_ASSERTS
  23. // Store lock context
  24. mLockContext = inBodyManager;
  25. #endif // JPH_ENABLE_ASSERTS
  26. // Store max bodies
  27. mMaxBodies = inBodyManager->GetMaxBodies();
  28. // Initialize tracking data
  29. mTracking.resize(mMaxBodies);
  30. // Init allocator
  31. // Estimate the amount of nodes we're going to need
  32. uint32 num_leaves = (uint32)(mMaxBodies + 1) / 2; // Assume 50% fill
  33. uint32 num_leaves_plus_internal_nodes = num_leaves + (num_leaves + 2) / 3; // = Sum(num_leaves * 4^-i) with i = [0, Inf].
  34. mAllocator.Init(2 * num_leaves_plus_internal_nodes, 256); // We use double the amount of nodes while rebuilding the tree during Update()
  35. // Init sub trees
  36. mLayers = new QuadTree [mNumLayers];
  37. for (uint l = 0; l < mNumLayers; ++l)
  38. {
  39. mLayers[l].Init(mAllocator);
  40. #if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
  41. // Set the name of the layer
  42. mLayers[l].SetName(inLayerInterface.GetBroadPhaseLayerName(BroadPhaseLayer(BroadPhaseLayer::Type(l))));
  43. #endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED
  44. }
  45. }
  46. void BroadPhaseQuadTree::FrameSync()
  47. {
  48. JPH_PROFILE_FUNCTION();
  49. // Take a unique lock on the old query lock so that we know no one is using the old nodes anymore.
  50. // Note that nothing should be locked at this point to avoid risking a lock inversion deadlock.
  51. // Note that in other places where we lock this mutex we don't use SharedLock to detect lock inversions. As long as
  52. // nothing else is locked this is safe. This is why BroadPhaseQuery should be the highest priority lock.
  53. UniqueLock root_lock(mQueryLocks[mQueryLockIdx ^ 1] JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseQuery));
  54. for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)
  55. mLayers[l].DiscardOldTree();
  56. }
  57. void BroadPhaseQuadTree::Optimize()
  58. {
  59. JPH_PROFILE_FUNCTION();
  60. // Free the previous tree so we can create a new optimized tree
  61. FrameSync();
  62. LockModifications();
  63. for (uint l = 0; l < mNumLayers; ++l)
  64. {
  65. QuadTree &tree = mLayers[l];
  66. if (tree.HasBodies() || tree.IsDirty())
  67. {
  68. QuadTree::UpdateState update_state;
  69. tree.UpdatePrepare(mBodyManager->GetBodies(), mTracking, update_state, true);
  70. tree.UpdateFinalize(mBodyManager->GetBodies(), mTracking, update_state);
  71. }
  72. }
  73. UnlockModifications();
  74. // Free the tree from before we created a new optimized tree
  75. FrameSync();
  76. mNextLayerToUpdate = 0;
  77. }
  78. void BroadPhaseQuadTree::LockModifications()
  79. {
  80. // From this point on we prevent modifications to the tree
  81. PhysicsLock::sLock(mUpdateMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseUpdate));
  82. }
  83. BroadPhase::UpdateState BroadPhaseQuadTree::UpdatePrepare()
  84. {
  85. // LockModifications should have been called
  86. JPH_ASSERT(mUpdateMutex.is_locked());
  87. // Create update state
  88. UpdateState update_state;
  89. UpdateStateImpl *update_state_impl = reinterpret_cast<UpdateStateImpl *>(&update_state);
  90. // Loop until we've seen all layers
  91. for (uint iteration = 0; iteration < mNumLayers; ++iteration)
  92. {
  93. // Get the layer
  94. QuadTree &tree = mLayers[mNextLayerToUpdate];
  95. mNextLayerToUpdate = (mNextLayerToUpdate + 1) % mNumLayers;
  96. // If it is dirty we update this one
  97. if (tree.HasBodies() && tree.IsDirty() && tree.CanBeUpdated())
  98. {
  99. update_state_impl->mTree = &tree;
  100. tree.UpdatePrepare(mBodyManager->GetBodies(), mTracking, update_state_impl->mUpdateState, false);
  101. return update_state;
  102. }
  103. }
  104. // Nothing to update
  105. update_state_impl->mTree = nullptr;
  106. return update_state;
  107. }
  108. void BroadPhaseQuadTree::UpdateFinalize(const UpdateState &inUpdateState)
  109. {
  110. // LockModifications should have been called
  111. JPH_ASSERT(mUpdateMutex.is_locked());
  112. // Test if a tree was updated
  113. const UpdateStateImpl *update_state_impl = reinterpret_cast<const UpdateStateImpl *>(&inUpdateState);
  114. if (update_state_impl->mTree == nullptr)
  115. return;
  116. update_state_impl->mTree->UpdateFinalize(mBodyManager->GetBodies(), mTracking, update_state_impl->mUpdateState);
  117. // Make all queries from now on use the new lock
  118. mQueryLockIdx = mQueryLockIdx ^ 1;
  119. }
  120. void BroadPhaseQuadTree::UnlockModifications()
  121. {
  122. // From this point on we allow modifications to the tree again
  123. PhysicsLock::sUnlock(mUpdateMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseUpdate));
  124. }
  125. BroadPhase::AddState BroadPhaseQuadTree::AddBodiesPrepare(BodyID *ioBodies, int inNumber)
  126. {
  127. JPH_PROFILE_FUNCTION();
  128. if (inNumber <= 0)
  129. return nullptr;
  130. const BodyVector &bodies = mBodyManager->GetBodies();
  131. JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());
  132. LayerState *state = new LayerState [mNumLayers];
  133. // Sort bodies on layer
  134. Body * const * const bodies_ptr = bodies.data(); // C pointer or else sort is incredibly slow in debug mode
  135. QuickSort(ioBodies, ioBodies + inNumber, [bodies_ptr](BodyID inLHS, BodyID inRHS) { return bodies_ptr[inLHS.GetIndex()]->GetBroadPhaseLayer() < bodies_ptr[inRHS.GetIndex()]->GetBroadPhaseLayer(); });
  136. BodyID *b_start = ioBodies, *b_end = ioBodies + inNumber;
  137. while (b_start < b_end)
  138. {
  139. // Get broadphase layer
  140. BroadPhaseLayer::Type broadphase_layer = (BroadPhaseLayer::Type)bodies[b_start->GetIndex()]->GetBroadPhaseLayer();
  141. JPH_ASSERT(broadphase_layer < mNumLayers);
  142. // Find first body with different layer
  143. BodyID *b_mid = std::upper_bound(b_start, b_end, broadphase_layer, [bodies_ptr](BroadPhaseLayer::Type inLayer, BodyID inBodyID) { return inLayer < (BroadPhaseLayer::Type)bodies_ptr[inBodyID.GetIndex()]->GetBroadPhaseLayer(); });
  144. // Keep track of state for this layer
  145. LayerState &layer_state = state[broadphase_layer];
  146. layer_state.mBodyStart = b_start;
  147. layer_state.mBodyEnd = b_mid;
  148. // Insert all bodies of the same layer
  149. mLayers[broadphase_layer].AddBodiesPrepare(bodies, mTracking, b_start, int(b_mid - b_start), layer_state.mAddState);
  150. // Keep track in which tree we placed the object
  151. for (const BodyID *b = b_start; b < b_mid; ++b)
  152. {
  153. uint32 index = b->GetIndex();
  154. JPH_ASSERT(bodies[index]->GetID() == *b, "Provided BodyID doesn't match BodyID in body manager");
  155. JPH_ASSERT(!bodies[index]->IsInBroadPhase());
  156. Tracking &t = mTracking[index];
  157. JPH_ASSERT(t.mBroadPhaseLayer == (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid);
  158. t.mBroadPhaseLayer = broadphase_layer;
  159. JPH_ASSERT(t.mObjectLayer == cObjectLayerInvalid);
  160. t.mObjectLayer = bodies[index]->GetObjectLayer();
  161. }
  162. // Repeat
  163. b_start = b_mid;
  164. }
  165. return state;
  166. }
  167. void BroadPhaseQuadTree::AddBodiesFinalize(BodyID *ioBodies, int inNumber, AddState inAddState)
  168. {
  169. JPH_PROFILE_FUNCTION();
  170. if (inNumber <= 0)
  171. {
  172. JPH_ASSERT(inAddState == nullptr);
  173. return;
  174. }
  175. // This cannot run concurrently with UpdatePrepare()/UpdateFinalize()
  176. SharedLock lock(mUpdateMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseUpdate));
  177. BodyVector &bodies = mBodyManager->GetBodies();
  178. JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());
  179. LayerState *state = (LayerState *)inAddState;
  180. for (BroadPhaseLayer::Type broadphase_layer = 0; broadphase_layer < mNumLayers; broadphase_layer++)
  181. {
  182. const LayerState &l = state[broadphase_layer];
  183. if (l.mBodyStart != nullptr)
  184. {
  185. // Insert all bodies of the same layer
  186. mLayers[broadphase_layer].AddBodiesFinalize(mTracking, int(l.mBodyEnd - l.mBodyStart), l.mAddState);
  187. // Mark added to broadphase
  188. for (const BodyID *b = l.mBodyStart; b < l.mBodyEnd; ++b)
  189. {
  190. uint32 index = b->GetIndex();
  191. JPH_ASSERT(bodies[index]->GetID() == *b, "Provided BodyID doesn't match BodyID in body manager");
  192. JPH_ASSERT(mTracking[index].mBroadPhaseLayer == broadphase_layer);
  193. JPH_ASSERT(mTracking[index].mObjectLayer == bodies[index]->GetObjectLayer());
  194. JPH_ASSERT(!bodies[index]->IsInBroadPhase());
  195. bodies[index]->SetInBroadPhaseInternal(true);
  196. }
  197. }
  198. }
  199. delete [] state;
  200. }
  201. void BroadPhaseQuadTree::AddBodiesAbort(BodyID *ioBodies, int inNumber, AddState inAddState)
  202. {
  203. JPH_PROFILE_FUNCTION();
  204. if (inNumber <= 0)
  205. {
  206. JPH_ASSERT(inAddState == nullptr);
  207. return;
  208. }
  209. JPH_IF_ENABLE_ASSERTS(const BodyVector &bodies = mBodyManager->GetBodies();)
  210. JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());
  211. LayerState *state = (LayerState *)inAddState;
  212. for (BroadPhaseLayer::Type broadphase_layer = 0; broadphase_layer < mNumLayers; broadphase_layer++)
  213. {
  214. const LayerState &l = state[broadphase_layer];
  215. if (l.mBodyStart != nullptr)
  216. {
  217. // Insert all bodies of the same layer
  218. mLayers[broadphase_layer].AddBodiesAbort(mTracking, l.mAddState);
  219. // Reset bookkeeping
  220. for (const BodyID *b = l.mBodyStart; b < l.mBodyEnd; ++b)
  221. {
  222. uint32 index = b->GetIndex();
  223. JPH_ASSERT(bodies[index]->GetID() == *b, "Provided BodyID doesn't match BodyID in body manager");
  224. JPH_ASSERT(!bodies[index]->IsInBroadPhase());
  225. Tracking &t = mTracking[index];
  226. JPH_ASSERT(t.mBroadPhaseLayer == broadphase_layer);
  227. t.mBroadPhaseLayer = (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid;
  228. t.mObjectLayer = cObjectLayerInvalid;
  229. }
  230. }
  231. }
  232. delete [] state;
  233. }
  234. void BroadPhaseQuadTree::RemoveBodies(BodyID *ioBodies, int inNumber)
  235. {
  236. JPH_PROFILE_FUNCTION();
  237. if (inNumber <= 0)
  238. return;
  239. // This cannot run concurrently with UpdatePrepare()/UpdateFinalize()
  240. SharedLock lock(mUpdateMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseUpdate));
  241. BodyVector &bodies = mBodyManager->GetBodies();
  242. JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());
  243. // Sort bodies on layer
  244. Tracking *tracking = mTracking.data(); // C pointer or else sort is incredibly slow in debug mode
  245. QuickSort(ioBodies, ioBodies + inNumber, [tracking](BodyID inLHS, BodyID inRHS) { return tracking[inLHS.GetIndex()].mBroadPhaseLayer < tracking[inRHS.GetIndex()].mBroadPhaseLayer; });
  246. BodyID *b_start = ioBodies, *b_end = ioBodies + inNumber;
  247. while (b_start < b_end)
  248. {
  249. // Get broad phase layer
  250. BroadPhaseLayer::Type broadphase_layer = mTracking[b_start->GetIndex()].mBroadPhaseLayer;
  251. JPH_ASSERT(broadphase_layer != (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid);
  252. // Find first body with different layer
  253. BodyID *b_mid = std::upper_bound(b_start, b_end, broadphase_layer, [tracking](BroadPhaseLayer::Type inLayer, BodyID inBodyID) { return inLayer < tracking[inBodyID.GetIndex()].mBroadPhaseLayer; });
  254. // Remove all bodies of the same layer
  255. mLayers[broadphase_layer].RemoveBodies(bodies, mTracking, b_start, int(b_mid - b_start));
  256. for (const BodyID *b = b_start; b < b_mid; ++b)
  257. {
  258. // Reset bookkeeping
  259. uint32 index = b->GetIndex();
  260. Tracking &t = tracking[index];
  261. t.mBroadPhaseLayer = (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid;
  262. t.mObjectLayer = cObjectLayerInvalid;
  263. // Mark removed from broadphase
  264. JPH_ASSERT(bodies[index]->IsInBroadPhase());
  265. bodies[index]->SetInBroadPhaseInternal(false);
  266. }
  267. // Repeat
  268. b_start = b_mid;
  269. }
  270. }
  271. void BroadPhaseQuadTree::NotifyBodiesAABBChanged(BodyID *ioBodies, int inNumber, bool inTakeLock)
  272. {
  273. JPH_PROFILE_FUNCTION();
  274. if (inNumber <= 0)
  275. return;
  276. // This cannot run concurrently with UpdatePrepare()/UpdateFinalize()
  277. if (inTakeLock)
  278. PhysicsLock::sLockShared(mUpdateMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseUpdate));
  279. else
  280. JPH_ASSERT(mUpdateMutex.is_locked());
  281. const BodyVector &bodies = mBodyManager->GetBodies();
  282. JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());
  283. // Sort bodies on layer
  284. const Tracking *tracking = mTracking.data(); // C pointer or else sort is incredibly slow in debug mode
  285. QuickSort(ioBodies, ioBodies + inNumber, [tracking](BodyID inLHS, BodyID inRHS) { return tracking[inLHS.GetIndex()].mBroadPhaseLayer < tracking[inRHS.GetIndex()].mBroadPhaseLayer; });
  286. BodyID *b_start = ioBodies, *b_end = ioBodies + inNumber;
  287. while (b_start < b_end)
  288. {
  289. // Get broadphase layer
  290. BroadPhaseLayer::Type broadphase_layer = tracking[b_start->GetIndex()].mBroadPhaseLayer;
  291. JPH_ASSERT(broadphase_layer != (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid);
  292. // Find first body with different layer
  293. BodyID *b_mid = std::upper_bound(b_start, b_end, broadphase_layer, [tracking](BroadPhaseLayer::Type inLayer, BodyID inBodyID) { return inLayer < tracking[inBodyID.GetIndex()].mBroadPhaseLayer; });
  294. // Notify all bodies of the same layer changed
  295. mLayers[broadphase_layer].NotifyBodiesAABBChanged(bodies, mTracking, b_start, int(b_mid - b_start));
  296. // Repeat
  297. b_start = b_mid;
  298. }
  299. if (inTakeLock)
  300. PhysicsLock::sUnlockShared(mUpdateMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseUpdate));
  301. }
  302. void BroadPhaseQuadTree::NotifyBodiesLayerChanged(BodyID *ioBodies, int inNumber)
  303. {
  304. JPH_PROFILE_FUNCTION();
  305. if (inNumber <= 0)
  306. return;
  307. // First sort the bodies that actually changed layer to beginning of the array
  308. const BodyVector &bodies = mBodyManager->GetBodies();
  309. JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());
  310. for (BodyID *body_id = ioBodies + inNumber - 1; body_id >= ioBodies; --body_id)
  311. {
  312. uint32 index = body_id->GetIndex();
  313. JPH_ASSERT(bodies[index]->GetID() == *body_id, "Provided BodyID doesn't match BodyID in body manager");
  314. const Body *body = bodies[index];
  315. BroadPhaseLayer::Type broadphase_layer = (BroadPhaseLayer::Type)body->GetBroadPhaseLayer();
  316. JPH_ASSERT(broadphase_layer < mNumLayers);
  317. if (mTracking[index].mBroadPhaseLayer == broadphase_layer)
  318. {
  319. // Update tracking information
  320. mTracking[index].mObjectLayer = body->GetObjectLayer();
  321. // Move the body to the end, layer didn't change
  322. std::swap(*body_id, ioBodies[inNumber - 1]);
  323. --inNumber;
  324. }
  325. }
  326. if (inNumber > 0)
  327. {
  328. // Changing layer requires us to remove from one tree and add to another, so this is equivalent to removing all bodies first and then adding them again
  329. RemoveBodies(ioBodies, inNumber);
  330. AddState add_state = AddBodiesPrepare(ioBodies, inNumber);
  331. AddBodiesFinalize(ioBodies, inNumber, add_state);
  332. }
  333. }
  334. void BroadPhaseQuadTree::CastRay(const RayCast &inRay, RayCastBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const
  335. {
  336. JPH_PROFILE_FUNCTION();
  337. JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());
  338. // Prevent this from running in parallel with node deletion in FrameSync(), see notes there
  339. shared_lock lock(mQueryLocks[mQueryLockIdx]);
  340. // Loop over all layers and test the ones that could hit
  341. for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)
  342. {
  343. const QuadTree &tree = mLayers[l];
  344. if (tree.HasBodies() && inBroadPhaseLayerFilter.ShouldCollide(BroadPhaseLayer(l)))
  345. {
  346. JPH_PROFILE(tree.GetName());
  347. tree.CastRay(inRay, ioCollector, inObjectLayerFilter, mTracking);
  348. if (ioCollector.ShouldEarlyOut())
  349. break;
  350. }
  351. }
  352. }
  353. void BroadPhaseQuadTree::CollideAABox(const AABox &inBox, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const
  354. {
  355. JPH_PROFILE_FUNCTION();
  356. JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());
  357. // Prevent this from running in parallel with node deletion in FrameSync(), see notes there
  358. shared_lock lock(mQueryLocks[mQueryLockIdx]);
  359. // Loop over all layers and test the ones that could hit
  360. for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)
  361. {
  362. const QuadTree &tree = mLayers[l];
  363. if (tree.HasBodies() && inBroadPhaseLayerFilter.ShouldCollide(BroadPhaseLayer(l)))
  364. {
  365. JPH_PROFILE(tree.GetName());
  366. tree.CollideAABox(inBox, ioCollector, inObjectLayerFilter, mTracking);
  367. if (ioCollector.ShouldEarlyOut())
  368. break;
  369. }
  370. }
  371. }
  372. void BroadPhaseQuadTree::CollideSphere(Vec3Arg inCenter, float inRadius, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const
  373. {
  374. JPH_PROFILE_FUNCTION();
  375. JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());
  376. // Prevent this from running in parallel with node deletion in FrameSync(), see notes there
  377. shared_lock lock(mQueryLocks[mQueryLockIdx]);
  378. // Loop over all layers and test the ones that could hit
  379. for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)
  380. {
  381. const QuadTree &tree = mLayers[l];
  382. if (tree.HasBodies() && inBroadPhaseLayerFilter.ShouldCollide(BroadPhaseLayer(l)))
  383. {
  384. JPH_PROFILE(tree.GetName());
  385. tree.CollideSphere(inCenter, inRadius, ioCollector, inObjectLayerFilter, mTracking);
  386. if (ioCollector.ShouldEarlyOut())
  387. break;
  388. }
  389. }
  390. }
  391. void BroadPhaseQuadTree::CollidePoint(Vec3Arg inPoint, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const
  392. {
  393. JPH_PROFILE_FUNCTION();
  394. JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());
  395. // Prevent this from running in parallel with node deletion in FrameSync(), see notes there
  396. shared_lock lock(mQueryLocks[mQueryLockIdx]);
  397. // Loop over all layers and test the ones that could hit
  398. for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)
  399. {
  400. const QuadTree &tree = mLayers[l];
  401. if (tree.HasBodies() && inBroadPhaseLayerFilter.ShouldCollide(BroadPhaseLayer(l)))
  402. {
  403. JPH_PROFILE(tree.GetName());
  404. tree.CollidePoint(inPoint, ioCollector, inObjectLayerFilter, mTracking);
  405. if (ioCollector.ShouldEarlyOut())
  406. break;
  407. }
  408. }
  409. }
  410. void BroadPhaseQuadTree::CollideOrientedBox(const OrientedBox &inBox, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const
  411. {
  412. JPH_PROFILE_FUNCTION();
  413. JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());
  414. // Prevent this from running in parallel with node deletion in FrameSync(), see notes there
  415. shared_lock lock(mQueryLocks[mQueryLockIdx]);
  416. // Loop over all layers and test the ones that could hit
  417. for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)
  418. {
  419. const QuadTree &tree = mLayers[l];
  420. if (tree.HasBodies() && inBroadPhaseLayerFilter.ShouldCollide(BroadPhaseLayer(l)))
  421. {
  422. JPH_PROFILE(tree.GetName());
  423. tree.CollideOrientedBox(inBox, ioCollector, inObjectLayerFilter, mTracking);
  424. if (ioCollector.ShouldEarlyOut())
  425. break;
  426. }
  427. }
  428. }
  429. void BroadPhaseQuadTree::CastAABoxNoLock(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const
  430. {
  431. JPH_PROFILE_FUNCTION();
  432. JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());
  433. // Loop over all layers and test the ones that could hit
  434. for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)
  435. {
  436. const QuadTree &tree = mLayers[l];
  437. if (tree.HasBodies() && inBroadPhaseLayerFilter.ShouldCollide(BroadPhaseLayer(l)))
  438. {
  439. JPH_PROFILE(tree.GetName());
  440. tree.CastAABox(inBox, ioCollector, inObjectLayerFilter, mTracking);
  441. if (ioCollector.ShouldEarlyOut())
  442. break;
  443. }
  444. }
  445. }
  446. void BroadPhaseQuadTree::CastAABox(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const
  447. {
  448. // Prevent this from running in parallel with node deletion in FrameSync(), see notes there
  449. shared_lock lock(mQueryLocks[mQueryLockIdx]);
  450. CastAABoxNoLock(inBox, ioCollector, inBroadPhaseLayerFilter, inObjectLayerFilter);
  451. }
  452. void BroadPhaseQuadTree::FindCollidingPairs(BodyID *ioActiveBodies, int inNumActiveBodies, float inSpeculativeContactDistance, const ObjectVsBroadPhaseLayerFilter &inObjectVsBroadPhaseLayerFilter, const ObjectLayerPairFilter &inObjectLayerPairFilter, BodyPairCollector &ioPairCollector) const
  453. {
  454. JPH_PROFILE_FUNCTION();
  455. const BodyVector &bodies = mBodyManager->GetBodies();
  456. JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());
  457. // Note that we don't take any locks at this point. We know that the tree is not going to be swapped or deleted while finding collision pairs due to the way the jobs are scheduled in the PhysicsSystem::Update.
  458. // Sort bodies on layer
  459. const Tracking *tracking = mTracking.data(); // C pointer or else sort is incredibly slow in debug mode
  460. QuickSort(ioActiveBodies, ioActiveBodies + inNumActiveBodies, [tracking](BodyID inLHS, BodyID inRHS) { return tracking[inLHS.GetIndex()].mObjectLayer < tracking[inRHS.GetIndex()].mObjectLayer; });
  461. BodyID *b_start = ioActiveBodies, *b_end = ioActiveBodies + inNumActiveBodies;
  462. while (b_start < b_end)
  463. {
  464. // Get broadphase layer
  465. ObjectLayer object_layer = tracking[b_start->GetIndex()].mObjectLayer;
  466. JPH_ASSERT(object_layer != cObjectLayerInvalid);
  467. // Find first body with different layer
  468. BodyID *b_mid = std::upper_bound(b_start, b_end, object_layer, [tracking](ObjectLayer inLayer, BodyID inBodyID) { return inLayer < tracking[inBodyID.GetIndex()].mObjectLayer; });
  469. // Loop over all layers and test the ones that could hit
  470. for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)
  471. {
  472. const QuadTree &tree = mLayers[l];
  473. if (tree.HasBodies() && inObjectVsBroadPhaseLayerFilter.ShouldCollide(object_layer, BroadPhaseLayer(l)))
  474. {
  475. JPH_PROFILE(tree.GetName());
  476. tree.FindCollidingPairs(bodies, b_start, int(b_mid - b_start), inSpeculativeContactDistance, ioPairCollector, inObjectLayerPairFilter);
  477. }
  478. }
  479. // Repeat
  480. b_start = b_mid;
  481. }
  482. }
  483. AABox BroadPhaseQuadTree::GetBounds() const
  484. {
  485. // Prevent this from running in parallel with node deletion in FrameSync(), see notes there
  486. shared_lock lock(mQueryLocks[mQueryLockIdx]);
  487. AABox bounds;
  488. for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)
  489. bounds.Encapsulate(mLayers[l].GetBounds());
  490. return bounds;
  491. }
  492. #ifdef JPH_TRACK_BROADPHASE_STATS
  493. void BroadPhaseQuadTree::ReportStats()
  494. {
  495. Trace("Query Type, Filter Description, Tree Name, Num Queries, Total Time (%%), Total Time Excl. Collector (%%), Nodes Visited, Bodies Visited, Hits Reported, Hits Reported vs Bodies Visited (%%), Hits Reported vs Nodes Visited");
  496. uint64 total_ticks = 0;
  497. for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)
  498. total_ticks += mLayers[l].GetTicks100Pct();
  499. for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)
  500. mLayers[l].ReportStats(total_ticks);
  501. }
  502. #endif // JPH_TRACK_BROADPHASE_STATS
  503. JPH_NAMESPACE_END