Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Collision/BroadPhase/BroadPhaseQuadTree.cpp
9918 views
1
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
2
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
3
// SPDX-License-Identifier: MIT
4
5
#include <Jolt/Jolt.h>
6
#include <Jolt/Physics/Collision/BroadPhase/BroadPhaseQuadTree.h>
7
#include <Jolt/Physics/Collision/RayCast.h>
8
#include <Jolt/Physics/Collision/AABoxCast.h>
9
#include <Jolt/Physics/Collision/CastResult.h>
10
#include <Jolt/Core/QuickSort.h>
11
12
JPH_NAMESPACE_BEGIN
13
14
BroadPhaseQuadTree::~BroadPhaseQuadTree()
15
{
16
delete [] mLayers;
17
}
18
19
void BroadPhaseQuadTree::Init(BodyManager *inBodyManager, const BroadPhaseLayerInterface &inLayerInterface)
20
{
21
BroadPhase::Init(inBodyManager, inLayerInterface);
22
23
// Store input parameters
24
mBroadPhaseLayerInterface = &inLayerInterface;
25
mNumLayers = inLayerInterface.GetNumBroadPhaseLayers();
26
JPH_ASSERT(mNumLayers < (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid);
27
28
#ifdef JPH_ENABLE_ASSERTS
29
// Store lock context
30
mLockContext = inBodyManager;
31
#endif // JPH_ENABLE_ASSERTS
32
33
// Store max bodies
34
mMaxBodies = inBodyManager->GetMaxBodies();
35
36
// Initialize tracking data
37
mTracking.resize(mMaxBodies);
38
39
// Init allocator
40
// Estimate the amount of nodes we're going to need
41
uint32 num_leaves = (uint32)(mMaxBodies + 1) / 2; // Assume 50% fill
42
uint32 num_leaves_plus_internal_nodes = num_leaves + (num_leaves + 2) / 3; // = Sum(num_leaves * 4^-i) with i = [0, Inf].
43
mAllocator.Init(2 * num_leaves_plus_internal_nodes, 256); // We use double the amount of nodes while rebuilding the tree during Update()
44
45
// Init sub trees
46
mLayers = new QuadTree [mNumLayers];
47
for (uint l = 0; l < mNumLayers; ++l)
48
{
49
mLayers[l].Init(mAllocator);
50
51
#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
52
// Set the name of the layer
53
mLayers[l].SetName(inLayerInterface.GetBroadPhaseLayerName(BroadPhaseLayer(BroadPhaseLayer::Type(l))));
54
#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED
55
}
56
}
57
58
void BroadPhaseQuadTree::FrameSync()
59
{
60
JPH_PROFILE_FUNCTION();
61
62
// Take a unique lock on the old query lock so that we know no one is using the old nodes anymore.
63
// Note that nothing should be locked at this point to avoid risking a lock inversion deadlock.
64
// Note that in other places where we lock this mutex we don't use SharedLock to detect lock inversions. As long as
65
// nothing else is locked this is safe. This is why BroadPhaseQuery should be the highest priority lock.
66
UniqueLock root_lock(mQueryLocks[mQueryLockIdx ^ 1] JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseQuery));
67
68
for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)
69
mLayers[l].DiscardOldTree();
70
}
71
72
void BroadPhaseQuadTree::Optimize()
73
{
74
JPH_PROFILE_FUNCTION();
75
76
// Free the previous tree so we can create a new optimized tree
77
FrameSync();
78
79
LockModifications();
80
81
for (uint l = 0; l < mNumLayers; ++l)
82
{
83
QuadTree &tree = mLayers[l];
84
if (tree.HasBodies() || tree.IsDirty())
85
{
86
QuadTree::UpdateState update_state;
87
tree.UpdatePrepare(mBodyManager->GetBodies(), mTracking, update_state, true);
88
tree.UpdateFinalize(mBodyManager->GetBodies(), mTracking, update_state);
89
}
90
}
91
92
UnlockModifications();
93
94
// Free the tree from before we created a new optimized tree
95
FrameSync();
96
97
mNextLayerToUpdate = 0;
98
}
99
100
void BroadPhaseQuadTree::LockModifications()
101
{
102
// From this point on we prevent modifications to the tree
103
PhysicsLock::sLock(mUpdateMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseUpdate));
104
}
105
106
BroadPhase::UpdateState BroadPhaseQuadTree::UpdatePrepare()
107
{
108
// LockModifications should have been called
109
JPH_ASSERT(mUpdateMutex.is_locked());
110
111
// Create update state
112
UpdateState update_state;
113
UpdateStateImpl *update_state_impl = reinterpret_cast<UpdateStateImpl *>(&update_state);
114
115
// Loop until we've seen all layers
116
for (uint iteration = 0; iteration < mNumLayers; ++iteration)
117
{
118
// Get the layer
119
QuadTree &tree = mLayers[mNextLayerToUpdate];
120
mNextLayerToUpdate = (mNextLayerToUpdate + 1) % mNumLayers;
121
122
// If it is dirty we update this one
123
if (tree.HasBodies() && tree.IsDirty() && tree.CanBeUpdated())
124
{
125
update_state_impl->mTree = &tree;
126
tree.UpdatePrepare(mBodyManager->GetBodies(), mTracking, update_state_impl->mUpdateState, false);
127
return update_state;
128
}
129
}
130
131
// Nothing to update
132
update_state_impl->mTree = nullptr;
133
return update_state;
134
}
135
136
void BroadPhaseQuadTree::UpdateFinalize(const UpdateState &inUpdateState)
137
{
138
// LockModifications should have been called
139
JPH_ASSERT(mUpdateMutex.is_locked());
140
141
// Test if a tree was updated
142
const UpdateStateImpl *update_state_impl = reinterpret_cast<const UpdateStateImpl *>(&inUpdateState);
143
if (update_state_impl->mTree == nullptr)
144
return;
145
146
update_state_impl->mTree->UpdateFinalize(mBodyManager->GetBodies(), mTracking, update_state_impl->mUpdateState);
147
148
// Make all queries from now on use the new lock
149
mQueryLockIdx = mQueryLockIdx ^ 1;
150
}
151
152
void BroadPhaseQuadTree::UnlockModifications()
153
{
154
// From this point on we allow modifications to the tree again
155
PhysicsLock::sUnlock(mUpdateMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseUpdate));
156
}
157
158
BroadPhase::AddState BroadPhaseQuadTree::AddBodiesPrepare(BodyID *ioBodies, int inNumber)
159
{
160
JPH_PROFILE_FUNCTION();
161
162
if (inNumber <= 0)
163
return nullptr;
164
165
const BodyVector &bodies = mBodyManager->GetBodies();
166
JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());
167
168
LayerState *state = new LayerState [mNumLayers];
169
170
// Sort bodies on layer
171
Body * const * const bodies_ptr = bodies.data(); // C pointer or else sort is incredibly slow in debug mode
172
QuickSort(ioBodies, ioBodies + inNumber, [bodies_ptr](BodyID inLHS, BodyID inRHS) { return bodies_ptr[inLHS.GetIndex()]->GetBroadPhaseLayer() < bodies_ptr[inRHS.GetIndex()]->GetBroadPhaseLayer(); });
173
174
BodyID *b_start = ioBodies, *b_end = ioBodies + inNumber;
175
while (b_start < b_end)
176
{
177
// Get broadphase layer
178
BroadPhaseLayer::Type broadphase_layer = (BroadPhaseLayer::Type)bodies[b_start->GetIndex()]->GetBroadPhaseLayer();
179
JPH_ASSERT(broadphase_layer < mNumLayers);
180
181
// Find first body with different layer
182
BodyID *b_mid = std::upper_bound(b_start, b_end, broadphase_layer, [bodies_ptr](BroadPhaseLayer::Type inLayer, BodyID inBodyID) { return inLayer < (BroadPhaseLayer::Type)bodies_ptr[inBodyID.GetIndex()]->GetBroadPhaseLayer(); });
183
184
// Keep track of state for this layer
185
LayerState &layer_state = state[broadphase_layer];
186
layer_state.mBodyStart = b_start;
187
layer_state.mBodyEnd = b_mid;
188
189
// Insert all bodies of the same layer
190
mLayers[broadphase_layer].AddBodiesPrepare(bodies, mTracking, b_start, int(b_mid - b_start), layer_state.mAddState);
191
192
// Keep track in which tree we placed the object
193
for (const BodyID *b = b_start; b < b_mid; ++b)
194
{
195
uint32 index = b->GetIndex();
196
JPH_ASSERT(bodies[index]->GetID() == *b, "Provided BodyID doesn't match BodyID in body manager");
197
JPH_ASSERT(!bodies[index]->IsInBroadPhase());
198
Tracking &t = mTracking[index];
199
JPH_ASSERT(t.mBroadPhaseLayer == (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid);
200
t.mBroadPhaseLayer = broadphase_layer;
201
JPH_ASSERT(t.mObjectLayer == cObjectLayerInvalid);
202
t.mObjectLayer = bodies[index]->GetObjectLayer();
203
}
204
205
// Repeat
206
b_start = b_mid;
207
}
208
209
return state;
210
}
211
212
void BroadPhaseQuadTree::AddBodiesFinalize(BodyID *ioBodies, int inNumber, AddState inAddState)
213
{
214
JPH_PROFILE_FUNCTION();
215
216
if (inNumber <= 0)
217
{
218
JPH_ASSERT(inAddState == nullptr);
219
return;
220
}
221
222
// This cannot run concurrently with UpdatePrepare()/UpdateFinalize()
223
SharedLock lock(mUpdateMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseUpdate));
224
225
BodyVector &bodies = mBodyManager->GetBodies();
226
JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());
227
228
LayerState *state = (LayerState *)inAddState;
229
230
for (BroadPhaseLayer::Type broadphase_layer = 0; broadphase_layer < mNumLayers; broadphase_layer++)
231
{
232
const LayerState &l = state[broadphase_layer];
233
if (l.mBodyStart != nullptr)
234
{
235
// Insert all bodies of the same layer
236
mLayers[broadphase_layer].AddBodiesFinalize(mTracking, int(l.mBodyEnd - l.mBodyStart), l.mAddState);
237
238
// Mark added to broadphase
239
for (const BodyID *b = l.mBodyStart; b < l.mBodyEnd; ++b)
240
{
241
uint32 index = b->GetIndex();
242
JPH_ASSERT(bodies[index]->GetID() == *b, "Provided BodyID doesn't match BodyID in body manager");
243
JPH_ASSERT(mTracking[index].mBroadPhaseLayer == broadphase_layer);
244
JPH_ASSERT(mTracking[index].mObjectLayer == bodies[index]->GetObjectLayer());
245
JPH_ASSERT(!bodies[index]->IsInBroadPhase());
246
bodies[index]->SetInBroadPhaseInternal(true);
247
}
248
}
249
}
250
251
delete [] state;
252
}
253
254
void BroadPhaseQuadTree::AddBodiesAbort(BodyID *ioBodies, int inNumber, AddState inAddState)
255
{
256
JPH_PROFILE_FUNCTION();
257
258
if (inNumber <= 0)
259
{
260
JPH_ASSERT(inAddState == nullptr);
261
return;
262
}
263
264
JPH_IF_ENABLE_ASSERTS(const BodyVector &bodies = mBodyManager->GetBodies();)
265
JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());
266
267
LayerState *state = (LayerState *)inAddState;
268
269
for (BroadPhaseLayer::Type broadphase_layer = 0; broadphase_layer < mNumLayers; broadphase_layer++)
270
{
271
const LayerState &l = state[broadphase_layer];
272
if (l.mBodyStart != nullptr)
273
{
274
// Insert all bodies of the same layer
275
mLayers[broadphase_layer].AddBodiesAbort(mTracking, l.mAddState);
276
277
// Reset bookkeeping
278
for (const BodyID *b = l.mBodyStart; b < l.mBodyEnd; ++b)
279
{
280
uint32 index = b->GetIndex();
281
JPH_ASSERT(bodies[index]->GetID() == *b, "Provided BodyID doesn't match BodyID in body manager");
282
JPH_ASSERT(!bodies[index]->IsInBroadPhase());
283
Tracking &t = mTracking[index];
284
JPH_ASSERT(t.mBroadPhaseLayer == broadphase_layer);
285
t.mBroadPhaseLayer = (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid;
286
t.mObjectLayer = cObjectLayerInvalid;
287
}
288
}
289
}
290
291
delete [] state;
292
}
293
294
void BroadPhaseQuadTree::RemoveBodies(BodyID *ioBodies, int inNumber)
295
{
296
JPH_PROFILE_FUNCTION();
297
298
if (inNumber <= 0)
299
return;
300
301
// This cannot run concurrently with UpdatePrepare()/UpdateFinalize()
302
SharedLock lock(mUpdateMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseUpdate));
303
304
BodyVector &bodies = mBodyManager->GetBodies();
305
JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());
306
307
// Sort bodies on layer
308
Tracking *tracking = mTracking.data(); // C pointer or else sort is incredibly slow in debug mode
309
QuickSort(ioBodies, ioBodies + inNumber, [tracking](BodyID inLHS, BodyID inRHS) { return tracking[inLHS.GetIndex()].mBroadPhaseLayer < tracking[inRHS.GetIndex()].mBroadPhaseLayer; });
310
311
BodyID *b_start = ioBodies, *b_end = ioBodies + inNumber;
312
while (b_start < b_end)
313
{
314
// Get broad phase layer
315
BroadPhaseLayer::Type broadphase_layer = mTracking[b_start->GetIndex()].mBroadPhaseLayer;
316
JPH_ASSERT(broadphase_layer != (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid);
317
318
// Find first body with different layer
319
BodyID *b_mid = std::upper_bound(b_start, b_end, broadphase_layer, [tracking](BroadPhaseLayer::Type inLayer, BodyID inBodyID) { return inLayer < tracking[inBodyID.GetIndex()].mBroadPhaseLayer; });
320
321
// Remove all bodies of the same layer
322
mLayers[broadphase_layer].RemoveBodies(bodies, mTracking, b_start, int(b_mid - b_start));
323
324
for (const BodyID *b = b_start; b < b_mid; ++b)
325
{
326
// Reset bookkeeping
327
uint32 index = b->GetIndex();
328
Tracking &t = tracking[index];
329
t.mBroadPhaseLayer = (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid;
330
t.mObjectLayer = cObjectLayerInvalid;
331
332
// Mark removed from broadphase
333
JPH_ASSERT(bodies[index]->IsInBroadPhase());
334
bodies[index]->SetInBroadPhaseInternal(false);
335
}
336
337
// Repeat
338
b_start = b_mid;
339
}
340
}
341
342
void BroadPhaseQuadTree::NotifyBodiesAABBChanged(BodyID *ioBodies, int inNumber, bool inTakeLock)
343
{
344
JPH_PROFILE_FUNCTION();
345
346
if (inNumber <= 0)
347
return;
348
349
// This cannot run concurrently with UpdatePrepare()/UpdateFinalize()
350
if (inTakeLock)
351
PhysicsLock::sLockShared(mUpdateMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseUpdate));
352
else
353
JPH_ASSERT(mUpdateMutex.is_locked());
354
355
const BodyVector &bodies = mBodyManager->GetBodies();
356
JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());
357
358
// Sort bodies on layer
359
const Tracking *tracking = mTracking.data(); // C pointer or else sort is incredibly slow in debug mode
360
QuickSort(ioBodies, ioBodies + inNumber, [tracking](BodyID inLHS, BodyID inRHS) { return tracking[inLHS.GetIndex()].mBroadPhaseLayer < tracking[inRHS.GetIndex()].mBroadPhaseLayer; });
361
362
BodyID *b_start = ioBodies, *b_end = ioBodies + inNumber;
363
while (b_start < b_end)
364
{
365
// Get broadphase layer
366
BroadPhaseLayer::Type broadphase_layer = tracking[b_start->GetIndex()].mBroadPhaseLayer;
367
JPH_ASSERT(broadphase_layer != (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid);
368
369
// Find first body with different layer
370
BodyID *b_mid = std::upper_bound(b_start, b_end, broadphase_layer, [tracking](BroadPhaseLayer::Type inLayer, BodyID inBodyID) { return inLayer < tracking[inBodyID.GetIndex()].mBroadPhaseLayer; });
371
372
// Notify all bodies of the same layer changed
373
mLayers[broadphase_layer].NotifyBodiesAABBChanged(bodies, mTracking, b_start, int(b_mid - b_start));
374
375
// Repeat
376
b_start = b_mid;
377
}
378
379
if (inTakeLock)
380
PhysicsLock::sUnlockShared(mUpdateMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseUpdate));
381
}
382
383
void BroadPhaseQuadTree::NotifyBodiesLayerChanged(BodyID *ioBodies, int inNumber)
384
{
385
JPH_PROFILE_FUNCTION();
386
387
if (inNumber <= 0)
388
return;
389
390
// First sort the bodies that actually changed layer to beginning of the array
391
const BodyVector &bodies = mBodyManager->GetBodies();
392
JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());
393
for (BodyID *body_id = ioBodies + inNumber - 1; body_id >= ioBodies; --body_id)
394
{
395
uint32 index = body_id->GetIndex();
396
JPH_ASSERT(bodies[index]->GetID() == *body_id, "Provided BodyID doesn't match BodyID in body manager");
397
const Body *body = bodies[index];
398
BroadPhaseLayer::Type broadphase_layer = (BroadPhaseLayer::Type)body->GetBroadPhaseLayer();
399
JPH_ASSERT(broadphase_layer < mNumLayers);
400
if (mTracking[index].mBroadPhaseLayer == broadphase_layer)
401
{
402
// Update tracking information
403
mTracking[index].mObjectLayer = body->GetObjectLayer();
404
405
// Move the body to the end, layer didn't change
406
std::swap(*body_id, ioBodies[inNumber - 1]);
407
--inNumber;
408
}
409
}
410
411
if (inNumber > 0)
412
{
413
// Changing layer requires us to remove from one tree and add to another, so this is equivalent to removing all bodies first and then adding them again
414
RemoveBodies(ioBodies, inNumber);
415
AddState add_state = AddBodiesPrepare(ioBodies, inNumber);
416
AddBodiesFinalize(ioBodies, inNumber, add_state);
417
}
418
}
419
420
void BroadPhaseQuadTree::CastRay(const RayCast &inRay, RayCastBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const
421
{
422
JPH_PROFILE_FUNCTION();
423
424
JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());
425
426
// Prevent this from running in parallel with node deletion in FrameSync(), see notes there
427
shared_lock lock(mQueryLocks[mQueryLockIdx]);
428
429
// Loop over all layers and test the ones that could hit
430
for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)
431
{
432
const QuadTree &tree = mLayers[l];
433
if (tree.HasBodies() && inBroadPhaseLayerFilter.ShouldCollide(BroadPhaseLayer(l)))
434
{
435
JPH_PROFILE(tree.GetName());
436
tree.CastRay(inRay, ioCollector, inObjectLayerFilter, mTracking);
437
if (ioCollector.ShouldEarlyOut())
438
break;
439
}
440
}
441
}
442
443
void BroadPhaseQuadTree::CollideAABox(const AABox &inBox, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const
444
{
445
JPH_PROFILE_FUNCTION();
446
447
JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());
448
449
// Prevent this from running in parallel with node deletion in FrameSync(), see notes there
450
shared_lock lock(mQueryLocks[mQueryLockIdx]);
451
452
// Loop over all layers and test the ones that could hit
453
for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)
454
{
455
const QuadTree &tree = mLayers[l];
456
if (tree.HasBodies() && inBroadPhaseLayerFilter.ShouldCollide(BroadPhaseLayer(l)))
457
{
458
JPH_PROFILE(tree.GetName());
459
tree.CollideAABox(inBox, ioCollector, inObjectLayerFilter, mTracking);
460
if (ioCollector.ShouldEarlyOut())
461
break;
462
}
463
}
464
}
465
466
void BroadPhaseQuadTree::CollideSphere(Vec3Arg inCenter, float inRadius, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const
467
{
468
JPH_PROFILE_FUNCTION();
469
470
JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());
471
472
// Prevent this from running in parallel with node deletion in FrameSync(), see notes there
473
shared_lock lock(mQueryLocks[mQueryLockIdx]);
474
475
// Loop over all layers and test the ones that could hit
476
for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)
477
{
478
const QuadTree &tree = mLayers[l];
479
if (tree.HasBodies() && inBroadPhaseLayerFilter.ShouldCollide(BroadPhaseLayer(l)))
480
{
481
JPH_PROFILE(tree.GetName());
482
tree.CollideSphere(inCenter, inRadius, ioCollector, inObjectLayerFilter, mTracking);
483
if (ioCollector.ShouldEarlyOut())
484
break;
485
}
486
}
487
}
488
489
void BroadPhaseQuadTree::CollidePoint(Vec3Arg inPoint, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const
490
{
491
JPH_PROFILE_FUNCTION();
492
493
JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());
494
495
// Prevent this from running in parallel with node deletion in FrameSync(), see notes there
496
shared_lock lock(mQueryLocks[mQueryLockIdx]);
497
498
// Loop over all layers and test the ones that could hit
499
for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)
500
{
501
const QuadTree &tree = mLayers[l];
502
if (tree.HasBodies() && inBroadPhaseLayerFilter.ShouldCollide(BroadPhaseLayer(l)))
503
{
504
JPH_PROFILE(tree.GetName());
505
tree.CollidePoint(inPoint, ioCollector, inObjectLayerFilter, mTracking);
506
if (ioCollector.ShouldEarlyOut())
507
break;
508
}
509
}
510
}
511
512
void BroadPhaseQuadTree::CollideOrientedBox(const OrientedBox &inBox, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const
513
{
514
JPH_PROFILE_FUNCTION();
515
516
JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());
517
518
// Prevent this from running in parallel with node deletion in FrameSync(), see notes there
519
shared_lock lock(mQueryLocks[mQueryLockIdx]);
520
521
// Loop over all layers and test the ones that could hit
522
for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)
523
{
524
const QuadTree &tree = mLayers[l];
525
if (tree.HasBodies() && inBroadPhaseLayerFilter.ShouldCollide(BroadPhaseLayer(l)))
526
{
527
JPH_PROFILE(tree.GetName());
528
tree.CollideOrientedBox(inBox, ioCollector, inObjectLayerFilter, mTracking);
529
if (ioCollector.ShouldEarlyOut())
530
break;
531
}
532
}
533
}
534
535
void BroadPhaseQuadTree::CastAABoxNoLock(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const
536
{
537
JPH_PROFILE_FUNCTION();
538
539
JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());
540
541
// Loop over all layers and test the ones that could hit
542
for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)
543
{
544
const QuadTree &tree = mLayers[l];
545
if (tree.HasBodies() && inBroadPhaseLayerFilter.ShouldCollide(BroadPhaseLayer(l)))
546
{
547
JPH_PROFILE(tree.GetName());
548
tree.CastAABox(inBox, ioCollector, inObjectLayerFilter, mTracking);
549
if (ioCollector.ShouldEarlyOut())
550
break;
551
}
552
}
553
}
554
555
void BroadPhaseQuadTree::CastAABox(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const
556
{
557
// Prevent this from running in parallel with node deletion in FrameSync(), see notes there
558
shared_lock lock(mQueryLocks[mQueryLockIdx]);
559
560
CastAABoxNoLock(inBox, ioCollector, inBroadPhaseLayerFilter, inObjectLayerFilter);
561
}
562
563
void BroadPhaseQuadTree::FindCollidingPairs(BodyID *ioActiveBodies, int inNumActiveBodies, float inSpeculativeContactDistance, const ObjectVsBroadPhaseLayerFilter &inObjectVsBroadPhaseLayerFilter, const ObjectLayerPairFilter &inObjectLayerPairFilter, BodyPairCollector &ioPairCollector) const
564
{
565
JPH_PROFILE_FUNCTION();
566
567
const BodyVector &bodies = mBodyManager->GetBodies();
568
JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());
569
570
// Note that we don't take any locks at this point. We know that the tree is not going to be swapped or deleted while finding collision pairs due to the way the jobs are scheduled in the PhysicsSystem::Update.
571
572
// Sort bodies on layer
573
const Tracking *tracking = mTracking.data(); // C pointer or else sort is incredibly slow in debug mode
574
QuickSort(ioActiveBodies, ioActiveBodies + inNumActiveBodies, [tracking](BodyID inLHS, BodyID inRHS) { return tracking[inLHS.GetIndex()].mObjectLayer < tracking[inRHS.GetIndex()].mObjectLayer; });
575
576
BodyID *b_start = ioActiveBodies, *b_end = ioActiveBodies + inNumActiveBodies;
577
while (b_start < b_end)
578
{
579
// Get broadphase layer
580
ObjectLayer object_layer = tracking[b_start->GetIndex()].mObjectLayer;
581
JPH_ASSERT(object_layer != cObjectLayerInvalid);
582
583
// Find first body with different layer
584
BodyID *b_mid = std::upper_bound(b_start, b_end, object_layer, [tracking](ObjectLayer inLayer, BodyID inBodyID) { return inLayer < tracking[inBodyID.GetIndex()].mObjectLayer; });
585
586
// Loop over all layers and test the ones that could hit
587
for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)
588
{
589
const QuadTree &tree = mLayers[l];
590
if (tree.HasBodies() && inObjectVsBroadPhaseLayerFilter.ShouldCollide(object_layer, BroadPhaseLayer(l)))
591
{
592
JPH_PROFILE(tree.GetName());
593
tree.FindCollidingPairs(bodies, b_start, int(b_mid - b_start), inSpeculativeContactDistance, ioPairCollector, inObjectLayerPairFilter);
594
}
595
}
596
597
// Repeat
598
b_start = b_mid;
599
}
600
}
601
602
AABox BroadPhaseQuadTree::GetBounds() const
603
{
604
// Prevent this from running in parallel with node deletion in FrameSync(), see notes there
605
shared_lock lock(mQueryLocks[mQueryLockIdx]);
606
607
AABox bounds;
608
for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)
609
bounds.Encapsulate(mLayers[l].GetBounds());
610
return bounds;
611
}
612
613
#ifdef JPH_TRACK_BROADPHASE_STATS
614
615
void BroadPhaseQuadTree::ReportStats()
616
{
617
Trace("Query Type, Filter Description, Tree Name, Num Queries, Total Time (%%), Total Time Excl. Collector (%%), Nodes Visited, Bodies Visited, Hits Reported, Hits Reported vs Bodies Visited (%%), Hits Reported vs Nodes Visited");
618
619
uint64 total_ticks = 0;
620
for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)
621
total_ticks += mLayers[l].GetTicks100Pct();
622
623
for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)
624
mLayers[l].ReportStats(total_ticks);
625
}
626
627
#endif // JPH_TRACK_BROADPHASE_STATS
628
629
JPH_NAMESPACE_END
630
631