Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Body/BodyManager.cpp
9912 views
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)1// SPDX-FileCopyrightText: 2021 Jorrit Rouwe2// SPDX-License-Identifier: MIT34#include <Jolt/Jolt.h>56#include <Jolt/Physics/Body/BodyManager.h>7#include <Jolt/Physics/PhysicsSettings.h>8#include <Jolt/Physics/Body/BodyCreationSettings.h>9#include <Jolt/Physics/Body/BodyLock.h>10#include <Jolt/Physics/Body/BodyActivationListener.h>11#include <Jolt/Physics/SoftBody/SoftBodyMotionProperties.h>12#include <Jolt/Physics/SoftBody/SoftBodyCreationSettings.h>13#include <Jolt/Physics/SoftBody/SoftBodyShape.h>14#include <Jolt/Physics/StateRecorder.h>15#include <Jolt/Core/StringTools.h>16#include <Jolt/Core/QuickSort.h>17#ifdef JPH_DEBUG_RENDERER18#include <Jolt/Renderer/DebugRenderer.h>19#include <Jolt/Physics/Body/BodyFilter.h>20#endif // JPH_DEBUG_RENDERER2122JPH_NAMESPACE_BEGIN2324#ifdef JPH_ENABLE_ASSERTS25static thread_local bool sOverrideAllowActivation = false;26static thread_local bool sOverrideAllowDeactivation = false;2728bool BodyManager::sGetOverrideAllowActivation()29{30return sOverrideAllowActivation;31}3233void BodyManager::sSetOverrideAllowActivation(bool inValue)34{35sOverrideAllowActivation = inValue;36}3738bool BodyManager::sGetOverrideAllowDeactivation()39{40return sOverrideAllowDeactivation;41}4243void BodyManager::sSetOverrideAllowDeactivation(bool inValue)44{45sOverrideAllowDeactivation = inValue;46}47#endif4849// Helper class that combines a body and its motion properties50class BodyWithMotionProperties : public Body51{52public:53JPH_OVERRIDE_NEW_DELETE5455MotionProperties mMotionProperties;56};5758// Helper class that combines a soft body its motion properties and shape59class SoftBodyWithMotionPropertiesAndShape : public Body60{61public:62SoftBodyWithMotionPropertiesAndShape()63{64mShape.SetEmbedded();65}6667SoftBodyMotionProperties mMotionProperties;68SoftBodyShape mShape;69};7071inline void BodyManager::sDeleteBody(Body *inBody)72{73if (inBody->mMotionProperties != nullptr)74{75JPH_IF_ENABLE_ASSERTS(inBody->mMotionProperties = nullptr;)76if (inBody->IsSoftBody())77{78inBody->mShape = nullptr; // Release the shape to avoid assertion on shape destruction because of embedded object with refcount > 079delete static_cast<SoftBodyWithMotionPropertiesAndShape *>(inBody);80}81else82delete static_cast<BodyWithMotionProperties *>(inBody);83}84else85delete inBody;86}8788BodyManager::~BodyManager()89{90UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList));9192// Destroy any bodies that are still alive93for (Body *b : mBodies)94if (sIsValidBodyPointer(b))95sDeleteBody(b);9697for (BodyID *active_bodies : mActiveBodies)98delete [] active_bodies;99}100101void BodyManager::Init(uint inMaxBodies, uint inNumBodyMutexes, const BroadPhaseLayerInterface &inLayerInterface)102{103UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList));104105// Num body mutexes must be a power of two and not bigger than our MutexMask106uint num_body_mutexes = Clamp<uint>(GetNextPowerOf2(inNumBodyMutexes == 0? 2 * thread::hardware_concurrency() : inNumBodyMutexes), 1, sizeof(MutexMask) * 8);107#ifdef JPH_TSAN_ENABLED108num_body_mutexes = min(num_body_mutexes, 32U); // TSAN errors out when locking too many mutexes on the same thread, see: https://github.com/google/sanitizers/issues/950109#endif110111// Allocate the body mutexes112mBodyMutexes.Init(num_body_mutexes);113114// Allocate space for bodies115mBodies.reserve(inMaxBodies);116117// Allocate space for active bodies118for (BodyID *&active_bodies : mActiveBodies)119{120JPH_ASSERT(active_bodies == nullptr);121active_bodies = new BodyID [inMaxBodies];122}123124// Allocate space for sequence numbers125mBodySequenceNumbers.resize(inMaxBodies, 0);126127// Keep layer interface128mBroadPhaseLayerInterface = &inLayerInterface;129}130131uint BodyManager::GetNumBodies() const132{133UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList));134135return mNumBodies;136}137138BodyManager::BodyStats BodyManager::GetBodyStats() const139{140UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList));141142BodyStats stats;143stats.mNumBodies = mNumBodies;144stats.mMaxBodies = uint(mBodies.capacity());145146for (const Body *body : mBodies)147if (sIsValidBodyPointer(body))148{149if (body->IsSoftBody())150{151stats.mNumSoftBodies++;152if (body->IsActive())153stats.mNumActiveSoftBodies++;154}155else156{157switch (body->GetMotionType())158{159case EMotionType::Static:160stats.mNumBodiesStatic++;161break;162163case EMotionType::Dynamic:164stats.mNumBodiesDynamic++;165if (body->IsActive())166stats.mNumActiveBodiesDynamic++;167break;168169case EMotionType::Kinematic:170stats.mNumBodiesKinematic++;171if (body->IsActive())172stats.mNumActiveBodiesKinematic++;173break;174}175}176}177178return stats;179}180181Body *BodyManager::AllocateBody(const BodyCreationSettings &inBodyCreationSettings) const182{183// Fill in basic properties184Body *body;185if (inBodyCreationSettings.HasMassProperties())186{187BodyWithMotionProperties *bmp = new BodyWithMotionProperties;188body = bmp;189body->mMotionProperties = &bmp->mMotionProperties;190}191else192{193body = new Body;194}195body->mBodyType = EBodyType::RigidBody;196body->mShape = inBodyCreationSettings.GetShape();197body->mUserData = inBodyCreationSettings.mUserData;198body->SetFriction(inBodyCreationSettings.mFriction);199body->SetRestitution(inBodyCreationSettings.mRestitution);200body->mMotionType = inBodyCreationSettings.mMotionType;201if (inBodyCreationSettings.mIsSensor)202body->SetIsSensor(true);203if (inBodyCreationSettings.mCollideKinematicVsNonDynamic)204body->SetCollideKinematicVsNonDynamic(true);205if (inBodyCreationSettings.mUseManifoldReduction)206body->SetUseManifoldReduction(true);207if (inBodyCreationSettings.mApplyGyroscopicForce)208body->SetApplyGyroscopicForce(true);209if (inBodyCreationSettings.mEnhancedInternalEdgeRemoval)210body->SetEnhancedInternalEdgeRemoval(true);211SetBodyObjectLayerInternal(*body, inBodyCreationSettings.mObjectLayer);212body->mObjectLayer = inBodyCreationSettings.mObjectLayer;213body->mCollisionGroup = inBodyCreationSettings.mCollisionGroup;214215if (inBodyCreationSettings.HasMassProperties())216{217MotionProperties *mp = body->mMotionProperties;218mp->SetLinearDamping(inBodyCreationSettings.mLinearDamping);219mp->SetAngularDamping(inBodyCreationSettings.mAngularDamping);220mp->SetMaxLinearVelocity(inBodyCreationSettings.mMaxLinearVelocity);221mp->SetMaxAngularVelocity(inBodyCreationSettings.mMaxAngularVelocity);222mp->SetMassProperties(inBodyCreationSettings.mAllowedDOFs, inBodyCreationSettings.GetMassProperties());223mp->SetLinearVelocity(inBodyCreationSettings.mLinearVelocity); // Needs to happen after setting the max linear/angular velocity and setting allowed DOFs224mp->SetAngularVelocity(inBodyCreationSettings.mAngularVelocity);225mp->SetGravityFactor(inBodyCreationSettings.mGravityFactor);226mp->SetNumVelocityStepsOverride(inBodyCreationSettings.mNumVelocityStepsOverride);227mp->SetNumPositionStepsOverride(inBodyCreationSettings.mNumPositionStepsOverride);228mp->mMotionQuality = inBodyCreationSettings.mMotionQuality;229mp->mAllowSleeping = inBodyCreationSettings.mAllowSleeping;230JPH_IF_ENABLE_ASSERTS(mp->mCachedBodyType = body->mBodyType;)231JPH_IF_ENABLE_ASSERTS(mp->mCachedMotionType = body->mMotionType;)232}233234// Position body235body->SetPositionAndRotationInternal(inBodyCreationSettings.mPosition, inBodyCreationSettings.mRotation);236237return body;238}239240/// Create a soft body using creation settings. The returned body will not be part of the body manager yet.241Body *BodyManager::AllocateSoftBody(const SoftBodyCreationSettings &inSoftBodyCreationSettings) const242{243// Fill in basic properties244SoftBodyWithMotionPropertiesAndShape *bmp = new SoftBodyWithMotionPropertiesAndShape;245SoftBodyMotionProperties *mp = &bmp->mMotionProperties;246SoftBodyShape *shape = &bmp->mShape;247Body *body = bmp;248shape->mSoftBodyMotionProperties = mp;249body->mBodyType = EBodyType::SoftBody;250body->mMotionProperties = mp;251body->mShape = shape;252body->mUserData = inSoftBodyCreationSettings.mUserData;253body->SetFriction(inSoftBodyCreationSettings.mFriction);254body->SetRestitution(inSoftBodyCreationSettings.mRestitution);255body->mMotionType = EMotionType::Dynamic;256SetBodyObjectLayerInternal(*body, inSoftBodyCreationSettings.mObjectLayer);257body->mObjectLayer = inSoftBodyCreationSettings.mObjectLayer;258body->mCollisionGroup = inSoftBodyCreationSettings.mCollisionGroup;259mp->SetLinearDamping(inSoftBodyCreationSettings.mLinearDamping);260mp->SetAngularDamping(0);261mp->SetMaxLinearVelocity(inSoftBodyCreationSettings.mMaxLinearVelocity);262mp->SetMaxAngularVelocity(FLT_MAX);263mp->SetLinearVelocity(Vec3::sZero());264mp->SetAngularVelocity(Vec3::sZero());265mp->SetGravityFactor(inSoftBodyCreationSettings.mGravityFactor);266mp->mMotionQuality = EMotionQuality::Discrete;267mp->mAllowSleeping = inSoftBodyCreationSettings.mAllowSleeping;268JPH_IF_ENABLE_ASSERTS(mp->mCachedBodyType = body->mBodyType;)269JPH_IF_ENABLE_ASSERTS(mp->mCachedMotionType = body->mMotionType;)270mp->Initialize(inSoftBodyCreationSettings);271272body->SetPositionAndRotationInternal(inSoftBodyCreationSettings.mPosition, inSoftBodyCreationSettings.mMakeRotationIdentity? Quat::sIdentity() : inSoftBodyCreationSettings.mRotation);273274return body;275}276277void BodyManager::FreeBody(Body *inBody) const278{279JPH_ASSERT(inBody->GetID().IsInvalid(), "This function should only be called on a body that doesn't have an ID yet, use DestroyBody otherwise");280281sDeleteBody(inBody);282}283284bool BodyManager::AddBody(Body *ioBody)285{286// Return error when body was already added287if (!ioBody->GetID().IsInvalid())288return false;289290// Determine next free index291uint32 idx;292{293UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList));294295if (mBodyIDFreeListStart != cBodyIDFreeListEnd)296{297// Pop an item from the freelist298JPH_ASSERT(mBodyIDFreeListStart & cIsFreedBody);299idx = uint32(mBodyIDFreeListStart >> cFreedBodyIndexShift);300JPH_ASSERT(!sIsValidBodyPointer(mBodies[idx]));301mBodyIDFreeListStart = uintptr_t(mBodies[idx]);302mBodies[idx] = ioBody;303}304else305{306if (mBodies.size() < mBodies.capacity())307{308// Allocate a new entry, note that the array should not actually resize since we've reserved it at init time309idx = uint32(mBodies.size());310mBodies.push_back(ioBody);311}312else313{314// Out of bodies315return false;316}317}318319// Update cached number of bodies320mNumBodies++;321}322323// Get next sequence number and assign the ID324uint8 seq_no = GetNextSequenceNumber(idx);325ioBody->mID = BodyID(idx, seq_no);326return true;327}328329bool BodyManager::AddBodyWithCustomID(Body *ioBody, const BodyID &inBodyID)330{331// Return error when body was already added332if (!ioBody->GetID().IsInvalid())333return false;334335{336UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList));337338// Check if index is beyond the max body ID339uint32 idx = inBodyID.GetIndex();340if (idx >= mBodies.capacity())341return false; // Return error342343if (idx < mBodies.size())344{345// Body array entry has already been allocated, check if there's a free body here346if (sIsValidBodyPointer(mBodies[idx]))347return false; // Return error348349// Remove the entry from the freelist350uintptr_t idx_start = mBodyIDFreeListStart >> cFreedBodyIndexShift;351if (idx == idx_start)352{353// First entry, easy to remove, the start of the list is our next354mBodyIDFreeListStart = uintptr_t(mBodies[idx]);355}356else357{358// Loop over the freelist and find the entry in the freelist pointing to our index359// TODO: This is O(N), see if this becomes a performance problem (don't want to put the freed bodies in a double linked list)360uintptr_t cur, next;361for (cur = idx_start; cur != cBodyIDFreeListEnd >> cFreedBodyIndexShift; cur = next)362{363next = uintptr_t(mBodies[cur]) >> cFreedBodyIndexShift;364if (next == idx)365{366mBodies[cur] = mBodies[idx];367break;368}369}370JPH_ASSERT(cur != cBodyIDFreeListEnd >> cFreedBodyIndexShift);371}372373// Put the body in the slot374mBodies[idx] = ioBody;375}376else377{378// Ensure that all body IDs up to this body ID have been allocated and added to the free list379while (idx > mBodies.size())380{381// Push the id onto the freelist382mBodies.push_back((Body *)mBodyIDFreeListStart);383mBodyIDFreeListStart = (uintptr_t(mBodies.size() - 1) << cFreedBodyIndexShift) | cIsFreedBody;384}385386// Add the element to the list387mBodies.push_back(ioBody);388}389390// Update cached number of bodies391mNumBodies++;392}393394// Assign the ID395ioBody->mID = inBodyID;396return true;397}398399Body *BodyManager::RemoveBodyInternal(const BodyID &inBodyID)400{401// Get body402uint32 idx = inBodyID.GetIndex();403Body *body = mBodies[idx];404405// Validate that it can be removed406JPH_ASSERT(body->GetID() == inBodyID);407JPH_ASSERT(!body->IsActive());408JPH_ASSERT(!body->IsInBroadPhase(), "Use BodyInterface::RemoveBody to remove this body first!");409410// Push the id onto the freelist411mBodies[idx] = (Body *)mBodyIDFreeListStart;412mBodyIDFreeListStart = (uintptr_t(idx) << cFreedBodyIndexShift) | cIsFreedBody;413414return body;415}416417#if defined(JPH_DEBUG) && defined(JPH_ENABLE_ASSERTS)418419void BodyManager::ValidateFreeList() const420{421// Check that the freelist is correct422size_t num_freed = 0;423for (uintptr_t start = mBodyIDFreeListStart; start != cBodyIDFreeListEnd; start = uintptr_t(mBodies[start >> cFreedBodyIndexShift]))424{425JPH_ASSERT(start & cIsFreedBody);426num_freed++;427}428JPH_ASSERT(mNumBodies == mBodies.size() - num_freed);429}430431#endif // defined(JPH_DEBUG) && _defined(JPH_ENABLE_ASSERTS)432433void BodyManager::RemoveBodies(const BodyID *inBodyIDs, int inNumber, Body **outBodies)434{435// Don't take lock if no bodies are to be destroyed436if (inNumber <= 0)437return;438439UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList));440441// Update cached number of bodies442JPH_ASSERT(mNumBodies >= (uint)inNumber);443mNumBodies -= inNumber;444445for (const BodyID *b = inBodyIDs, *b_end = inBodyIDs + inNumber; b < b_end; b++)446{447// Remove body448Body *body = RemoveBodyInternal(*b);449450// Clear the ID451body->mID = BodyID();452453// Return the body to the caller454if (outBodies != nullptr)455{456*outBodies = body;457++outBodies;458}459}460461#if defined(JPH_DEBUG) && defined(JPH_ENABLE_ASSERTS)462ValidateFreeList();463#endif // defined(JPH_DEBUG) && _defined(JPH_ENABLE_ASSERTS)464}465466void BodyManager::DestroyBodies(const BodyID *inBodyIDs, int inNumber)467{468// Don't take lock if no bodies are to be destroyed469if (inNumber <= 0)470return;471472UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList));473474// Update cached number of bodies475JPH_ASSERT(mNumBodies >= (uint)inNumber);476mNumBodies -= inNumber;477478for (const BodyID *b = inBodyIDs, *b_end = inBodyIDs + inNumber; b < b_end; b++)479{480// Remove body481Body *body = RemoveBodyInternal(*b);482483// Free the body484sDeleteBody(body);485}486487#if defined(JPH_DEBUG) && defined(JPH_ENABLE_ASSERTS)488ValidateFreeList();489#endif // defined(JPH_DEBUG) && _defined(JPH_ENABLE_ASSERTS)490}491492void BodyManager::AddBodyToActiveBodies(Body &ioBody)493{494// Select the correct array to use495int type = (int)ioBody.GetBodyType();496atomic<uint32> &num_active_bodies = mNumActiveBodies[type];497BodyID *active_bodies = mActiveBodies[type];498499MotionProperties *mp = ioBody.mMotionProperties;500uint32 num_active_bodies_val = num_active_bodies.load(memory_order_relaxed);501mp->mIndexInActiveBodies = num_active_bodies_val;502JPH_ASSERT(num_active_bodies_val < GetMaxBodies());503active_bodies[num_active_bodies_val] = ioBody.GetID();504num_active_bodies.fetch_add(1, memory_order_release); // Increment atomic after setting the body ID so that PhysicsSystem::JobFindCollisions (which doesn't lock the mActiveBodiesMutex) will only read valid IDs505506// Count CCD bodies507if (mp->GetMotionQuality() == EMotionQuality::LinearCast)508mNumActiveCCDBodies++;509}510511void BodyManager::RemoveBodyFromActiveBodies(Body &ioBody)512{513// Select the correct array to use514int type = (int)ioBody.GetBodyType();515atomic<uint32> &num_active_bodies = mNumActiveBodies[type];516BodyID *active_bodies = mActiveBodies[type];517518uint32 last_body_index = num_active_bodies.load(memory_order_relaxed) - 1;519MotionProperties *mp = ioBody.mMotionProperties;520if (mp->mIndexInActiveBodies != last_body_index)521{522// This is not the last body, use the last body to fill the hole523BodyID last_body_id = active_bodies[last_body_index];524active_bodies[mp->mIndexInActiveBodies] = last_body_id;525526// Update that body's index in the active list527Body &last_body = *mBodies[last_body_id.GetIndex()];528JPH_ASSERT(last_body.mMotionProperties->mIndexInActiveBodies == last_body_index);529last_body.mMotionProperties->mIndexInActiveBodies = mp->mIndexInActiveBodies;530}531532// Mark this body as no longer active533mp->mIndexInActiveBodies = Body::cInactiveIndex;534535// Remove unused element from active bodies list536num_active_bodies.fetch_sub(1, memory_order_release);537538// Count CCD bodies539if (mp->GetMotionQuality() == EMotionQuality::LinearCast)540mNumActiveCCDBodies--;541}542543void BodyManager::ActivateBodies(const BodyID *inBodyIDs, int inNumber)544{545// Don't take lock if no bodies are to be activated546if (inNumber <= 0)547return;548549UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList));550551JPH_ASSERT(!mActiveBodiesLocked || sOverrideAllowActivation);552553for (const BodyID *b = inBodyIDs, *b_end = inBodyIDs + inNumber; b < b_end; b++)554if (!b->IsInvalid())555{556BodyID body_id = *b;557Body &body = *mBodies[body_id.GetIndex()];558559JPH_ASSERT(body.GetID() == body_id);560JPH_ASSERT(body.IsInBroadPhase(), "Use BodyInterface::AddBody to add the body first!");561562if (!body.IsStatic())563{564// Reset sleeping timer so that we don't immediately go to sleep again565body.ResetSleepTimer();566567// Check if we're sleeping568if (body.mMotionProperties->mIndexInActiveBodies == Body::cInactiveIndex)569{570AddBodyToActiveBodies(body);571572// Call activation listener573if (mActivationListener != nullptr)574mActivationListener->OnBodyActivated(body_id, body.GetUserData());575}576}577}578}579580void BodyManager::DeactivateBodies(const BodyID *inBodyIDs, int inNumber)581{582// Don't take lock if no bodies are to be deactivated583if (inNumber <= 0)584return;585586UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList));587588JPH_ASSERT(!mActiveBodiesLocked || sOverrideAllowDeactivation);589590for (const BodyID *b = inBodyIDs, *b_end = inBodyIDs + inNumber; b < b_end; b++)591if (!b->IsInvalid())592{593BodyID body_id = *b;594Body &body = *mBodies[body_id.GetIndex()];595596JPH_ASSERT(body.GetID() == body_id);597JPH_ASSERT(body.IsInBroadPhase(), "Use BodyInterface::AddBody to add the body first!");598599if (body.mMotionProperties != nullptr600&& body.mMotionProperties->mIndexInActiveBodies != Body::cInactiveIndex)601{602// Remove the body from the active bodies list603RemoveBodyFromActiveBodies(body);604605// Mark this body as no longer active606body.mMotionProperties->mIslandIndex = Body::cInactiveIndex;607608// Reset velocity609body.mMotionProperties->mLinearVelocity = Vec3::sZero();610body.mMotionProperties->mAngularVelocity = Vec3::sZero();611612// Call activation listener613if (mActivationListener != nullptr)614mActivationListener->OnBodyDeactivated(body_id, body.GetUserData());615}616}617}618619void BodyManager::SetMotionQuality(Body &ioBody, EMotionQuality inMotionQuality)620{621MotionProperties *mp = ioBody.GetMotionPropertiesUnchecked();622if (mp != nullptr && mp->GetMotionQuality() != inMotionQuality)623{624UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList));625626JPH_ASSERT(!mActiveBodiesLocked);627628bool is_active = ioBody.IsActive();629if (is_active && mp->GetMotionQuality() == EMotionQuality::LinearCast)630--mNumActiveCCDBodies;631632mp->mMotionQuality = inMotionQuality;633634if (is_active && mp->GetMotionQuality() == EMotionQuality::LinearCast)635++mNumActiveCCDBodies;636}637}638639void BodyManager::GetActiveBodies(EBodyType inType, BodyIDVector &outBodyIDs) const640{641JPH_PROFILE_FUNCTION();642643UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList));644645const BodyID *active_bodies = mActiveBodies[(int)inType];646outBodyIDs.assign(active_bodies, active_bodies + mNumActiveBodies[(int)inType].load(memory_order_relaxed));647}648649void BodyManager::GetBodyIDs(BodyIDVector &outBodies) const650{651JPH_PROFILE_FUNCTION();652653UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList));654655// Reserve space for all bodies656outBodies.clear();657outBodies.reserve(mNumBodies);658659// Iterate the list and find the bodies that are not null660for (const Body *b : mBodies)661if (sIsValidBodyPointer(b))662outBodies.push_back(b->GetID());663664// Validate that our reservation was correct665JPH_ASSERT(outBodies.size() == mNumBodies);666}667668void BodyManager::SetBodyActivationListener(BodyActivationListener *inListener)669{670UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList));671672mActivationListener = inListener;673}674675BodyManager::MutexMask BodyManager::GetMutexMask(const BodyID *inBodies, int inNumber) const676{677JPH_ASSERT(sizeof(MutexMask) * 8 >= mBodyMutexes.GetNumMutexes(), "MutexMask must have enough bits");678679if (inNumber >= (int)mBodyMutexes.GetNumMutexes())680{681// Just lock everything if there are too many bodies682return GetAllBodiesMutexMask();683}684else685{686MutexMask mask = 0;687for (const BodyID *b = inBodies, *b_end = inBodies + inNumber; b < b_end; ++b)688if (!b->IsInvalid())689{690uint32 index = mBodyMutexes.GetMutexIndex(b->GetIndex());691mask |= (MutexMask(1) << index);692}693return mask;694}695}696697void BodyManager::LockRead(MutexMask inMutexMask) const698{699JPH_IF_ENABLE_ASSERTS(PhysicsLock::sCheckLock(this, EPhysicsLockTypes::PerBody));700701int index = 0;702for (MutexMask mask = inMutexMask; mask != 0; mask >>= 1, index++)703if (mask & 1)704mBodyMutexes.GetMutexByIndex(index).lock_shared();705}706707void BodyManager::UnlockRead(MutexMask inMutexMask) const708{709JPH_IF_ENABLE_ASSERTS(PhysicsLock::sCheckUnlock(this, EPhysicsLockTypes::PerBody));710711int index = 0;712for (MutexMask mask = inMutexMask; mask != 0; mask >>= 1, index++)713if (mask & 1)714mBodyMutexes.GetMutexByIndex(index).unlock_shared();715}716717void BodyManager::LockWrite(MutexMask inMutexMask) const718{719JPH_IF_ENABLE_ASSERTS(PhysicsLock::sCheckLock(this, EPhysicsLockTypes::PerBody));720721int index = 0;722for (MutexMask mask = inMutexMask; mask != 0; mask >>= 1, index++)723if (mask & 1)724mBodyMutexes.GetMutexByIndex(index).lock();725}726727void BodyManager::UnlockWrite(MutexMask inMutexMask) const728{729JPH_IF_ENABLE_ASSERTS(PhysicsLock::sCheckUnlock(this, EPhysicsLockTypes::PerBody));730731int index = 0;732for (MutexMask mask = inMutexMask; mask != 0; mask >>= 1, index++)733if (mask & 1)734mBodyMutexes.GetMutexByIndex(index).unlock();735}736737void BodyManager::LockAllBodies() const738{739JPH_IF_ENABLE_ASSERTS(PhysicsLock::sCheckLock(this, EPhysicsLockTypes::PerBody));740mBodyMutexes.LockAll();741742PhysicsLock::sLock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList));743}744745void BodyManager::UnlockAllBodies() const746{747PhysicsLock::sUnlock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList));748749JPH_IF_ENABLE_ASSERTS(PhysicsLock::sCheckUnlock(this, EPhysicsLockTypes::PerBody));750mBodyMutexes.UnlockAll();751}752753void BodyManager::SaveState(StateRecorder &inStream, const StateRecorderFilter *inFilter) const754{755{756LockAllBodies();757758// Determine which bodies to save759Array<const Body *> bodies;760bodies.reserve(mNumBodies);761for (const Body *b : mBodies)762if (sIsValidBodyPointer(b) && b->IsInBroadPhase() && (inFilter == nullptr || inFilter->ShouldSaveBody(*b)))763bodies.push_back(b);764765// Write state of bodies766uint32 num_bodies = (uint32)bodies.size();767inStream.Write(num_bodies);768for (const Body *b : bodies)769{770inStream.Write(b->GetID());771inStream.Write(b->IsActive());772b->SaveState(inStream);773}774775UnlockAllBodies();776}777}778779bool BodyManager::RestoreState(StateRecorder &inStream)780{781BodyIDVector bodies_to_activate, bodies_to_deactivate;782783{784LockAllBodies();785786if (inStream.IsValidating())787{788// Read state of bodies, note this reads it in a way to be consistent with validation789uint32 old_num_bodies = 0;790for (const Body *b : mBodies)791if (sIsValidBodyPointer(b) && b->IsInBroadPhase())792++old_num_bodies;793uint32 num_bodies = old_num_bodies; // Initialize to current value for validation794inStream.Read(num_bodies);795if (num_bodies != old_num_bodies)796{797JPH_ASSERT(false, "Cannot handle adding/removing bodies");798UnlockAllBodies();799return false;800}801802for (Body *b : mBodies)803if (sIsValidBodyPointer(b) && b->IsInBroadPhase())804{805BodyID body_id = b->GetID(); // Initialize to current value for validation806inStream.Read(body_id);807if (body_id != b->GetID())808{809JPH_ASSERT(false, "Cannot handle adding/removing bodies");810UnlockAllBodies();811return false;812}813bool is_active = b->IsActive(); // Initialize to current value for validation814inStream.Read(is_active);815if (is_active != b->IsActive())816{817if (is_active)818bodies_to_activate.push_back(body_id);819else820bodies_to_deactivate.push_back(body_id);821}822b->RestoreState(inStream);823}824}825else826{827// Not validating, we can be a bit more loose, read number of bodies828uint32 num_bodies = 0;829inStream.Read(num_bodies);830831// Iterate over the stored bodies and restore their state832for (uint32 idx = 0; idx < num_bodies; ++idx)833{834BodyID body_id;835inStream.Read(body_id);836Body *b = TryGetBody(body_id);837if (b == nullptr)838{839JPH_ASSERT(false, "Restoring state for non-existing body");840UnlockAllBodies();841return false;842}843bool is_active;844inStream.Read(is_active);845if (is_active != b->IsActive())846{847if (is_active)848bodies_to_activate.push_back(body_id);849else850bodies_to_deactivate.push_back(body_id);851}852b->RestoreState(inStream);853}854}855856UnlockAllBodies();857}858859{860UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList));861862for (BodyID body_id : bodies_to_activate)863{864Body *body = TryGetBody(body_id);865AddBodyToActiveBodies(*body);866}867868for (BodyID body_id : bodies_to_deactivate)869{870Body *body = TryGetBody(body_id);871RemoveBodyFromActiveBodies(*body);872}873}874875return true;876}877878void BodyManager::SaveBodyState(const Body &inBody, StateRecorder &inStream) const879{880inStream.Write(inBody.IsActive());881882inBody.SaveState(inStream);883}884885void BodyManager::RestoreBodyState(Body &ioBody, StateRecorder &inStream)886{887bool is_active = ioBody.IsActive();888inStream.Read(is_active);889890ioBody.RestoreState(inStream);891892if (is_active != ioBody.IsActive())893{894UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList));895896JPH_ASSERT(!mActiveBodiesLocked || sOverrideAllowActivation);897898if (is_active)899AddBodyToActiveBodies(ioBody);900else901RemoveBodyFromActiveBodies(ioBody);902}903}904905#ifdef JPH_DEBUG_RENDERER906void BodyManager::Draw(const DrawSettings &inDrawSettings, const PhysicsSettings &inPhysicsSettings, DebugRenderer *inRenderer, const BodyDrawFilter *inBodyFilter)907{908JPH_PROFILE_FUNCTION();909910LockAllBodies();911912for (const Body *body : mBodies)913if (sIsValidBodyPointer(body) && body->IsInBroadPhase() && (!inBodyFilter || inBodyFilter->ShouldDraw(*body)))914{915JPH_ASSERT(mBodies[body->GetID().GetIndex()] == body);916917bool is_sensor = body->IsSensor();918919// Determine drawing mode920Color color;921if (is_sensor)922color = Color::sYellow;923else924switch (inDrawSettings.mDrawShapeColor)925{926case EShapeColor::InstanceColor:927// Each instance has own color928color = Color::sGetDistinctColor(body->mID.GetIndex());929break;930931case EShapeColor::ShapeTypeColor:932color = ShapeFunctions::sGet(body->GetShape()->GetSubType()).mColor;933break;934935case EShapeColor::MotionTypeColor:936// Determine color based on motion type937switch (body->mMotionType)938{939case EMotionType::Static:940color = Color::sGrey;941break;942943case EMotionType::Kinematic:944color = Color::sGreen;945break;946947case EMotionType::Dynamic:948color = Color::sGetDistinctColor(body->mID.GetIndex());949break;950951default:952JPH_ASSERT(false);953color = Color::sBlack;954break;955}956break;957958case EShapeColor::SleepColor:959// Determine color based on motion type960switch (body->mMotionType)961{962case EMotionType::Static:963color = Color::sGrey;964break;965966case EMotionType::Kinematic:967color = body->IsActive()? Color::sGreen : Color::sRed;968break;969970case EMotionType::Dynamic:971color = body->IsActive()? Color::sYellow : Color::sRed;972break;973974default:975JPH_ASSERT(false);976color = Color::sBlack;977break;978}979break;980981case EShapeColor::IslandColor:982// Determine color based on motion type983switch (body->mMotionType)984{985case EMotionType::Static:986color = Color::sGrey;987break;988989case EMotionType::Kinematic:990case EMotionType::Dynamic:991{992uint32 idx = body->GetMotionProperties()->GetIslandIndexInternal();993color = idx != Body::cInactiveIndex? Color::sGetDistinctColor(idx) : Color::sLightGrey;994}995break;996997default:998JPH_ASSERT(false);999color = Color::sBlack;1000break;1001}1002break;10031004case EShapeColor::MaterialColor:1005color = Color::sWhite;1006break;10071008default:1009JPH_ASSERT(false);1010color = Color::sBlack;1011break;1012}10131014// Draw the results of GetSupportFunction1015if (inDrawSettings.mDrawGetSupportFunction)1016body->mShape->DrawGetSupportFunction(inRenderer, body->GetCenterOfMassTransform(), Vec3::sOne(), color, inDrawSettings.mDrawSupportDirection);10171018// Draw the results of GetSupportingFace1019if (inDrawSettings.mDrawGetSupportingFace)1020body->mShape->DrawGetSupportingFace(inRenderer, body->GetCenterOfMassTransform(), Vec3::sOne());10211022// Draw the shape1023if (inDrawSettings.mDrawShape)1024body->mShape->Draw(inRenderer, body->GetCenterOfMassTransform(), Vec3::sOne(), color, inDrawSettings.mDrawShapeColor == EShapeColor::MaterialColor, inDrawSettings.mDrawShapeWireframe || is_sensor);10251026// Draw bounding box1027if (inDrawSettings.mDrawBoundingBox)1028inRenderer->DrawWireBox(body->mBounds, color);10291030// Draw center of mass transform1031if (inDrawSettings.mDrawCenterOfMassTransform)1032inRenderer->DrawCoordinateSystem(body->GetCenterOfMassTransform(), 0.2f);10331034// Draw world transform1035if (inDrawSettings.mDrawWorldTransform)1036inRenderer->DrawCoordinateSystem(body->GetWorldTransform(), 0.2f);10371038// Draw world space linear and angular velocity1039if (inDrawSettings.mDrawVelocity)1040{1041RVec3 pos = body->GetCenterOfMassPosition();1042inRenderer->DrawArrow(pos, pos + body->GetLinearVelocity(), Color::sGreen, 0.1f);1043inRenderer->DrawArrow(pos, pos + body->GetAngularVelocity(), Color::sRed, 0.1f);1044}10451046if (inDrawSettings.mDrawMassAndInertia && body->IsDynamic())1047{1048const MotionProperties *mp = body->GetMotionProperties();1049if (mp->GetInverseMass() > 0.0f1050&& !Vec3::sEquals(mp->GetInverseInertiaDiagonal(), Vec3::sZero()).TestAnyXYZTrue())1051{1052// Invert mass again1053float mass = 1.0f / mp->GetInverseMass();10541055// Invert diagonal again1056Vec3 diagonal = mp->GetInverseInertiaDiagonal().Reciprocal();10571058// Determine how big of a box has the equivalent inertia1059Vec3 box_size = MassProperties::sGetEquivalentSolidBoxSize(mass, diagonal);10601061// Draw box with equivalent inertia1062inRenderer->DrawWireBox(body->GetCenterOfMassTransform() * Mat44::sRotation(mp->GetInertiaRotation()), AABox(-0.5f * box_size, 0.5f * box_size), Color::sOrange);10631064// Draw mass1065inRenderer->DrawText3D(body->GetCenterOfMassPosition(), StringFormat("%.2f", (double)mass), Color::sOrange, 0.2f);1066}1067}10681069if (inDrawSettings.mDrawSleepStats && body->IsDynamic() && body->IsActive())1070{1071// Draw stats to know which bodies could go to sleep1072String text = StringFormat("t: %.1f", (double)body->mMotionProperties->mSleepTestTimer);1073uint8 g = uint8(Clamp(255.0f * body->mMotionProperties->mSleepTestTimer / inPhysicsSettings.mTimeBeforeSleep, 0.0f, 255.0f));1074Color sleep_color = Color(0, 255 - g, g);1075inRenderer->DrawText3D(body->GetCenterOfMassPosition(), text, sleep_color, 0.2f);1076for (int i = 0; i < 3; ++i)1077inRenderer->DrawWireSphere(JPH_IF_DOUBLE_PRECISION(body->mMotionProperties->GetSleepTestOffset() +) body->mMotionProperties->mSleepTestSpheres[i].GetCenter(), body->mMotionProperties->mSleepTestSpheres[i].GetRadius(), sleep_color);1078}10791080if (body->IsSoftBody())1081{1082const SoftBodyMotionProperties *mp = static_cast<const SoftBodyMotionProperties *>(body->GetMotionProperties());1083RMat44 com = body->GetCenterOfMassTransform();10841085if (inDrawSettings.mDrawSoftBodyVertices)1086mp->DrawVertices(inRenderer, com);10871088if (inDrawSettings.mDrawSoftBodyVertexVelocities)1089mp->DrawVertexVelocities(inRenderer, com);10901091if (inDrawSettings.mDrawSoftBodyEdgeConstraints)1092mp->DrawEdgeConstraints(inRenderer, com, inDrawSettings.mDrawSoftBodyConstraintColor);10931094if (inDrawSettings.mDrawSoftBodyBendConstraints)1095mp->DrawBendConstraints(inRenderer, com, inDrawSettings.mDrawSoftBodyConstraintColor);10961097if (inDrawSettings.mDrawSoftBodyVolumeConstraints)1098mp->DrawVolumeConstraints(inRenderer, com, inDrawSettings.mDrawSoftBodyConstraintColor);10991100if (inDrawSettings.mDrawSoftBodySkinConstraints)1101mp->DrawSkinConstraints(inRenderer, com, inDrawSettings.mDrawSoftBodyConstraintColor);11021103if (inDrawSettings.mDrawSoftBodyLRAConstraints)1104mp->DrawLRAConstraints(inRenderer, com, inDrawSettings.mDrawSoftBodyConstraintColor);11051106if (inDrawSettings.mDrawSoftBodyPredictedBounds)1107mp->DrawPredictedBounds(inRenderer, com);1108}1109}11101111UnlockAllBodies();1112}1113#endif // JPH_DEBUG_RENDERER11141115void BodyManager::InvalidateContactCacheForBody(Body &ioBody)1116{1117// If this is the first time we flip the collision cache invalid flag, we need to add it to an internal list to ensure we reset the flag at the end of the physics update1118if (ioBody.InvalidateContactCacheInternal())1119{1120lock_guard lock(mBodiesCacheInvalidMutex);1121mBodiesCacheInvalid.push_back(ioBody.GetID());1122}1123}11241125void BodyManager::ValidateContactCacheForAllBodies()1126{1127lock_guard lock(mBodiesCacheInvalidMutex);11281129for (const BodyID &b : mBodiesCacheInvalid)1130{1131// The body may have been removed between the call to InvalidateContactCacheForBody and this call, so check if it still exists1132Body *body = TryGetBody(b);1133if (body != nullptr)1134body->ValidateContactCacheInternal();1135}1136mBodiesCacheInvalid.clear();1137}11381139#ifdef JPH_DEBUG1140void BodyManager::ValidateActiveBodyBounds()1141{1142UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList));11431144for (uint type = 0; type < cBodyTypeCount; ++type)1145for (BodyID *id = mActiveBodies[type], *id_end = mActiveBodies[type] + mNumActiveBodies[type].load(memory_order_relaxed); id < id_end; ++id)1146{1147const Body *body = mBodies[id->GetIndex()];1148body->ValidateCachedBounds();1149}1150}1151#endif // JPH_DEBUG11521153JPH_NAMESPACE_END115411551156