Jolt Physics
A multi core friendly Game Physics Engine
Loading...
Searching...
No Matches
QuadTree.h
Go to the documentation of this file.
1// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
2// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
3// SPDX-License-Identifier: MIT
4
5#pragma once
6
8#include <Jolt/Core/Atomics.h>
12
13//#define JPH_DUMP_BROADPHASE_TREE
14
16
20class QuadTree : public NonCopyable
21{
22public:
24
25private:
26 // Forward declare
27 class AtomicNodeID;
28
30 class NodeID
31 {
32 public:
34
36 inline NodeID() = default;
37
39 static inline NodeID sInvalid() { return NodeID(cInvalidNodeIndex); }
40 static inline NodeID sFromBodyID(BodyID inID) { NodeID node_id(inID.GetIndexAndSequenceNumber()); JPH_ASSERT(node_id.IsBody()); return node_id; }
41 static inline NodeID sFromNodeIndex(uint32 inIdx) { NodeID node_id(inIdx | cIsNode); JPH_ASSERT(node_id.IsNode()); return node_id; }
42
44 inline bool IsValid() const { return mID != cInvalidNodeIndex; }
45 inline bool IsBody() const { return (mID & cIsNode) == 0; }
46 inline bool IsNode() const { return (mID & cIsNode) != 0; }
47
49 inline BodyID GetBodyID() const { JPH_ASSERT(IsBody()); return BodyID(mID); }
50 inline uint32 GetNodeIndex() const { JPH_ASSERT(IsNode()); return mID & ~cIsNode; }
51
53 inline bool operator == (const BodyID &inRHS) const { return mID == inRHS.GetIndexAndSequenceNumber(); }
54 inline bool operator == (const NodeID &inRHS) const { return mID == inRHS.mID; }
55
56 private:
57 friend class AtomicNodeID;
58
59 inline explicit NodeID(uint32 inID) : mID(inID) { }
60
61 static const uint32 cIsNode = BodyID::cBroadPhaseBit;
62
63 uint32 mID;
64 };
65
66 static_assert(sizeof(NodeID) == sizeof(BodyID), "Body id's should have the same size as NodeIDs");
67
69 class AtomicNodeID
70 {
71 public:
73 AtomicNodeID() = default;
74 explicit AtomicNodeID(const NodeID &inRHS) : mID(inRHS.mID) { }
75
77 inline void operator = (const NodeID &inRHS) { mID = inRHS.mID; }
78
80 inline operator NodeID () const { return NodeID(mID); }
81
83 inline bool IsValid() const { return mID != cInvalidNodeIndex; }
84
86 inline bool operator == (const BodyID &inRHS) const { return mID == inRHS.GetIndexAndSequenceNumber(); }
87 inline bool operator == (const NodeID &inRHS) const { return mID == inRHS.mID; }
88
90 inline bool CompareExchange(NodeID inOld, NodeID inNew) { return mID.compare_exchange_strong(inOld.mID, inNew.mID); }
91
92 private:
93 atomic<uint32> mID;
94 };
95
97 class Node
98 {
99 public:
101 explicit Node(bool inIsChanged);
102
104 void GetNodeBounds(AABox &outBounds) const;
105
107 void GetChildBounds(int inChildIndex, AABox &outBounds) const;
108
110 void SetChildBounds(int inChildIndex, const AABox &inBounds);
111
113 void InvalidateChildBounds(int inChildIndex);
114
116 bool EncapsulateChildBounds(int inChildIndex, const AABox &inBounds);
117
119 atomic<float> mBoundsMinX[4];
120 atomic<float> mBoundsMinY[4];
121 atomic<float> mBoundsMinZ[4];
122 atomic<float> mBoundsMaxX[4];
123 atomic<float> mBoundsMaxY[4];
124 atomic<float> mBoundsMaxZ[4];
125
127 AtomicNodeID mChildNodeID[4];
128
131 atomic<uint32> mParentNodeIndex = cInvalidNodeIndex;
132
135 atomic<uint32> mIsChanged;
136
137 // Padding to align to 124 bytes
138 uint32 mPadding = 0;
139 };
140
141 // Maximum size of the stack during tree walk
142 static constexpr int cStackSize = 128;
143
144 static_assert(sizeof(atomic<float>) == 4, "Assuming that an atomic doesn't add any additional storage");
145 static_assert(sizeof(atomic<uint32>) == 4, "Assuming that an atomic doesn't add any additional storage");
146 static_assert(is_trivially_destructible<Node>(), "Assuming that we don't have a destructor");
147
148public:
151
152 static_assert(Allocator::ObjectStorageSize == 128, "Node should be 128 bytes");
153
155 struct Tracking
156 {
158 Tracking() = default;
159 Tracking(const Tracking &inRHS) : mBroadPhaseLayer(inRHS.mBroadPhaseLayer.load()), mObjectLayer(inRHS.mObjectLayer.load()), mBodyLocation(inRHS.mBodyLocation.load()) { }
160
162 static const uint32 cInvalidBodyLocation = 0xffffffff;
163
164 atomic<BroadPhaseLayer::Type> mBroadPhaseLayer = (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid;
165 atomic<ObjectLayer> mObjectLayer = cObjectLayerInvalid;
167 };
168
170
172 ~QuadTree();
173
174#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
176 void SetName(const char *inName) { mName = inName; }
177 inline const char * GetName() const { return mName; }
178#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED
179
181 inline bool HasBodies() const { return mNumBodies != 0; }
182
184 inline bool IsDirty() const { return mIsDirty; }
185
187 inline bool CanBeUpdated() const { return mFreeNodeBatch.mNumObjects == 0; }
188
190 void Init(Allocator &inAllocator);
191
193 {
194 NodeID mRootNodeID;
195 };
196
198 void DiscardOldTree();
199
202 void UpdatePrepare(const BodyVector &inBodies, TrackingVector &ioTracking, UpdateState &outUpdateState, bool inFullRebuild);
203 void UpdateFinalize(const BodyVector &inBodies, const TrackingVector &inTracking, const UpdateState &inUpdateState);
204
206 struct AddState
207 {
208 NodeID mLeafID = NodeID::sInvalid();
210 };
211
215 void AddBodiesPrepare(const BodyVector &inBodies, TrackingVector &ioTracking, BodyID *ioBodyIDs, int inNumber, AddState &outState);
216
218 void AddBodiesFinalize(TrackingVector &ioTracking, int inNumberBodies, const AddState &inState);
219
222 void AddBodiesAbort(TrackingVector &ioTracking, const AddState &inState);
223
225 void RemoveBodies(const BodyVector &inBodies, TrackingVector &ioTracking, const BodyID *ioBodyIDs, int inNumber);
226
228 void NotifyBodiesAABBChanged(const BodyVector &inBodies, const TrackingVector &inTracking, const BodyID *ioBodyIDs, int inNumber);
229
231 void CastRay(const RayCast &inRay, RayCastBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const;
232
234 void CollideAABox(const AABox &inBox, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const;
235
237 void CollideSphere(Vec3Arg inCenter, float inRadius, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const;
238
240 void CollidePoint(Vec3Arg inPoint, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const;
241
243 void CollideOrientedBox(const OrientedBox &inBox, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const;
244
246 void CastAABox(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const;
247
249 void FindCollidingPairs(const BodyVector &inBodies, const BodyID *inActiveBodies, int inNumActiveBodies, float inSpeculativeContactDistance, BodyPairCollector &ioPairCollector, const ObjectLayerPairFilter &inObjectLayerPairFilter) const;
250
251#ifdef JPH_TRACK_BROADPHASE_STATS
253 void ReportStats() const;
254#endif // JPH_TRACK_BROADPHASE_STATS
255
256private:
258 static const uint32 cInvalidNodeIndex = 0xffffffff;
259 static const float cLargeFloat;
260 static const AABox cInvalidBounds;
261
263 struct RootNode
264 {
266 inline NodeID GetNodeID() const { return NodeID::sFromNodeIndex(mIndex); }
267
269 atomic<uint32> mIndex { cInvalidNodeIndex };
270 };
271
273 void GetBodyLocation(const TrackingVector &inTracking, BodyID inBodyID, uint32 &outNodeIdx, uint32 &outChildIdx) const;
274 void SetBodyLocation(TrackingVector &ioTracking, BodyID inBodyID, uint32 inNodeIdx, uint32 inChildIdx) const;
275 static void sInvalidateBodyLocation(TrackingVector &ioTracking, BodyID inBodyID);
276
278 JPH_INLINE const RootNode & GetCurrentRoot() const { return mRootNode[mRootNodeIndex]; }
279 JPH_INLINE RootNode & GetCurrentRoot() { return mRootNode[mRootNodeIndex]; }
280
282 inline AABox GetNodeOrBodyBounds(const BodyVector &inBodies, NodeID inNodeID) const;
283
285 inline void MarkNodeAndParentsChanged(uint32 inNodeIndex);
286
288 inline void WidenAndMarkNodeAndParentsChanged(uint32 inNodeIndex, const AABox &inNewBounds);
289
291 inline uint32 AllocateNode(bool inIsChanged);
292
294 inline bool TryInsertLeaf(TrackingVector &ioTracking, int inNodeIndex, NodeID inLeafID, const AABox &inLeafBounds, int inLeafNumBodies);
295
297 inline bool TryCreateNewRoot(TrackingVector &ioTracking, atomic<uint32> &ioRootNodeIndex, NodeID inLeafID, const AABox &inLeafBounds, int inLeafNumBodies);
298
300 NodeID BuildTree(const BodyVector &inBodies, TrackingVector &ioTracking, NodeID *ioNodeIDs, int inNumber, uint inMaxDepthMarkChanged, AABox &outBounds);
301
304 static void sPartition(NodeID *ioNodeIDs, Vec3 *ioNodeCenters, int inNumber, int &outMidPoint);
305
309 static void sPartition4(NodeID *ioNodeIDs, Vec3 *ioNodeCenters, int inBegin, int inEnd, int *outSplit);
310
311#ifdef _DEBUG
314 void ValidateTree(const BodyVector &inBodies, const TrackingVector &inTracking, uint32 inNodeIndex, uint32 inNumExpectedBodies) const;
315#endif
316
317#ifdef JPH_DUMP_BROADPHASE_TREE
319 void DumpTree(const NodeID &inRoot, const char *inFileNamePrefix) const;
320#endif
321
322#ifdef JPH_TRACK_BROADPHASE_STATS
324 mutable Mutex mStatsMutex;
325
326 struct Stat
327 {
328 uint64 mNumQueries = 0;
329 uint64 mNodesVisited = 0;
330 uint64 mBodiesVisited = 0;
331 uint64 mHitsReported = 0;
332 uint64 mTotalTicks = 0;
333 uint64 mCollectorTicks = 0;
334 };
335
336 using LayerToStats = UnorderedMap<String, Stat>;
337
339 void ReportStats(const char *inName, const LayerToStats &inLayer) const;
340
341 mutable LayerToStats mCastRayStats;
342 mutable LayerToStats mCollideAABoxStats;
343 mutable LayerToStats mCollideSphereStats;
344 mutable LayerToStats mCollidePointStats;
345 mutable LayerToStats mCollideOrientedBoxStats;
346 mutable LayerToStats mCastAABoxStats;
347#endif // JPH_TRACK_BROADPHASE_STATS
348
350 uint GetMaxTreeDepth(const NodeID &inNodeID) const;
351
353 template <class Visitor>
354 JPH_INLINE void WalkTree(const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking, Visitor &ioVisitor JPH_IF_TRACK_BROADPHASE_STATS(, LayerToStats &ioStats)) const;
355
356#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
358 const char * mName = "Layer";
359#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED
360
362 atomic<uint32> mNumBodies { 0 };
363
366 RootNode mRootNode[2];
367 atomic<uint32> mRootNodeIndex { 0 };
368
370 Allocator * mAllocator = nullptr;
371
373 Allocator::Batch mFreeNodeBatch;
374
376 atomic<bool> mIsDirty = false;
377};
378
Array< Body * > BodyVector
Array of bodies.
Definition: BodyManager.h:23
#define JPH_IF_TRACK_BROADPHASE_STATS(...)
Definition: BroadPhase.h:16
uint32_t uint32
Definition: Core.h:312
unsigned int uint
Definition: Core.h:309
#define JPH_NAMESPACE_END
Definition: Core.h:240
uint64_t uint64
Definition: Core.h:313
#define JPH_NAMESPACE_BEGIN
Definition: Core.h:234
#define JPH_ASSERT(...)
Definition: IssueReporting.h:33
#define JPH_OVERRIDE_NEW_DELETE
Macro to override the new and delete functions.
Definition: Memory.h:29
std::vector< T, STLAllocator< T > > Array
Definition: STLAllocator.h:81
std::unordered_map< Key, T, Hash, KeyEqual, STLAllocator< pair< const Key, T > > > UnorderedMap
Definition: UnorderedMap.h:13
Axis aligned box.
Definition: AABox.h:16
ID of a body. This is a way of reasoning about bodies in a multithreaded simulation while avoiding ra...
Definition: BodyID.h:13
static constexpr uint32 cBroadPhaseBit
This bit is used by the broadphase.
Definition: BodyID.h:18
uint32 GetIndexAndSequenceNumber() const
Returns the index and sequence number combined in an uint32.
Definition: BodyID.h:58
uint8 Type
Definition: BroadPhaseLayer.h:20
Virtual interface that allows collecting multiple collision results.
Definition: CollisionCollector.h:45
static const int ObjectStorageSize
Size of an object + bookkeeping for the freelist.
Definition: FixedSizeFreeList.h:77
Definition: Mutex.h:122
Class that makes another class non-copyable. Usage: Inherit from NonCopyable.
Definition: NonCopyable.h:11
Filter class for object layers.
Definition: ObjectLayer.h:28
Filter class to test if two objects can collide based on their object layer. Used while finding colli...
Definition: ObjectLayer.h:50
Oriented box.
Definition: OrientedBox.h:18
Definition: QuadTree.h:21
const char * GetName() const
Definition: QuadTree.h:177
bool HasBodies() const
Check if there is anything in the tree.
Definition: QuadTree.h:181
void CastRay(const RayCast &inRay, RayCastBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const
Cast a ray and get the intersecting bodies in ioCollector.
Definition: QuadTree.cpp:1038
void FindCollidingPairs(const BodyVector &inBodies, const BodyID *inActiveBodies, int inNumActiveBodies, float inSpeculativeContactDistance, BodyPairCollector &ioPairCollector, const ObjectLayerPairFilter &inObjectLayerPairFilter) const
Find all colliding pairs between dynamic bodies, calls ioPairCollector for every pair found.
Definition: QuadTree.cpp:1351
~QuadTree()
Destructor.
Definition: QuadTree.cpp:143
void CollideOrientedBox(const OrientedBox &inBox, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const
Get bodies intersecting with an oriented box and any hits to ioCollector.
Definition: QuadTree.cpp:1243
void AddBodiesAbort(TrackingVector &ioTracking, const AddState &inState)
Definition: QuadTree.cpp:822
void Init(Allocator &inAllocator)
Initialization.
Definition: QuadTree.cpp:198
void DiscardOldTree()
Will throw away the previous frame's nodes so that we can start building a new tree in the background...
Definition: QuadTree.cpp:207
void AddBodiesPrepare(const BodyVector &inBodies, TrackingVector &ioTracking, BodyID *ioBodyIDs, int inNumber, AddState &outState)
Definition: QuadTree.cpp:777
void CollideAABox(const AABox &inBox, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const
Get bodies intersecting with inBox in ioCollector.
Definition: QuadTree.cpp:1093
void NotifyBodiesAABBChanged(const BodyVector &inBodies, const TrackingVector &inTracking, const BodyID *ioBodyIDs, int inNumber)
Call whenever the aabb of a body changes.
Definition: QuadTree.cpp:899
bool IsDirty() const
Check if the tree needs an UpdatePrepare/Finalize()
Definition: QuadTree.h:184
void UpdateFinalize(const BodyVector &inBodies, const TrackingVector &inTracking, const UpdateState &inUpdateState)
Definition: QuadTree.cpp:358
void CastAABox(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const
Cast a box and get intersecting bodies in ioCollector.
Definition: QuadTree.cpp:1291
bool CanBeUpdated() const
Check if this tree can get an UpdatePrepare/Finalize() or if it needs a DiscardOldTree() first.
Definition: QuadTree.h:187
void AddBodiesFinalize(TrackingVector &ioTracking, int inNumberBodies, const AddState &inState)
Finalize adding bodies to the quadtree, supply the same number of bodies as in AddBodiesPrepare.
Definition: QuadTree.cpp:799
void CollidePoint(Vec3Arg inPoint, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const
Get bodies intersecting with a point and any hits to ioCollector.
Definition: QuadTree.cpp:1195
void CollideSphere(Vec3Arg inCenter, float inRadius, CollideShapeBodyCollector &ioCollector, const ObjectLayerFilter &inObjectLayerFilter, const TrackingVector &inTracking) const
Get bodies intersecting with a sphere in ioCollector.
Definition: QuadTree.cpp:1141
Array< Tracking > TrackingVector
Definition: QuadTree.h:169
void UpdatePrepare(const BodyVector &inBodies, TrackingVector &ioTracking, UpdateState &outUpdateState, bool inFullRebuild)
Definition: QuadTree.cpp:224
void SetName(const char *inName)
Name of the tree for debugging purposes.
Definition: QuadTree.h:176
void RemoveBodies(const BodyVector &inBodies, TrackingVector &ioTracking, const BodyID *ioBodyIDs, int inNumber)
Remove inNumber bodies in ioBodyIDs from the quadtree.
Definition: QuadTree.cpp:863
FixedSizeFreeList< Node > Allocator
Class that allocates tree nodes, can be shared between multiple trees.
Definition: QuadTree.h:150
Definition: Vec3.h:16
Structure that holds AABox moving linearly through 3d space.
Definition: AABoxCast.h:13
Temporary data structure to pass information between AddBodiesPrepare and AddBodiesFinalize/Abort.
Definition: QuadTree.h:207
AABox mLeafBounds
Definition: QuadTree.h:209
NodeID mLeafID
Definition: QuadTree.h:208
Data to track location of a Body in the tree.
Definition: QuadTree.h:156
atomic< BroadPhaseLayer::Type > mBroadPhaseLayer
Definition: QuadTree.h:164
static const uint32 cInvalidBodyLocation
Invalid body location identifier.
Definition: QuadTree.h:162
Tracking(const Tracking &inRHS)
Definition: QuadTree.h:159
atomic< ObjectLayer > mObjectLayer
Definition: QuadTree.h:165
atomic< uint32 > mBodyLocation
Definition: QuadTree.h:166
Tracking()=default
Constructor to satisfy the vector class.
Definition: QuadTree.h:193
NodeID mRootNodeID
This will be the new root node id.
Definition: QuadTree.h:194
Definition: RayCast.h:41