BroadPhaseQuadTree.cpp 21 KB

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