Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Collision/BroadPhase/BroadPhaseQuadTree.cpp
9918 views
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)1// SPDX-FileCopyrightText: 2021 Jorrit Rouwe2// SPDX-License-Identifier: MIT34#include <Jolt/Jolt.h>5#include <Jolt/Physics/Collision/BroadPhase/BroadPhaseQuadTree.h>6#include <Jolt/Physics/Collision/RayCast.h>7#include <Jolt/Physics/Collision/AABoxCast.h>8#include <Jolt/Physics/Collision/CastResult.h>9#include <Jolt/Core/QuickSort.h>1011JPH_NAMESPACE_BEGIN1213BroadPhaseQuadTree::~BroadPhaseQuadTree()14{15delete [] mLayers;16}1718void BroadPhaseQuadTree::Init(BodyManager *inBodyManager, const BroadPhaseLayerInterface &inLayerInterface)19{20BroadPhase::Init(inBodyManager, inLayerInterface);2122// Store input parameters23mBroadPhaseLayerInterface = &inLayerInterface;24mNumLayers = inLayerInterface.GetNumBroadPhaseLayers();25JPH_ASSERT(mNumLayers < (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid);2627#ifdef JPH_ENABLE_ASSERTS28// Store lock context29mLockContext = inBodyManager;30#endif // JPH_ENABLE_ASSERTS3132// Store max bodies33mMaxBodies = inBodyManager->GetMaxBodies();3435// Initialize tracking data36mTracking.resize(mMaxBodies);3738// Init allocator39// Estimate the amount of nodes we're going to need40uint32 num_leaves = (uint32)(mMaxBodies + 1) / 2; // Assume 50% fill41uint32 num_leaves_plus_internal_nodes = num_leaves + (num_leaves + 2) / 3; // = Sum(num_leaves * 4^-i) with i = [0, Inf].42mAllocator.Init(2 * num_leaves_plus_internal_nodes, 256); // We use double the amount of nodes while rebuilding the tree during Update()4344// Init sub trees45mLayers = new QuadTree [mNumLayers];46for (uint l = 0; l < mNumLayers; ++l)47{48mLayers[l].Init(mAllocator);4950#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)51// Set the name of the layer52mLayers[l].SetName(inLayerInterface.GetBroadPhaseLayerName(BroadPhaseLayer(BroadPhaseLayer::Type(l))));53#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED54}55}5657void BroadPhaseQuadTree::FrameSync()58{59JPH_PROFILE_FUNCTION();6061// Take a unique lock on the old query lock so that we know no one is using the old nodes anymore.62// Note that nothing should be locked at this point to avoid risking a lock inversion deadlock.63// Note that in other places where we lock this mutex we don't use SharedLock to detect lock inversions. As long as64// nothing else is locked this is safe. This is why BroadPhaseQuery should be the highest priority lock.65UniqueLock root_lock(mQueryLocks[mQueryLockIdx ^ 1] JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseQuery));6667for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)68mLayers[l].DiscardOldTree();69}7071void BroadPhaseQuadTree::Optimize()72{73JPH_PROFILE_FUNCTION();7475// Free the previous tree so we can create a new optimized tree76FrameSync();7778LockModifications();7980for (uint l = 0; l < mNumLayers; ++l)81{82QuadTree &tree = mLayers[l];83if (tree.HasBodies() || tree.IsDirty())84{85QuadTree::UpdateState update_state;86tree.UpdatePrepare(mBodyManager->GetBodies(), mTracking, update_state, true);87tree.UpdateFinalize(mBodyManager->GetBodies(), mTracking, update_state);88}89}9091UnlockModifications();9293// Free the tree from before we created a new optimized tree94FrameSync();9596mNextLayerToUpdate = 0;97}9899void BroadPhaseQuadTree::LockModifications()100{101// From this point on we prevent modifications to the tree102PhysicsLock::sLock(mUpdateMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseUpdate));103}104105BroadPhase::UpdateState BroadPhaseQuadTree::UpdatePrepare()106{107// LockModifications should have been called108JPH_ASSERT(mUpdateMutex.is_locked());109110// Create update state111UpdateState update_state;112UpdateStateImpl *update_state_impl = reinterpret_cast<UpdateStateImpl *>(&update_state);113114// Loop until we've seen all layers115for (uint iteration = 0; iteration < mNumLayers; ++iteration)116{117// Get the layer118QuadTree &tree = mLayers[mNextLayerToUpdate];119mNextLayerToUpdate = (mNextLayerToUpdate + 1) % mNumLayers;120121// If it is dirty we update this one122if (tree.HasBodies() && tree.IsDirty() && tree.CanBeUpdated())123{124update_state_impl->mTree = &tree;125tree.UpdatePrepare(mBodyManager->GetBodies(), mTracking, update_state_impl->mUpdateState, false);126return update_state;127}128}129130// Nothing to update131update_state_impl->mTree = nullptr;132return update_state;133}134135void BroadPhaseQuadTree::UpdateFinalize(const UpdateState &inUpdateState)136{137// LockModifications should have been called138JPH_ASSERT(mUpdateMutex.is_locked());139140// Test if a tree was updated141const UpdateStateImpl *update_state_impl = reinterpret_cast<const UpdateStateImpl *>(&inUpdateState);142if (update_state_impl->mTree == nullptr)143return;144145update_state_impl->mTree->UpdateFinalize(mBodyManager->GetBodies(), mTracking, update_state_impl->mUpdateState);146147// Make all queries from now on use the new lock148mQueryLockIdx = mQueryLockIdx ^ 1;149}150151void BroadPhaseQuadTree::UnlockModifications()152{153// From this point on we allow modifications to the tree again154PhysicsLock::sUnlock(mUpdateMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseUpdate));155}156157BroadPhase::AddState BroadPhaseQuadTree::AddBodiesPrepare(BodyID *ioBodies, int inNumber)158{159JPH_PROFILE_FUNCTION();160161if (inNumber <= 0)162return nullptr;163164const BodyVector &bodies = mBodyManager->GetBodies();165JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());166167LayerState *state = new LayerState [mNumLayers];168169// Sort bodies on layer170Body * const * const bodies_ptr = bodies.data(); // C pointer or else sort is incredibly slow in debug mode171QuickSort(ioBodies, ioBodies + inNumber, [bodies_ptr](BodyID inLHS, BodyID inRHS) { return bodies_ptr[inLHS.GetIndex()]->GetBroadPhaseLayer() < bodies_ptr[inRHS.GetIndex()]->GetBroadPhaseLayer(); });172173BodyID *b_start = ioBodies, *b_end = ioBodies + inNumber;174while (b_start < b_end)175{176// Get broadphase layer177BroadPhaseLayer::Type broadphase_layer = (BroadPhaseLayer::Type)bodies[b_start->GetIndex()]->GetBroadPhaseLayer();178JPH_ASSERT(broadphase_layer < mNumLayers);179180// Find first body with different layer181BodyID *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(); });182183// Keep track of state for this layer184LayerState &layer_state = state[broadphase_layer];185layer_state.mBodyStart = b_start;186layer_state.mBodyEnd = b_mid;187188// Insert all bodies of the same layer189mLayers[broadphase_layer].AddBodiesPrepare(bodies, mTracking, b_start, int(b_mid - b_start), layer_state.mAddState);190191// Keep track in which tree we placed the object192for (const BodyID *b = b_start; b < b_mid; ++b)193{194uint32 index = b->GetIndex();195JPH_ASSERT(bodies[index]->GetID() == *b, "Provided BodyID doesn't match BodyID in body manager");196JPH_ASSERT(!bodies[index]->IsInBroadPhase());197Tracking &t = mTracking[index];198JPH_ASSERT(t.mBroadPhaseLayer == (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid);199t.mBroadPhaseLayer = broadphase_layer;200JPH_ASSERT(t.mObjectLayer == cObjectLayerInvalid);201t.mObjectLayer = bodies[index]->GetObjectLayer();202}203204// Repeat205b_start = b_mid;206}207208return state;209}210211void BroadPhaseQuadTree::AddBodiesFinalize(BodyID *ioBodies, int inNumber, AddState inAddState)212{213JPH_PROFILE_FUNCTION();214215if (inNumber <= 0)216{217JPH_ASSERT(inAddState == nullptr);218return;219}220221// This cannot run concurrently with UpdatePrepare()/UpdateFinalize()222SharedLock lock(mUpdateMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseUpdate));223224BodyVector &bodies = mBodyManager->GetBodies();225JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());226227LayerState *state = (LayerState *)inAddState;228229for (BroadPhaseLayer::Type broadphase_layer = 0; broadphase_layer < mNumLayers; broadphase_layer++)230{231const LayerState &l = state[broadphase_layer];232if (l.mBodyStart != nullptr)233{234// Insert all bodies of the same layer235mLayers[broadphase_layer].AddBodiesFinalize(mTracking, int(l.mBodyEnd - l.mBodyStart), l.mAddState);236237// Mark added to broadphase238for (const BodyID *b = l.mBodyStart; b < l.mBodyEnd; ++b)239{240uint32 index = b->GetIndex();241JPH_ASSERT(bodies[index]->GetID() == *b, "Provided BodyID doesn't match BodyID in body manager");242JPH_ASSERT(mTracking[index].mBroadPhaseLayer == broadphase_layer);243JPH_ASSERT(mTracking[index].mObjectLayer == bodies[index]->GetObjectLayer());244JPH_ASSERT(!bodies[index]->IsInBroadPhase());245bodies[index]->SetInBroadPhaseInternal(true);246}247}248}249250delete [] state;251}252253void BroadPhaseQuadTree::AddBodiesAbort(BodyID *ioBodies, int inNumber, AddState inAddState)254{255JPH_PROFILE_FUNCTION();256257if (inNumber <= 0)258{259JPH_ASSERT(inAddState == nullptr);260return;261}262263JPH_IF_ENABLE_ASSERTS(const BodyVector &bodies = mBodyManager->GetBodies();)264JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());265266LayerState *state = (LayerState *)inAddState;267268for (BroadPhaseLayer::Type broadphase_layer = 0; broadphase_layer < mNumLayers; broadphase_layer++)269{270const LayerState &l = state[broadphase_layer];271if (l.mBodyStart != nullptr)272{273// Insert all bodies of the same layer274mLayers[broadphase_layer].AddBodiesAbort(mTracking, l.mAddState);275276// Reset bookkeeping277for (const BodyID *b = l.mBodyStart; b < l.mBodyEnd; ++b)278{279uint32 index = b->GetIndex();280JPH_ASSERT(bodies[index]->GetID() == *b, "Provided BodyID doesn't match BodyID in body manager");281JPH_ASSERT(!bodies[index]->IsInBroadPhase());282Tracking &t = mTracking[index];283JPH_ASSERT(t.mBroadPhaseLayer == broadphase_layer);284t.mBroadPhaseLayer = (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid;285t.mObjectLayer = cObjectLayerInvalid;286}287}288}289290delete [] state;291}292293void BroadPhaseQuadTree::RemoveBodies(BodyID *ioBodies, int inNumber)294{295JPH_PROFILE_FUNCTION();296297if (inNumber <= 0)298return;299300// This cannot run concurrently with UpdatePrepare()/UpdateFinalize()301SharedLock lock(mUpdateMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseUpdate));302303BodyVector &bodies = mBodyManager->GetBodies();304JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());305306// Sort bodies on layer307Tracking *tracking = mTracking.data(); // C pointer or else sort is incredibly slow in debug mode308QuickSort(ioBodies, ioBodies + inNumber, [tracking](BodyID inLHS, BodyID inRHS) { return tracking[inLHS.GetIndex()].mBroadPhaseLayer < tracking[inRHS.GetIndex()].mBroadPhaseLayer; });309310BodyID *b_start = ioBodies, *b_end = ioBodies + inNumber;311while (b_start < b_end)312{313// Get broad phase layer314BroadPhaseLayer::Type broadphase_layer = mTracking[b_start->GetIndex()].mBroadPhaseLayer;315JPH_ASSERT(broadphase_layer != (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid);316317// Find first body with different layer318BodyID *b_mid = std::upper_bound(b_start, b_end, broadphase_layer, [tracking](BroadPhaseLayer::Type inLayer, BodyID inBodyID) { return inLayer < tracking[inBodyID.GetIndex()].mBroadPhaseLayer; });319320// Remove all bodies of the same layer321mLayers[broadphase_layer].RemoveBodies(bodies, mTracking, b_start, int(b_mid - b_start));322323for (const BodyID *b = b_start; b < b_mid; ++b)324{325// Reset bookkeeping326uint32 index = b->GetIndex();327Tracking &t = tracking[index];328t.mBroadPhaseLayer = (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid;329t.mObjectLayer = cObjectLayerInvalid;330331// Mark removed from broadphase332JPH_ASSERT(bodies[index]->IsInBroadPhase());333bodies[index]->SetInBroadPhaseInternal(false);334}335336// Repeat337b_start = b_mid;338}339}340341void BroadPhaseQuadTree::NotifyBodiesAABBChanged(BodyID *ioBodies, int inNumber, bool inTakeLock)342{343JPH_PROFILE_FUNCTION();344345if (inNumber <= 0)346return;347348// This cannot run concurrently with UpdatePrepare()/UpdateFinalize()349if (inTakeLock)350PhysicsLock::sLockShared(mUpdateMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseUpdate));351else352JPH_ASSERT(mUpdateMutex.is_locked());353354const BodyVector &bodies = mBodyManager->GetBodies();355JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());356357// Sort bodies on layer358const Tracking *tracking = mTracking.data(); // C pointer or else sort is incredibly slow in debug mode359QuickSort(ioBodies, ioBodies + inNumber, [tracking](BodyID inLHS, BodyID inRHS) { return tracking[inLHS.GetIndex()].mBroadPhaseLayer < tracking[inRHS.GetIndex()].mBroadPhaseLayer; });360361BodyID *b_start = ioBodies, *b_end = ioBodies + inNumber;362while (b_start < b_end)363{364// Get broadphase layer365BroadPhaseLayer::Type broadphase_layer = tracking[b_start->GetIndex()].mBroadPhaseLayer;366JPH_ASSERT(broadphase_layer != (BroadPhaseLayer::Type)cBroadPhaseLayerInvalid);367368// Find first body with different layer369BodyID *b_mid = std::upper_bound(b_start, b_end, broadphase_layer, [tracking](BroadPhaseLayer::Type inLayer, BodyID inBodyID) { return inLayer < tracking[inBodyID.GetIndex()].mBroadPhaseLayer; });370371// Notify all bodies of the same layer changed372mLayers[broadphase_layer].NotifyBodiesAABBChanged(bodies, mTracking, b_start, int(b_mid - b_start));373374// Repeat375b_start = b_mid;376}377378if (inTakeLock)379PhysicsLock::sUnlockShared(mUpdateMutex JPH_IF_ENABLE_ASSERTS(, mLockContext, EPhysicsLockTypes::BroadPhaseUpdate));380}381382void BroadPhaseQuadTree::NotifyBodiesLayerChanged(BodyID *ioBodies, int inNumber)383{384JPH_PROFILE_FUNCTION();385386if (inNumber <= 0)387return;388389// First sort the bodies that actually changed layer to beginning of the array390const BodyVector &bodies = mBodyManager->GetBodies();391JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());392for (BodyID *body_id = ioBodies + inNumber - 1; body_id >= ioBodies; --body_id)393{394uint32 index = body_id->GetIndex();395JPH_ASSERT(bodies[index]->GetID() == *body_id, "Provided BodyID doesn't match BodyID in body manager");396const Body *body = bodies[index];397BroadPhaseLayer::Type broadphase_layer = (BroadPhaseLayer::Type)body->GetBroadPhaseLayer();398JPH_ASSERT(broadphase_layer < mNumLayers);399if (mTracking[index].mBroadPhaseLayer == broadphase_layer)400{401// Update tracking information402mTracking[index].mObjectLayer = body->GetObjectLayer();403404// Move the body to the end, layer didn't change405std::swap(*body_id, ioBodies[inNumber - 1]);406--inNumber;407}408}409410if (inNumber > 0)411{412// 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 again413RemoveBodies(ioBodies, inNumber);414AddState add_state = AddBodiesPrepare(ioBodies, inNumber);415AddBodiesFinalize(ioBodies, inNumber, add_state);416}417}418419void BroadPhaseQuadTree::CastRay(const RayCast &inRay, RayCastBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const420{421JPH_PROFILE_FUNCTION();422423JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());424425// Prevent this from running in parallel with node deletion in FrameSync(), see notes there426shared_lock lock(mQueryLocks[mQueryLockIdx]);427428// Loop over all layers and test the ones that could hit429for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)430{431const QuadTree &tree = mLayers[l];432if (tree.HasBodies() && inBroadPhaseLayerFilter.ShouldCollide(BroadPhaseLayer(l)))433{434JPH_PROFILE(tree.GetName());435tree.CastRay(inRay, ioCollector, inObjectLayerFilter, mTracking);436if (ioCollector.ShouldEarlyOut())437break;438}439}440}441442void BroadPhaseQuadTree::CollideAABox(const AABox &inBox, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const443{444JPH_PROFILE_FUNCTION();445446JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());447448// Prevent this from running in parallel with node deletion in FrameSync(), see notes there449shared_lock lock(mQueryLocks[mQueryLockIdx]);450451// Loop over all layers and test the ones that could hit452for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)453{454const QuadTree &tree = mLayers[l];455if (tree.HasBodies() && inBroadPhaseLayerFilter.ShouldCollide(BroadPhaseLayer(l)))456{457JPH_PROFILE(tree.GetName());458tree.CollideAABox(inBox, ioCollector, inObjectLayerFilter, mTracking);459if (ioCollector.ShouldEarlyOut())460break;461}462}463}464465void BroadPhaseQuadTree::CollideSphere(Vec3Arg inCenter, float inRadius, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const466{467JPH_PROFILE_FUNCTION();468469JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());470471// Prevent this from running in parallel with node deletion in FrameSync(), see notes there472shared_lock lock(mQueryLocks[mQueryLockIdx]);473474// Loop over all layers and test the ones that could hit475for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)476{477const QuadTree &tree = mLayers[l];478if (tree.HasBodies() && inBroadPhaseLayerFilter.ShouldCollide(BroadPhaseLayer(l)))479{480JPH_PROFILE(tree.GetName());481tree.CollideSphere(inCenter, inRadius, ioCollector, inObjectLayerFilter, mTracking);482if (ioCollector.ShouldEarlyOut())483break;484}485}486}487488void BroadPhaseQuadTree::CollidePoint(Vec3Arg inPoint, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const489{490JPH_PROFILE_FUNCTION();491492JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());493494// Prevent this from running in parallel with node deletion in FrameSync(), see notes there495shared_lock lock(mQueryLocks[mQueryLockIdx]);496497// Loop over all layers and test the ones that could hit498for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)499{500const QuadTree &tree = mLayers[l];501if (tree.HasBodies() && inBroadPhaseLayerFilter.ShouldCollide(BroadPhaseLayer(l)))502{503JPH_PROFILE(tree.GetName());504tree.CollidePoint(inPoint, ioCollector, inObjectLayerFilter, mTracking);505if (ioCollector.ShouldEarlyOut())506break;507}508}509}510511void BroadPhaseQuadTree::CollideOrientedBox(const OrientedBox &inBox, CollideShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const512{513JPH_PROFILE_FUNCTION();514515JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());516517// Prevent this from running in parallel with node deletion in FrameSync(), see notes there518shared_lock lock(mQueryLocks[mQueryLockIdx]);519520// Loop over all layers and test the ones that could hit521for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)522{523const QuadTree &tree = mLayers[l];524if (tree.HasBodies() && inBroadPhaseLayerFilter.ShouldCollide(BroadPhaseLayer(l)))525{526JPH_PROFILE(tree.GetName());527tree.CollideOrientedBox(inBox, ioCollector, inObjectLayerFilter, mTracking);528if (ioCollector.ShouldEarlyOut())529break;530}531}532}533534void BroadPhaseQuadTree::CastAABoxNoLock(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const535{536JPH_PROFILE_FUNCTION();537538JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());539540// Loop over all layers and test the ones that could hit541for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)542{543const QuadTree &tree = mLayers[l];544if (tree.HasBodies() && inBroadPhaseLayerFilter.ShouldCollide(BroadPhaseLayer(l)))545{546JPH_PROFILE(tree.GetName());547tree.CastAABox(inBox, ioCollector, inObjectLayerFilter, mTracking);548if (ioCollector.ShouldEarlyOut())549break;550}551}552}553554void BroadPhaseQuadTree::CastAABox(const AABoxCast &inBox, CastShapeBodyCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter) const555{556// Prevent this from running in parallel with node deletion in FrameSync(), see notes there557shared_lock lock(mQueryLocks[mQueryLockIdx]);558559CastAABoxNoLock(inBox, ioCollector, inBroadPhaseLayerFilter, inObjectLayerFilter);560}561562void BroadPhaseQuadTree::FindCollidingPairs(BodyID *ioActiveBodies, int inNumActiveBodies, float inSpeculativeContactDistance, const ObjectVsBroadPhaseLayerFilter &inObjectVsBroadPhaseLayerFilter, const ObjectLayerPairFilter &inObjectLayerPairFilter, BodyPairCollector &ioPairCollector) const563{564JPH_PROFILE_FUNCTION();565566const BodyVector &bodies = mBodyManager->GetBodies();567JPH_ASSERT(mMaxBodies == mBodyManager->GetMaxBodies());568569// 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.570571// Sort bodies on layer572const Tracking *tracking = mTracking.data(); // C pointer or else sort is incredibly slow in debug mode573QuickSort(ioActiveBodies, ioActiveBodies + inNumActiveBodies, [tracking](BodyID inLHS, BodyID inRHS) { return tracking[inLHS.GetIndex()].mObjectLayer < tracking[inRHS.GetIndex()].mObjectLayer; });574575BodyID *b_start = ioActiveBodies, *b_end = ioActiveBodies + inNumActiveBodies;576while (b_start < b_end)577{578// Get broadphase layer579ObjectLayer object_layer = tracking[b_start->GetIndex()].mObjectLayer;580JPH_ASSERT(object_layer != cObjectLayerInvalid);581582// Find first body with different layer583BodyID *b_mid = std::upper_bound(b_start, b_end, object_layer, [tracking](ObjectLayer inLayer, BodyID inBodyID) { return inLayer < tracking[inBodyID.GetIndex()].mObjectLayer; });584585// Loop over all layers and test the ones that could hit586for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)587{588const QuadTree &tree = mLayers[l];589if (tree.HasBodies() && inObjectVsBroadPhaseLayerFilter.ShouldCollide(object_layer, BroadPhaseLayer(l)))590{591JPH_PROFILE(tree.GetName());592tree.FindCollidingPairs(bodies, b_start, int(b_mid - b_start), inSpeculativeContactDistance, ioPairCollector, inObjectLayerPairFilter);593}594}595596// Repeat597b_start = b_mid;598}599}600601AABox BroadPhaseQuadTree::GetBounds() const602{603// Prevent this from running in parallel with node deletion in FrameSync(), see notes there604shared_lock lock(mQueryLocks[mQueryLockIdx]);605606AABox bounds;607for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)608bounds.Encapsulate(mLayers[l].GetBounds());609return bounds;610}611612#ifdef JPH_TRACK_BROADPHASE_STATS613614void BroadPhaseQuadTree::ReportStats()615{616Trace("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");617618uint64 total_ticks = 0;619for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)620total_ticks += mLayers[l].GetTicks100Pct();621622for (BroadPhaseLayer::Type l = 0; l < mNumLayers; ++l)623mLayers[l].ReportStats(total_ticks);624}625626#endif // JPH_TRACK_BROADPHASE_STATS627628JPH_NAMESPACE_END629630631