Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Body/BodyManager.cpp
9912 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
7
#include <Jolt/Physics/Body/BodyManager.h>
8
#include <Jolt/Physics/PhysicsSettings.h>
9
#include <Jolt/Physics/Body/BodyCreationSettings.h>
10
#include <Jolt/Physics/Body/BodyLock.h>
11
#include <Jolt/Physics/Body/BodyActivationListener.h>
12
#include <Jolt/Physics/SoftBody/SoftBodyMotionProperties.h>
13
#include <Jolt/Physics/SoftBody/SoftBodyCreationSettings.h>
14
#include <Jolt/Physics/SoftBody/SoftBodyShape.h>
15
#include <Jolt/Physics/StateRecorder.h>
16
#include <Jolt/Core/StringTools.h>
17
#include <Jolt/Core/QuickSort.h>
18
#ifdef JPH_DEBUG_RENDERER
19
#include <Jolt/Renderer/DebugRenderer.h>
20
#include <Jolt/Physics/Body/BodyFilter.h>
21
#endif // JPH_DEBUG_RENDERER
22
23
JPH_NAMESPACE_BEGIN
24
25
#ifdef JPH_ENABLE_ASSERTS
26
static thread_local bool sOverrideAllowActivation = false;
27
static thread_local bool sOverrideAllowDeactivation = false;
28
29
bool BodyManager::sGetOverrideAllowActivation()
30
{
31
return sOverrideAllowActivation;
32
}
33
34
void BodyManager::sSetOverrideAllowActivation(bool inValue)
35
{
36
sOverrideAllowActivation = inValue;
37
}
38
39
bool BodyManager::sGetOverrideAllowDeactivation()
40
{
41
return sOverrideAllowDeactivation;
42
}
43
44
void BodyManager::sSetOverrideAllowDeactivation(bool inValue)
45
{
46
sOverrideAllowDeactivation = inValue;
47
}
48
#endif
49
50
// Helper class that combines a body and its motion properties
51
class BodyWithMotionProperties : public Body
52
{
53
public:
54
JPH_OVERRIDE_NEW_DELETE
55
56
MotionProperties mMotionProperties;
57
};
58
59
// Helper class that combines a soft body its motion properties and shape
60
class SoftBodyWithMotionPropertiesAndShape : public Body
61
{
62
public:
63
SoftBodyWithMotionPropertiesAndShape()
64
{
65
mShape.SetEmbedded();
66
}
67
68
SoftBodyMotionProperties mMotionProperties;
69
SoftBodyShape mShape;
70
};
71
72
inline void BodyManager::sDeleteBody(Body *inBody)
73
{
74
if (inBody->mMotionProperties != nullptr)
75
{
76
JPH_IF_ENABLE_ASSERTS(inBody->mMotionProperties = nullptr;)
77
if (inBody->IsSoftBody())
78
{
79
inBody->mShape = nullptr; // Release the shape to avoid assertion on shape destruction because of embedded object with refcount > 0
80
delete static_cast<SoftBodyWithMotionPropertiesAndShape *>(inBody);
81
}
82
else
83
delete static_cast<BodyWithMotionProperties *>(inBody);
84
}
85
else
86
delete inBody;
87
}
88
89
BodyManager::~BodyManager()
90
{
91
UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList));
92
93
// Destroy any bodies that are still alive
94
for (Body *b : mBodies)
95
if (sIsValidBodyPointer(b))
96
sDeleteBody(b);
97
98
for (BodyID *active_bodies : mActiveBodies)
99
delete [] active_bodies;
100
}
101
102
void BodyManager::Init(uint inMaxBodies, uint inNumBodyMutexes, const BroadPhaseLayerInterface &inLayerInterface)
103
{
104
UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList));
105
106
// Num body mutexes must be a power of two and not bigger than our MutexMask
107
uint num_body_mutexes = Clamp<uint>(GetNextPowerOf2(inNumBodyMutexes == 0? 2 * thread::hardware_concurrency() : inNumBodyMutexes), 1, sizeof(MutexMask) * 8);
108
#ifdef JPH_TSAN_ENABLED
109
num_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/950
110
#endif
111
112
// Allocate the body mutexes
113
mBodyMutexes.Init(num_body_mutexes);
114
115
// Allocate space for bodies
116
mBodies.reserve(inMaxBodies);
117
118
// Allocate space for active bodies
119
for (BodyID *&active_bodies : mActiveBodies)
120
{
121
JPH_ASSERT(active_bodies == nullptr);
122
active_bodies = new BodyID [inMaxBodies];
123
}
124
125
// Allocate space for sequence numbers
126
mBodySequenceNumbers.resize(inMaxBodies, 0);
127
128
// Keep layer interface
129
mBroadPhaseLayerInterface = &inLayerInterface;
130
}
131
132
uint BodyManager::GetNumBodies() const
133
{
134
UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList));
135
136
return mNumBodies;
137
}
138
139
BodyManager::BodyStats BodyManager::GetBodyStats() const
140
{
141
UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList));
142
143
BodyStats stats;
144
stats.mNumBodies = mNumBodies;
145
stats.mMaxBodies = uint(mBodies.capacity());
146
147
for (const Body *body : mBodies)
148
if (sIsValidBodyPointer(body))
149
{
150
if (body->IsSoftBody())
151
{
152
stats.mNumSoftBodies++;
153
if (body->IsActive())
154
stats.mNumActiveSoftBodies++;
155
}
156
else
157
{
158
switch (body->GetMotionType())
159
{
160
case EMotionType::Static:
161
stats.mNumBodiesStatic++;
162
break;
163
164
case EMotionType::Dynamic:
165
stats.mNumBodiesDynamic++;
166
if (body->IsActive())
167
stats.mNumActiveBodiesDynamic++;
168
break;
169
170
case EMotionType::Kinematic:
171
stats.mNumBodiesKinematic++;
172
if (body->IsActive())
173
stats.mNumActiveBodiesKinematic++;
174
break;
175
}
176
}
177
}
178
179
return stats;
180
}
181
182
Body *BodyManager::AllocateBody(const BodyCreationSettings &inBodyCreationSettings) const
183
{
184
// Fill in basic properties
185
Body *body;
186
if (inBodyCreationSettings.HasMassProperties())
187
{
188
BodyWithMotionProperties *bmp = new BodyWithMotionProperties;
189
body = bmp;
190
body->mMotionProperties = &bmp->mMotionProperties;
191
}
192
else
193
{
194
body = new Body;
195
}
196
body->mBodyType = EBodyType::RigidBody;
197
body->mShape = inBodyCreationSettings.GetShape();
198
body->mUserData = inBodyCreationSettings.mUserData;
199
body->SetFriction(inBodyCreationSettings.mFriction);
200
body->SetRestitution(inBodyCreationSettings.mRestitution);
201
body->mMotionType = inBodyCreationSettings.mMotionType;
202
if (inBodyCreationSettings.mIsSensor)
203
body->SetIsSensor(true);
204
if (inBodyCreationSettings.mCollideKinematicVsNonDynamic)
205
body->SetCollideKinematicVsNonDynamic(true);
206
if (inBodyCreationSettings.mUseManifoldReduction)
207
body->SetUseManifoldReduction(true);
208
if (inBodyCreationSettings.mApplyGyroscopicForce)
209
body->SetApplyGyroscopicForce(true);
210
if (inBodyCreationSettings.mEnhancedInternalEdgeRemoval)
211
body->SetEnhancedInternalEdgeRemoval(true);
212
SetBodyObjectLayerInternal(*body, inBodyCreationSettings.mObjectLayer);
213
body->mObjectLayer = inBodyCreationSettings.mObjectLayer;
214
body->mCollisionGroup = inBodyCreationSettings.mCollisionGroup;
215
216
if (inBodyCreationSettings.HasMassProperties())
217
{
218
MotionProperties *mp = body->mMotionProperties;
219
mp->SetLinearDamping(inBodyCreationSettings.mLinearDamping);
220
mp->SetAngularDamping(inBodyCreationSettings.mAngularDamping);
221
mp->SetMaxLinearVelocity(inBodyCreationSettings.mMaxLinearVelocity);
222
mp->SetMaxAngularVelocity(inBodyCreationSettings.mMaxAngularVelocity);
223
mp->SetMassProperties(inBodyCreationSettings.mAllowedDOFs, inBodyCreationSettings.GetMassProperties());
224
mp->SetLinearVelocity(inBodyCreationSettings.mLinearVelocity); // Needs to happen after setting the max linear/angular velocity and setting allowed DOFs
225
mp->SetAngularVelocity(inBodyCreationSettings.mAngularVelocity);
226
mp->SetGravityFactor(inBodyCreationSettings.mGravityFactor);
227
mp->SetNumVelocityStepsOverride(inBodyCreationSettings.mNumVelocityStepsOverride);
228
mp->SetNumPositionStepsOverride(inBodyCreationSettings.mNumPositionStepsOverride);
229
mp->mMotionQuality = inBodyCreationSettings.mMotionQuality;
230
mp->mAllowSleeping = inBodyCreationSettings.mAllowSleeping;
231
JPH_IF_ENABLE_ASSERTS(mp->mCachedBodyType = body->mBodyType;)
232
JPH_IF_ENABLE_ASSERTS(mp->mCachedMotionType = body->mMotionType;)
233
}
234
235
// Position body
236
body->SetPositionAndRotationInternal(inBodyCreationSettings.mPosition, inBodyCreationSettings.mRotation);
237
238
return body;
239
}
240
241
/// Create a soft body using creation settings. The returned body will not be part of the body manager yet.
242
Body *BodyManager::AllocateSoftBody(const SoftBodyCreationSettings &inSoftBodyCreationSettings) const
243
{
244
// Fill in basic properties
245
SoftBodyWithMotionPropertiesAndShape *bmp = new SoftBodyWithMotionPropertiesAndShape;
246
SoftBodyMotionProperties *mp = &bmp->mMotionProperties;
247
SoftBodyShape *shape = &bmp->mShape;
248
Body *body = bmp;
249
shape->mSoftBodyMotionProperties = mp;
250
body->mBodyType = EBodyType::SoftBody;
251
body->mMotionProperties = mp;
252
body->mShape = shape;
253
body->mUserData = inSoftBodyCreationSettings.mUserData;
254
body->SetFriction(inSoftBodyCreationSettings.mFriction);
255
body->SetRestitution(inSoftBodyCreationSettings.mRestitution);
256
body->mMotionType = EMotionType::Dynamic;
257
SetBodyObjectLayerInternal(*body, inSoftBodyCreationSettings.mObjectLayer);
258
body->mObjectLayer = inSoftBodyCreationSettings.mObjectLayer;
259
body->mCollisionGroup = inSoftBodyCreationSettings.mCollisionGroup;
260
mp->SetLinearDamping(inSoftBodyCreationSettings.mLinearDamping);
261
mp->SetAngularDamping(0);
262
mp->SetMaxLinearVelocity(inSoftBodyCreationSettings.mMaxLinearVelocity);
263
mp->SetMaxAngularVelocity(FLT_MAX);
264
mp->SetLinearVelocity(Vec3::sZero());
265
mp->SetAngularVelocity(Vec3::sZero());
266
mp->SetGravityFactor(inSoftBodyCreationSettings.mGravityFactor);
267
mp->mMotionQuality = EMotionQuality::Discrete;
268
mp->mAllowSleeping = inSoftBodyCreationSettings.mAllowSleeping;
269
JPH_IF_ENABLE_ASSERTS(mp->mCachedBodyType = body->mBodyType;)
270
JPH_IF_ENABLE_ASSERTS(mp->mCachedMotionType = body->mMotionType;)
271
mp->Initialize(inSoftBodyCreationSettings);
272
273
body->SetPositionAndRotationInternal(inSoftBodyCreationSettings.mPosition, inSoftBodyCreationSettings.mMakeRotationIdentity? Quat::sIdentity() : inSoftBodyCreationSettings.mRotation);
274
275
return body;
276
}
277
278
void BodyManager::FreeBody(Body *inBody) const
279
{
280
JPH_ASSERT(inBody->GetID().IsInvalid(), "This function should only be called on a body that doesn't have an ID yet, use DestroyBody otherwise");
281
282
sDeleteBody(inBody);
283
}
284
285
bool BodyManager::AddBody(Body *ioBody)
286
{
287
// Return error when body was already added
288
if (!ioBody->GetID().IsInvalid())
289
return false;
290
291
// Determine next free index
292
uint32 idx;
293
{
294
UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList));
295
296
if (mBodyIDFreeListStart != cBodyIDFreeListEnd)
297
{
298
// Pop an item from the freelist
299
JPH_ASSERT(mBodyIDFreeListStart & cIsFreedBody);
300
idx = uint32(mBodyIDFreeListStart >> cFreedBodyIndexShift);
301
JPH_ASSERT(!sIsValidBodyPointer(mBodies[idx]));
302
mBodyIDFreeListStart = uintptr_t(mBodies[idx]);
303
mBodies[idx] = ioBody;
304
}
305
else
306
{
307
if (mBodies.size() < mBodies.capacity())
308
{
309
// Allocate a new entry, note that the array should not actually resize since we've reserved it at init time
310
idx = uint32(mBodies.size());
311
mBodies.push_back(ioBody);
312
}
313
else
314
{
315
// Out of bodies
316
return false;
317
}
318
}
319
320
// Update cached number of bodies
321
mNumBodies++;
322
}
323
324
// Get next sequence number and assign the ID
325
uint8 seq_no = GetNextSequenceNumber(idx);
326
ioBody->mID = BodyID(idx, seq_no);
327
return true;
328
}
329
330
bool BodyManager::AddBodyWithCustomID(Body *ioBody, const BodyID &inBodyID)
331
{
332
// Return error when body was already added
333
if (!ioBody->GetID().IsInvalid())
334
return false;
335
336
{
337
UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList));
338
339
// Check if index is beyond the max body ID
340
uint32 idx = inBodyID.GetIndex();
341
if (idx >= mBodies.capacity())
342
return false; // Return error
343
344
if (idx < mBodies.size())
345
{
346
// Body array entry has already been allocated, check if there's a free body here
347
if (sIsValidBodyPointer(mBodies[idx]))
348
return false; // Return error
349
350
// Remove the entry from the freelist
351
uintptr_t idx_start = mBodyIDFreeListStart >> cFreedBodyIndexShift;
352
if (idx == idx_start)
353
{
354
// First entry, easy to remove, the start of the list is our next
355
mBodyIDFreeListStart = uintptr_t(mBodies[idx]);
356
}
357
else
358
{
359
// Loop over the freelist and find the entry in the freelist pointing to our index
360
// 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)
361
uintptr_t cur, next;
362
for (cur = idx_start; cur != cBodyIDFreeListEnd >> cFreedBodyIndexShift; cur = next)
363
{
364
next = uintptr_t(mBodies[cur]) >> cFreedBodyIndexShift;
365
if (next == idx)
366
{
367
mBodies[cur] = mBodies[idx];
368
break;
369
}
370
}
371
JPH_ASSERT(cur != cBodyIDFreeListEnd >> cFreedBodyIndexShift);
372
}
373
374
// Put the body in the slot
375
mBodies[idx] = ioBody;
376
}
377
else
378
{
379
// Ensure that all body IDs up to this body ID have been allocated and added to the free list
380
while (idx > mBodies.size())
381
{
382
// Push the id onto the freelist
383
mBodies.push_back((Body *)mBodyIDFreeListStart);
384
mBodyIDFreeListStart = (uintptr_t(mBodies.size() - 1) << cFreedBodyIndexShift) | cIsFreedBody;
385
}
386
387
// Add the element to the list
388
mBodies.push_back(ioBody);
389
}
390
391
// Update cached number of bodies
392
mNumBodies++;
393
}
394
395
// Assign the ID
396
ioBody->mID = inBodyID;
397
return true;
398
}
399
400
Body *BodyManager::RemoveBodyInternal(const BodyID &inBodyID)
401
{
402
// Get body
403
uint32 idx = inBodyID.GetIndex();
404
Body *body = mBodies[idx];
405
406
// Validate that it can be removed
407
JPH_ASSERT(body->GetID() == inBodyID);
408
JPH_ASSERT(!body->IsActive());
409
JPH_ASSERT(!body->IsInBroadPhase(), "Use BodyInterface::RemoveBody to remove this body first!");
410
411
// Push the id onto the freelist
412
mBodies[idx] = (Body *)mBodyIDFreeListStart;
413
mBodyIDFreeListStart = (uintptr_t(idx) << cFreedBodyIndexShift) | cIsFreedBody;
414
415
return body;
416
}
417
418
#if defined(JPH_DEBUG) && defined(JPH_ENABLE_ASSERTS)
419
420
void BodyManager::ValidateFreeList() const
421
{
422
// Check that the freelist is correct
423
size_t num_freed = 0;
424
for (uintptr_t start = mBodyIDFreeListStart; start != cBodyIDFreeListEnd; start = uintptr_t(mBodies[start >> cFreedBodyIndexShift]))
425
{
426
JPH_ASSERT(start & cIsFreedBody);
427
num_freed++;
428
}
429
JPH_ASSERT(mNumBodies == mBodies.size() - num_freed);
430
}
431
432
#endif // defined(JPH_DEBUG) && _defined(JPH_ENABLE_ASSERTS)
433
434
void BodyManager::RemoveBodies(const BodyID *inBodyIDs, int inNumber, Body **outBodies)
435
{
436
// Don't take lock if no bodies are to be destroyed
437
if (inNumber <= 0)
438
return;
439
440
UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList));
441
442
// Update cached number of bodies
443
JPH_ASSERT(mNumBodies >= (uint)inNumber);
444
mNumBodies -= inNumber;
445
446
for (const BodyID *b = inBodyIDs, *b_end = inBodyIDs + inNumber; b < b_end; b++)
447
{
448
// Remove body
449
Body *body = RemoveBodyInternal(*b);
450
451
// Clear the ID
452
body->mID = BodyID();
453
454
// Return the body to the caller
455
if (outBodies != nullptr)
456
{
457
*outBodies = body;
458
++outBodies;
459
}
460
}
461
462
#if defined(JPH_DEBUG) && defined(JPH_ENABLE_ASSERTS)
463
ValidateFreeList();
464
#endif // defined(JPH_DEBUG) && _defined(JPH_ENABLE_ASSERTS)
465
}
466
467
void BodyManager::DestroyBodies(const BodyID *inBodyIDs, int inNumber)
468
{
469
// Don't take lock if no bodies are to be destroyed
470
if (inNumber <= 0)
471
return;
472
473
UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList));
474
475
// Update cached number of bodies
476
JPH_ASSERT(mNumBodies >= (uint)inNumber);
477
mNumBodies -= inNumber;
478
479
for (const BodyID *b = inBodyIDs, *b_end = inBodyIDs + inNumber; b < b_end; b++)
480
{
481
// Remove body
482
Body *body = RemoveBodyInternal(*b);
483
484
// Free the body
485
sDeleteBody(body);
486
}
487
488
#if defined(JPH_DEBUG) && defined(JPH_ENABLE_ASSERTS)
489
ValidateFreeList();
490
#endif // defined(JPH_DEBUG) && _defined(JPH_ENABLE_ASSERTS)
491
}
492
493
void BodyManager::AddBodyToActiveBodies(Body &ioBody)
494
{
495
// Select the correct array to use
496
int type = (int)ioBody.GetBodyType();
497
atomic<uint32> &num_active_bodies = mNumActiveBodies[type];
498
BodyID *active_bodies = mActiveBodies[type];
499
500
MotionProperties *mp = ioBody.mMotionProperties;
501
uint32 num_active_bodies_val = num_active_bodies.load(memory_order_relaxed);
502
mp->mIndexInActiveBodies = num_active_bodies_val;
503
JPH_ASSERT(num_active_bodies_val < GetMaxBodies());
504
active_bodies[num_active_bodies_val] = ioBody.GetID();
505
num_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 IDs
506
507
// Count CCD bodies
508
if (mp->GetMotionQuality() == EMotionQuality::LinearCast)
509
mNumActiveCCDBodies++;
510
}
511
512
void BodyManager::RemoveBodyFromActiveBodies(Body &ioBody)
513
{
514
// Select the correct array to use
515
int type = (int)ioBody.GetBodyType();
516
atomic<uint32> &num_active_bodies = mNumActiveBodies[type];
517
BodyID *active_bodies = mActiveBodies[type];
518
519
uint32 last_body_index = num_active_bodies.load(memory_order_relaxed) - 1;
520
MotionProperties *mp = ioBody.mMotionProperties;
521
if (mp->mIndexInActiveBodies != last_body_index)
522
{
523
// This is not the last body, use the last body to fill the hole
524
BodyID last_body_id = active_bodies[last_body_index];
525
active_bodies[mp->mIndexInActiveBodies] = last_body_id;
526
527
// Update that body's index in the active list
528
Body &last_body = *mBodies[last_body_id.GetIndex()];
529
JPH_ASSERT(last_body.mMotionProperties->mIndexInActiveBodies == last_body_index);
530
last_body.mMotionProperties->mIndexInActiveBodies = mp->mIndexInActiveBodies;
531
}
532
533
// Mark this body as no longer active
534
mp->mIndexInActiveBodies = Body::cInactiveIndex;
535
536
// Remove unused element from active bodies list
537
num_active_bodies.fetch_sub(1, memory_order_release);
538
539
// Count CCD bodies
540
if (mp->GetMotionQuality() == EMotionQuality::LinearCast)
541
mNumActiveCCDBodies--;
542
}
543
544
void BodyManager::ActivateBodies(const BodyID *inBodyIDs, int inNumber)
545
{
546
// Don't take lock if no bodies are to be activated
547
if (inNumber <= 0)
548
return;
549
550
UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList));
551
552
JPH_ASSERT(!mActiveBodiesLocked || sOverrideAllowActivation);
553
554
for (const BodyID *b = inBodyIDs, *b_end = inBodyIDs + inNumber; b < b_end; b++)
555
if (!b->IsInvalid())
556
{
557
BodyID body_id = *b;
558
Body &body = *mBodies[body_id.GetIndex()];
559
560
JPH_ASSERT(body.GetID() == body_id);
561
JPH_ASSERT(body.IsInBroadPhase(), "Use BodyInterface::AddBody to add the body first!");
562
563
if (!body.IsStatic())
564
{
565
// Reset sleeping timer so that we don't immediately go to sleep again
566
body.ResetSleepTimer();
567
568
// Check if we're sleeping
569
if (body.mMotionProperties->mIndexInActiveBodies == Body::cInactiveIndex)
570
{
571
AddBodyToActiveBodies(body);
572
573
// Call activation listener
574
if (mActivationListener != nullptr)
575
mActivationListener->OnBodyActivated(body_id, body.GetUserData());
576
}
577
}
578
}
579
}
580
581
void BodyManager::DeactivateBodies(const BodyID *inBodyIDs, int inNumber)
582
{
583
// Don't take lock if no bodies are to be deactivated
584
if (inNumber <= 0)
585
return;
586
587
UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList));
588
589
JPH_ASSERT(!mActiveBodiesLocked || sOverrideAllowDeactivation);
590
591
for (const BodyID *b = inBodyIDs, *b_end = inBodyIDs + inNumber; b < b_end; b++)
592
if (!b->IsInvalid())
593
{
594
BodyID body_id = *b;
595
Body &body = *mBodies[body_id.GetIndex()];
596
597
JPH_ASSERT(body.GetID() == body_id);
598
JPH_ASSERT(body.IsInBroadPhase(), "Use BodyInterface::AddBody to add the body first!");
599
600
if (body.mMotionProperties != nullptr
601
&& body.mMotionProperties->mIndexInActiveBodies != Body::cInactiveIndex)
602
{
603
// Remove the body from the active bodies list
604
RemoveBodyFromActiveBodies(body);
605
606
// Mark this body as no longer active
607
body.mMotionProperties->mIslandIndex = Body::cInactiveIndex;
608
609
// Reset velocity
610
body.mMotionProperties->mLinearVelocity = Vec3::sZero();
611
body.mMotionProperties->mAngularVelocity = Vec3::sZero();
612
613
// Call activation listener
614
if (mActivationListener != nullptr)
615
mActivationListener->OnBodyDeactivated(body_id, body.GetUserData());
616
}
617
}
618
}
619
620
void BodyManager::SetMotionQuality(Body &ioBody, EMotionQuality inMotionQuality)
621
{
622
MotionProperties *mp = ioBody.GetMotionPropertiesUnchecked();
623
if (mp != nullptr && mp->GetMotionQuality() != inMotionQuality)
624
{
625
UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList));
626
627
JPH_ASSERT(!mActiveBodiesLocked);
628
629
bool is_active = ioBody.IsActive();
630
if (is_active && mp->GetMotionQuality() == EMotionQuality::LinearCast)
631
--mNumActiveCCDBodies;
632
633
mp->mMotionQuality = inMotionQuality;
634
635
if (is_active && mp->GetMotionQuality() == EMotionQuality::LinearCast)
636
++mNumActiveCCDBodies;
637
}
638
}
639
640
void BodyManager::GetActiveBodies(EBodyType inType, BodyIDVector &outBodyIDs) const
641
{
642
JPH_PROFILE_FUNCTION();
643
644
UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList));
645
646
const BodyID *active_bodies = mActiveBodies[(int)inType];
647
outBodyIDs.assign(active_bodies, active_bodies + mNumActiveBodies[(int)inType].load(memory_order_relaxed));
648
}
649
650
void BodyManager::GetBodyIDs(BodyIDVector &outBodies) const
651
{
652
JPH_PROFILE_FUNCTION();
653
654
UniqueLock lock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList));
655
656
// Reserve space for all bodies
657
outBodies.clear();
658
outBodies.reserve(mNumBodies);
659
660
// Iterate the list and find the bodies that are not null
661
for (const Body *b : mBodies)
662
if (sIsValidBodyPointer(b))
663
outBodies.push_back(b->GetID());
664
665
// Validate that our reservation was correct
666
JPH_ASSERT(outBodies.size() == mNumBodies);
667
}
668
669
void BodyManager::SetBodyActivationListener(BodyActivationListener *inListener)
670
{
671
UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList));
672
673
mActivationListener = inListener;
674
}
675
676
BodyManager::MutexMask BodyManager::GetMutexMask(const BodyID *inBodies, int inNumber) const
677
{
678
JPH_ASSERT(sizeof(MutexMask) * 8 >= mBodyMutexes.GetNumMutexes(), "MutexMask must have enough bits");
679
680
if (inNumber >= (int)mBodyMutexes.GetNumMutexes())
681
{
682
// Just lock everything if there are too many bodies
683
return GetAllBodiesMutexMask();
684
}
685
else
686
{
687
MutexMask mask = 0;
688
for (const BodyID *b = inBodies, *b_end = inBodies + inNumber; b < b_end; ++b)
689
if (!b->IsInvalid())
690
{
691
uint32 index = mBodyMutexes.GetMutexIndex(b->GetIndex());
692
mask |= (MutexMask(1) << index);
693
}
694
return mask;
695
}
696
}
697
698
void BodyManager::LockRead(MutexMask inMutexMask) const
699
{
700
JPH_IF_ENABLE_ASSERTS(PhysicsLock::sCheckLock(this, EPhysicsLockTypes::PerBody));
701
702
int index = 0;
703
for (MutexMask mask = inMutexMask; mask != 0; mask >>= 1, index++)
704
if (mask & 1)
705
mBodyMutexes.GetMutexByIndex(index).lock_shared();
706
}
707
708
void BodyManager::UnlockRead(MutexMask inMutexMask) const
709
{
710
JPH_IF_ENABLE_ASSERTS(PhysicsLock::sCheckUnlock(this, EPhysicsLockTypes::PerBody));
711
712
int index = 0;
713
for (MutexMask mask = inMutexMask; mask != 0; mask >>= 1, index++)
714
if (mask & 1)
715
mBodyMutexes.GetMutexByIndex(index).unlock_shared();
716
}
717
718
void BodyManager::LockWrite(MutexMask inMutexMask) const
719
{
720
JPH_IF_ENABLE_ASSERTS(PhysicsLock::sCheckLock(this, EPhysicsLockTypes::PerBody));
721
722
int index = 0;
723
for (MutexMask mask = inMutexMask; mask != 0; mask >>= 1, index++)
724
if (mask & 1)
725
mBodyMutexes.GetMutexByIndex(index).lock();
726
}
727
728
void BodyManager::UnlockWrite(MutexMask inMutexMask) const
729
{
730
JPH_IF_ENABLE_ASSERTS(PhysicsLock::sCheckUnlock(this, EPhysicsLockTypes::PerBody));
731
732
int index = 0;
733
for (MutexMask mask = inMutexMask; mask != 0; mask >>= 1, index++)
734
if (mask & 1)
735
mBodyMutexes.GetMutexByIndex(index).unlock();
736
}
737
738
void BodyManager::LockAllBodies() const
739
{
740
JPH_IF_ENABLE_ASSERTS(PhysicsLock::sCheckLock(this, EPhysicsLockTypes::PerBody));
741
mBodyMutexes.LockAll();
742
743
PhysicsLock::sLock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList));
744
}
745
746
void BodyManager::UnlockAllBodies() const
747
{
748
PhysicsLock::sUnlock(mBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::BodiesList));
749
750
JPH_IF_ENABLE_ASSERTS(PhysicsLock::sCheckUnlock(this, EPhysicsLockTypes::PerBody));
751
mBodyMutexes.UnlockAll();
752
}
753
754
void BodyManager::SaveState(StateRecorder &inStream, const StateRecorderFilter *inFilter) const
755
{
756
{
757
LockAllBodies();
758
759
// Determine which bodies to save
760
Array<const Body *> bodies;
761
bodies.reserve(mNumBodies);
762
for (const Body *b : mBodies)
763
if (sIsValidBodyPointer(b) && b->IsInBroadPhase() && (inFilter == nullptr || inFilter->ShouldSaveBody(*b)))
764
bodies.push_back(b);
765
766
// Write state of bodies
767
uint32 num_bodies = (uint32)bodies.size();
768
inStream.Write(num_bodies);
769
for (const Body *b : bodies)
770
{
771
inStream.Write(b->GetID());
772
inStream.Write(b->IsActive());
773
b->SaveState(inStream);
774
}
775
776
UnlockAllBodies();
777
}
778
}
779
780
bool BodyManager::RestoreState(StateRecorder &inStream)
781
{
782
BodyIDVector bodies_to_activate, bodies_to_deactivate;
783
784
{
785
LockAllBodies();
786
787
if (inStream.IsValidating())
788
{
789
// Read state of bodies, note this reads it in a way to be consistent with validation
790
uint32 old_num_bodies = 0;
791
for (const Body *b : mBodies)
792
if (sIsValidBodyPointer(b) && b->IsInBroadPhase())
793
++old_num_bodies;
794
uint32 num_bodies = old_num_bodies; // Initialize to current value for validation
795
inStream.Read(num_bodies);
796
if (num_bodies != old_num_bodies)
797
{
798
JPH_ASSERT(false, "Cannot handle adding/removing bodies");
799
UnlockAllBodies();
800
return false;
801
}
802
803
for (Body *b : mBodies)
804
if (sIsValidBodyPointer(b) && b->IsInBroadPhase())
805
{
806
BodyID body_id = b->GetID(); // Initialize to current value for validation
807
inStream.Read(body_id);
808
if (body_id != b->GetID())
809
{
810
JPH_ASSERT(false, "Cannot handle adding/removing bodies");
811
UnlockAllBodies();
812
return false;
813
}
814
bool is_active = b->IsActive(); // Initialize to current value for validation
815
inStream.Read(is_active);
816
if (is_active != b->IsActive())
817
{
818
if (is_active)
819
bodies_to_activate.push_back(body_id);
820
else
821
bodies_to_deactivate.push_back(body_id);
822
}
823
b->RestoreState(inStream);
824
}
825
}
826
else
827
{
828
// Not validating, we can be a bit more loose, read number of bodies
829
uint32 num_bodies = 0;
830
inStream.Read(num_bodies);
831
832
// Iterate over the stored bodies and restore their state
833
for (uint32 idx = 0; idx < num_bodies; ++idx)
834
{
835
BodyID body_id;
836
inStream.Read(body_id);
837
Body *b = TryGetBody(body_id);
838
if (b == nullptr)
839
{
840
JPH_ASSERT(false, "Restoring state for non-existing body");
841
UnlockAllBodies();
842
return false;
843
}
844
bool is_active;
845
inStream.Read(is_active);
846
if (is_active != b->IsActive())
847
{
848
if (is_active)
849
bodies_to_activate.push_back(body_id);
850
else
851
bodies_to_deactivate.push_back(body_id);
852
}
853
b->RestoreState(inStream);
854
}
855
}
856
857
UnlockAllBodies();
858
}
859
860
{
861
UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList));
862
863
for (BodyID body_id : bodies_to_activate)
864
{
865
Body *body = TryGetBody(body_id);
866
AddBodyToActiveBodies(*body);
867
}
868
869
for (BodyID body_id : bodies_to_deactivate)
870
{
871
Body *body = TryGetBody(body_id);
872
RemoveBodyFromActiveBodies(*body);
873
}
874
}
875
876
return true;
877
}
878
879
void BodyManager::SaveBodyState(const Body &inBody, StateRecorder &inStream) const
880
{
881
inStream.Write(inBody.IsActive());
882
883
inBody.SaveState(inStream);
884
}
885
886
void BodyManager::RestoreBodyState(Body &ioBody, StateRecorder &inStream)
887
{
888
bool is_active = ioBody.IsActive();
889
inStream.Read(is_active);
890
891
ioBody.RestoreState(inStream);
892
893
if (is_active != ioBody.IsActive())
894
{
895
UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList));
896
897
JPH_ASSERT(!mActiveBodiesLocked || sOverrideAllowActivation);
898
899
if (is_active)
900
AddBodyToActiveBodies(ioBody);
901
else
902
RemoveBodyFromActiveBodies(ioBody);
903
}
904
}
905
906
#ifdef JPH_DEBUG_RENDERER
907
void BodyManager::Draw(const DrawSettings &inDrawSettings, const PhysicsSettings &inPhysicsSettings, DebugRenderer *inRenderer, const BodyDrawFilter *inBodyFilter)
908
{
909
JPH_PROFILE_FUNCTION();
910
911
LockAllBodies();
912
913
for (const Body *body : mBodies)
914
if (sIsValidBodyPointer(body) && body->IsInBroadPhase() && (!inBodyFilter || inBodyFilter->ShouldDraw(*body)))
915
{
916
JPH_ASSERT(mBodies[body->GetID().GetIndex()] == body);
917
918
bool is_sensor = body->IsSensor();
919
920
// Determine drawing mode
921
Color color;
922
if (is_sensor)
923
color = Color::sYellow;
924
else
925
switch (inDrawSettings.mDrawShapeColor)
926
{
927
case EShapeColor::InstanceColor:
928
// Each instance has own color
929
color = Color::sGetDistinctColor(body->mID.GetIndex());
930
break;
931
932
case EShapeColor::ShapeTypeColor:
933
color = ShapeFunctions::sGet(body->GetShape()->GetSubType()).mColor;
934
break;
935
936
case EShapeColor::MotionTypeColor:
937
// Determine color based on motion type
938
switch (body->mMotionType)
939
{
940
case EMotionType::Static:
941
color = Color::sGrey;
942
break;
943
944
case EMotionType::Kinematic:
945
color = Color::sGreen;
946
break;
947
948
case EMotionType::Dynamic:
949
color = Color::sGetDistinctColor(body->mID.GetIndex());
950
break;
951
952
default:
953
JPH_ASSERT(false);
954
color = Color::sBlack;
955
break;
956
}
957
break;
958
959
case EShapeColor::SleepColor:
960
// Determine color based on motion type
961
switch (body->mMotionType)
962
{
963
case EMotionType::Static:
964
color = Color::sGrey;
965
break;
966
967
case EMotionType::Kinematic:
968
color = body->IsActive()? Color::sGreen : Color::sRed;
969
break;
970
971
case EMotionType::Dynamic:
972
color = body->IsActive()? Color::sYellow : Color::sRed;
973
break;
974
975
default:
976
JPH_ASSERT(false);
977
color = Color::sBlack;
978
break;
979
}
980
break;
981
982
case EShapeColor::IslandColor:
983
// Determine color based on motion type
984
switch (body->mMotionType)
985
{
986
case EMotionType::Static:
987
color = Color::sGrey;
988
break;
989
990
case EMotionType::Kinematic:
991
case EMotionType::Dynamic:
992
{
993
uint32 idx = body->GetMotionProperties()->GetIslandIndexInternal();
994
color = idx != Body::cInactiveIndex? Color::sGetDistinctColor(idx) : Color::sLightGrey;
995
}
996
break;
997
998
default:
999
JPH_ASSERT(false);
1000
color = Color::sBlack;
1001
break;
1002
}
1003
break;
1004
1005
case EShapeColor::MaterialColor:
1006
color = Color::sWhite;
1007
break;
1008
1009
default:
1010
JPH_ASSERT(false);
1011
color = Color::sBlack;
1012
break;
1013
}
1014
1015
// Draw the results of GetSupportFunction
1016
if (inDrawSettings.mDrawGetSupportFunction)
1017
body->mShape->DrawGetSupportFunction(inRenderer, body->GetCenterOfMassTransform(), Vec3::sOne(), color, inDrawSettings.mDrawSupportDirection);
1018
1019
// Draw the results of GetSupportingFace
1020
if (inDrawSettings.mDrawGetSupportingFace)
1021
body->mShape->DrawGetSupportingFace(inRenderer, body->GetCenterOfMassTransform(), Vec3::sOne());
1022
1023
// Draw the shape
1024
if (inDrawSettings.mDrawShape)
1025
body->mShape->Draw(inRenderer, body->GetCenterOfMassTransform(), Vec3::sOne(), color, inDrawSettings.mDrawShapeColor == EShapeColor::MaterialColor, inDrawSettings.mDrawShapeWireframe || is_sensor);
1026
1027
// Draw bounding box
1028
if (inDrawSettings.mDrawBoundingBox)
1029
inRenderer->DrawWireBox(body->mBounds, color);
1030
1031
// Draw center of mass transform
1032
if (inDrawSettings.mDrawCenterOfMassTransform)
1033
inRenderer->DrawCoordinateSystem(body->GetCenterOfMassTransform(), 0.2f);
1034
1035
// Draw world transform
1036
if (inDrawSettings.mDrawWorldTransform)
1037
inRenderer->DrawCoordinateSystem(body->GetWorldTransform(), 0.2f);
1038
1039
// Draw world space linear and angular velocity
1040
if (inDrawSettings.mDrawVelocity)
1041
{
1042
RVec3 pos = body->GetCenterOfMassPosition();
1043
inRenderer->DrawArrow(pos, pos + body->GetLinearVelocity(), Color::sGreen, 0.1f);
1044
inRenderer->DrawArrow(pos, pos + body->GetAngularVelocity(), Color::sRed, 0.1f);
1045
}
1046
1047
if (inDrawSettings.mDrawMassAndInertia && body->IsDynamic())
1048
{
1049
const MotionProperties *mp = body->GetMotionProperties();
1050
if (mp->GetInverseMass() > 0.0f
1051
&& !Vec3::sEquals(mp->GetInverseInertiaDiagonal(), Vec3::sZero()).TestAnyXYZTrue())
1052
{
1053
// Invert mass again
1054
float mass = 1.0f / mp->GetInverseMass();
1055
1056
// Invert diagonal again
1057
Vec3 diagonal = mp->GetInverseInertiaDiagonal().Reciprocal();
1058
1059
// Determine how big of a box has the equivalent inertia
1060
Vec3 box_size = MassProperties::sGetEquivalentSolidBoxSize(mass, diagonal);
1061
1062
// Draw box with equivalent inertia
1063
inRenderer->DrawWireBox(body->GetCenterOfMassTransform() * Mat44::sRotation(mp->GetInertiaRotation()), AABox(-0.5f * box_size, 0.5f * box_size), Color::sOrange);
1064
1065
// Draw mass
1066
inRenderer->DrawText3D(body->GetCenterOfMassPosition(), StringFormat("%.2f", (double)mass), Color::sOrange, 0.2f);
1067
}
1068
}
1069
1070
if (inDrawSettings.mDrawSleepStats && body->IsDynamic() && body->IsActive())
1071
{
1072
// Draw stats to know which bodies could go to sleep
1073
String text = StringFormat("t: %.1f", (double)body->mMotionProperties->mSleepTestTimer);
1074
uint8 g = uint8(Clamp(255.0f * body->mMotionProperties->mSleepTestTimer / inPhysicsSettings.mTimeBeforeSleep, 0.0f, 255.0f));
1075
Color sleep_color = Color(0, 255 - g, g);
1076
inRenderer->DrawText3D(body->GetCenterOfMassPosition(), text, sleep_color, 0.2f);
1077
for (int i = 0; i < 3; ++i)
1078
inRenderer->DrawWireSphere(JPH_IF_DOUBLE_PRECISION(body->mMotionProperties->GetSleepTestOffset() +) body->mMotionProperties->mSleepTestSpheres[i].GetCenter(), body->mMotionProperties->mSleepTestSpheres[i].GetRadius(), sleep_color);
1079
}
1080
1081
if (body->IsSoftBody())
1082
{
1083
const SoftBodyMotionProperties *mp = static_cast<const SoftBodyMotionProperties *>(body->GetMotionProperties());
1084
RMat44 com = body->GetCenterOfMassTransform();
1085
1086
if (inDrawSettings.mDrawSoftBodyVertices)
1087
mp->DrawVertices(inRenderer, com);
1088
1089
if (inDrawSettings.mDrawSoftBodyVertexVelocities)
1090
mp->DrawVertexVelocities(inRenderer, com);
1091
1092
if (inDrawSettings.mDrawSoftBodyEdgeConstraints)
1093
mp->DrawEdgeConstraints(inRenderer, com, inDrawSettings.mDrawSoftBodyConstraintColor);
1094
1095
if (inDrawSettings.mDrawSoftBodyBendConstraints)
1096
mp->DrawBendConstraints(inRenderer, com, inDrawSettings.mDrawSoftBodyConstraintColor);
1097
1098
if (inDrawSettings.mDrawSoftBodyVolumeConstraints)
1099
mp->DrawVolumeConstraints(inRenderer, com, inDrawSettings.mDrawSoftBodyConstraintColor);
1100
1101
if (inDrawSettings.mDrawSoftBodySkinConstraints)
1102
mp->DrawSkinConstraints(inRenderer, com, inDrawSettings.mDrawSoftBodyConstraintColor);
1103
1104
if (inDrawSettings.mDrawSoftBodyLRAConstraints)
1105
mp->DrawLRAConstraints(inRenderer, com, inDrawSettings.mDrawSoftBodyConstraintColor);
1106
1107
if (inDrawSettings.mDrawSoftBodyPredictedBounds)
1108
mp->DrawPredictedBounds(inRenderer, com);
1109
}
1110
}
1111
1112
UnlockAllBodies();
1113
}
1114
#endif // JPH_DEBUG_RENDERER
1115
1116
void BodyManager::InvalidateContactCacheForBody(Body &ioBody)
1117
{
1118
// 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 update
1119
if (ioBody.InvalidateContactCacheInternal())
1120
{
1121
lock_guard lock(mBodiesCacheInvalidMutex);
1122
mBodiesCacheInvalid.push_back(ioBody.GetID());
1123
}
1124
}
1125
1126
void BodyManager::ValidateContactCacheForAllBodies()
1127
{
1128
lock_guard lock(mBodiesCacheInvalidMutex);
1129
1130
for (const BodyID &b : mBodiesCacheInvalid)
1131
{
1132
// The body may have been removed between the call to InvalidateContactCacheForBody and this call, so check if it still exists
1133
Body *body = TryGetBody(b);
1134
if (body != nullptr)
1135
body->ValidateContactCacheInternal();
1136
}
1137
mBodiesCacheInvalid.clear();
1138
}
1139
1140
#ifdef JPH_DEBUG
1141
void BodyManager::ValidateActiveBodyBounds()
1142
{
1143
UniqueLock lock(mActiveBodiesMutex JPH_IF_ENABLE_ASSERTS(, this, EPhysicsLockTypes::ActiveBodiesList));
1144
1145
for (uint type = 0; type < cBodyTypeCount; ++type)
1146
for (BodyID *id = mActiveBodies[type], *id_end = mActiveBodies[type] + mNumActiveBodies[type].load(memory_order_relaxed); id < id_end; ++id)
1147
{
1148
const Body *body = mBodies[id->GetIndex()];
1149
body->ValidateCachedBounds();
1150
}
1151
}
1152
#endif // JPH_DEBUG
1153
1154
JPH_NAMESPACE_END
1155
1156