2
0

QuadTree.h 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #pragma once
  5. #include <Jolt/Core/FixedSizeFreeList.h>
  6. #include <Jolt/Core/Atomics.h>
  7. #include <Jolt/Core/NonCopyable.h>
  8. #include <Jolt/Physics/Body/BodyManager.h>
  9. #include <Jolt/Physics/Collision/BroadPhase/BroadPhase.h>
  10. //#define JPH_DUMP_BROADPHASE_TREE
  11. JPH_NAMESPACE_BEGIN
  12. /// Internal tree structure in broadphase, is essentially a quad AABB tree.
  13. /// Tree is lockless (except for UpdatePrepare/Finalize() function), modifying objects in the tree will widen the aabbs of parent nodes to make the node fit.
  14. /// During the UpdatePrepare/Finalize() call the tree is rebuilt to achieve a tight fit again.
  15. class JPH_EXPORT QuadTree : public NonCopyable
  16. {
  17. public:
  18. JPH_OVERRIDE_NEW_DELETE
  19. private:
  20. // Forward declare
  21. class AtomicNodeID;
  22. /// Class that points to either a body or a node in the tree
  23. class NodeID
  24. {
  25. public:
  26. JPH_OVERRIDE_NEW_DELETE
  27. /// Default constructor does not initialize
  28. inline NodeID() = default;
  29. /// Construct a node ID
  30. static inline NodeID sInvalid() { return NodeID(cInvalidNodeIndex); }
  31. static inline NodeID sFromBodyID(BodyID inID) { NodeID node_id(inID.GetIndexAndSequenceNumber()); JPH_ASSERT(node_id.IsBody()); return node_id; }
  32. static inline NodeID sFromNodeIndex(uint32 inIdx) { NodeID node_id(inIdx | cIsNode); JPH_ASSERT(node_id.IsNode()); return node_id; }
  33. /// Check what type of ID it is
  34. inline bool IsValid() const { return mID != cInvalidNodeIndex; }
  35. inline bool IsBody() const { return (mID & cIsNode) == 0; }
  36. inline bool IsNode() const { return (mID & cIsNode) != 0; }
  37. /// Get body or node index
  38. inline BodyID GetBodyID() const { JPH_ASSERT(IsBody()); return BodyID(mID); }
  39. inline uint32 GetNodeIndex() const { JPH_ASSERT(IsNode()); return mID & ~cIsNode; }
  40. /// Comparison
  41. inline bool operator == (const BodyID &inRHS) const { return mID == inRHS.GetIndexAndSequenceNumber(); }
  42. inline bool operator == (const NodeID &inRHS) const { return mID == inRHS.mID; }
  43. private:
  44. friend class AtomicNodeID;
  45. inline explicit NodeID(uint32 inID) : mID(inID) { }
  46. static const uint32 cIsNode = BodyID::cBroadPhaseBit; ///< If this bit is set it means that the ID refers to a node, otherwise it refers to a body
  47. uint32 mID;
  48. };
  49. static_assert(sizeof(NodeID) == sizeof(BodyID), "Body id's should have the same size as NodeIDs");
  50. /// A NodeID that uses atomics to store the value
  51. class AtomicNodeID
  52. {
  53. public:
  54. /// Constructor
  55. AtomicNodeID() = default;
  56. explicit AtomicNodeID(const NodeID &inRHS) : mID(inRHS.mID) { }
  57. /// Assignment
  58. inline void operator = (const NodeID &inRHS) { mID = inRHS.mID; }
  59. /// Getting the value
  60. inline operator NodeID () const { return NodeID(mID); }
  61. /// Check if the ID is valid
  62. inline bool IsValid() const { return mID != cInvalidNodeIndex; }
  63. /// Comparison
  64. inline bool operator == (const BodyID &inRHS) const { return mID == inRHS.GetIndexAndSequenceNumber(); }
  65. inline bool operator == (const NodeID &inRHS) const { return mID == inRHS.mID; }
  66. /// Atomically compare and swap value. Expects inOld value, replaces with inNew value or returns false
  67. inline bool CompareExchange(NodeID inOld, NodeID inNew) { return mID.compare_exchange_strong(inOld.mID, inNew.mID); }
  68. private:
  69. atomic<uint32> mID;
  70. };
  71. /// Class that represents a node in the tree
  72. class Node
  73. {
  74. public:
  75. /// Construct node
  76. explicit Node(bool inIsChanged);
  77. /// Get bounding box encapsulating all children
  78. void GetNodeBounds(AABox &outBounds) const;
  79. /// Get bounding box in a consistent way with the functions below (check outBounds.IsValid() before using the box)
  80. void GetChildBounds(int inChildIndex, AABox &outBounds) const;
  81. /// Set the bounds in such a way that other threads will either see a fully correct bounding box or a bounding box with no volume
  82. void SetChildBounds(int inChildIndex, const AABox &inBounds);
  83. /// Invalidate bounding box in such a way that other threads will not temporarily see a very large bounding box
  84. void InvalidateChildBounds(int inChildIndex);
  85. /// Encapsulate inBounds in node bounds, returns true if there were changes
  86. bool EncapsulateChildBounds(int inChildIndex, const AABox &inBounds);
  87. /// Bounding box for child nodes or bodies (all initially set to invalid so no collision test will ever traverse to the leaf)
  88. atomic<float> mBoundsMinX[4];
  89. atomic<float> mBoundsMinY[4];
  90. atomic<float> mBoundsMinZ[4];
  91. atomic<float> mBoundsMaxX[4];
  92. atomic<float> mBoundsMaxY[4];
  93. atomic<float> mBoundsMaxZ[4];
  94. /// Index of child node or body ID.
  95. AtomicNodeID mChildNodeID[4];
  96. /// Index of the parent node.
  97. /// Note: This value is unreliable during the UpdatePrepare/Finalize() function as a node may be relinked to the newly built tree.
  98. atomic<uint32> mParentNodeIndex = cInvalidNodeIndex;
  99. /// If this part of the tree has changed, if not, we will treat this sub tree as a single body during the UpdatePrepare/Finalize().
  100. /// If any changes are made to an object inside this sub tree then the direct path from the body to the top of the tree will become changed.
  101. atomic<uint32> mIsChanged;
  102. // Padding to align to 124 bytes
  103. uint32 mPadding = 0;
  104. };
  105. // Maximum size of the stack during tree walk
  106. static constexpr int cStackSize = 128;
  107. static_assert(sizeof(atomic<float>) == 4, "Assuming that an atomic doesn't add any additional storage");
  108. static_assert(sizeof(atomic<uint32>) == 4, "Assuming that an atomic doesn't add any additional storage");
  109. static_assert(is_trivially_destructible<Node>(), "Assuming that we don't have a destructor");
  110. public:
  111. /// Class that allocates tree nodes, can be shared between multiple trees
  112. using Allocator = FixedSizeFreeList<Node>;
  113. static_assert(Allocator::ObjectStorageSize == 128, "Node should be 128 bytes");
  114. /// Data to track location of a Body in the tree
  115. struct Tracking
  116. {
  117. /// Constructor to satisfy the vector class
  118. Tracking() = default;
  119. Tracking(const Tracking &inRHS) : mBroadPhaseLayer(inRHS.mBroadPhaseLayer.load()), mObjectLayer(inRHS.mObjectLayer.load()), mBodyLocation(inRHS.mBodyLocation.load()) { }
  120. /// Invalid body location identifier
  121. static const uint32 cInvalidBodyLocation = 0xffffffff;
  122. atomic<BroadPhaseLayer::Type> mBroadPhaseLayer = (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid;
  123. atomic<ObjectLayer> mObjectLayer = cObjectLayerInvalid;
  124. atomic<uint32> mBodyLocation { cInvalidBodyLocation };
  125. };
  126. using TrackingVector = Array<Tracking>;
  127. /// Destructor
  128. ~QuadTree();
  129. #if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
  130. /// Name of the tree for debugging purposes
  131. void SetName(const char *inName) { mName = inName; }
  132. inline const char * GetName() const { return mName; }
  133. #endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED
  134. /// Check if there is anything in the tree
  135. inline bool HasBodies() const { return mNumBodies != 0; }
  136. /// Check if the tree needs an UpdatePrepare/Finalize()
  137. inline bool IsDirty() const { return mIsDirty; }
  138. /// Check if this tree can get an UpdatePrepare/Finalize() or if it needs a DiscardOldTree() first
  139. inline bool CanBeUpdated() const { return mFreeNodeBatch.mNumObjects == 0; }
  140. /// Initialization
  141. void Init(Allocator &inAllocator);
  142. struct UpdateState
  143. {
  144. NodeID mRootNodeID; ///< This will be the new root node id
  145. };
  146. /// Will throw away the previous frame's nodes so that we can start building a new tree in the background
  147. void DiscardOldTree();
  148. /// Update the broadphase, needs to be called regularly to achieve a tight fit of the tree when bodies have been modified.
  149. /// UpdatePrepare() will build the tree, UpdateFinalize() will lock the root of the tree shortly and swap the trees and afterwards clean up temporary data structures.
  150. void UpdatePrepare(const BodyVector &inBodies, TrackingVector &ioTracking, UpdateState &outUpdateState, bool inFullRebuild);
  151. void UpdateFinalize(const BodyVector &inBodies, const TrackingVector &inTracking, const UpdateState &inUpdateState);
  152. /// Temporary data structure to pass information between AddBodiesPrepare and AddBodiesFinalize/Abort
  153. struct AddState
  154. {
  155. NodeID mLeafID = NodeID::sInvalid();
  156. AABox mLeafBounds;
  157. };
  158. /// Prepare adding inNumber bodies at ioBodyIDs to the quad tree, returns the state in outState that should be used in AddBodiesFinalize.
  159. /// This can be done on a background thread without influencing the broadphase.
  160. /// ioBodyIDs may be shuffled around by this function.
  161. void AddBodiesPrepare(const BodyVector &inBodies, TrackingVector &ioTracking, BodyID *ioBodyIDs, int inNumber, AddState &outState);
  162. /// Finalize adding bodies to the quadtree, supply the same number of bodies as in AddBodiesPrepare.
  163. void AddBodiesFinalize(TrackingVector &ioTracking, int inNumberBodies, const AddState &inState);
  164. /// Abort adding bodies to the quadtree, supply the same bodies and state as in AddBodiesPrepare.
  165. /// This can be done on a background thread without influencing the broadphase.
  166. void AddBodiesAbort(TrackingVector &ioTracking, const AddState &inState);
  167. /// Remove inNumber bodies in ioBodyIDs from the quadtree.
  168. void RemoveBodies(const BodyVector &inBodies, TrackingVector &ioTracking, const BodyID *ioBodyIDs, int inNumber);
  169. /// Call whenever the aabb of a body changes.
  170. void NotifyBodiesAABBChanged(const BodyVector &inBodies, const TrackingVector &inTracking, const BodyID *ioBodyIDs, int inNumber);
  171. /// Cast a ray and get the intersecting bodies in ioCollector.
  172. void CastRay(const RayCast &inRay, RayCastBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const;
  173. /// Get bodies intersecting with inBox in ioCollector
  174. void CollideAABox(const AABox &inBox, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const;
  175. /// Get bodies intersecting with a sphere in ioCollector
  176. void CollideSphere(Vec3Arg inCenter, float inRadius, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const;
  177. /// Get bodies intersecting with a point and any hits to ioCollector
  178. void CollidePoint(Vec3Arg inPoint, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const;
  179. /// Get bodies intersecting with an oriented box and any hits to ioCollector
  180. void CollideOrientedBox(const OrientedBox &inBox, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const;
  181. /// Cast a box and get intersecting bodies in ioCollector
  182. void CastAABox(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const;
  183. /// Find all colliding pairs between dynamic bodies, calls ioPairCollector for every pair found
  184. void FindCollidingPairs(const BodyVector &inBodies, const BodyID *inActiveBodies, int inNumActiveBodies, float inSpeculativeContactDistance, BodyPairCollector &ioPairCollector, const ObjectLayerPairFilter &inObjectLayerPairFilter) const;
  185. #ifdef JPH_TRACK_BROADPHASE_STATS
  186. /// Sum up all the ticks spent in the various layers
  187. uint64 GetTicks100Pct() const;
  188. /// Trace the stats of this tree to the TTY
  189. void ReportStats(uint64 inTicks100Pct) const;
  190. #endif // JPH_TRACK_BROADPHASE_STATS
  191. private:
  192. /// Constants
  193. static const uint32 cInvalidNodeIndex = 0xffffffff; ///< Value used to indicate node index is invalid
  194. static const float cLargeFloat; ///< A large floating point number that is small enough to not cause any overflows
  195. static const AABox cInvalidBounds; ///< Invalid bounding box using cLargeFloat
  196. /// We alternate between two trees in order to let collision queries complete in parallel to adding/removing objects to the tree
  197. struct RootNode
  198. {
  199. /// Get the ID of the root node
  200. inline NodeID GetNodeID() const { return NodeID::sFromNodeIndex(mIndex); }
  201. /// Index of the root node of the tree (this is always a node, never a body id)
  202. atomic<uint32> mIndex { cInvalidNodeIndex };
  203. };
  204. /// Caches location of body inBodyID in the tracker, body can be found in mNodes[inNodeIdx].mChildNodeID[inChildIdx]
  205. void GetBodyLocation(const TrackingVector &inTracking, BodyID inBodyID, uint32 &outNodeIdx, uint32 &outChildIdx) const;
  206. void SetBodyLocation(TrackingVector &ioTracking, BodyID inBodyID, uint32 inNodeIdx, uint32 inChildIdx) const;
  207. static void sInvalidateBodyLocation(TrackingVector &ioTracking, BodyID inBodyID);
  208. /// Get the current root of the tree
  209. JPH_INLINE const RootNode & GetCurrentRoot() const { return mRootNode[mRootNodeIndex]; }
  210. JPH_INLINE RootNode & GetCurrentRoot() { return mRootNode[mRootNodeIndex]; }
  211. /// Depending on if inNodeID is a body or tree node return the bounding box
  212. inline AABox GetNodeOrBodyBounds(const BodyVector &inBodies, NodeID inNodeID) const;
  213. /// Mark node and all of its parents as changed
  214. inline void MarkNodeAndParentsChanged(uint32 inNodeIndex);
  215. /// Widen parent bounds of node inNodeIndex to encapsulate inNewBounds, also mark node and all of its parents as changed
  216. inline void WidenAndMarkNodeAndParentsChanged(uint32 inNodeIndex, const AABox &inNewBounds);
  217. /// Allocate a new node
  218. inline uint32 AllocateNode(bool inIsChanged);
  219. /// Try to insert a new leaf to the tree at inNodeIndex
  220. inline bool TryInsertLeaf(TrackingVector &ioTracking, int inNodeIndex, NodeID inLeafID, const AABox &inLeafBounds, int inLeafNumBodies);
  221. /// Try to replace the existing root with a new root that contains both the existing root and the new leaf
  222. inline bool TryCreateNewRoot(TrackingVector &ioTracking, atomic<uint32> &ioRootNodeIndex, NodeID inLeafID, const AABox &inLeafBounds, int inLeafNumBodies);
  223. /// Build a tree for ioBodyIDs, returns the NodeID of the root (which will be the ID of a single body if inNumber = 1). All tree levels up to inMaxDepthMarkChanged will be marked as 'changed'.
  224. NodeID BuildTree(const BodyVector &inBodies, TrackingVector &ioTracking, NodeID *ioNodeIDs, int inNumber, uint inMaxDepthMarkChanged, AABox &outBounds);
  225. /// Sorts ioNodeIDs spatially into 2 groups. Second groups starts at ioNodeIDs + outMidPoint.
  226. /// After the function returns ioNodeIDs and ioNodeCenters will be shuffled
  227. static void sPartition(NodeID *ioNodeIDs, Vec3 *ioNodeCenters, int inNumber, int &outMidPoint);
  228. /// Sorts ioNodeIDs from inBegin to (but excluding) inEnd spatially into 4 groups.
  229. /// outSplit needs to be 5 ints long, when the function returns each group runs from outSplit[i] to (but excluding) outSplit[i + 1]
  230. /// After the function returns ioNodeIDs and ioNodeCenters will be shuffled
  231. static void sPartition4(NodeID *ioNodeIDs, Vec3 *ioNodeCenters, int inBegin, int inEnd, int *outSplit);
  232. #ifdef _DEBUG
  233. /// Validate that the tree is consistent.
  234. /// Note: This function only works if the tree is not modified while we're traversing it.
  235. void ValidateTree(const BodyVector &inBodies, const TrackingVector &inTracking, uint32 inNodeIndex, uint32 inNumExpectedBodies) const;
  236. #endif
  237. #ifdef JPH_DUMP_BROADPHASE_TREE
  238. /// Dump the tree in DOT format (see: https://graphviz.org/)
  239. void DumpTree(const NodeID &inRoot, const char *inFileNamePrefix) const;
  240. #endif
  241. #ifdef JPH_TRACK_BROADPHASE_STATS
  242. /// Mutex protecting the various LayerToStats members
  243. mutable Mutex mStatsMutex;
  244. struct Stat
  245. {
  246. uint64 mNumQueries = 0;
  247. uint64 mNodesVisited = 0;
  248. uint64 mBodiesVisited = 0;
  249. uint64 mHitsReported = 0;
  250. uint64 mTotalTicks = 0;
  251. uint64 mCollectorTicks = 0;
  252. };
  253. using LayerToStats = UnorderedMap<String, Stat>;
  254. /// Sum up all the ticks in a layer
  255. uint64 GetTicks100Pct(const LayerToStats &inLayer) const;
  256. /// Trace the stats of a single query type to the TTY
  257. void ReportStats(const char *inName, const LayerToStats &inLayer, uint64 inTicks100Pct) const;
  258. mutable LayerToStats mCastRayStats;
  259. mutable LayerToStats mCollideAABoxStats;
  260. mutable LayerToStats mCollideSphereStats;
  261. mutable LayerToStats mCollidePointStats;
  262. mutable LayerToStats mCollideOrientedBoxStats;
  263. mutable LayerToStats mCastAABoxStats;
  264. #endif // JPH_TRACK_BROADPHASE_STATS
  265. /// Debug function to get the depth of the tree from node inNodeID
  266. uint GetMaxTreeDepth(const NodeID &inNodeID) const;
  267. /// Walk the node tree calling the Visitor::VisitNodes for each node encountered and Visitor::VisitBody for each body encountered
  268. template <class Visitor>
  269. JPH_INLINE void WalkTree(const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking, Visitor &ioVisitor JPH_IF_TRACK_BROADPHASE_STATS(, LayerToStats &ioStats)) const;
  270. #if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
  271. /// Name of this tree for debugging purposes
  272. const char * mName = "Layer";
  273. #endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED
  274. /// Number of bodies currently in the tree
  275. atomic<uint32> mNumBodies { 0 };
  276. /// We alternate between two tree root nodes. When updating, we activate the new tree and we keep the old tree alive.
  277. /// for queries that are in progress until the next time DiscardOldTree() is called.
  278. RootNode mRootNode[2];
  279. atomic<uint32> mRootNodeIndex { 0 };
  280. /// Allocator that controls adding / freeing nodes
  281. Allocator * mAllocator = nullptr;
  282. /// This is a list of nodes that must be deleted after the trees are swapped and the old tree is no longer in use
  283. Allocator::Batch mFreeNodeBatch;
  284. /// Flag to keep track of changes to the broadphase, if false, we don't need to UpdatePrepare/Finalize()
  285. atomic<bool> mIsDirty = false;
  286. };
  287. JPH_NAMESPACE_END