QuadTree.cpp 53 KB

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