QuadTree.cpp 51 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #include <Jolt.h>
  4. #include <Physics/Collision/BroadPhase/BroadPhaseQuadTree.h>
  5. #include <Physics/Collision/RayCast.h>
  6. #include <Physics/Collision/AABoxCast.h>
  7. #include <Physics/Collision/CastResult.h>
  8. #include <Physics/Collision/SortReverseAndStore.h>
  9. #include <Physics/Body/BodyPair.h>
  10. #include <Physics/PhysicsLock.h>
  11. #include <Geometry/AABox4.h>
  12. #include <Geometry/RayAABox.h>
  13. #include <Geometry/OrientedBox.h>
  14. namespace JPH {
  15. ////////////////////////////////////////////////////////////////////////////////////////////////////////
  16. // QuadTree::Node
  17. ////////////////////////////////////////////////////////////////////////////////////////////////////////
  18. QuadTree::Node::Node(bool inLocked)
  19. {
  20. // First reset bounds
  21. Vec4 val = Vec4::sReplicate(cLargeFloat);
  22. val.StoreFloat4((Float4 *)&mBoundsMinX);
  23. val.StoreFloat4((Float4 *)&mBoundsMinY);
  24. val.StoreFloat4((Float4 *)&mBoundsMinZ);
  25. val = Vec4::sReplicate(-cLargeFloat);
  26. val.StoreFloat4((Float4 *)&mBoundsMaxX);
  27. val.StoreFloat4((Float4 *)&mBoundsMaxY);
  28. val.StoreFloat4((Float4 *)&mBoundsMaxZ);
  29. // Reset child node ids
  30. mChildNodeID[0] = NodeID::sInvalid();
  31. mChildNodeID[1] = NodeID::sInvalid();
  32. mChildNodeID[2] = NodeID::sInvalid();
  33. mChildNodeID[3] = NodeID::sInvalid();
  34. // Reset parent node index
  35. mParentNodeIndex = cInvalidNodeIndex;
  36. // Store lock status
  37. mIsLocked = inLocked;
  38. }
  39. void QuadTree::Node::GetChildBounds(int inChildIndex, AABox &outBounds) const
  40. {
  41. // Read bounding box in order min -> max
  42. outBounds.mMin = Vec3(mBoundsMinX[inChildIndex], mBoundsMinY[inChildIndex], mBoundsMinZ[inChildIndex]);
  43. outBounds.mMax = Vec3(mBoundsMaxX[inChildIndex], mBoundsMaxY[inChildIndex], mBoundsMaxZ[inChildIndex]);
  44. }
  45. void QuadTree::Node::SetChildBounds(int inChildIndex, const AABox &inBounds)
  46. {
  47. // Set max first (this keeps the bounding box invalid for reading threads)
  48. mBoundsMaxZ[inChildIndex] = inBounds.mMax.GetZ();
  49. mBoundsMaxY[inChildIndex] = inBounds.mMax.GetY();
  50. mBoundsMaxX[inChildIndex] = inBounds.mMax.GetX();
  51. // Then set min (and make box valid)
  52. mBoundsMinZ[inChildIndex] = inBounds.mMin.GetZ();
  53. mBoundsMinY[inChildIndex] = inBounds.mMin.GetY();
  54. mBoundsMinX[inChildIndex] = inBounds.mMin.GetX(); // Min X becomes valid last
  55. }
  56. void QuadTree::Node::InvalidateChildBounds(int inChildIndex)
  57. {
  58. // First we make the box invalid by setting the min to cLargeFloat
  59. mBoundsMinX[inChildIndex] = cLargeFloat; // Min X becomes invalid first
  60. mBoundsMinY[inChildIndex] = cLargeFloat;
  61. mBoundsMinZ[inChildIndex] = cLargeFloat;
  62. // Then we reset the max values too
  63. mBoundsMaxX[inChildIndex] = -cLargeFloat;
  64. mBoundsMaxY[inChildIndex] = -cLargeFloat;
  65. mBoundsMaxZ[inChildIndex] = -cLargeFloat;
  66. }
  67. void QuadTree::Node::GetNodeBounds(AABox &outBounds) const
  68. {
  69. // Get first child bounds
  70. GetChildBounds(0, outBounds);
  71. // Encapsulate other child bounds
  72. for (int child_idx = 1; child_idx < 4; ++child_idx)
  73. {
  74. AABox tmp;
  75. GetChildBounds(child_idx, tmp);
  76. outBounds.Encapsulate(tmp);
  77. }
  78. }
  79. bool QuadTree::Node::EncapsulateChildBounds(int inChildIndex, const AABox &inBounds)
  80. {
  81. bool changed = AtomicMin(mBoundsMinX[inChildIndex], inBounds.mMin.GetX());
  82. changed |= AtomicMin(mBoundsMinY[inChildIndex], inBounds.mMin.GetY());
  83. changed |= AtomicMin(mBoundsMinZ[inChildIndex], inBounds.mMin.GetZ());
  84. changed |= AtomicMax(mBoundsMaxX[inChildIndex], inBounds.mMax.GetX());
  85. changed |= AtomicMax(mBoundsMaxY[inChildIndex], inBounds.mMax.GetY());
  86. changed |= AtomicMax(mBoundsMaxZ[inChildIndex], inBounds.mMax.GetZ());
  87. return changed;
  88. }
  89. ////////////////////////////////////////////////////////////////////////////////////////////////////////
  90. // QuadTree
  91. ////////////////////////////////////////////////////////////////////////////////////////////////////////
  92. const float QuadTree::cLargeFloat = 1.0e30f;
  93. const AABox QuadTree::cInvalidBounds(Vec3::sReplicate(cLargeFloat), Vec3::sReplicate(-cLargeFloat));
  94. void QuadTree::GetBodyLocation(const TrackingVector &inTracking, BodyID inBodyID, uint32 &outNodeIdx, uint32 &outChildIdx) const
  95. {
  96. uint32 body_location = inTracking[inBodyID.GetIndex()].mBodyLocation;
  97. JPH_ASSERT(body_location != Tracking::cInvalidBodyLocation);
  98. outNodeIdx = body_location & 0x3fffffff;
  99. outChildIdx = body_location >> 30;
  100. JPH_ASSERT(mAllocator->Get(outNodeIdx).mChildNodeID[outChildIdx] == inBodyID, "Make sure that the body is in the node where it should be");
  101. }
  102. void QuadTree::SetBodyLocation(TrackingVector &ioTracking, BodyID inBodyID, uint32 inNodeIdx, uint32 inChildIdx) const
  103. {
  104. JPH_ASSERT(inNodeIdx <= 0x3fffffff);
  105. JPH_ASSERT(inChildIdx < 4);
  106. JPH_ASSERT(mAllocator->Get(inNodeIdx).mChildNodeID[inChildIdx] == inBodyID, "Make sure that the body is in the node where it should be");
  107. ioTracking[inBodyID.GetIndex()].mBodyLocation = inNodeIdx + (inChildIdx << 30);
  108. #ifdef JPH_ENABLE_ASSERTS
  109. uint32 v1, v2;
  110. GetBodyLocation(ioTracking, inBodyID, v1, v2);
  111. JPH_ASSERT(v1 == inNodeIdx);
  112. JPH_ASSERT(v2 == inChildIdx);
  113. #endif
  114. }
  115. void QuadTree::InvalidateBodyLocation(TrackingVector &ioTracking, BodyID inBodyID)
  116. {
  117. ioTracking[inBodyID.GetIndex()].mBodyLocation = Tracking::cInvalidBodyLocation;
  118. }
  119. QuadTree::~QuadTree()
  120. {
  121. // Get rid of any nodes that are still to be freed
  122. DiscardOldTree();
  123. // Get the current root node
  124. RootNode &root_node = GetCurrentRoot();
  125. // Collect all bodies
  126. Allocator::Batch free_batch;
  127. NodeID node_stack[cStackSize];
  128. node_stack[0] = root_node.GetNodeID();
  129. JPH_ASSERT(node_stack[0].IsValid());
  130. if (node_stack[0].IsNode())
  131. {
  132. int top = 0;
  133. do
  134. {
  135. // Process node
  136. NodeID node_id = node_stack[top];
  137. JPH_ASSERT(!node_id.IsBody());
  138. uint32 node_idx = node_id.GetNodeIndex();
  139. const Node &node = mAllocator->Get(node_idx);
  140. // Recurse and get all child nodes
  141. for (int i = 0; i < 4; ++i)
  142. {
  143. NodeID child_node_id = node.mChildNodeID[i];
  144. if (child_node_id.IsValid() && child_node_id.IsNode())
  145. {
  146. JPH_ASSERT(top < cStackSize);
  147. node_stack[top] = child_node_id;
  148. top++;
  149. }
  150. }
  151. // Mark node to be freed
  152. mAllocator->AddObjectToBatch(free_batch, node_idx);
  153. --top;
  154. }
  155. while (top >= 0);
  156. }
  157. // Now free all nodes
  158. mAllocator->DestructObjectBatch(free_batch);
  159. }
  160. uint32 QuadTree::AllocateNode(bool inLocked)
  161. {
  162. uint32 index = mAllocator->ConstructObject(inLocked);
  163. if (index == Allocator::cInvalidObjectIndex)
  164. {
  165. Trace("QuadTree: Out of nodes!");
  166. JPH_CRASH;
  167. }
  168. return index;
  169. }
  170. void QuadTree::Init(Allocator &inAllocator)
  171. {
  172. // Store allocator
  173. mAllocator = &inAllocator;
  174. // Allocate root node
  175. mRootNode[mRootNodeIndex].mIndex = AllocateNode(false);
  176. }
  177. void QuadTree::DiscardOldTree()
  178. {
  179. // Check if there is an old tree
  180. RootNode &old_root_node = mRootNode[mRootNodeIndex ^ 1];
  181. if (old_root_node.mIndex != cInvalidNodeIndex)
  182. {
  183. // Clear the root
  184. old_root_node.mIndex = cInvalidNodeIndex;
  185. // Now free all old nodes
  186. mAllocator->DestructObjectBatch(mFreeNodeBatch);
  187. // Clear the batch
  188. mFreeNodeBatch = Allocator::Batch();
  189. }
  190. }
  191. void QuadTree::UpdatePrepare(const BodyVector &inBodies, TrackingVector &ioTracking, UpdateState &outUpdateState)
  192. {
  193. #ifdef JPH_ENABLE_ASSERTS
  194. // We only read positions
  195. BodyAccess::Grant grant(BodyAccess::EAccess::None, BodyAccess::EAccess::Read);
  196. #endif
  197. // Assert we have no nodes pending deletion, this means DiscardOldTree wasn't called yet
  198. JPH_ASSERT(mFreeNodeBatch.mNumObjects == 0);
  199. // Mark tree non-dirty
  200. mIsDirty = false;
  201. // Get the current root node
  202. RootNode &root_node = GetCurrentRoot();
  203. // Assert sane data
  204. #ifdef _DEBUG
  205. ValidateTree(inBodies, ioTracking, root_node.mIndex, mNumBodies);
  206. #endif
  207. // Create space for all body ID's
  208. outUpdateState.mAllNodeIDs = new NodeID [mNumBodies];
  209. NodeID *cur_node_id = outUpdateState.mAllNodeIDs;
  210. // Collect all bodies
  211. NodeID node_stack[cStackSize];
  212. node_stack[0] = root_node.GetNodeID();
  213. JPH_ASSERT(node_stack[0].IsValid());
  214. int top = 0;
  215. do
  216. {
  217. // Check if node is a body
  218. NodeID node_id = node_stack[top];
  219. if (node_id.IsBody())
  220. {
  221. // Validate that we're still in the right layer
  222. #ifdef JPH_ENABLE_ASSERTS
  223. uint32 body_index = node_id.GetBodyID().GetIndex();
  224. JPH_ASSERT(ioTracking[body_index].mObjectLayer == inBodies[body_index]->GetObjectLayer());
  225. #endif
  226. // Store body
  227. *cur_node_id = node_id;
  228. ++cur_node_id;
  229. }
  230. else
  231. {
  232. // Process normal node
  233. uint32 node_idx = node_id.GetNodeIndex();
  234. const Node &node = mAllocator->Get(node_idx);
  235. if (node.mIsLocked)
  236. {
  237. // Node is locked, treat it as a whole
  238. *cur_node_id = node_id;
  239. ++cur_node_id;
  240. }
  241. else
  242. {
  243. // Node is not locked, recurse and get all children
  244. for (int i = 0; i < 4; ++i)
  245. {
  246. NodeID child_node_id = node.mChildNodeID[i];
  247. if (child_node_id.IsValid())
  248. {
  249. JPH_ASSERT(top < cStackSize);
  250. node_stack[top] = child_node_id;
  251. top++;
  252. }
  253. }
  254. // Mark node to be freed
  255. mAllocator->AddObjectToBatch(mFreeNodeBatch, node_idx);
  256. }
  257. }
  258. --top;
  259. }
  260. while (top >= 0);
  261. // Check that our book keeping matches
  262. uint32 num_node_ids = uint32(cur_node_id - outUpdateState.mAllNodeIDs);
  263. JPH_ASSERT(num_node_ids <= mNumBodies);
  264. // This will be the new root node id
  265. NodeID root_node_id;
  266. if (num_node_ids > 0)
  267. {
  268. // Build new tree
  269. AABox root_bounds;
  270. root_node_id = BuildTree(inBodies, ioTracking, outUpdateState.mAllNodeIDs, num_node_ids, cInvalidNodeIndex, false, root_bounds);
  271. if (root_node_id.IsBody())
  272. {
  273. // For a single body we need to allocate a new root node
  274. uint32 root_idx = AllocateNode(false);
  275. Node &root = mAllocator->Get(root_idx);
  276. root.SetChildBounds(0, root_bounds);
  277. root.mChildNodeID[0] = root_node_id;
  278. SetBodyLocation(ioTracking, root_node_id.GetBodyID(), root_idx, 0);
  279. root_node_id = NodeID::sFromNodeIndex(root_idx);
  280. }
  281. else
  282. {
  283. // We can't have a locked node as root, allocate a new root in this case
  284. Node &old_root_node = mAllocator->Get(root_node_id.GetNodeIndex());
  285. if (old_root_node.mIsLocked)
  286. {
  287. // Link old root under new root
  288. uint32 root_idx = AllocateNode(false);
  289. Node &root = mAllocator->Get(root_idx);
  290. root.SetChildBounds(0, root_bounds);
  291. root.mChildNodeID[0] = root_node_id;
  292. root_node_id = NodeID::sFromNodeIndex(root_idx);
  293. old_root_node.mParentNodeIndex = root_idx;
  294. }
  295. }
  296. }
  297. else
  298. {
  299. // Empty tree, create root node
  300. uint32 root_idx = AllocateNode(false);
  301. root_node_id = NodeID::sFromNodeIndex(root_idx);
  302. }
  303. outUpdateState.mRootNodeID = root_node_id;
  304. }
  305. void QuadTree::UpdateFinalize([[maybe_unused]] const BodyVector &inBodies, [[maybe_unused]] TrackingVector &ioTracking, UpdateState &inUpdateState)
  306. {
  307. // Tree building is complete, now we switch the old with the new tree
  308. uint32 new_root_idx = mRootNodeIndex ^ 1;
  309. RootNode &new_root_node = mRootNode[new_root_idx];
  310. {
  311. // Note: We don't need to lock here as the old tree stays available so any queries
  312. // that use it can continue using it until DiscardOldTree is called. This slot
  313. // should be empty and unused at this moment.
  314. JPH_ASSERT(new_root_node.mIndex == cInvalidNodeIndex);
  315. new_root_node.mIndex = inUpdateState.mRootNodeID.GetNodeIndex();
  316. }
  317. // All queries that start from now on will use this new tree
  318. mRootNodeIndex = new_root_idx;
  319. // Delete temporary data
  320. delete [] inUpdateState.mAllNodeIDs;
  321. #ifdef _DEBUG
  322. ValidateTree(inBodies, ioTracking, new_root_node.mIndex, mNumBodies);
  323. #endif
  324. }
  325. void QuadTree::sPartition(NodeID *ioNodeIDs, Vec3 *ioNodeCenters, int inNumber, int &outMidPoint)
  326. {
  327. // Handle trivial case
  328. if (inNumber <= 4)
  329. {
  330. outMidPoint = inNumber / 2;
  331. return;
  332. }
  333. // Calculate bounding box of box centers
  334. Vec3 center_min = Vec3::sReplicate(cLargeFloat);
  335. Vec3 center_max = Vec3::sReplicate(-cLargeFloat);
  336. for (Vec3 *c = ioNodeCenters, *c_end = ioNodeCenters + inNumber; c < c_end; ++c)
  337. {
  338. Vec3 center = *c;
  339. center_min = Vec3::sMin(center_min, center);
  340. center_max = Vec3::sMax(center_max, center);
  341. }
  342. // Calculate split plane
  343. int dimension = (center_max - center_min).GetHighestComponentIndex();
  344. float split = 0.5f * (center_min + center_max)[dimension];
  345. // Divide bodies
  346. int start = 0, end = inNumber;
  347. while (start < end)
  348. {
  349. // Search for first element that is on the right hand side of the split plane
  350. while (start < end && ioNodeCenters[start][dimension] < split)
  351. ++start;
  352. // Search for the first element that is on the left hand side of the split plane
  353. while (start < end && ioNodeCenters[end - 1][dimension] >= split)
  354. --end;
  355. if (start < end)
  356. {
  357. // Swap the two elements
  358. swap(ioNodeIDs[start], ioNodeIDs[end - 1]);
  359. swap(ioNodeCenters[start], ioNodeCenters[end - 1]);
  360. ++start;
  361. --end;
  362. }
  363. }
  364. JPH_ASSERT(start == end);
  365. if (start > 0 && start < inNumber)
  366. {
  367. // Success!
  368. outMidPoint = start;
  369. }
  370. else
  371. {
  372. // Failed to divide bodies
  373. outMidPoint = inNumber / 2;
  374. }
  375. }
  376. void QuadTree::sPartition4(NodeID *ioNodeIDs, Vec3 *ioNodeCenters, int inBegin, int inEnd, int *outSplit)
  377. {
  378. NodeID *node_ids = ioNodeIDs + inBegin;
  379. Vec3 *node_centers = ioNodeCenters + inBegin;
  380. int number = inEnd - inBegin;
  381. // Partition entire range
  382. sPartition(node_ids, node_centers, number, outSplit[2]);
  383. // Partition lower half
  384. sPartition(node_ids, node_centers, outSplit[2], outSplit[1]);
  385. // Partition upper half
  386. sPartition(node_ids + outSplit[2], node_centers + outSplit[2], number - outSplit[2], outSplit[3]);
  387. // Convert to proper range
  388. outSplit[0] = inBegin;
  389. outSplit[1] += inBegin;
  390. outSplit[2] += inBegin;
  391. outSplit[3] += outSplit[2];
  392. outSplit[4] = inEnd;
  393. }
  394. AABox QuadTree::GetNodeOrBodyBounds(const BodyVector &inBodies, NodeID inNodeID) const
  395. {
  396. if (inNodeID.IsNode())
  397. {
  398. // It is a node
  399. uint32 node_idx = inNodeID.GetNodeIndex();
  400. const Node &node = mAllocator->Get(node_idx);
  401. AABox bounds;
  402. node.GetNodeBounds(bounds);
  403. return bounds;
  404. }
  405. else
  406. {
  407. // It is a body
  408. return inBodies[inNodeID.GetBodyID().GetIndex()]->GetWorldSpaceBounds();
  409. }
  410. }
  411. QuadTree::NodeID QuadTree::BuildTree(const BodyVector &inBodies, TrackingVector &ioTracking, NodeID *ioNodeIDs, int inNumber, uint32 inParentNodeIndex, bool inLocked, AABox &outBounds)
  412. {
  413. // Trivial case: No bodies in tree
  414. if (inNumber == 0)
  415. {
  416. outBounds = cInvalidBounds;
  417. return NodeID::sInvalid();
  418. }
  419. // Trivial case: When we have 1 body or node, return it
  420. if (inNumber == 1)
  421. {
  422. if (ioNodeIDs->IsNode())
  423. {
  424. // When returning an existing node as root, ensure that no parent has been set
  425. Node &node = mAllocator->Get(ioNodeIDs->GetNodeIndex());
  426. node.mParentNodeIndex = cInvalidNodeIndex;
  427. }
  428. outBounds = GetNodeOrBodyBounds(inBodies, *ioNodeIDs);
  429. return *ioNodeIDs;
  430. }
  431. // Calculate centers of all bodies that are to be inserted
  432. Vec3 *centers = new Vec3 [inNumber];
  433. JPH_ASSERT(IsAligned(centers, 16));
  434. Vec3 *c = centers;
  435. for (NodeID *n = ioNodeIDs, *n_end = ioNodeIDs + inNumber; n < n_end; ++n, ++c)
  436. *c = GetNodeOrBodyBounds(inBodies, *n).GetCenter();
  437. // The algorithm is a recursive tree build, but to avoid the call overhead we keep track of a stack here
  438. struct StackEntry
  439. {
  440. uint32 mNodeIdx; // Node index of node that is generated
  441. int mChildIdx; // Index of child that we're currently processing
  442. int mSplit[5]; // Indices where the node ID's have been split to form 4 partitions
  443. Vec3 mNodeBoundsMin; // Bounding box of this node, accumulated while iterating over children
  444. Vec3 mNodeBoundsMax;
  445. };
  446. static_assert(sizeof(StackEntry) == 64);
  447. StackEntry stack[cStackSize / 4]; // We don't process 4 at a time in this loop but 1, so the stack can be 4x as small
  448. int top = 0;
  449. // Create root node
  450. stack[0].mNodeIdx = AllocateNode(inLocked);
  451. stack[0].mChildIdx = -1;
  452. stack[0].mNodeBoundsMin = Vec3::sReplicate(cLargeFloat);
  453. stack[0].mNodeBoundsMax = Vec3::sReplicate(-cLargeFloat);
  454. sPartition4(ioNodeIDs, centers, 0, inNumber, stack[0].mSplit);
  455. for (;;)
  456. {
  457. StackEntry &cur_stack = stack[top];
  458. // Next child
  459. cur_stack.mChildIdx++;
  460. // Check if all children processed
  461. if (cur_stack.mChildIdx >= 4)
  462. {
  463. // Terminate if there's nothing left to pop
  464. if (top <= 0)
  465. break;
  466. // Add our bounds to our parents bounds
  467. StackEntry &prev_stack = stack[top - 1];
  468. prev_stack.mNodeBoundsMin = Vec3::sMin(prev_stack.mNodeBoundsMin, cur_stack.mNodeBoundsMin);
  469. prev_stack.mNodeBoundsMax = Vec3::sMax(prev_stack.mNodeBoundsMax, cur_stack.mNodeBoundsMax);
  470. // Store parent node
  471. Node &node = mAllocator->Get(cur_stack.mNodeIdx);
  472. node.mParentNodeIndex = prev_stack.mNodeIdx;
  473. // Store this node's properties in the parent node
  474. Node &parent_node = mAllocator->Get(prev_stack.mNodeIdx);
  475. parent_node.mChildNodeID[prev_stack.mChildIdx] = NodeID::sFromNodeIndex(cur_stack.mNodeIdx);
  476. parent_node.SetChildBounds(prev_stack.mChildIdx, AABox(cur_stack.mNodeBoundsMin, cur_stack.mNodeBoundsMax));
  477. // Pop entry from stack
  478. --top;
  479. }
  480. else
  481. {
  482. // Get low and high index to bodies to process
  483. int low = cur_stack.mSplit[cur_stack.mChildIdx];
  484. int high = cur_stack.mSplit[cur_stack.mChildIdx + 1];
  485. int num_bodies = high - low;
  486. if (num_bodies == 1)
  487. {
  488. // Get body info
  489. NodeID child_node_id = ioNodeIDs[low];
  490. AABox bounds = GetNodeOrBodyBounds(inBodies, child_node_id);
  491. // Update node
  492. Node &node = mAllocator->Get(cur_stack.mNodeIdx);
  493. node.mChildNodeID[cur_stack.mChildIdx] = child_node_id;
  494. node.SetChildBounds(cur_stack.mChildIdx, bounds);
  495. if (child_node_id.IsNode())
  496. {
  497. // Update parent for this node
  498. Node &child_node = mAllocator->Get(child_node_id.GetNodeIndex());
  499. child_node.mParentNodeIndex = cur_stack.mNodeIdx;
  500. }
  501. else
  502. {
  503. // Set location in tracking
  504. SetBodyLocation(ioTracking, child_node_id.GetBodyID(), cur_stack.mNodeIdx, cur_stack.mChildIdx);
  505. }
  506. // Encapsulate bounding box in parent
  507. cur_stack.mNodeBoundsMin = Vec3::sMin(cur_stack.mNodeBoundsMin, bounds.mMin);
  508. cur_stack.mNodeBoundsMax = Vec3::sMax(cur_stack.mNodeBoundsMax, bounds.mMax);
  509. }
  510. else if (num_bodies > 1)
  511. {
  512. // Allocate new node
  513. StackEntry &new_stack = stack[++top];
  514. JPH_ASSERT(top < cStackSize / 4);
  515. new_stack.mNodeIdx = AllocateNode(inLocked);
  516. new_stack.mChildIdx = -1;
  517. new_stack.mNodeBoundsMin = Vec3::sReplicate(cLargeFloat);
  518. new_stack.mNodeBoundsMax = Vec3::sReplicate(-cLargeFloat);
  519. sPartition4(ioNodeIDs, centers, low, high, new_stack.mSplit);
  520. }
  521. }
  522. }
  523. // Delete temporary data
  524. delete [] centers;
  525. // Store bounding box of root
  526. outBounds.mMin = stack[0].mNodeBoundsMin;
  527. outBounds.mMax = stack[0].mNodeBoundsMax;
  528. // Return root
  529. return NodeID::sFromNodeIndex(stack[0].mNodeIdx);
  530. }
  531. void QuadTree::UnlockNodeAndParents(uint32 inNodeIndex)
  532. {
  533. uint32 node_idx = inNodeIndex;
  534. do
  535. {
  536. // If node is not locked, parent won't be either
  537. Node &node = mAllocator->Get(node_idx);
  538. if (!node.mIsLocked)
  539. break;
  540. // Mark node as unlocked
  541. node.mIsLocked = false;
  542. // Get our parent
  543. node_idx = node.mParentNodeIndex;
  544. }
  545. while (node_idx != cInvalidNodeIndex);
  546. }
  547. void QuadTree::WidenAndUnlockNodeAndParents(uint32 inNodeIndex, const AABox &inNewBounds)
  548. {
  549. uint32 node_idx = inNodeIndex;
  550. for (;;)
  551. {
  552. // Mark node as unlocked
  553. Node &node = mAllocator->Get(node_idx);
  554. node.mIsLocked = false;
  555. // Get our parent
  556. uint32 parent_idx = node.mParentNodeIndex;
  557. if (parent_idx == cInvalidNodeIndex)
  558. break;
  559. // Find which child of the parent we're in
  560. Node &parent_node = mAllocator->Get(parent_idx);
  561. NodeID node_id = NodeID::sFromNodeIndex(node_idx);
  562. int child_idx = -1;
  563. for (int i = 0; i < 4; ++i)
  564. if (parent_node.mChildNodeID[i] == node_id)
  565. {
  566. // Found one, set the node index and child index and update the bounding box too
  567. child_idx = i;
  568. break;
  569. }
  570. JPH_ASSERT(child_idx != -1, "Nodes don't get removed from the tree, we must have found it");
  571. // To avoid any race conditions with other threads we only enlarge bounding boxes
  572. if (!parent_node.EncapsulateChildBounds(child_idx, inNewBounds))
  573. {
  574. // No changes to bounding box, only unlocking remains to be done
  575. if (parent_node.mIsLocked)
  576. UnlockNodeAndParents(parent_idx);
  577. break;
  578. }
  579. // Update node index
  580. node_idx = parent_idx;
  581. }
  582. }
  583. bool QuadTree::TryInsertLeaf(TrackingVector &ioTracking, int inNodeIndex, NodeID inLeafID, const AABox &inLeafBounds, int inLeafNumBodies)
  584. {
  585. // Tentively assign the node as parent
  586. bool leaf_is_node = inLeafID.IsNode();
  587. if (leaf_is_node)
  588. {
  589. uint32 leaf_idx = inLeafID.GetNodeIndex();
  590. mAllocator->Get(leaf_idx).mParentNodeIndex = inNodeIndex;
  591. }
  592. // Fetch node that we're adding to
  593. Node &node = mAllocator->Get(inNodeIndex);
  594. // Find an empty child
  595. for (uint32 child_idx = 0; child_idx < 4; ++child_idx)
  596. if (node.mChildNodeID[child_idx].IsValid())
  597. {
  598. // Check if we can claim it
  599. if (node.mChildNodeID[child_idx].CompareExchange(NodeID::sInvalid(), inLeafID))
  600. {
  601. // We managed to add it to the node
  602. // If leaf was a body, we need to update its bookkeeping
  603. if (!leaf_is_node)
  604. SetBodyLocation(ioTracking, inLeafID.GetBodyID(), inNodeIndex, child_idx);
  605. // Now set the bounding box making the child valid for queries
  606. node.SetChildBounds(child_idx, inLeafBounds);
  607. // Widen the bounds for our parents too
  608. WidenAndUnlockNodeAndParents(inNodeIndex, inLeafBounds);
  609. // Update body counter
  610. mNumBodies += inLeafNumBodies;
  611. // And we're done
  612. return true;
  613. }
  614. }
  615. return false;
  616. }
  617. bool QuadTree::TryCreateNewRoot(TrackingVector &ioTracking, atomic<uint32> &ioRootNodeIndex, NodeID inLeafID, const AABox &inLeafBounds, int inLeafNumBodies)
  618. {
  619. // Fetch old root
  620. uint32 root_idx = ioRootNodeIndex;
  621. Node &root = mAllocator->Get(root_idx);
  622. // Create new root
  623. uint32 new_root_idx = AllocateNode(false);
  624. Node &new_root = mAllocator->Get(new_root_idx);
  625. // First child is current root
  626. new_root.mChildNodeID[0] = NodeID::sFromNodeIndex(root_idx);
  627. AABox old_root_bounds;
  628. root.GetNodeBounds(old_root_bounds);
  629. new_root.SetChildBounds(0, old_root_bounds);
  630. // Second child is new leaf
  631. new_root.mChildNodeID[1] = inLeafID;
  632. new_root.SetChildBounds(1, inLeafBounds);
  633. // Tentatively assign new root as parent
  634. bool leaf_is_node = inLeafID.IsNode();
  635. if (leaf_is_node)
  636. {
  637. uint32 leaf_idx = inLeafID.GetNodeIndex();
  638. mAllocator->Get(leaf_idx).mParentNodeIndex = new_root_idx;
  639. }
  640. // Try to swap it
  641. if (ioRootNodeIndex.compare_exchange_strong(root_idx, new_root_idx))
  642. {
  643. // We managed to set the new root
  644. // If leaf was a body, we need to update its bookkeeping
  645. if (!leaf_is_node)
  646. SetBodyLocation(ioTracking, inLeafID.GetBodyID(), new_root_idx, 1);
  647. // Store parent node for old root
  648. root.mParentNodeIndex = new_root_idx;
  649. // If a bounds change was in progress while we were assigning the root it will not have updated the root volume (since it did not know the new root)
  650. // so we calculate the bounding box again and encapsulate it (to avoid conflicts with other threads which may adjust the old root bounds while we're reading them)
  651. root.GetNodeBounds(old_root_bounds);
  652. new_root.EncapsulateChildBounds(0, old_root_bounds);
  653. // Update body counter
  654. mNumBodies += inLeafNumBodies;
  655. // And we're done
  656. return true;
  657. }
  658. // Failed to swap, someone else must have created a new root, try again
  659. mAllocator->DestructObject(new_root_idx);
  660. return false;
  661. }
  662. void QuadTree::AddBodiesPrepare(const BodyVector &inBodies, TrackingVector &ioTracking, BodyID *ioBodyIDs, int inNumber, AddState &outState)
  663. {
  664. // Assert sane input
  665. JPH_ASSERT(ioBodyIDs != nullptr);
  666. JPH_ASSERT(inNumber > 0);
  667. #ifdef JPH_ENABLE_ASSERTS
  668. // Below we just cast the body ID's to node ID's, check here that that is valid
  669. for (const BodyID *b = ioBodyIDs, *b_end = ioBodyIDs + inNumber; b < b_end; ++b)
  670. NodeID::sFromBodyID(*b);
  671. #endif
  672. // Build subtree for the new bodies
  673. outState.mLeafID = BuildTree(inBodies, ioTracking, (NodeID *)ioBodyIDs, inNumber, cInvalidNodeIndex, true, outState.mLeafBounds);
  674. #ifdef _DEBUG
  675. if (outState.mLeafID.IsNode())
  676. ValidateTree(inBodies, ioTracking, outState.mLeafID.GetNodeIndex(), inNumber);
  677. #endif
  678. }
  679. void QuadTree::AddBodiesFinalize(TrackingVector &ioTracking, int inNumberBodies, const AddState &inState)
  680. {
  681. // Assert sane input
  682. JPH_ASSERT(inNumberBodies > 0);
  683. // Mark tree dirty
  684. mIsDirty = true;
  685. // Get the current root node
  686. RootNode &root_node = GetCurrentRoot();
  687. for (;;)
  688. {
  689. // Check if we can insert the body in the root
  690. if (TryInsertLeaf(ioTracking, root_node.mIndex, inState.mLeafID, inState.mLeafBounds, inNumberBodies))
  691. return;
  692. // Check if we can create a new root
  693. if (TryCreateNewRoot(ioTracking, root_node.mIndex, inState.mLeafID, inState.mLeafBounds, inNumberBodies))
  694. return;
  695. }
  696. }
  697. void QuadTree::AddBodiesAbort(TrackingVector &ioTracking, const AddState &inState)
  698. {
  699. // Collect all bodies
  700. Allocator::Batch free_batch;
  701. NodeID node_stack[cStackSize];
  702. node_stack[0] = inState.mLeafID;
  703. JPH_ASSERT(node_stack[0].IsValid());
  704. int top = 0;
  705. do
  706. {
  707. // Check if node is a body
  708. NodeID child_node_id = node_stack[top];
  709. if (child_node_id.IsBody())
  710. {
  711. // Reset location of body
  712. InvalidateBodyLocation(ioTracking, child_node_id.GetBodyID());
  713. }
  714. else
  715. {
  716. // Process normal node
  717. uint32 node_idx = child_node_id.GetNodeIndex();
  718. const Node &node = mAllocator->Get(node_idx);
  719. for (int i = 0; i < 4; ++i)
  720. {
  721. NodeID sub_child_node_id = node.mChildNodeID[i];
  722. if (sub_child_node_id.IsValid())
  723. {
  724. JPH_ASSERT(top < cStackSize);
  725. node_stack[top] = sub_child_node_id;
  726. top++;
  727. }
  728. }
  729. // Mark it to be freed
  730. mAllocator->AddObjectToBatch(free_batch, node_idx);
  731. }
  732. --top;
  733. }
  734. while (top >= 0);
  735. // Now free all nodes as a single batch
  736. mAllocator->DestructObjectBatch(free_batch);
  737. }
  738. void QuadTree::RemoveBodies([[maybe_unused]] const BodyVector &inBodies, TrackingVector &ioTracking, BodyID *ioBodyIDs, int inNumber)
  739. {
  740. // Assert sane input
  741. JPH_ASSERT(ioBodyIDs != nullptr);
  742. JPH_ASSERT(inNumber > 0);
  743. // Mark tree dirty
  744. mIsDirty = true;
  745. for (BodyID *cur = ioBodyIDs, *end = ioBodyIDs + inNumber; cur < end; ++cur)
  746. {
  747. // Check if BodyID is correct
  748. JPH_ASSERT(inBodies[cur->GetIndex()]->GetID() == *cur, "Provided BodyID doesn't match BodyID in body manager");
  749. // Get location of body
  750. uint32 node_idx, child_idx;
  751. GetBodyLocation(ioTracking, *cur, node_idx, child_idx);
  752. // First we reset our internal bookkeeping
  753. InvalidateBodyLocation(ioTracking, *cur);
  754. // Then we make the bounding box invalid, no queries can find this node anymore
  755. Node &node = mAllocator->Get(node_idx);
  756. node.InvalidateChildBounds(child_idx);
  757. // Finally we reset the child id, this makes the node available for adds again
  758. node.mChildNodeID[child_idx] = NodeID::sInvalid();
  759. // We don't need to bubble up our bounding box changes to our parents since we never make volumes smaller, only bigger
  760. // But we do need to unlock the nodes so that the tree can be rebuilt
  761. UnlockNodeAndParents(node_idx);
  762. }
  763. mNumBodies -= inNumber;
  764. }
  765. void QuadTree::NotifyBodiesAABBChanged(const BodyVector &inBodies, const TrackingVector &inTracking, BodyID *ioBodyIDs, int inNumber)
  766. {
  767. // Assert sane input
  768. JPH_ASSERT(ioBodyIDs != nullptr);
  769. JPH_ASSERT(inNumber > 0);
  770. for (BodyID *cur = ioBodyIDs, *end = ioBodyIDs + inNumber; cur < end; ++cur)
  771. {
  772. // Check if BodyID is correct
  773. const Body *body = inBodies[cur->GetIndex()];
  774. JPH_ASSERT(body->GetID() == *cur, "Provided BodyID doesn't match BodyID in body manager");
  775. // Get the new bounding box
  776. const AABox &new_bounds = body->GetWorldSpaceBounds();
  777. // Get location of body
  778. uint32 node_idx, child_idx;
  779. GetBodyLocation(inTracking, *cur, node_idx, child_idx);
  780. // Widen bounds for node
  781. Node &node = mAllocator->Get(node_idx);
  782. if (node.EncapsulateChildBounds(child_idx, new_bounds))
  783. {
  784. // Mark tree dirty
  785. mIsDirty = true;
  786. // If bounds changed, widen the bounds for our parents too
  787. WidenAndUnlockNodeAndParents(node_idx, new_bounds);
  788. }
  789. }
  790. }
  791. template <class Visitor>
  792. JPH_INLINE void QuadTree::WalkTree(const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking, Visitor &ioVisitor JPH_IF_TRACK_BROADPHASE_STATS(, LayerToStats &ioStats)) const
  793. {
  794. // Get the root
  795. const RootNode &root_node = GetCurrentRoot();
  796. #ifdef JPH_TRACK_BROADPHASE_STATS
  797. // Start tracking stats
  798. int bodies_visited = 0;
  799. int hits_collected = 0;
  800. int nodes_visited = 0;
  801. uint64 collector_ticks = 0;
  802. uint64 start = GetProcessorTickCount();
  803. #endif // JPH_TRACK_BROADPHASE_STATS
  804. NodeID node_stack[cStackSize];
  805. node_stack[0] = root_node.GetNodeID();
  806. int top = 0;
  807. do
  808. {
  809. // Check if node is a body
  810. NodeID child_node_id = node_stack[top];
  811. if (child_node_id.IsBody())
  812. {
  813. // Track amount of bodies visited
  814. JPH_IF_TRACK_BROADPHASE_STATS(++bodies_visited;)
  815. BodyID body_id = child_node_id.GetBodyID();
  816. ObjectLayer object_layer = inTracking[body_id.GetIndex()].mObjectLayer; // We're not taking a lock on the body, so it may be in the process of being removed so check if the object layer is invalid
  817. if (object_layer != cObjectLayerInvalid && inObjectLayerFilter.ShouldCollide(object_layer))
  818. {
  819. JPH_PROFILE("VisitBody");
  820. // Track amount of hits
  821. JPH_IF_TRACK_BROADPHASE_STATS(++hits_collected;)
  822. // Start track time the collector takes
  823. JPH_IF_TRACK_BROADPHASE_STATS(uint64 collector_start = GetProcessorTickCount();)
  824. // We found a body we collide with, call our visitor
  825. ioVisitor.VisitBody(body_id, top);
  826. // End track time the collector takes
  827. JPH_IF_TRACK_BROADPHASE_STATS(collector_ticks += GetProcessorTickCount() - collector_start;)
  828. // Check if we're done
  829. if (ioVisitor.ShouldAbort())
  830. break;
  831. }
  832. }
  833. else if (child_node_id.IsValid())
  834. {
  835. JPH_IF_TRACK_BROADPHASE_STATS(++nodes_visited;)
  836. // Check if stack can hold more nodes
  837. if (top + 4 < cStackSize)
  838. {
  839. // Process normal node
  840. const Node &node = mAllocator->Get(child_node_id.GetNodeIndex());
  841. JPH_ASSERT(IsAligned(&node, JPH_CACHE_LINE_SIZE));
  842. // Load bounds of 4 children
  843. Vec4 bounds_minx = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMinX);
  844. Vec4 bounds_miny = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMinY);
  845. Vec4 bounds_minz = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMinZ);
  846. Vec4 bounds_maxx = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMaxX);
  847. Vec4 bounds_maxy = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMaxY);
  848. Vec4 bounds_maxz = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMaxZ);
  849. // Load ids for 4 children
  850. UVec4 child_ids = UVec4::sLoadInt4Aligned((const uint32 *)&node.mChildNodeID[0]);
  851. // Check which sub nodes to visit
  852. int num_results = ioVisitor.VisitNodes(bounds_minx, bounds_miny, bounds_minz, bounds_maxx, bounds_maxy, bounds_maxz, child_ids, top);
  853. child_ids.StoreInt4((uint32 *)&node_stack[top]);
  854. top += num_results;
  855. }
  856. else
  857. JPH_ASSERT(false, "Stack full!");
  858. }
  859. // Fetch next node until we find one that the visitor wants to see
  860. do
  861. --top;
  862. while (top >= 0 && !ioVisitor.ShouldVisitNode(top));
  863. }
  864. while (top >= 0);
  865. #ifdef JPH_TRACK_BROADPHASE_STATS
  866. // Calculate total time the broadphase walk took
  867. uint64 total_ticks = GetProcessorTickCount() - start;
  868. // Update stats under lock protection (slow!)
  869. {
  870. unique_lock lock(mStatsMutex);
  871. Stat &s = ioStats[inObjectLayerFilter.GetDescription()];
  872. s.mNumQueries++;
  873. s.mNodesVisited += nodes_visited;
  874. s.mBodiesVisited += bodies_visited;
  875. s.mHitsReported += hits_collected;
  876. s.mTotalTicks += total_ticks;
  877. s.mCollectorTicks += collector_ticks;
  878. }
  879. #endif // JPH_TRACK_BROADPHASE_STATS
  880. }
  881. void QuadTree::CastRay(const RayCast &inRay, RayCastBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const
  882. {
  883. class Visitor
  884. {
  885. public:
  886. /// Constructor
  887. JPH_INLINE Visitor(const RayCast &inRay, RayCastBodyCollector &ioCollector) :
  888. mOrigin(inRay.mOrigin),
  889. mInvDirection(inRay.mDirection),
  890. mCollector(ioCollector)
  891. {
  892. mFractionStack[0] = -1;
  893. }
  894. /// Returns true if further processing of the tree should be aborted
  895. JPH_INLINE bool ShouldAbort() const
  896. {
  897. return mCollector.ShouldEarlyOut();
  898. }
  899. /// Returns true if this node / body should be visited, false if no hit can be generated
  900. JPH_INLINE bool ShouldVisitNode(int inStackTop) const
  901. {
  902. return mFractionStack[inStackTop] < mCollector.GetEarlyOutFraction();
  903. }
  904. /// Visit nodes, returns number of hits found and sorts ioChildNodeIDs so that they are at the beginning of the vector.
  905. JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioChildNodeIDs, int inStackTop)
  906. {
  907. // Test the ray against 4 bounding boxes
  908. Vec4 fraction = RayAABox4(mOrigin, mInvDirection, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
  909. // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)
  910. return SortReverseAndStore(fraction, mCollector.GetEarlyOutFraction(), ioChildNodeIDs, &mFractionStack[inStackTop]);
  911. }
  912. /// Visit a body, returns false if the algorithm should terminate because no hits can be generated anymore
  913. JPH_INLINE void VisitBody(const BodyID &inBodyID, int inStackTop)
  914. {
  915. // Store potential hit with body
  916. BroadPhaseCastResult result { inBodyID, mFractionStack[inStackTop] };
  917. mCollector.AddHit(result);
  918. }
  919. private:
  920. Vec3 mOrigin;
  921. RayInvDirection mInvDirection;
  922. RayCastBodyCollector & mCollector;
  923. float mFractionStack[cStackSize];
  924. };
  925. Visitor visitor(inRay, ioCollector);
  926. WalkTree(inObjectLayerFilter, inTracking, visitor JPH_IF_TRACK_BROADPHASE_STATS(, mCastRayStats));
  927. }
  928. void QuadTree::CollideAABox(const AABox &inBox, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const
  929. {
  930. class Visitor
  931. {
  932. public:
  933. /// Constructor
  934. JPH_INLINE Visitor(const AABox &inBox, CollideShapeBodyCollector &ioCollector) :
  935. mBox(inBox),
  936. mCollector(ioCollector)
  937. {
  938. }
  939. /// Returns true if further processing of the tree should be aborted
  940. JPH_INLINE bool ShouldAbort() const
  941. {
  942. return mCollector.ShouldEarlyOut();
  943. }
  944. /// Returns true if this node / body should be visited, false if no hit can be generated
  945. JPH_INLINE bool ShouldVisitNode(int inStackTop) const
  946. {
  947. return true;
  948. }
  949. /// Visit nodes, returns number of hits found and sorts ioChildNodeIDs so that they are at the beginning of the vector.
  950. JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioChildNodeIDs, int inStackTop) const
  951. {
  952. // Test the box vs 4 boxes
  953. UVec4 hitting = AABox4VsBox(mBox, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
  954. // Count how many results are hitting
  955. int num_results = hitting.CountTrues();
  956. if (num_results > 0)
  957. {
  958. // Sort trues first
  959. UVec4::sSort4True(hitting, ioChildNodeIDs);
  960. }
  961. return num_results;
  962. }
  963. /// Visit a body, returns false if the algorithm should terminate because no hits can be generated anymore
  964. JPH_INLINE void VisitBody(const BodyID &inBodyID, int inStackTop)
  965. {
  966. // Store potential hit with body
  967. mCollector.AddHit(inBodyID);
  968. }
  969. private:
  970. const AABox & mBox;
  971. CollideShapeBodyCollector & mCollector;
  972. };
  973. Visitor visitor(inBox, ioCollector);
  974. WalkTree(inObjectLayerFilter, inTracking, visitor JPH_IF_TRACK_BROADPHASE_STATS(, mCollideAABoxStats));
  975. }
  976. void QuadTree::CollideSphere(Vec3Arg inCenter, float inRadius, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const
  977. {
  978. class Visitor
  979. {
  980. public:
  981. /// Constructor
  982. JPH_INLINE Visitor(Vec3Arg inCenter, float inRadius, CollideShapeBodyCollector &ioCollector) :
  983. mCenterX(inCenter.SplatX()),
  984. mCenterY(inCenter.SplatY()),
  985. mCenterZ(inCenter.SplatZ()),
  986. mRadiusSq(Vec4::sReplicate(Square(inRadius))),
  987. mCollector(ioCollector)
  988. {
  989. }
  990. /// Returns true if further processing of the tree should be aborted
  991. JPH_INLINE bool ShouldAbort() const
  992. {
  993. return mCollector.ShouldEarlyOut();
  994. }
  995. /// Returns true if this node / body should be visited, false if no hit can be generated
  996. JPH_INLINE bool ShouldVisitNode(int inStackTop) const
  997. {
  998. return true;
  999. }
  1000. /// Visit nodes, returns number of hits found and sorts ioChildNodeIDs so that they are at the beginning of the vector.
  1001. JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioChildNodeIDs, int inStackTop) const
  1002. {
  1003. // Test 4 boxes vs sphere
  1004. UVec4 hitting = AABox4VsSphere(mCenterX, mCenterY, mCenterZ, mRadiusSq, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
  1005. // Count how many results are hitting
  1006. int num_results = hitting.CountTrues();
  1007. if (num_results > 0)
  1008. {
  1009. // Sort trues first
  1010. UVec4::sSort4True(hitting, ioChildNodeIDs);
  1011. }
  1012. return num_results;
  1013. }
  1014. /// Visit a body, returns false if the algorithm should terminate because no hits can be generated anymore
  1015. JPH_INLINE void VisitBody(const BodyID &inBodyID, int inStackTop)
  1016. {
  1017. // Store potential hit with body
  1018. mCollector.AddHit(inBodyID);
  1019. }
  1020. private:
  1021. Vec4 mCenterX;
  1022. Vec4 mCenterY;
  1023. Vec4 mCenterZ;
  1024. Vec4 mRadiusSq;
  1025. CollideShapeBodyCollector & mCollector;
  1026. };
  1027. Visitor visitor(inCenter, inRadius, ioCollector);
  1028. WalkTree(inObjectLayerFilter, inTracking, visitor JPH_IF_TRACK_BROADPHASE_STATS(, mCollideSphereStats));
  1029. }
  1030. void QuadTree::CollidePoint(Vec3Arg inPoint, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const
  1031. {
  1032. class Visitor
  1033. {
  1034. public:
  1035. /// Constructor
  1036. JPH_INLINE Visitor(Vec3Arg inPoint, CollideShapeBodyCollector &ioCollector) :
  1037. mPoint(inPoint),
  1038. mCollector(ioCollector)
  1039. {
  1040. }
  1041. /// Returns true if further processing of the tree should be aborted
  1042. JPH_INLINE bool ShouldAbort() const
  1043. {
  1044. return mCollector.ShouldEarlyOut();
  1045. }
  1046. /// Returns true if this node / body should be visited, false if no hit can be generated
  1047. JPH_INLINE bool ShouldVisitNode(int inStackTop) const
  1048. {
  1049. return true;
  1050. }
  1051. /// Visit nodes, returns number of hits found and sorts ioChildNodeIDs so that they are at the beginning of the vector.
  1052. JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioChildNodeIDs, int inStackTop) const
  1053. {
  1054. // Test if point overlaps with box
  1055. UVec4 hitting = AABox4VsPoint(mPoint, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
  1056. // Count how many results are hitting
  1057. int num_results = hitting.CountTrues();
  1058. if (num_results > 0)
  1059. {
  1060. // Sort trues first
  1061. UVec4::sSort4True(hitting, ioChildNodeIDs);
  1062. }
  1063. return num_results;
  1064. }
  1065. /// Visit a body, returns false if the algorithm should terminate because no hits can be generated anymore
  1066. JPH_INLINE void VisitBody(const BodyID &inBodyID, int inStackTop)
  1067. {
  1068. // Store potential hit with body
  1069. mCollector.AddHit(inBodyID);
  1070. }
  1071. private:
  1072. Vec3 mPoint;
  1073. CollideShapeBodyCollector & mCollector;
  1074. };
  1075. Visitor visitor(inPoint, ioCollector);
  1076. WalkTree(inObjectLayerFilter, inTracking, visitor JPH_IF_TRACK_BROADPHASE_STATS(, mCollidePointStats));
  1077. }
  1078. void QuadTree::CollideOrientedBox(const OrientedBox &inBox, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const
  1079. {
  1080. class Visitor
  1081. {
  1082. public:
  1083. /// Constructor
  1084. JPH_INLINE Visitor(const OrientedBox &inBox, CollideShapeBodyCollector &ioCollector) :
  1085. mBox(inBox),
  1086. mCollector(ioCollector)
  1087. {
  1088. }
  1089. /// Returns true if further processing of the tree should be aborted
  1090. JPH_INLINE bool ShouldAbort() const
  1091. {
  1092. return mCollector.ShouldEarlyOut();
  1093. }
  1094. /// Returns true if this node / body should be visited, false if no hit can be generated
  1095. JPH_INLINE bool ShouldVisitNode(int inStackTop) const
  1096. {
  1097. return true;
  1098. }
  1099. /// Visit nodes, returns number of hits found and sorts ioChildNodeIDs so that they are at the beginning of the vector.
  1100. JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioChildNodeIDs, int inStackTop) const
  1101. {
  1102. // Test if point overlaps with box
  1103. UVec4 hitting = AABox4VsBox(mBox, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
  1104. // Count how many results are hitting
  1105. int num_results = hitting.CountTrues();
  1106. if (num_results > 0)
  1107. {
  1108. // Sort trues first
  1109. UVec4::sSort4True(hitting, ioChildNodeIDs);
  1110. }
  1111. return num_results;
  1112. }
  1113. /// Visit a body, returns false if the algorithm should terminate because no hits can be generated anymore
  1114. JPH_INLINE void VisitBody(const BodyID &inBodyID, int inStackTop)
  1115. {
  1116. // Store potential hit with body
  1117. mCollector.AddHit(inBodyID);
  1118. }
  1119. private:
  1120. OrientedBox mBox;
  1121. CollideShapeBodyCollector & mCollector;
  1122. };
  1123. Visitor visitor(inBox, ioCollector);
  1124. WalkTree(inObjectLayerFilter, inTracking, visitor JPH_IF_TRACK_BROADPHASE_STATS(, mCollideOrientedBoxStats));
  1125. }
  1126. void QuadTree::CastAABox(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const
  1127. {
  1128. class Visitor
  1129. {
  1130. public:
  1131. /// Constructor
  1132. JPH_INLINE Visitor(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector) :
  1133. mOrigin(inBox.mBox.GetCenter()),
  1134. mExtent(inBox.mBox.GetExtent()),
  1135. mInvDirection(inBox.mDirection),
  1136. mCollector(ioCollector)
  1137. {
  1138. mFractionStack[0] = -1;
  1139. }
  1140. /// Returns true if further processing of the tree should be aborted
  1141. JPH_INLINE bool ShouldAbort() const
  1142. {
  1143. return mCollector.ShouldEarlyOut();
  1144. }
  1145. /// Returns true if this node / body should be visited, false if no hit can be generated
  1146. JPH_INLINE bool ShouldVisitNode(int inStackTop) const
  1147. {
  1148. return mFractionStack[inStackTop] < mCollector.GetEarlyOutFraction();
  1149. }
  1150. /// Visit nodes, returns number of hits found and sorts ioChildNodeIDs so that they are at the beginning of the vector.
  1151. JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioChildNodeIDs, int inStackTop)
  1152. {
  1153. // Enlarge them by the casted aabox extents
  1154. AABox4EnlargeWithExtent(mExtent, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
  1155. // Test 4 children
  1156. Vec4 fraction = RayAABox4(mOrigin, mInvDirection, inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
  1157. // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)
  1158. return SortReverseAndStore(fraction, mCollector.GetEarlyOutFraction(), ioChildNodeIDs, &mFractionStack[inStackTop]);
  1159. }
  1160. /// Visit a body, returns false if the algorithm should terminate because no hits can be generated anymore
  1161. JPH_INLINE void VisitBody(const BodyID &inBodyID, int inStackTop)
  1162. {
  1163. // Store potential hit with body
  1164. BroadPhaseCastResult result { inBodyID, mFractionStack[inStackTop] };
  1165. mCollector.AddHit(result);
  1166. }
  1167. private:
  1168. Vec3 mOrigin;
  1169. Vec3 mExtent;
  1170. RayInvDirection mInvDirection;
  1171. CastShapeBodyCollector & mCollector;
  1172. float mFractionStack[cStackSize];
  1173. };
  1174. Visitor visitor(inBox, ioCollector);
  1175. WalkTree(inObjectLayerFilter, inTracking, visitor JPH_IF_TRACK_BROADPHASE_STATS(, mCastAABoxStats));
  1176. }
  1177. void QuadTree::FindCollidingPairs(const BodyVector &inBodies, const BodyID *inActiveBodies, int inNumActiveBodies, float inSpeculativeContactDistance, BodyPairCollector &ioPairCollector, ObjectLayerPairFilter inObjectLayerPairFilter) const
  1178. {
  1179. // Note that we don't lock the tree 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.
  1180. // We double check this at the end of the function.
  1181. const RootNode &root_node = GetCurrentRoot();
  1182. JPH_ASSERT(root_node.mIndex != cInvalidNodeIndex);
  1183. // Assert sane input
  1184. JPH_ASSERT(inActiveBodies != nullptr);
  1185. JPH_ASSERT(inNumActiveBodies > 0);
  1186. NodeID node_stack[cStackSize];
  1187. // Loop over all active bodies
  1188. for (int b1 = 0; b1 < inNumActiveBodies; ++b1)
  1189. {
  1190. BodyID b1_id = inActiveBodies[b1];
  1191. const Body &body1 = *inBodies[b1_id.GetIndex()];
  1192. JPH_ASSERT(!body1.IsStatic());
  1193. // Expand the bounding box by the speculative contact distance
  1194. AABox bounds1 = body1.GetWorldSpaceBounds();
  1195. bounds1.ExpandBy(Vec3::sReplicate(inSpeculativeContactDistance));
  1196. // Test each body with the tree
  1197. node_stack[0] = root_node.GetNodeID();
  1198. int top = 0;
  1199. do
  1200. {
  1201. // Check if node is a body
  1202. NodeID child_node_id = node_stack[top];
  1203. if (child_node_id.IsBody())
  1204. {
  1205. // Don't collide with self
  1206. BodyID b2_id = child_node_id.GetBodyID();
  1207. if (b1_id != b2_id)
  1208. {
  1209. // Collision between dynamic pairs need to be picked up only once
  1210. const Body &body2 = *inBodies[b2_id.GetIndex()];
  1211. if (inObjectLayerPairFilter(body1.GetObjectLayer(), body2.GetObjectLayer())
  1212. && Body::sFindCollidingPairsCanCollide(body1, body2))
  1213. {
  1214. // In the broadphase we widen the bounding box when a body moves, do a final
  1215. // check to see if the bounding boxes actually overlap
  1216. if (bounds1.Overlaps(body2.GetWorldSpaceBounds()))
  1217. {
  1218. // Store potential hit between bodies
  1219. ioPairCollector.AddHit({ b1_id, b2_id });
  1220. }
  1221. }
  1222. }
  1223. }
  1224. else if (child_node_id.IsValid())
  1225. {
  1226. // Process normal node
  1227. const Node &node = mAllocator->Get(child_node_id.GetNodeIndex());
  1228. JPH_ASSERT(IsAligned(&node, JPH_CACHE_LINE_SIZE));
  1229. // Get bounds of 4 children
  1230. Vec4 bounds_minx = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMinX);
  1231. Vec4 bounds_miny = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMinY);
  1232. Vec4 bounds_minz = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMinZ);
  1233. Vec4 bounds_maxx = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMaxX);
  1234. Vec4 bounds_maxy = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMaxY);
  1235. Vec4 bounds_maxz = Vec4::sLoadFloat4Aligned((const Float4 *)&node.mBoundsMaxZ);
  1236. // Test overlap
  1237. UVec4 overlap = AABox4VsBox(bounds1, bounds_minx, bounds_miny, bounds_minz, bounds_maxx, bounds_maxy, bounds_maxz);
  1238. int num_results = overlap.CountTrues();
  1239. if (num_results > 0)
  1240. {
  1241. // Load ids for 4 children
  1242. UVec4 child_ids = UVec4::sLoadInt4Aligned((const uint32 *)&node.mChildNodeID[0]);
  1243. // Sort so that overlaps are first
  1244. UVec4::sSort4True(overlap, child_ids);
  1245. // Push them onto the stack
  1246. if (top + 4 < cStackSize)
  1247. {
  1248. child_ids.StoreInt4((uint32 *)&node_stack[top]);
  1249. top += num_results;
  1250. }
  1251. else
  1252. JPH_ASSERT(false, "Stack full!");
  1253. }
  1254. }
  1255. --top;
  1256. }
  1257. while (top >= 0);
  1258. }
  1259. // Test that the root node was not swapped while finding collision pairs.
  1260. // This would mean that UpdateFinalize/DiscardOldTree ran during collision detection which should not be possible due to the way the jobs are scheduled.
  1261. JPH_ASSERT(root_node.mIndex != cInvalidNodeIndex);
  1262. JPH_ASSERT(&root_node == &GetCurrentRoot());
  1263. }
  1264. #ifdef _DEBUG
  1265. void QuadTree::ValidateTree(const BodyVector &inBodies, const TrackingVector &inTracking, uint32 inNodeIndex, uint32 inNumExpectedBodies) const
  1266. {
  1267. JPH_PROFILE_FUNCTION();
  1268. // Root should be valid
  1269. JPH_ASSERT(inNodeIndex != cInvalidNodeIndex);
  1270. // To avoid call overhead, create a stack in place
  1271. struct StackEntry
  1272. {
  1273. uint32 mNodeIndex;
  1274. uint32 mParentNodeIndex;
  1275. };
  1276. StackEntry stack[cStackSize];
  1277. stack[0].mNodeIndex = inNodeIndex;
  1278. stack[0].mParentNodeIndex = cInvalidNodeIndex;
  1279. int top = 0;
  1280. uint32 num_bodies = 0;
  1281. do
  1282. {
  1283. // Copy entry from the stack
  1284. StackEntry cur_stack = stack[top];
  1285. // Validate parent
  1286. const Node &node = mAllocator->Get(cur_stack.mNodeIndex);
  1287. JPH_ASSERT(node.mParentNodeIndex == cur_stack.mParentNodeIndex);
  1288. // Validate that when a parent is locked that all of its children are also
  1289. JPH_ASSERT(cur_stack.mParentNodeIndex == cInvalidNodeIndex || !mAllocator->Get(cur_stack.mParentNodeIndex).mIsLocked || node.mIsLocked);
  1290. // Loop children
  1291. for (uint32 i = 0; i < 4; ++i)
  1292. {
  1293. NodeID child_node_id = node.mChildNodeID[i];
  1294. if (child_node_id.IsValid())
  1295. {
  1296. if (child_node_id.IsNode())
  1297. {
  1298. // Child is a node, recurse
  1299. uint32 child_idx = child_node_id.GetNodeIndex();
  1300. StackEntry &new_entry = stack[top++];
  1301. new_entry.mNodeIndex = child_idx;
  1302. new_entry.mParentNodeIndex = cur_stack.mNodeIndex;
  1303. // Validate that the bounding box is bigger or equal to the bounds in the tree
  1304. // Bounding box could also be invalid if all children of our child were removed
  1305. AABox child_bounds;
  1306. node.GetChildBounds(i, child_bounds);
  1307. AABox real_child_bounds;
  1308. mAllocator->Get(child_idx).GetNodeBounds(real_child_bounds);
  1309. JPH_ASSERT(child_bounds.Contains(real_child_bounds) || !real_child_bounds.IsValid());
  1310. }
  1311. else
  1312. {
  1313. // Increment number of bodies found
  1314. ++num_bodies;
  1315. // Check if tracker matches position of body
  1316. uint32 node_idx, child_idx;
  1317. GetBodyLocation(inTracking, child_node_id.GetBodyID(), node_idx, child_idx);
  1318. JPH_ASSERT(node_idx == cur_stack.mNodeIndex);
  1319. JPH_ASSERT(child_idx == i);
  1320. // Validate that the body bounds are bigger or equal to the bounds in the tree
  1321. AABox body_bounds;
  1322. node.GetChildBounds(i, body_bounds);
  1323. const Body *body = inBodies[child_node_id.GetBodyID().GetIndex()];
  1324. AABox cached_body_bounds = body->GetWorldSpaceBounds();
  1325. AABox real_body_bounds = body->GetShape()->GetWorldSpaceBounds(body->GetCenterOfMassTransform(), Vec3::sReplicate(1.0f));
  1326. JPH_ASSERT(cached_body_bounds == real_body_bounds); // Check that cached body bounds are up to date
  1327. JPH_ASSERT(body_bounds.Contains(real_body_bounds));
  1328. }
  1329. }
  1330. }
  1331. --top;
  1332. }
  1333. while (top >= 0);
  1334. // Check that the amount of bodies in the tree matches our counter
  1335. JPH_ASSERT(num_bodies == inNumExpectedBodies);
  1336. }
  1337. #endif
  1338. #ifdef JPH_TRACK_BROADPHASE_STATS
  1339. void QuadTree::ReportStats(const char *inName, const LayerToStats &inLayer) const
  1340. {
  1341. uint64 ticks_per_sec = GetProcessorTicksPerSecond();
  1342. for (const LayerToStats::value_type &kv : inLayer)
  1343. {
  1344. double total_time = 1000.0 * double(kv.second.mTotalTicks) / double(ticks_per_sec);
  1345. double total_time_excl_collector = 1000.0 * double(kv.second.mTotalTicks - kv.second.mCollectorTicks) / double(ticks_per_sec);
  1346. double hits_reported_vs_bodies_visited = kv.second.mBodiesVisited > 0? 100.0 * double(kv.second.mHitsReported) / double(kv.second.mBodiesVisited) : 100.0;
  1347. double hits_reported_vs_nodes_visited = kv.second.mNodesVisited > 0? double(kv.second.mHitsReported) / double(kv.second.mNodesVisited) : -1.0f;
  1348. stringstream str;
  1349. str << inName << ", " << kv.first << ", " << mName << ", " << kv.second.mNumQueries << ", " << total_time << ", " << total_time_excl_collector << ", " << kv.second.mNodesVisited << ", " << kv.second.mBodiesVisited << ", " << kv.second.mHitsReported << ", " << hits_reported_vs_bodies_visited << ", " << hits_reported_vs_nodes_visited;
  1350. Trace(str.str().c_str());
  1351. }
  1352. }
  1353. void QuadTree::ReportStats() const
  1354. {
  1355. unique_lock lock(mStatsMutex);
  1356. ReportStats("RayCast", mCastRayStats);
  1357. ReportStats("CollideAABox", mCollideAABoxStats);
  1358. ReportStats("CollideSphere", mCollideSphereStats);
  1359. ReportStats("CollidePoint", mCollidePointStats);
  1360. ReportStats("CollideOrientedBox", mCollideOrientedBoxStats);
  1361. ReportStats("CastAABox", mCastAABoxStats);
  1362. }
  1363. #endif // JPH_TRACK_BROADPHASE_STATS
  1364. } // JPH