Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Constraints/ContactConstraintManager.cpp
9913 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/Constraints/ContactConstraintManager.h>
8
#include <Jolt/Physics/Constraints/CalculateSolverSteps.h>
9
#include <Jolt/Physics/Body/Body.h>
10
#include <Jolt/Physics/PhysicsUpdateContext.h>
11
#include <Jolt/Physics/PhysicsSettings.h>
12
#include <Jolt/Physics/PhysicsSystem.h>
13
#include <Jolt/Physics/IslandBuilder.h>
14
#include <Jolt/Physics/DeterminismLog.h>
15
#include <Jolt/Core/TempAllocator.h>
16
#include <Jolt/Core/QuickSort.h>
17
#ifdef JPH_DEBUG_RENDERER
18
#include <Jolt/Renderer/DebugRenderer.h>
19
#endif // JPH_DEBUG_RENDERER
20
21
JPH_NAMESPACE_BEGIN
22
23
using namespace literals;
24
25
#ifdef JPH_DEBUG_RENDERER
26
bool ContactConstraintManager::sDrawContactPoint = false;
27
bool ContactConstraintManager::sDrawSupportingFaces = false;
28
bool ContactConstraintManager::sDrawContactPointReduction = false;
29
bool ContactConstraintManager::sDrawContactManifolds = false;
30
#endif // JPH_DEBUG_RENDERER
31
32
//#define JPH_MANIFOLD_CACHE_DEBUG
33
34
////////////////////////////////////////////////////////////////////////////////////////////////////////
35
// ContactConstraintManager::WorldContactPoint
36
////////////////////////////////////////////////////////////////////////////////////////////////////////
37
38
void ContactConstraintManager::WorldContactPoint::CalculateNonPenetrationConstraintProperties(const Body &inBody1, float inInvMass1, float inInvInertiaScale1, const Body &inBody2, float inInvMass2, float inInvInertiaScale2, RVec3Arg inWorldSpacePosition1, RVec3Arg inWorldSpacePosition2, Vec3Arg inWorldSpaceNormal)
39
{
40
// Calculate collision points relative to body
41
RVec3 p = 0.5_r * (inWorldSpacePosition1 + inWorldSpacePosition2);
42
Vec3 r1 = Vec3(p - inBody1.GetCenterOfMassPosition());
43
Vec3 r2 = Vec3(p - inBody2.GetCenterOfMassPosition());
44
45
mNonPenetrationConstraint.CalculateConstraintPropertiesWithMassOverride(inBody1, inInvMass1, inInvInertiaScale1, r1, inBody2, inInvMass2, inInvInertiaScale2, r2, inWorldSpaceNormal);
46
}
47
48
template <EMotionType Type1, EMotionType Type2>
49
JPH_INLINE void ContactConstraintManager::WorldContactPoint::TemplatedCalculateFrictionAndNonPenetrationConstraintProperties(float inDeltaTime, float inGravityDeltaTimeDotNormal, const Body &inBody1, const Body &inBody2, float inInvM1, float inInvM2, Mat44Arg inInvI1, Mat44Arg inInvI2, RVec3Arg inWorldSpacePosition1, RVec3Arg inWorldSpacePosition2, Vec3Arg inWorldSpaceNormal, Vec3Arg inWorldSpaceTangent1, Vec3Arg inWorldSpaceTangent2, const ContactSettings &inSettings, float inMinVelocityForRestitution)
50
{
51
JPH_DET_LOG("TemplatedCalculateFrictionAndNonPenetrationConstraintProperties: p1: " << inWorldSpacePosition1 << " p2: " << inWorldSpacePosition2
52
<< " normal: " << inWorldSpaceNormal << " tangent1: " << inWorldSpaceTangent1 << " tangent2: " << inWorldSpaceTangent2
53
<< " restitution: " << inSettings.mCombinedRestitution << " friction: " << inSettings.mCombinedFriction << " minv: " << inMinVelocityForRestitution
54
<< " surface_vel: " << inSettings.mRelativeLinearSurfaceVelocity << " surface_ang: " << inSettings.mRelativeAngularSurfaceVelocity);
55
56
// Calculate collision points relative to body
57
RVec3 p = 0.5_r * (inWorldSpacePosition1 + inWorldSpacePosition2);
58
Vec3 r1 = Vec3(p - inBody1.GetCenterOfMassPosition());
59
Vec3 r2 = Vec3(p - inBody2.GetCenterOfMassPosition());
60
61
// The gravity is applied in the beginning of the time step. If we get here, there was a collision
62
// at the beginning of the time step, so we've applied too much gravity. This means that our
63
// calculated restitution can be too high, so when we apply restitution, we cancel the added
64
// velocity due to gravity.
65
float gravity_dt_dot_normal;
66
67
// Calculate velocity of collision points
68
Vec3 relative_velocity;
69
if constexpr (Type1 != EMotionType::Static && Type2 != EMotionType::Static)
70
{
71
const MotionProperties *mp1 = inBody1.GetMotionPropertiesUnchecked();
72
const MotionProperties *mp2 = inBody2.GetMotionPropertiesUnchecked();
73
relative_velocity = mp2->GetPointVelocityCOM(r2) - mp1->GetPointVelocityCOM(r1);
74
gravity_dt_dot_normal = inGravityDeltaTimeDotNormal * (mp2->GetGravityFactor() - mp1->GetGravityFactor());
75
}
76
else if constexpr (Type1 != EMotionType::Static)
77
{
78
const MotionProperties *mp1 = inBody1.GetMotionPropertiesUnchecked();
79
relative_velocity = -mp1->GetPointVelocityCOM(r1);
80
gravity_dt_dot_normal = inGravityDeltaTimeDotNormal * mp1->GetGravityFactor();
81
}
82
else if constexpr (Type2 != EMotionType::Static)
83
{
84
const MotionProperties *mp2 = inBody2.GetMotionPropertiesUnchecked();
85
relative_velocity = mp2->GetPointVelocityCOM(r2);
86
gravity_dt_dot_normal = inGravityDeltaTimeDotNormal * mp2->GetGravityFactor();
87
}
88
else
89
{
90
JPH_ASSERT(false); // Static vs static makes no sense
91
relative_velocity = Vec3::sZero();
92
gravity_dt_dot_normal = 0.0f;
93
}
94
float normal_velocity = relative_velocity.Dot(inWorldSpaceNormal);
95
96
// How much the shapes are penetrating (> 0 if penetrating, < 0 if separated)
97
float penetration = Vec3(inWorldSpacePosition1 - inWorldSpacePosition2).Dot(inWorldSpaceNormal);
98
99
// If there is no penetration, this is a speculative contact and we will apply a bias to the contact constraint
100
// so that the constraint becomes relative_velocity . contact normal > -penetration / delta_time
101
// instead of relative_velocity . contact normal > 0
102
// See: GDC 2013: "Physics for Game Programmers; Continuous Collision" - Erin Catto
103
float speculative_contact_velocity_bias = max(0.0f, -penetration / inDeltaTime);
104
105
// Determine if the velocity is big enough for restitution
106
float normal_velocity_bias;
107
if (inSettings.mCombinedRestitution > 0.0f && normal_velocity < -inMinVelocityForRestitution)
108
{
109
// We have a velocity that is big enough for restitution. This is where speculative contacts don't work
110
// great as we have to decide now if we're going to apply the restitution or not. If the relative
111
// velocity is big enough for a hit, we apply the restitution (in the end, due to other constraints,
112
// the objects may actually not collide and we will have applied restitution incorrectly). Another
113
// artifact that occurs because of this approximation is that the object will bounce from its current
114
// position rather than from a position where it is touching the other object. This causes the object
115
// to appear to move faster for 1 frame (the opposite of time stealing).
116
if (normal_velocity < -speculative_contact_velocity_bias)
117
normal_velocity_bias = inSettings.mCombinedRestitution * (normal_velocity - gravity_dt_dot_normal);
118
else
119
// In this case we have predicted that we don't hit the other object, but if we do (due to other constraints changing velocities)
120
// the speculative contact will prevent penetration but will not apply restitution leading to another artifact.
121
normal_velocity_bias = speculative_contact_velocity_bias;
122
}
123
else
124
{
125
// No restitution. We can safely apply our contact velocity bias.
126
normal_velocity_bias = speculative_contact_velocity_bias;
127
}
128
129
mNonPenetrationConstraint.TemplatedCalculateConstraintProperties<Type1, Type2>(inInvM1, inInvI1, r1, inInvM2, inInvI2, r2, inWorldSpaceNormal, normal_velocity_bias);
130
131
// Calculate friction part
132
if (inSettings.mCombinedFriction > 0.0f)
133
{
134
// Get surface velocity relative to tangents
135
Vec3 ws_surface_velocity = inSettings.mRelativeLinearSurfaceVelocity + inSettings.mRelativeAngularSurfaceVelocity.Cross(r1);
136
float surface_velocity1 = inWorldSpaceTangent1.Dot(ws_surface_velocity);
137
float surface_velocity2 = inWorldSpaceTangent2.Dot(ws_surface_velocity);
138
139
// Implement friction as 2 AxisConstraintParts
140
mFrictionConstraint1.TemplatedCalculateConstraintProperties<Type1, Type2>(inInvM1, inInvI1, r1, inInvM2, inInvI2, r2, inWorldSpaceTangent1, surface_velocity1);
141
mFrictionConstraint2.TemplatedCalculateConstraintProperties<Type1, Type2>(inInvM1, inInvI1, r1, inInvM2, inInvI2, r2, inWorldSpaceTangent2, surface_velocity2);
142
}
143
else
144
{
145
// Turn off friction constraint
146
mFrictionConstraint1.Deactivate();
147
mFrictionConstraint2.Deactivate();
148
}
149
}
150
151
////////////////////////////////////////////////////////////////////////////////////////////////////////
152
// ContactConstraintManager::ContactConstraint
153
////////////////////////////////////////////////////////////////////////////////////////////////////////
154
155
#ifdef JPH_DEBUG_RENDERER
156
void ContactConstraintManager::ContactConstraint::Draw(DebugRenderer *inRenderer, ColorArg inManifoldColor) const
157
{
158
if (mContactPoints.empty())
159
return;
160
161
// Get body transforms
162
RMat44 transform_body1 = mBody1->GetCenterOfMassTransform();
163
RMat44 transform_body2 = mBody2->GetCenterOfMassTransform();
164
165
RVec3 prev_point = transform_body1 * Vec3::sLoadFloat3Unsafe(mContactPoints.back().mContactPoint->mPosition1);
166
for (const WorldContactPoint &wcp : mContactPoints)
167
{
168
// Test if any lambda from the previous frame was transferred
169
float radius = wcp.mNonPenetrationConstraint.GetTotalLambda() == 0.0f
170
&& wcp.mFrictionConstraint1.GetTotalLambda() == 0.0f
171
&& wcp.mFrictionConstraint2.GetTotalLambda() == 0.0f? 0.1f : 0.2f;
172
173
RVec3 next_point = transform_body1 * Vec3::sLoadFloat3Unsafe(wcp.mContactPoint->mPosition1);
174
inRenderer->DrawMarker(next_point, Color::sCyan, radius);
175
inRenderer->DrawMarker(transform_body2 * Vec3::sLoadFloat3Unsafe(wcp.mContactPoint->mPosition2), Color::sPurple, radius);
176
177
// Draw edge
178
inRenderer->DrawArrow(prev_point, next_point, inManifoldColor, 0.05f);
179
prev_point = next_point;
180
}
181
182
// Draw normal
183
RVec3 wp = transform_body1 * Vec3::sLoadFloat3Unsafe(mContactPoints[0].mContactPoint->mPosition1);
184
inRenderer->DrawArrow(wp, wp + GetWorldSpaceNormal(), Color::sRed, 0.05f);
185
186
// Get tangents
187
Vec3 t1, t2;
188
GetTangents(t1, t2);
189
190
// Draw tangents
191
inRenderer->DrawLine(wp, wp + t1, Color::sGreen);
192
inRenderer->DrawLine(wp, wp + t2, Color::sBlue);
193
}
194
#endif // JPH_DEBUG_RENDERER
195
196
////////////////////////////////////////////////////////////////////////////////////////////////////////
197
// ContactConstraintManager::CachedContactPoint
198
////////////////////////////////////////////////////////////////////////////////////////////////////////
199
200
void ContactConstraintManager::CachedContactPoint::SaveState(StateRecorder &inStream) const
201
{
202
inStream.Write(mPosition1);
203
inStream.Write(mPosition2);
204
inStream.Write(mNonPenetrationLambda);
205
inStream.Write(mFrictionLambda);
206
}
207
208
void ContactConstraintManager::CachedContactPoint::RestoreState(StateRecorder &inStream)
209
{
210
inStream.Read(mPosition1);
211
inStream.Read(mPosition2);
212
inStream.Read(mNonPenetrationLambda);
213
inStream.Read(mFrictionLambda);
214
}
215
216
////////////////////////////////////////////////////////////////////////////////////////////////////////
217
// ContactConstraintManager::CachedManifold
218
////////////////////////////////////////////////////////////////////////////////////////////////////////
219
220
void ContactConstraintManager::CachedManifold::SaveState(StateRecorder &inStream) const
221
{
222
inStream.Write(mContactNormal);
223
}
224
225
void ContactConstraintManager::CachedManifold::RestoreState(StateRecorder &inStream)
226
{
227
inStream.Read(mContactNormal);
228
}
229
230
////////////////////////////////////////////////////////////////////////////////////////////////////////
231
// ContactConstraintManager::CachedBodyPair
232
////////////////////////////////////////////////////////////////////////////////////////////////////////
233
234
void ContactConstraintManager::CachedBodyPair::SaveState(StateRecorder &inStream) const
235
{
236
inStream.Write(mDeltaPosition);
237
inStream.Write(mDeltaRotation);
238
}
239
240
void ContactConstraintManager::CachedBodyPair::RestoreState(StateRecorder &inStream)
241
{
242
inStream.Read(mDeltaPosition);
243
inStream.Read(mDeltaRotation);
244
}
245
246
////////////////////////////////////////////////////////////////////////////////////////////////////////
247
// ContactConstraintManager::ManifoldCache
248
////////////////////////////////////////////////////////////////////////////////////////////////////////
249
250
void ContactConstraintManager::ManifoldCache::Init(uint inMaxBodyPairs, uint inMaxContactConstraints, uint inCachedManifoldsSize)
251
{
252
uint max_body_pairs = min(inMaxBodyPairs, cMaxBodyPairsLimit);
253
JPH_ASSERT(max_body_pairs == inMaxBodyPairs, "Cannot support this many body pairs!");
254
JPH_ASSERT(inMaxContactConstraints <= cMaxContactConstraintsLimit); // Should have been enforced by caller
255
256
mAllocator.Init(uint(min(uint64(max_body_pairs) * sizeof(BodyPairMap::KeyValue) + inCachedManifoldsSize, uint64(~uint(0)))));
257
258
mCachedManifolds.Init(GetNextPowerOf2(inMaxContactConstraints));
259
mCachedBodyPairs.Init(GetNextPowerOf2(max_body_pairs));
260
}
261
262
void ContactConstraintManager::ManifoldCache::Clear()
263
{
264
JPH_PROFILE_FUNCTION();
265
266
mCachedManifolds.Clear();
267
mCachedBodyPairs.Clear();
268
mAllocator.Clear();
269
270
#ifdef JPH_ENABLE_ASSERTS
271
// Mark as incomplete
272
mIsFinalized = false;
273
#endif
274
}
275
276
void ContactConstraintManager::ManifoldCache::Prepare(uint inExpectedNumBodyPairs, uint inExpectedNumManifolds)
277
{
278
// Minimum amount of buckets to use in the hash map
279
constexpr uint32 cMinBuckets = 1024;
280
281
// Use the next higher power of 2 of amount of objects in the cache from last frame to determine the amount of buckets in this frame
282
mCachedManifolds.SetNumBuckets(min(max(cMinBuckets, GetNextPowerOf2(inExpectedNumManifolds)), mCachedManifolds.GetMaxBuckets()));
283
mCachedBodyPairs.SetNumBuckets(min(max(cMinBuckets, GetNextPowerOf2(inExpectedNumBodyPairs)), mCachedBodyPairs.GetMaxBuckets()));
284
}
285
286
const ContactConstraintManager::MKeyValue *ContactConstraintManager::ManifoldCache::Find(const SubShapeIDPair &inKey, uint64 inKeyHash) const
287
{
288
JPH_ASSERT(mIsFinalized);
289
return mCachedManifolds.Find(inKey, inKeyHash);
290
}
291
292
ContactConstraintManager::MKeyValue *ContactConstraintManager::ManifoldCache::Create(ContactAllocator &ioContactAllocator, const SubShapeIDPair &inKey, uint64 inKeyHash, int inNumContactPoints)
293
{
294
JPH_ASSERT(!mIsFinalized);
295
MKeyValue *kv = mCachedManifolds.Create(ioContactAllocator, inKey, inKeyHash, CachedManifold::sGetRequiredExtraSize(inNumContactPoints));
296
if (kv == nullptr)
297
{
298
ioContactAllocator.mErrors |= EPhysicsUpdateError::ManifoldCacheFull;
299
return nullptr;
300
}
301
kv->GetValue().mNumContactPoints = uint16(inNumContactPoints);
302
++ioContactAllocator.mNumManifolds;
303
return kv;
304
}
305
306
ContactConstraintManager::MKVAndCreated ContactConstraintManager::ManifoldCache::FindOrCreate(ContactAllocator &ioContactAllocator, const SubShapeIDPair &inKey, uint64 inKeyHash, int inNumContactPoints)
307
{
308
MKeyValue *kv = const_cast<MKeyValue *>(mCachedManifolds.Find(inKey, inKeyHash));
309
if (kv != nullptr)
310
return { kv, false };
311
312
return { Create(ioContactAllocator, inKey, inKeyHash, inNumContactPoints), true };
313
}
314
315
uint32 ContactConstraintManager::ManifoldCache::ToHandle(const MKeyValue *inKeyValue) const
316
{
317
JPH_ASSERT(!mIsFinalized);
318
return mCachedManifolds.ToHandle(inKeyValue);
319
}
320
321
const ContactConstraintManager::MKeyValue *ContactConstraintManager::ManifoldCache::FromHandle(uint32 inHandle) const
322
{
323
JPH_ASSERT(mIsFinalized);
324
return mCachedManifolds.FromHandle(inHandle);
325
}
326
327
const ContactConstraintManager::BPKeyValue *ContactConstraintManager::ManifoldCache::Find(const BodyPair &inKey, uint64 inKeyHash) const
328
{
329
JPH_ASSERT(mIsFinalized);
330
return mCachedBodyPairs.Find(inKey, inKeyHash);
331
}
332
333
ContactConstraintManager::BPKeyValue *ContactConstraintManager::ManifoldCache::Create(ContactAllocator &ioContactAllocator, const BodyPair &inKey, uint64 inKeyHash)
334
{
335
JPH_ASSERT(!mIsFinalized);
336
BPKeyValue *kv = mCachedBodyPairs.Create(ioContactAllocator, inKey, inKeyHash, 0);
337
if (kv == nullptr)
338
{
339
ioContactAllocator.mErrors |= EPhysicsUpdateError::BodyPairCacheFull;
340
return nullptr;
341
}
342
++ioContactAllocator.mNumBodyPairs;
343
return kv;
344
}
345
346
void ContactConstraintManager::ManifoldCache::GetAllBodyPairsSorted(Array<const BPKeyValue *> &outAll) const
347
{
348
JPH_ASSERT(mIsFinalized);
349
mCachedBodyPairs.GetAllKeyValues(outAll);
350
351
// Sort by key
352
QuickSort(outAll.begin(), outAll.end(), [](const BPKeyValue *inLHS, const BPKeyValue *inRHS) {
353
return inLHS->GetKey() < inRHS->GetKey();
354
});
355
}
356
357
void ContactConstraintManager::ManifoldCache::GetAllManifoldsSorted(const CachedBodyPair &inBodyPair, Array<const MKeyValue *> &outAll) const
358
{
359
JPH_ASSERT(mIsFinalized);
360
361
// Iterate through the attached manifolds
362
for (uint32 handle = inBodyPair.mFirstCachedManifold; handle != ManifoldMap::cInvalidHandle; handle = FromHandle(handle)->GetValue().mNextWithSameBodyPair)
363
{
364
const MKeyValue *kv = mCachedManifolds.FromHandle(handle);
365
outAll.push_back(kv);
366
}
367
368
// Sort by key
369
QuickSort(outAll.begin(), outAll.end(), [](const MKeyValue *inLHS, const MKeyValue *inRHS) {
370
return inLHS->GetKey() < inRHS->GetKey();
371
});
372
}
373
374
void ContactConstraintManager::ManifoldCache::GetAllCCDManifoldsSorted(Array<const MKeyValue *> &outAll) const
375
{
376
mCachedManifolds.GetAllKeyValues(outAll);
377
378
for (int i = (int)outAll.size() - 1; i >= 0; --i)
379
if ((outAll[i]->GetValue().mFlags & (uint16)CachedManifold::EFlags::CCDContact) == 0)
380
{
381
outAll[i] = outAll.back();
382
outAll.pop_back();
383
}
384
385
// Sort by key
386
QuickSort(outAll.begin(), outAll.end(), [](const MKeyValue *inLHS, const MKeyValue *inRHS) {
387
return inLHS->GetKey() < inRHS->GetKey();
388
});
389
}
390
391
void ContactConstraintManager::ManifoldCache::ContactPointRemovedCallbacks(ContactListener *inListener)
392
{
393
JPH_PROFILE_FUNCTION();
394
395
for (MKeyValue &kv : mCachedManifolds)
396
if ((kv.GetValue().mFlags & uint16(CachedManifold::EFlags::ContactPersisted)) == 0)
397
inListener->OnContactRemoved(kv.GetKey());
398
}
399
400
#ifdef JPH_ENABLE_ASSERTS
401
402
void ContactConstraintManager::ManifoldCache::Finalize()
403
{
404
mIsFinalized = true;
405
406
#ifdef JPH_MANIFOLD_CACHE_DEBUG
407
Trace("ManifoldMap:");
408
mCachedManifolds.TraceStats();
409
Trace("BodyPairMap:");
410
mCachedBodyPairs.TraceStats();
411
#endif // JPH_MANIFOLD_CACHE_DEBUG
412
}
413
414
#endif
415
416
void ContactConstraintManager::ManifoldCache::SaveState(StateRecorder &inStream, const StateRecorderFilter *inFilter) const
417
{
418
JPH_ASSERT(mIsFinalized);
419
420
// Get contents of cache
421
Array<const BPKeyValue *> all_bp;
422
GetAllBodyPairsSorted(all_bp);
423
424
// Determine which ones to save
425
Array<const BPKeyValue *> selected_bp;
426
if (inFilter == nullptr)
427
selected_bp = std::move(all_bp);
428
else
429
{
430
selected_bp.reserve(all_bp.size());
431
for (const BPKeyValue *bp_kv : all_bp)
432
if (inFilter->ShouldSaveContact(bp_kv->GetKey().mBodyA, bp_kv->GetKey().mBodyB))
433
selected_bp.push_back(bp_kv);
434
}
435
436
// Write body pairs
437
uint32 num_body_pairs = uint32(selected_bp.size());
438
inStream.Write(num_body_pairs);
439
for (const BPKeyValue *bp_kv : selected_bp)
440
{
441
// Write body pair key
442
inStream.Write(bp_kv->GetKey());
443
444
// Write body pair
445
const CachedBodyPair &bp = bp_kv->GetValue();
446
bp.SaveState(inStream);
447
448
// Get attached manifolds
449
Array<const MKeyValue *> all_m;
450
GetAllManifoldsSorted(bp, all_m);
451
452
// Write num manifolds
453
uint32 num_manifolds = uint32(all_m.size());
454
inStream.Write(num_manifolds);
455
456
// Write all manifolds
457
for (const MKeyValue *m_kv : all_m)
458
{
459
// Write key
460
inStream.Write(m_kv->GetKey());
461
const CachedManifold &cm = m_kv->GetValue();
462
JPH_ASSERT((cm.mFlags & (uint16)CachedManifold::EFlags::CCDContact) == 0);
463
464
// Write amount of contacts
465
inStream.Write(cm.mNumContactPoints);
466
467
// Write manifold
468
cm.SaveState(inStream);
469
470
// Write contact points
471
for (uint32 i = 0; i < cm.mNumContactPoints; ++i)
472
cm.mContactPoints[i].SaveState(inStream);
473
}
474
}
475
476
// Get CCD manifolds
477
Array<const MKeyValue *> all_m;
478
GetAllCCDManifoldsSorted(all_m);
479
480
// Determine which ones to save
481
Array<const MKeyValue *> selected_m;
482
if (inFilter == nullptr)
483
selected_m = std::move(all_m);
484
else
485
{
486
selected_m.reserve(all_m.size());
487
for (const MKeyValue *m_kv : all_m)
488
if (inFilter->ShouldSaveContact(m_kv->GetKey().GetBody1ID(), m_kv->GetKey().GetBody2ID()))
489
selected_m.push_back(m_kv);
490
}
491
492
// Write all CCD manifold keys
493
uint32 num_manifolds = uint32(selected_m.size());
494
inStream.Write(num_manifolds);
495
for (const MKeyValue *m_kv : selected_m)
496
inStream.Write(m_kv->GetKey());
497
}
498
499
bool ContactConstraintManager::ManifoldCache::RestoreState(const ManifoldCache &inReadCache, StateRecorder &inStream, const StateRecorderFilter *inFilter)
500
{
501
JPH_ASSERT(!mIsFinalized);
502
503
bool success = true;
504
505
// Create a contact allocator for restoring the contact cache
506
ContactAllocator contact_allocator(GetContactAllocator());
507
508
// When validating, get all existing body pairs
509
Array<const BPKeyValue *> all_bp;
510
if (inStream.IsValidating())
511
inReadCache.GetAllBodyPairsSorted(all_bp);
512
513
// Read amount of body pairs
514
uint32 num_body_pairs;
515
if (inStream.IsValidating())
516
num_body_pairs = uint32(all_bp.size());
517
inStream.Read(num_body_pairs);
518
519
// Read entire cache
520
for (uint32 i = 0; i < num_body_pairs; ++i)
521
{
522
// Read key
523
BodyPair body_pair_key;
524
if (inStream.IsValidating() && i < all_bp.size())
525
body_pair_key = all_bp[i]->GetKey();
526
inStream.Read(body_pair_key);
527
528
// Check if we want to restore this contact
529
if (inFilter == nullptr || inFilter->ShouldRestoreContact(body_pair_key.mBodyA, body_pair_key.mBodyB))
530
{
531
// Create new entry for this body pair
532
uint64 body_pair_hash = body_pair_key.GetHash();
533
BPKeyValue *bp_kv = Create(contact_allocator, body_pair_key, body_pair_hash);
534
if (bp_kv == nullptr)
535
{
536
// Out of cache space
537
success = false;
538
break;
539
}
540
CachedBodyPair &bp = bp_kv->GetValue();
541
542
// Read body pair
543
if (inStream.IsValidating() && i < all_bp.size())
544
memcpy(&bp, &all_bp[i]->GetValue(), sizeof(CachedBodyPair));
545
bp.RestoreState(inStream);
546
547
// When validating, get all existing manifolds
548
Array<const MKeyValue *> all_m;
549
if (inStream.IsValidating())
550
inReadCache.GetAllManifoldsSorted(all_bp[i]->GetValue(), all_m);
551
552
// Read amount of manifolds
553
uint32 num_manifolds = 0;
554
if (inStream.IsValidating())
555
num_manifolds = uint32(all_m.size());
556
inStream.Read(num_manifolds);
557
558
uint32 handle = ManifoldMap::cInvalidHandle;
559
for (uint32 j = 0; j < num_manifolds; ++j)
560
{
561
// Read key
562
SubShapeIDPair sub_shape_key;
563
if (inStream.IsValidating() && j < all_m.size())
564
sub_shape_key = all_m[j]->GetKey();
565
inStream.Read(sub_shape_key);
566
uint64 sub_shape_key_hash = sub_shape_key.GetHash();
567
568
// Read amount of contact points
569
uint16 num_contact_points = 0;
570
if (inStream.IsValidating() && j < all_m.size())
571
num_contact_points = all_m[j]->GetValue().mNumContactPoints;
572
inStream.Read(num_contact_points);
573
574
// Read manifold
575
MKeyValue *m_kv = Create(contact_allocator, sub_shape_key, sub_shape_key_hash, num_contact_points);
576
if (m_kv == nullptr)
577
{
578
// Out of cache space
579
success = false;
580
break;
581
}
582
CachedManifold &cm = m_kv->GetValue();
583
if (inStream.IsValidating() && j < all_m.size())
584
{
585
memcpy(&cm, &all_m[j]->GetValue(), CachedManifold::sGetRequiredTotalSize(num_contact_points));
586
cm.mNumContactPoints = uint16(num_contact_points); // Restore num contact points
587
}
588
cm.RestoreState(inStream);
589
cm.mNextWithSameBodyPair = handle;
590
handle = ToHandle(m_kv);
591
592
// Read contact points
593
for (uint32 k = 0; k < num_contact_points; ++k)
594
cm.mContactPoints[k].RestoreState(inStream);
595
}
596
bp.mFirstCachedManifold = handle;
597
}
598
else
599
{
600
// Skip the contact
601
CachedBodyPair bp;
602
bp.RestoreState(inStream);
603
uint32 num_manifolds = 0;
604
inStream.Read(num_manifolds);
605
for (uint32 j = 0; j < num_manifolds; ++j)
606
{
607
SubShapeIDPair sub_shape_key;
608
inStream.Read(sub_shape_key);
609
uint16 num_contact_points;
610
inStream.Read(num_contact_points);
611
CachedManifold cm;
612
cm.RestoreState(inStream);
613
for (uint32 k = 0; k < num_contact_points; ++k)
614
cm.mContactPoints[0].RestoreState(inStream);
615
}
616
}
617
}
618
619
// When validating, get all existing CCD manifolds
620
Array<const MKeyValue *> all_m;
621
if (inStream.IsValidating())
622
inReadCache.GetAllCCDManifoldsSorted(all_m);
623
624
// Read amount of CCD manifolds
625
uint32 num_manifolds;
626
if (inStream.IsValidating())
627
num_manifolds = uint32(all_m.size());
628
inStream.Read(num_manifolds);
629
630
for (uint32 j = 0; j < num_manifolds; ++j)
631
{
632
// Read key
633
SubShapeIDPair sub_shape_key;
634
if (inStream.IsValidating() && j < all_m.size())
635
sub_shape_key = all_m[j]->GetKey();
636
inStream.Read(sub_shape_key);
637
638
// Check if we want to restore this contact
639
if (inFilter == nullptr || inFilter->ShouldRestoreContact(sub_shape_key.GetBody1ID(), sub_shape_key.GetBody2ID()))
640
{
641
// Create CCD manifold
642
uint64 sub_shape_key_hash = sub_shape_key.GetHash();
643
MKeyValue *m_kv = Create(contact_allocator, sub_shape_key, sub_shape_key_hash, 0);
644
if (m_kv == nullptr)
645
{
646
// Out of cache space
647
success = false;
648
break;
649
}
650
CachedManifold &cm = m_kv->GetValue();
651
cm.mFlags |= (uint16)CachedManifold::EFlags::CCDContact;
652
}
653
}
654
655
#ifdef JPH_ENABLE_ASSERTS
656
// We don't finalize until the last part is restored
657
if (inStream.IsLastPart())
658
mIsFinalized = true;
659
#endif
660
661
return success;
662
}
663
664
////////////////////////////////////////////////////////////////////////////////////////////////////////
665
// ContactConstraintManager
666
////////////////////////////////////////////////////////////////////////////////////////////////////////
667
668
ContactConstraintManager::ContactConstraintManager(const PhysicsSettings &inPhysicsSettings) :
669
mPhysicsSettings(inPhysicsSettings)
670
{
671
#ifdef JPH_ENABLE_ASSERTS
672
// For the first frame mark this empty buffer as finalized
673
mCache[mCacheWriteIdx ^ 1].Finalize();
674
#endif
675
}
676
677
ContactConstraintManager::~ContactConstraintManager()
678
{
679
JPH_ASSERT(mConstraints == nullptr);
680
}
681
682
void ContactConstraintManager::Init(uint inMaxBodyPairs, uint inMaxContactConstraints)
683
{
684
// Limit the number of constraints so that the allocation size fits in an unsigned integer
685
mMaxConstraints = min(inMaxContactConstraints, cMaxContactConstraintsLimit);
686
JPH_ASSERT(mMaxConstraints == inMaxContactConstraints, "Cannot support this many contact constraints!");
687
688
// Calculate worst case cache usage
689
constexpr uint cMaxManifoldSizePerConstraint = sizeof(CachedManifold) + (MaxContactPoints - 1) * sizeof(CachedContactPoint);
690
static_assert(cMaxManifoldSizePerConstraint < sizeof(ContactConstraint)); // If not true, then the next line can overflow
691
uint cached_manifolds_size = mMaxConstraints * cMaxManifoldSizePerConstraint;
692
693
// Init the caches
694
mCache[0].Init(inMaxBodyPairs, mMaxConstraints, cached_manifolds_size);
695
mCache[1].Init(inMaxBodyPairs, mMaxConstraints, cached_manifolds_size);
696
}
697
698
void ContactConstraintManager::PrepareConstraintBuffer(PhysicsUpdateContext *inContext)
699
{
700
// Store context
701
mUpdateContext = inContext;
702
703
// Allocate temporary constraint buffer
704
JPH_ASSERT(mConstraints == nullptr);
705
mConstraints = (ContactConstraint *)inContext->mTempAllocator->Allocate(mMaxConstraints * sizeof(ContactConstraint));
706
}
707
708
template <EMotionType Type1, EMotionType Type2>
709
JPH_INLINE void ContactConstraintManager::TemplatedCalculateFrictionAndNonPenetrationConstraintProperties(ContactConstraint &ioConstraint, const ContactSettings &inSettings, float inDeltaTime, Vec3Arg inGravityDeltaTime, RMat44Arg inTransformBody1, RMat44Arg inTransformBody2, const Body &inBody1, const Body &inBody2)
710
{
711
// Calculate scaled mass and inertia
712
Mat44 inv_i1;
713
if constexpr (Type1 == EMotionType::Dynamic)
714
{
715
const MotionProperties *mp1 = inBody1.GetMotionPropertiesUnchecked();
716
inv_i1 = inSettings.mInvInertiaScale1 * mp1->GetInverseInertiaForRotation(inTransformBody1.GetRotation());
717
}
718
else
719
{
720
inv_i1 = Mat44::sZero();
721
}
722
723
Mat44 inv_i2;
724
if constexpr (Type2 == EMotionType::Dynamic)
725
{
726
const MotionProperties *mp2 = inBody2.GetMotionPropertiesUnchecked();
727
inv_i2 = inSettings.mInvInertiaScale2 * mp2->GetInverseInertiaForRotation(inTransformBody2.GetRotation());
728
}
729
else
730
{
731
inv_i2 = Mat44::sZero();
732
}
733
734
// Calculate tangents
735
Vec3 t1, t2;
736
ioConstraint.GetTangents(t1, t2);
737
738
Vec3 ws_normal = ioConstraint.GetWorldSpaceNormal();
739
740
// Calculate value for restitution correction
741
float gravity_dt_dot_normal = inGravityDeltaTime.Dot(ws_normal);
742
743
// Setup velocity constraint properties
744
float min_velocity_for_restitution = mPhysicsSettings.mMinVelocityForRestitution;
745
for (WorldContactPoint &wcp : ioConstraint.mContactPoints)
746
{
747
RVec3 p1 = inTransformBody1 * Vec3::sLoadFloat3Unsafe(wcp.mContactPoint->mPosition1);
748
RVec3 p2 = inTransformBody2 * Vec3::sLoadFloat3Unsafe(wcp.mContactPoint->mPosition2);
749
wcp.TemplatedCalculateFrictionAndNonPenetrationConstraintProperties<Type1, Type2>(inDeltaTime, gravity_dt_dot_normal, inBody1, inBody2, ioConstraint.mInvMass1, ioConstraint.mInvMass2, inv_i1, inv_i2, p1, p2, ws_normal, t1, t2, inSettings, min_velocity_for_restitution);
750
}
751
}
752
753
inline void ContactConstraintManager::CalculateFrictionAndNonPenetrationConstraintProperties(ContactConstraint &ioConstraint, const ContactSettings &inSettings, float inDeltaTime, Vec3Arg inGravityDeltaTime, RMat44Arg inTransformBody1, RMat44Arg inTransformBody2, const Body &inBody1, const Body &inBody2)
754
{
755
// Dispatch to the correct templated form
756
switch (inBody1.GetMotionType())
757
{
758
case EMotionType::Dynamic:
759
switch (inBody2.GetMotionType())
760
{
761
case EMotionType::Dynamic:
762
TemplatedCalculateFrictionAndNonPenetrationConstraintProperties<EMotionType::Dynamic, EMotionType::Dynamic>(ioConstraint, inSettings, inDeltaTime, inGravityDeltaTime, inTransformBody1, inTransformBody2, inBody1, inBody2);
763
break;
764
765
case EMotionType::Kinematic:
766
TemplatedCalculateFrictionAndNonPenetrationConstraintProperties<EMotionType::Dynamic, EMotionType::Kinematic>(ioConstraint, inSettings, inDeltaTime, inGravityDeltaTime, inTransformBody1, inTransformBody2, inBody1, inBody2);
767
break;
768
769
case EMotionType::Static:
770
TemplatedCalculateFrictionAndNonPenetrationConstraintProperties<EMotionType::Dynamic, EMotionType::Static>(ioConstraint, inSettings, inDeltaTime, inGravityDeltaTime, inTransformBody1, inTransformBody2, inBody1, inBody2);
771
break;
772
773
default:
774
JPH_ASSERT(false);
775
break;
776
}
777
break;
778
779
case EMotionType::Kinematic:
780
JPH_ASSERT(inBody2.IsDynamic());
781
TemplatedCalculateFrictionAndNonPenetrationConstraintProperties<EMotionType::Kinematic, EMotionType::Dynamic>(ioConstraint, inSettings, inDeltaTime, inGravityDeltaTime, inTransformBody1, inTransformBody2, inBody1, inBody2);
782
break;
783
784
case EMotionType::Static:
785
JPH_ASSERT(inBody2.IsDynamic());
786
TemplatedCalculateFrictionAndNonPenetrationConstraintProperties<EMotionType::Static, EMotionType::Dynamic>(ioConstraint, inSettings, inDeltaTime, inGravityDeltaTime, inTransformBody1, inTransformBody2, inBody1, inBody2);
787
break;
788
789
default:
790
JPH_ASSERT(false);
791
break;
792
}
793
}
794
795
void ContactConstraintManager::GetContactsFromCache(ContactAllocator &ioContactAllocator, Body &inBody1, Body &inBody2, bool &outPairHandled, bool &outConstraintCreated)
796
{
797
JPH_PROFILE_FUNCTION();
798
799
// Start with nothing found and not handled
800
outConstraintCreated = false;
801
outPairHandled = false;
802
803
// Swap bodies so that body 1 id < body 2 id
804
Body *body1, *body2;
805
if (inBody1.GetID() < inBody2.GetID())
806
{
807
body1 = &inBody1;
808
body2 = &inBody2;
809
}
810
else
811
{
812
body1 = &inBody2;
813
body2 = &inBody1;
814
}
815
816
// Find the cached body pair
817
BodyPair body_pair_key(body1->GetID(), body2->GetID());
818
uint64 body_pair_hash = body_pair_key.GetHash();
819
const ManifoldCache &read_cache = mCache[mCacheWriteIdx ^ 1];
820
const BPKeyValue *kv = read_cache.Find(body_pair_key, body_pair_hash);
821
if (kv == nullptr)
822
return;
823
const CachedBodyPair &input_cbp = kv->GetValue();
824
825
// Get relative translation
826
Quat inv_r1 = body1->GetRotation().Conjugated();
827
Vec3 delta_position = inv_r1 * Vec3(body2->GetCenterOfMassPosition() - body1->GetCenterOfMassPosition());
828
829
// Get old position delta
830
Vec3 old_delta_position = Vec3::sLoadFloat3Unsafe(input_cbp.mDeltaPosition);
831
832
// Check if bodies are still roughly in the same relative position
833
if ((delta_position - old_delta_position).LengthSq() > mPhysicsSettings.mBodyPairCacheMaxDeltaPositionSq)
834
return;
835
836
// Determine relative orientation
837
Quat delta_rotation = inv_r1 * body2->GetRotation();
838
839
// Reconstruct old quaternion delta
840
Quat old_delta_rotation = Quat::sLoadFloat3Unsafe(input_cbp.mDeltaRotation);
841
842
// Check if bodies are still roughly in the same relative orientation
843
// The delta between 2 quaternions p and q is: p q^* = [rotation_axis * sin(angle / 2), cos(angle / 2)]
844
// From the W component we can extract the angle: cos(angle / 2) = px * qx + py * qy + pz * qz + pw * qw = p . q
845
// Since we want to abort if the rotation is smaller than -angle or bigger than angle, we can write the comparison as |p . q| < cos(angle / 2)
846
if (abs(delta_rotation.Dot(old_delta_rotation)) < mPhysicsSettings.mBodyPairCacheCosMaxDeltaRotationDiv2)
847
return;
848
849
// The cache is valid, return that we've handled this body pair
850
outPairHandled = true;
851
852
// Copy the cached body pair to this frame
853
ManifoldCache &write_cache = mCache[mCacheWriteIdx];
854
BPKeyValue *output_bp_kv = write_cache.Create(ioContactAllocator, body_pair_key, body_pair_hash);
855
if (output_bp_kv == nullptr)
856
return; // Out of cache space
857
CachedBodyPair *output_cbp = &output_bp_kv->GetValue();
858
memcpy(output_cbp, &input_cbp, sizeof(CachedBodyPair));
859
860
// If there were no contacts, we have handled the contact
861
if (input_cbp.mFirstCachedManifold == ManifoldMap::cInvalidHandle)
862
return;
863
864
// Get body transforms
865
RMat44 transform_body1 = body1->GetCenterOfMassTransform();
866
RMat44 transform_body2 = body2->GetCenterOfMassTransform();
867
868
// Get time step
869
float delta_time = mUpdateContext->mStepDeltaTime;
870
871
// Calculate value for restitution correction
872
Vec3 gravity_dt = mUpdateContext->mPhysicsSystem->GetGravity() * delta_time;
873
874
// Copy manifolds
875
uint32 output_handle = ManifoldMap::cInvalidHandle;
876
uint32 input_handle = input_cbp.mFirstCachedManifold;
877
do
878
{
879
JPH_PROFILE("Add Constraint From Cached Manifold");
880
881
// Find the existing manifold
882
const MKeyValue *input_kv = read_cache.FromHandle(input_handle);
883
const SubShapeIDPair &input_key = input_kv->GetKey();
884
const CachedManifold &input_cm = input_kv->GetValue();
885
JPH_ASSERT(input_cm.mNumContactPoints > 0); // There should be contact points in this manifold!
886
887
// Create room for manifold in write buffer and copy data
888
uint64 input_hash = input_key.GetHash();
889
MKeyValue *output_kv = write_cache.Create(ioContactAllocator, input_key, input_hash, input_cm.mNumContactPoints);
890
if (output_kv == nullptr)
891
break; // Out of cache space
892
CachedManifold *output_cm = &output_kv->GetValue();
893
memcpy(output_cm, &input_cm, CachedManifold::sGetRequiredTotalSize(input_cm.mNumContactPoints));
894
895
// Link the object under the body pairs
896
output_cm->mNextWithSameBodyPair = output_handle;
897
output_handle = write_cache.ToHandle(output_kv);
898
899
// Calculate default contact settings
900
ContactSettings settings;
901
settings.mCombinedFriction = mCombineFriction(*body1, input_key.GetSubShapeID1(), *body2, input_key.GetSubShapeID2());
902
settings.mCombinedRestitution = mCombineRestitution(*body1, input_key.GetSubShapeID1(), *body2, input_key.GetSubShapeID2());
903
settings.mIsSensor = body1->IsSensor() || body2->IsSensor();
904
905
// Calculate world space contact normal
906
Vec3 world_space_normal = transform_body2.Multiply3x3(Vec3::sLoadFloat3Unsafe(output_cm->mContactNormal)).Normalized();
907
908
// Call contact listener to update settings
909
if (mContactListener != nullptr)
910
{
911
// Convert constraint to manifold structure for callback
912
ContactManifold manifold;
913
manifold.mWorldSpaceNormal = world_space_normal;
914
manifold.mSubShapeID1 = input_key.GetSubShapeID1();
915
manifold.mSubShapeID2 = input_key.GetSubShapeID2();
916
manifold.mBaseOffset = transform_body1.GetTranslation();
917
manifold.mRelativeContactPointsOn1.resize(output_cm->mNumContactPoints);
918
manifold.mRelativeContactPointsOn2.resize(output_cm->mNumContactPoints);
919
Mat44 local_transform_body2 = transform_body2.PostTranslated(-manifold.mBaseOffset).ToMat44();
920
float penetration_depth = -FLT_MAX;
921
for (uint32 i = 0; i < output_cm->mNumContactPoints; ++i)
922
{
923
const CachedContactPoint &ccp = output_cm->mContactPoints[i];
924
manifold.mRelativeContactPointsOn1[i] = transform_body1.Multiply3x3(Vec3::sLoadFloat3Unsafe(ccp.mPosition1));
925
manifold.mRelativeContactPointsOn2[i] = local_transform_body2 * Vec3::sLoadFloat3Unsafe(ccp.mPosition2);
926
penetration_depth = max(penetration_depth, (manifold.mRelativeContactPointsOn1[0] - manifold.mRelativeContactPointsOn2[0]).Dot(world_space_normal));
927
}
928
manifold.mPenetrationDepth = penetration_depth; // We don't have the penetration depth anymore, estimate it
929
930
// Notify callback
931
mContactListener->OnContactPersisted(*body1, *body2, manifold, settings);
932
}
933
934
JPH_ASSERT(settings.mIsSensor || !(body1->IsSensor() || body2->IsSensor()), "Sensors cannot be converted into regular bodies by a contact callback!");
935
if (!settings.mIsSensor // If one of the bodies is a sensor, don't actually create the constraint
936
&& ((body1->IsDynamic() && settings.mInvMassScale1 != 0.0f) // One of the bodies must have mass to be able to create a contact constraint
937
|| (body2->IsDynamic() && settings.mInvMassScale2 != 0.0f)))
938
{
939
// Add contact constraint in world space for the solver
940
uint32 constraint_idx = mNumConstraints++;
941
if (constraint_idx >= mMaxConstraints)
942
{
943
ioContactAllocator.mErrors |= EPhysicsUpdateError::ContactConstraintsFull;
944
break;
945
}
946
947
// A constraint will be created
948
outConstraintCreated = true;
949
950
ContactConstraint &constraint = mConstraints[constraint_idx];
951
new (&constraint) ContactConstraint();
952
constraint.mBody1 = body1;
953
constraint.mBody2 = body2;
954
constraint.mSortKey = input_hash;
955
world_space_normal.StoreFloat3(&constraint.mWorldSpaceNormal);
956
constraint.mCombinedFriction = settings.mCombinedFriction;
957
constraint.mInvMass1 = body1->GetMotionPropertiesUnchecked() != nullptr? settings.mInvMassScale1 * body1->GetMotionPropertiesUnchecked()->GetInverseMassUnchecked() : 0.0f;
958
constraint.mInvInertiaScale1 = settings.mInvInertiaScale1;
959
constraint.mInvMass2 = body2->GetMotionPropertiesUnchecked() != nullptr? settings.mInvMassScale2 * body2->GetMotionPropertiesUnchecked()->GetInverseMassUnchecked() : 0.0f;
960
constraint.mInvInertiaScale2 = settings.mInvInertiaScale2;
961
constraint.mContactPoints.resize(output_cm->mNumContactPoints);
962
for (uint32 i = 0; i < output_cm->mNumContactPoints; ++i)
963
{
964
CachedContactPoint &ccp = output_cm->mContactPoints[i];
965
WorldContactPoint &wcp = constraint.mContactPoints[i];
966
wcp.mNonPenetrationConstraint.SetTotalLambda(ccp.mNonPenetrationLambda);
967
wcp.mFrictionConstraint1.SetTotalLambda(ccp.mFrictionLambda[0]);
968
wcp.mFrictionConstraint2.SetTotalLambda(ccp.mFrictionLambda[1]);
969
wcp.mContactPoint = &ccp;
970
}
971
972
JPH_DET_LOG("GetContactsFromCache: id1: " << constraint.mBody1->GetID() << " id2: " << constraint.mBody2->GetID() << " key: " << constraint.mSortKey);
973
974
// Calculate friction and non-penetration constraint properties for all contact points
975
CalculateFrictionAndNonPenetrationConstraintProperties(constraint, settings, delta_time, gravity_dt, transform_body1, transform_body2, *body1, *body2);
976
977
// Notify island builder
978
mUpdateContext->mIslandBuilder->LinkContact(constraint_idx, body1->GetIndexInActiveBodiesInternal(), body2->GetIndexInActiveBodiesInternal());
979
980
#ifdef JPH_DEBUG_RENDERER
981
// Draw the manifold
982
if (sDrawContactManifolds)
983
constraint.Draw(DebugRenderer::sInstance, Color::sYellow);
984
#endif // JPH_DEBUG_RENDERER
985
}
986
987
// Mark contact as persisted so that we won't fire OnContactRemoved callbacks
988
input_cm.mFlags |= (uint16)CachedManifold::EFlags::ContactPersisted;
989
990
// Fetch the next manifold
991
input_handle = input_cm.mNextWithSameBodyPair;
992
}
993
while (input_handle != ManifoldMap::cInvalidHandle);
994
output_cbp->mFirstCachedManifold = output_handle;
995
}
996
997
ContactConstraintManager::BodyPairHandle ContactConstraintManager::AddBodyPair(ContactAllocator &ioContactAllocator, const Body &inBody1, const Body &inBody2)
998
{
999
JPH_PROFILE_FUNCTION();
1000
1001
// Swap bodies so that body 1 id < body 2 id
1002
const Body *body1, *body2;
1003
if (inBody1.GetID() < inBody2.GetID())
1004
{
1005
body1 = &inBody1;
1006
body2 = &inBody2;
1007
}
1008
else
1009
{
1010
body1 = &inBody2;
1011
body2 = &inBody1;
1012
}
1013
1014
// Add an entry
1015
BodyPair body_pair_key(body1->GetID(), body2->GetID());
1016
uint64 body_pair_hash = body_pair_key.GetHash();
1017
BPKeyValue *body_pair_kv = mCache[mCacheWriteIdx].Create(ioContactAllocator, body_pair_key, body_pair_hash);
1018
if (body_pair_kv == nullptr)
1019
return nullptr; // Out of cache space
1020
CachedBodyPair *cbp = &body_pair_kv->GetValue();
1021
cbp->mFirstCachedManifold = ManifoldMap::cInvalidHandle;
1022
1023
// Get relative translation
1024
Quat inv_r1 = body1->GetRotation().Conjugated();
1025
Vec3 delta_position = inv_r1 * Vec3(body2->GetCenterOfMassPosition() - body1->GetCenterOfMassPosition());
1026
1027
// Store it
1028
delta_position.StoreFloat3(&cbp->mDeltaPosition);
1029
1030
// Determine relative orientation
1031
Quat delta_rotation = inv_r1 * body2->GetRotation();
1032
1033
// Store it
1034
delta_rotation.StoreFloat3(&cbp->mDeltaRotation);
1035
1036
return cbp;
1037
}
1038
1039
template <EMotionType Type1, EMotionType Type2>
1040
bool ContactConstraintManager::TemplatedAddContactConstraint(ContactAllocator &ioContactAllocator, BodyPairHandle inBodyPairHandle, Body &inBody1, Body &inBody2, const ContactManifold &inManifold)
1041
{
1042
// Calculate hash
1043
SubShapeIDPair key { inBody1.GetID(), inManifold.mSubShapeID1, inBody2.GetID(), inManifold.mSubShapeID2 };
1044
uint64 key_hash = key.GetHash();
1045
1046
// Determine number of contact points
1047
int num_contact_points = (int)inManifold.mRelativeContactPointsOn1.size();
1048
JPH_ASSERT(num_contact_points <= MaxContactPoints);
1049
JPH_ASSERT(num_contact_points == (int)inManifold.mRelativeContactPointsOn2.size());
1050
1051
// Reserve space for new contact cache entry
1052
// Note that for dynamic vs dynamic we always require the first body to have a lower body id to get a consistent key
1053
// under which to look up the contact
1054
ManifoldCache &write_cache = mCache[mCacheWriteIdx];
1055
MKeyValue *new_manifold_kv = write_cache.Create(ioContactAllocator, key, key_hash, num_contact_points);
1056
if (new_manifold_kv == nullptr)
1057
return false; // Out of cache space
1058
CachedManifold *new_manifold = &new_manifold_kv->GetValue();
1059
1060
// Transform the world space normal to the space of body 2 (this is usually the static body)
1061
RMat44 inverse_transform_body2 = inBody2.GetInverseCenterOfMassTransform();
1062
inverse_transform_body2.Multiply3x3(inManifold.mWorldSpaceNormal).Normalized().StoreFloat3(&new_manifold->mContactNormal);
1063
1064
// Settings object that gets passed to the callback
1065
ContactSettings settings;
1066
settings.mCombinedFriction = mCombineFriction(inBody1, inManifold.mSubShapeID1, inBody2, inManifold.mSubShapeID2);
1067
settings.mCombinedRestitution = mCombineRestitution(inBody1, inManifold.mSubShapeID1, inBody2, inManifold.mSubShapeID2);
1068
settings.mIsSensor = inBody1.IsSensor() || inBody2.IsSensor();
1069
1070
// Get the contact points for the old cache entry
1071
const ManifoldCache &read_cache = mCache[mCacheWriteIdx ^ 1];
1072
const MKeyValue *old_manifold_kv = read_cache.Find(key, key_hash);
1073
const CachedContactPoint *ccp_start;
1074
const CachedContactPoint *ccp_end;
1075
if (old_manifold_kv != nullptr)
1076
{
1077
// Call point persisted listener
1078
if (mContactListener != nullptr)
1079
mContactListener->OnContactPersisted(inBody1, inBody2, inManifold, settings);
1080
1081
// Fetch the contact points from the old manifold
1082
const CachedManifold *old_manifold = &old_manifold_kv->GetValue();
1083
ccp_start = old_manifold->mContactPoints;
1084
ccp_end = ccp_start + old_manifold->mNumContactPoints;
1085
1086
// Mark contact as persisted so that we won't fire OnContactRemoved callbacks
1087
old_manifold->mFlags |= (uint16)CachedManifold::EFlags::ContactPersisted;
1088
}
1089
else
1090
{
1091
// Call point added listener
1092
if (mContactListener != nullptr)
1093
mContactListener->OnContactAdded(inBody1, inBody2, inManifold, settings);
1094
1095
// No contact points available from old manifold
1096
ccp_start = nullptr;
1097
ccp_end = nullptr;
1098
}
1099
1100
// Get inverse transform for body 1
1101
RMat44 inverse_transform_body1 = inBody1.GetInverseCenterOfMassTransform();
1102
1103
bool contact_constraint_created = false;
1104
1105
// If one of the bodies is a sensor, don't actually create the constraint
1106
JPH_ASSERT(settings.mIsSensor || !(inBody1.IsSensor() || inBody2.IsSensor()), "Sensors cannot be converted into regular bodies by a contact callback!");
1107
if (!settings.mIsSensor
1108
&& ((inBody1.IsDynamic() && settings.mInvMassScale1 != 0.0f) // One of the bodies must have mass to be able to create a contact constraint
1109
|| (inBody2.IsDynamic() && settings.mInvMassScale2 != 0.0f)))
1110
{
1111
// Add contact constraint
1112
uint32 constraint_idx = mNumConstraints++;
1113
if (constraint_idx >= mMaxConstraints)
1114
{
1115
ioContactAllocator.mErrors |= EPhysicsUpdateError::ContactConstraintsFull;
1116
1117
// Manifold has been created already, we're not filling it in, so we need to reset the contact number of points.
1118
// Note that we don't hook it up to the body pair cache so that it won't be used as a cache during the next simulation.
1119
new_manifold->mNumContactPoints = 0;
1120
return false;
1121
}
1122
1123
// We will create a contact constraint
1124
contact_constraint_created = true;
1125
1126
ContactConstraint &constraint = mConstraints[constraint_idx];
1127
new (&constraint) ContactConstraint();
1128
constraint.mBody1 = &inBody1;
1129
constraint.mBody2 = &inBody2;
1130
constraint.mSortKey = key_hash;
1131
inManifold.mWorldSpaceNormal.StoreFloat3(&constraint.mWorldSpaceNormal);
1132
constraint.mCombinedFriction = settings.mCombinedFriction;
1133
constraint.mInvMass1 = inBody1.GetMotionPropertiesUnchecked() != nullptr? settings.mInvMassScale1 * inBody1.GetMotionPropertiesUnchecked()->GetInverseMassUnchecked() : 0.0f;
1134
constraint.mInvInertiaScale1 = settings.mInvInertiaScale1;
1135
constraint.mInvMass2 = inBody2.GetMotionPropertiesUnchecked() != nullptr? settings.mInvMassScale2 * inBody2.GetMotionPropertiesUnchecked()->GetInverseMassUnchecked() : 0.0f;
1136
constraint.mInvInertiaScale2 = settings.mInvInertiaScale2;
1137
1138
JPH_DET_LOG("TemplatedAddContactConstraint: id1: " << constraint.mBody1->GetID() << " id2: " << constraint.mBody2->GetID() << " key: " << constraint.mSortKey);
1139
1140
// Notify island builder
1141
mUpdateContext->mIslandBuilder->LinkContact(constraint_idx, inBody1.GetIndexInActiveBodiesInternal(), inBody2.GetIndexInActiveBodiesInternal());
1142
1143
// Get time step
1144
float delta_time = mUpdateContext->mStepDeltaTime;
1145
1146
// Calculate value for restitution correction
1147
float gravity_dt_dot_normal = inManifold.mWorldSpaceNormal.Dot(mUpdateContext->mPhysicsSystem->GetGravity() * delta_time);
1148
1149
// Calculate scaled mass and inertia
1150
float inv_m1;
1151
Mat44 inv_i1;
1152
if constexpr (Type1 == EMotionType::Dynamic)
1153
{
1154
const MotionProperties *mp1 = inBody1.GetMotionPropertiesUnchecked();
1155
inv_m1 = settings.mInvMassScale1 * mp1->GetInverseMass();
1156
inv_i1 = settings.mInvInertiaScale1 * mp1->GetInverseInertiaForRotation(inverse_transform_body1.Transposed3x3());
1157
}
1158
else
1159
{
1160
inv_m1 = 0.0f;
1161
inv_i1 = Mat44::sZero();
1162
}
1163
1164
float inv_m2;
1165
Mat44 inv_i2;
1166
if constexpr (Type2 == EMotionType::Dynamic)
1167
{
1168
const MotionProperties *mp2 = inBody2.GetMotionPropertiesUnchecked();
1169
inv_m2 = settings.mInvMassScale2 * mp2->GetInverseMass();
1170
inv_i2 = settings.mInvInertiaScale2 * mp2->GetInverseInertiaForRotation(inverse_transform_body2.Transposed3x3());
1171
}
1172
else
1173
{
1174
inv_m2 = 0.0f;
1175
inv_i2 = Mat44::sZero();
1176
}
1177
1178
// Calculate tangents
1179
Vec3 t1, t2;
1180
constraint.GetTangents(t1, t2);
1181
1182
constraint.mContactPoints.resize(num_contact_points);
1183
for (int i = 0; i < num_contact_points; ++i)
1184
{
1185
// Convert to world space and set positions
1186
WorldContactPoint &wcp = constraint.mContactPoints[i];
1187
RVec3 p1_ws = inManifold.mBaseOffset + inManifold.mRelativeContactPointsOn1[i];
1188
RVec3 p2_ws = inManifold.mBaseOffset + inManifold.mRelativeContactPointsOn2[i];
1189
1190
// Convert to local space to the body
1191
Vec3 p1_ls = Vec3(inverse_transform_body1 * p1_ws);
1192
Vec3 p2_ls = Vec3(inverse_transform_body2 * p2_ws);
1193
1194
// Check if we have a close contact point from last update
1195
bool lambda_set = false;
1196
for (const CachedContactPoint *ccp = ccp_start; ccp < ccp_end; ccp++)
1197
if (Vec3::sLoadFloat3Unsafe(ccp->mPosition1).IsClose(p1_ls, mPhysicsSettings.mContactPointPreserveLambdaMaxDistSq)
1198
&& Vec3::sLoadFloat3Unsafe(ccp->mPosition2).IsClose(p2_ls, mPhysicsSettings.mContactPointPreserveLambdaMaxDistSq))
1199
{
1200
// Get lambdas from previous frame
1201
wcp.mNonPenetrationConstraint.SetTotalLambda(ccp->mNonPenetrationLambda);
1202
wcp.mFrictionConstraint1.SetTotalLambda(ccp->mFrictionLambda[0]);
1203
wcp.mFrictionConstraint2.SetTotalLambda(ccp->mFrictionLambda[1]);
1204
lambda_set = true;
1205
break;
1206
}
1207
if (!lambda_set)
1208
{
1209
wcp.mNonPenetrationConstraint.SetTotalLambda(0.0f);
1210
wcp.mFrictionConstraint1.SetTotalLambda(0.0f);
1211
wcp.mFrictionConstraint2.SetTotalLambda(0.0f);
1212
}
1213
1214
// Create new contact point
1215
CachedContactPoint &cp = new_manifold->mContactPoints[i];
1216
p1_ls.StoreFloat3(&cp.mPosition1);
1217
p2_ls.StoreFloat3(&cp.mPosition2);
1218
wcp.mContactPoint = &cp;
1219
1220
// Setup velocity constraint
1221
wcp.TemplatedCalculateFrictionAndNonPenetrationConstraintProperties<Type1, Type2>(delta_time, gravity_dt_dot_normal, inBody1, inBody2, inv_m1, inv_m2, inv_i1, inv_i2, p1_ws, p2_ws, inManifold.mWorldSpaceNormal, t1, t2, settings, mPhysicsSettings.mMinVelocityForRestitution);
1222
}
1223
1224
#ifdef JPH_DEBUG_RENDERER
1225
// Draw the manifold
1226
if (sDrawContactManifolds)
1227
constraint.Draw(DebugRenderer::sInstance, Color::sOrange);
1228
#endif // JPH_DEBUG_RENDERER
1229
}
1230
else
1231
{
1232
// Store the contact manifold in the cache
1233
for (int i = 0; i < num_contact_points; ++i)
1234
{
1235
// Convert to local space to the body
1236
Vec3 p1 = Vec3(inverse_transform_body1 * (inManifold.mBaseOffset + inManifold.mRelativeContactPointsOn1[i]));
1237
Vec3 p2 = Vec3(inverse_transform_body2 * (inManifold.mBaseOffset + inManifold.mRelativeContactPointsOn2[i]));
1238
1239
// Create new contact point
1240
CachedContactPoint &cp = new_manifold->mContactPoints[i];
1241
p1.StoreFloat3(&cp.mPosition1);
1242
p2.StoreFloat3(&cp.mPosition2);
1243
1244
// Reset contact impulses, we haven't applied any
1245
cp.mNonPenetrationLambda = 0.0f;
1246
cp.mFrictionLambda[0] = 0.0f;
1247
cp.mFrictionLambda[1] = 0.0f;
1248
}
1249
}
1250
1251
// Store cached contact point in body pair cache
1252
CachedBodyPair *cbp = reinterpret_cast<CachedBodyPair *>(inBodyPairHandle);
1253
new_manifold->mNextWithSameBodyPair = cbp->mFirstCachedManifold;
1254
cbp->mFirstCachedManifold = write_cache.ToHandle(new_manifold_kv);
1255
1256
// A contact constraint was added
1257
return contact_constraint_created;
1258
}
1259
1260
bool ContactConstraintManager::AddContactConstraint(ContactAllocator &ioContactAllocator, BodyPairHandle inBodyPairHandle, Body &inBody1, Body &inBody2, const ContactManifold &inManifold)
1261
{
1262
JPH_PROFILE_FUNCTION();
1263
1264
JPH_DET_LOG("AddContactConstraint: id1: " << inBody1.GetID() << " id2: " << inBody2.GetID()
1265
<< " subshape1: " << inManifold.mSubShapeID1 << " subshape2: " << inManifold.mSubShapeID2
1266
<< " normal: " << inManifold.mWorldSpaceNormal << " pendepth: " << inManifold.mPenetrationDepth);
1267
1268
JPH_ASSERT(inManifold.mWorldSpaceNormal.IsNormalized());
1269
1270
// Swap bodies so that body 1 id < body 2 id
1271
const ContactManifold *manifold;
1272
Body *body1, *body2;
1273
ContactManifold temp;
1274
if (inBody2.GetID() < inBody1.GetID())
1275
{
1276
body1 = &inBody2;
1277
body2 = &inBody1;
1278
temp = inManifold.SwapShapes();
1279
manifold = &temp;
1280
}
1281
else
1282
{
1283
body1 = &inBody1;
1284
body2 = &inBody2;
1285
manifold = &inManifold;
1286
}
1287
1288
// Dispatch to the correct templated form
1289
// Note: Non-dynamic vs non-dynamic can happen in this case due to one body being a sensor, so we need to have an extended switch case here
1290
switch (body1->GetMotionType())
1291
{
1292
case EMotionType::Dynamic:
1293
{
1294
switch (body2->GetMotionType())
1295
{
1296
case EMotionType::Dynamic:
1297
return TemplatedAddContactConstraint<EMotionType::Dynamic, EMotionType::Dynamic>(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold);
1298
1299
case EMotionType::Kinematic:
1300
return TemplatedAddContactConstraint<EMotionType::Dynamic, EMotionType::Kinematic>(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold);
1301
1302
case EMotionType::Static:
1303
return TemplatedAddContactConstraint<EMotionType::Dynamic, EMotionType::Static>(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold);
1304
1305
default:
1306
JPH_ASSERT(false);
1307
break;
1308
}
1309
break;
1310
}
1311
1312
case EMotionType::Kinematic:
1313
switch (body2->GetMotionType())
1314
{
1315
case EMotionType::Dynamic:
1316
return TemplatedAddContactConstraint<EMotionType::Kinematic, EMotionType::Dynamic>(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold);
1317
1318
case EMotionType::Kinematic:
1319
return TemplatedAddContactConstraint<EMotionType::Kinematic, EMotionType::Kinematic>(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold);
1320
1321
case EMotionType::Static:
1322
return TemplatedAddContactConstraint<EMotionType::Kinematic, EMotionType::Static>(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold);
1323
1324
default:
1325
JPH_ASSERT(false);
1326
break;
1327
}
1328
break;
1329
1330
case EMotionType::Static:
1331
switch (body2->GetMotionType())
1332
{
1333
case EMotionType::Dynamic:
1334
return TemplatedAddContactConstraint<EMotionType::Static, EMotionType::Dynamic>(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold);
1335
1336
case EMotionType::Kinematic:
1337
return TemplatedAddContactConstraint<EMotionType::Static, EMotionType::Kinematic>(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold);
1338
1339
case EMotionType::Static: // Static vs static not possible
1340
default:
1341
JPH_ASSERT(false);
1342
break;
1343
}
1344
break;
1345
1346
default:
1347
JPH_ASSERT(false);
1348
break;
1349
}
1350
1351
return false;
1352
}
1353
1354
void ContactConstraintManager::OnCCDContactAdded(ContactAllocator &ioContactAllocator, const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &outSettings)
1355
{
1356
JPH_ASSERT(inManifold.mWorldSpaceNormal.IsNormalized());
1357
1358
// Calculate contact settings
1359
outSettings.mCombinedFriction = mCombineFriction(inBody1, inManifold.mSubShapeID1, inBody2, inManifold.mSubShapeID2);
1360
outSettings.mCombinedRestitution = mCombineRestitution(inBody1, inManifold.mSubShapeID1, inBody2, inManifold.mSubShapeID2);
1361
outSettings.mIsSensor = false; // For now, no sensors are supported during CCD
1362
1363
// The remainder of this function only deals with calling contact callbacks, if there's no contact callback we also don't need to do this work
1364
if (mContactListener != nullptr)
1365
{
1366
// Swap bodies so that body 1 id < body 2 id
1367
const ContactManifold *manifold;
1368
const Body *body1, *body2;
1369
ContactManifold temp;
1370
if (inBody2.GetID() < inBody1.GetID())
1371
{
1372
body1 = &inBody2;
1373
body2 = &inBody1;
1374
temp = inManifold.SwapShapes();
1375
manifold = &temp;
1376
}
1377
else
1378
{
1379
body1 = &inBody1;
1380
body2 = &inBody2;
1381
manifold = &inManifold;
1382
}
1383
1384
// Calculate hash
1385
SubShapeIDPair key { body1->GetID(), manifold->mSubShapeID1, body2->GetID(), manifold->mSubShapeID2 };
1386
uint64 key_hash = key.GetHash();
1387
1388
// Check if we already created this contact this physics update
1389
ManifoldCache &write_cache = mCache[mCacheWriteIdx];
1390
MKVAndCreated new_manifold_kv = write_cache.FindOrCreate(ioContactAllocator, key, key_hash, 0);
1391
if (new_manifold_kv.second)
1392
{
1393
// This contact is new for this physics update, check if previous update we already had this contact.
1394
const ManifoldCache &read_cache = mCache[mCacheWriteIdx ^ 1];
1395
const MKeyValue *old_manifold_kv = read_cache.Find(key, key_hash);
1396
if (old_manifold_kv == nullptr)
1397
{
1398
// New contact
1399
mContactListener->OnContactAdded(*body1, *body2, *manifold, outSettings);
1400
}
1401
else
1402
{
1403
// Existing contact
1404
mContactListener->OnContactPersisted(*body1, *body2, *manifold, outSettings);
1405
1406
// Mark contact as persisted so that we won't fire OnContactRemoved callbacks
1407
old_manifold_kv->GetValue().mFlags |= (uint16)CachedManifold::EFlags::ContactPersisted;
1408
}
1409
1410
// Check if the cache is full
1411
if (new_manifold_kv.first != nullptr)
1412
{
1413
// We don't store any contact points in this manifold as it is not for caching impulses, we only need to know that the contact was created
1414
CachedManifold &new_manifold = new_manifold_kv.first->GetValue();
1415
new_manifold.mContactNormal = { 0, 0, 0 };
1416
new_manifold.mFlags |= (uint16)CachedManifold::EFlags::CCDContact;
1417
}
1418
}
1419
else
1420
{
1421
// Already found this contact this physics update.
1422
// Note that we can trigger OnContactPersisted multiple times per physics update, but otherwise we have no way of obtaining the settings
1423
mContactListener->OnContactPersisted(*body1, *body2, *manifold, outSettings);
1424
}
1425
1426
// If we swapped body1 and body2 we need to swap the mass scales back
1427
if (manifold == &temp)
1428
{
1429
std::swap(outSettings.mInvMassScale1, outSettings.mInvMassScale2);
1430
std::swap(outSettings.mInvInertiaScale1, outSettings.mInvInertiaScale2);
1431
// Note we do not need to negate the relative surface velocity as it is not applied by the CCD collision constraint
1432
}
1433
}
1434
1435
JPH_ASSERT(outSettings.mIsSensor || !(inBody1.IsSensor() || inBody2.IsSensor()), "Sensors cannot be converted into regular bodies by a contact callback!");
1436
}
1437
1438
void ContactConstraintManager::SortContacts(uint32 *inConstraintIdxBegin, uint32 *inConstraintIdxEnd) const
1439
{
1440
JPH_PROFILE_FUNCTION();
1441
1442
QuickSort(inConstraintIdxBegin, inConstraintIdxEnd, [this](uint32 inLHS, uint32 inRHS) {
1443
const ContactConstraint &lhs = mConstraints[inLHS];
1444
const ContactConstraint &rhs = mConstraints[inRHS];
1445
1446
// Most of the time the sort key will be different so we sort on that
1447
if (lhs.mSortKey != rhs.mSortKey)
1448
return lhs.mSortKey < rhs.mSortKey;
1449
1450
// If they're equal we use the IDs of body 1 to order
1451
if (lhs.mBody1 != rhs.mBody1)
1452
return lhs.mBody1->GetID() < rhs.mBody1->GetID();
1453
1454
// If they're still equal we use the IDs of body 2 to order
1455
if (lhs.mBody2 != rhs.mBody2)
1456
return lhs.mBody2->GetID() < rhs.mBody2->GetID();
1457
1458
JPH_ASSERT(inLHS == inRHS, "Hash collision, ordering will be inconsistent");
1459
return false;
1460
});
1461
}
1462
1463
void ContactConstraintManager::FinalizeContactCacheAndCallContactPointRemovedCallbacks(uint inExpectedNumBodyPairs, uint inExpectedNumManifolds)
1464
{
1465
JPH_PROFILE_FUNCTION();
1466
1467
#ifdef JPH_ENABLE_ASSERTS
1468
// Mark cache as finalized
1469
ManifoldCache &old_write_cache = mCache[mCacheWriteIdx];
1470
old_write_cache.Finalize();
1471
1472
// Check that the count of body pairs and manifolds that we tracked outside of the cache (to avoid contention on an atomic) is correct
1473
JPH_ASSERT(old_write_cache.GetNumBodyPairs() == inExpectedNumBodyPairs);
1474
JPH_ASSERT(old_write_cache.GetNumManifolds() == inExpectedNumManifolds);
1475
#endif
1476
1477
// Buffers are now complete, make write buffer the read buffer
1478
mCacheWriteIdx ^= 1;
1479
1480
// Get the old read cache / new write cache
1481
ManifoldCache &old_read_cache = mCache[mCacheWriteIdx];
1482
1483
// Call the contact point removal callbacks
1484
if (mContactListener != nullptr)
1485
old_read_cache.ContactPointRemovedCallbacks(mContactListener);
1486
1487
// We're done with the old read cache now
1488
old_read_cache.Clear();
1489
1490
// Use the amount of contacts from the last iteration to determine the amount of buckets to use in the hash map for the next iteration
1491
old_read_cache.Prepare(inExpectedNumBodyPairs, inExpectedNumManifolds);
1492
}
1493
1494
bool ContactConstraintManager::WereBodiesInContact(const BodyID &inBody1ID, const BodyID &inBody2ID) const
1495
{
1496
// The body pair needs to be in the cache and it needs to have a manifold (otherwise it's just a record indicating that there are no collisions)
1497
const ManifoldCache &read_cache = mCache[mCacheWriteIdx ^ 1];
1498
BodyPair key;
1499
if (inBody1ID < inBody2ID)
1500
key = BodyPair(inBody1ID, inBody2ID);
1501
else
1502
key = BodyPair(inBody2ID, inBody1ID);
1503
uint64 key_hash = key.GetHash();
1504
const BPKeyValue *kv = read_cache.Find(key, key_hash);
1505
return kv != nullptr && kv->GetValue().mFirstCachedManifold != ManifoldMap::cInvalidHandle;
1506
}
1507
1508
template <EMotionType Type1, EMotionType Type2>
1509
JPH_INLINE void ContactConstraintManager::sWarmStartConstraint(ContactConstraint &ioConstraint, MotionProperties *ioMotionProperties1, MotionProperties *ioMotionProperties2, float inWarmStartImpulseRatio)
1510
{
1511
// Calculate tangents
1512
Vec3 t1, t2;
1513
ioConstraint.GetTangents(t1, t2);
1514
1515
Vec3 ws_normal = ioConstraint.GetWorldSpaceNormal();
1516
1517
for (WorldContactPoint &wcp : ioConstraint.mContactPoints)
1518
{
1519
// Warm starting: Apply impulse from last frame
1520
if (wcp.mFrictionConstraint1.IsActive() || wcp.mFrictionConstraint2.IsActive())
1521
{
1522
wcp.mFrictionConstraint1.TemplatedWarmStart<Type1, Type2>(ioMotionProperties1, ioConstraint.mInvMass1, ioMotionProperties2, ioConstraint.mInvMass2, t1, inWarmStartImpulseRatio);
1523
wcp.mFrictionConstraint2.TemplatedWarmStart<Type1, Type2>(ioMotionProperties1, ioConstraint.mInvMass1, ioMotionProperties2, ioConstraint.mInvMass2, t2, inWarmStartImpulseRatio);
1524
}
1525
wcp.mNonPenetrationConstraint.TemplatedWarmStart<Type1, Type2>(ioMotionProperties1, ioConstraint.mInvMass1, ioMotionProperties2, ioConstraint.mInvMass2, ws_normal, inWarmStartImpulseRatio);
1526
}
1527
}
1528
1529
template <class MotionPropertiesCallback>
1530
void ContactConstraintManager::WarmStartVelocityConstraints(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inWarmStartImpulseRatio, MotionPropertiesCallback &ioCallback)
1531
{
1532
JPH_PROFILE_FUNCTION();
1533
1534
for (const uint32 *constraint_idx = inConstraintIdxBegin; constraint_idx < inConstraintIdxEnd; ++constraint_idx)
1535
{
1536
ContactConstraint &constraint = mConstraints[*constraint_idx];
1537
1538
// Fetch bodies
1539
Body &body1 = *constraint.mBody1;
1540
EMotionType motion_type1 = body1.GetMotionType();
1541
MotionProperties *motion_properties1 = body1.GetMotionPropertiesUnchecked();
1542
1543
Body &body2 = *constraint.mBody2;
1544
EMotionType motion_type2 = body2.GetMotionType();
1545
MotionProperties *motion_properties2 = body2.GetMotionPropertiesUnchecked();
1546
1547
// Dispatch to the correct templated form
1548
// Note: Warm starting doesn't differentiate between kinematic/static bodies so we handle both as static bodies
1549
if (motion_type1 == EMotionType::Dynamic)
1550
{
1551
if (motion_type2 == EMotionType::Dynamic)
1552
{
1553
sWarmStartConstraint<EMotionType::Dynamic, EMotionType::Dynamic>(constraint, motion_properties1, motion_properties2, inWarmStartImpulseRatio);
1554
1555
ioCallback(motion_properties2);
1556
}
1557
else
1558
sWarmStartConstraint<EMotionType::Dynamic, EMotionType::Static>(constraint, motion_properties1, motion_properties2, inWarmStartImpulseRatio);
1559
1560
ioCallback(motion_properties1);
1561
}
1562
else
1563
{
1564
JPH_ASSERT(motion_type2 == EMotionType::Dynamic);
1565
1566
sWarmStartConstraint<EMotionType::Static, EMotionType::Dynamic>(constraint, motion_properties1, motion_properties2, inWarmStartImpulseRatio);
1567
1568
ioCallback(motion_properties2);
1569
}
1570
}
1571
}
1572
1573
// Specialize for the two body callback types
1574
template void ContactConstraintManager::WarmStartVelocityConstraints<CalculateSolverSteps>(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inWarmStartImpulseRatio, CalculateSolverSteps &ioCallback);
1575
template void ContactConstraintManager::WarmStartVelocityConstraints<DummyCalculateSolverSteps>(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inWarmStartImpulseRatio, DummyCalculateSolverSteps &ioCallback);
1576
1577
template <EMotionType Type1, EMotionType Type2>
1578
JPH_INLINE bool ContactConstraintManager::sSolveVelocityConstraint(ContactConstraint &ioConstraint, MotionProperties *ioMotionProperties1, MotionProperties *ioMotionProperties2)
1579
{
1580
bool any_impulse_applied = false;
1581
1582
// Calculate tangents
1583
Vec3 t1, t2;
1584
ioConstraint.GetTangents(t1, t2);
1585
1586
// First apply all friction constraints (non-penetration is more important than friction)
1587
for (WorldContactPoint &wcp : ioConstraint.mContactPoints)
1588
{
1589
// Check if friction is enabled
1590
if (wcp.mFrictionConstraint1.IsActive() || wcp.mFrictionConstraint2.IsActive())
1591
{
1592
// Calculate impulse to stop motion in tangential direction
1593
float lambda1 = wcp.mFrictionConstraint1.TemplatedSolveVelocityConstraintGetTotalLambda<Type1, Type2>(ioMotionProperties1, ioMotionProperties2, t1);
1594
float lambda2 = wcp.mFrictionConstraint2.TemplatedSolveVelocityConstraintGetTotalLambda<Type1, Type2>(ioMotionProperties1, ioMotionProperties2, t2);
1595
float total_lambda_sq = Square(lambda1) + Square(lambda2);
1596
1597
// Calculate max impulse that can be applied. Note that we're using the non-penetration impulse from the previous iteration here.
1598
// We do this because non-penetration is more important so is solved last (the last things that are solved in an iterative solver
1599
// contribute the most).
1600
float max_lambda_f = ioConstraint.mCombinedFriction * wcp.mNonPenetrationConstraint.GetTotalLambda();
1601
1602
// If the total lambda that we will apply is too large, scale it back
1603
if (total_lambda_sq > Square(max_lambda_f))
1604
{
1605
float scale = max_lambda_f / sqrt(total_lambda_sq);
1606
lambda1 *= scale;
1607
lambda2 *= scale;
1608
}
1609
1610
// Apply the friction impulse
1611
if (wcp.mFrictionConstraint1.TemplatedSolveVelocityConstraintApplyLambda<Type1, Type2>(ioMotionProperties1, ioConstraint.mInvMass1, ioMotionProperties2, ioConstraint.mInvMass2, t1, lambda1))
1612
any_impulse_applied = true;
1613
if (wcp.mFrictionConstraint2.TemplatedSolveVelocityConstraintApplyLambda<Type1, Type2>(ioMotionProperties1, ioConstraint.mInvMass1, ioMotionProperties2, ioConstraint.mInvMass2, t2, lambda2))
1614
any_impulse_applied = true;
1615
}
1616
}
1617
1618
Vec3 ws_normal = ioConstraint.GetWorldSpaceNormal();
1619
1620
// Then apply all non-penetration constraints
1621
for (WorldContactPoint &wcp : ioConstraint.mContactPoints)
1622
{
1623
// Solve non penetration velocities
1624
if (wcp.mNonPenetrationConstraint.TemplatedSolveVelocityConstraint<Type1, Type2>(ioMotionProperties1, ioConstraint.mInvMass1, ioMotionProperties2, ioConstraint.mInvMass2, ws_normal, 0.0f, FLT_MAX))
1625
any_impulse_applied = true;
1626
}
1627
1628
return any_impulse_applied;
1629
}
1630
1631
bool ContactConstraintManager::SolveVelocityConstraints(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd)
1632
{
1633
JPH_PROFILE_FUNCTION();
1634
1635
bool any_impulse_applied = false;
1636
1637
for (const uint32 *constraint_idx = inConstraintIdxBegin; constraint_idx < inConstraintIdxEnd; ++constraint_idx)
1638
{
1639
ContactConstraint &constraint = mConstraints[*constraint_idx];
1640
1641
// Fetch bodies
1642
Body &body1 = *constraint.mBody1;
1643
EMotionType motion_type1 = body1.GetMotionType();
1644
MotionProperties *motion_properties1 = body1.GetMotionPropertiesUnchecked();
1645
1646
Body &body2 = *constraint.mBody2;
1647
EMotionType motion_type2 = body2.GetMotionType();
1648
MotionProperties *motion_properties2 = body2.GetMotionPropertiesUnchecked();
1649
1650
// Dispatch to the correct templated form
1651
switch (motion_type1)
1652
{
1653
case EMotionType::Dynamic:
1654
switch (motion_type2)
1655
{
1656
case EMotionType::Dynamic:
1657
any_impulse_applied |= sSolveVelocityConstraint<EMotionType::Dynamic, EMotionType::Dynamic>(constraint, motion_properties1, motion_properties2);
1658
break;
1659
1660
case EMotionType::Kinematic:
1661
any_impulse_applied |= sSolveVelocityConstraint<EMotionType::Dynamic, EMotionType::Kinematic>(constraint, motion_properties1, motion_properties2);
1662
break;
1663
1664
case EMotionType::Static:
1665
any_impulse_applied |= sSolveVelocityConstraint<EMotionType::Dynamic, EMotionType::Static>(constraint, motion_properties1, motion_properties2);
1666
break;
1667
1668
default:
1669
JPH_ASSERT(false);
1670
break;
1671
}
1672
break;
1673
1674
case EMotionType::Kinematic:
1675
JPH_ASSERT(motion_type2 == EMotionType::Dynamic);
1676
any_impulse_applied |= sSolveVelocityConstraint<EMotionType::Kinematic, EMotionType::Dynamic>(constraint, motion_properties1, motion_properties2);
1677
break;
1678
1679
case EMotionType::Static:
1680
JPH_ASSERT(motion_type2 == EMotionType::Dynamic);
1681
any_impulse_applied |= sSolveVelocityConstraint<EMotionType::Static, EMotionType::Dynamic>(constraint, motion_properties1, motion_properties2);
1682
break;
1683
1684
default:
1685
JPH_ASSERT(false);
1686
break;
1687
}
1688
}
1689
1690
return any_impulse_applied;
1691
}
1692
1693
void ContactConstraintManager::StoreAppliedImpulses(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd) const
1694
{
1695
// Copy back total applied impulse to cache for the next frame
1696
for (const uint32 *constraint_idx = inConstraintIdxBegin; constraint_idx < inConstraintIdxEnd; ++constraint_idx)
1697
{
1698
const ContactConstraint &constraint = mConstraints[*constraint_idx];
1699
1700
for (const WorldContactPoint &wcp : constraint.mContactPoints)
1701
{
1702
wcp.mContactPoint->mNonPenetrationLambda = wcp.mNonPenetrationConstraint.GetTotalLambda();
1703
wcp.mContactPoint->mFrictionLambda[0] = wcp.mFrictionConstraint1.GetTotalLambda();
1704
wcp.mContactPoint->mFrictionLambda[1] = wcp.mFrictionConstraint2.GetTotalLambda();
1705
}
1706
}
1707
}
1708
1709
bool ContactConstraintManager::SolvePositionConstraints(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd)
1710
{
1711
JPH_PROFILE_FUNCTION();
1712
1713
bool any_impulse_applied = false;
1714
1715
for (const uint32 *constraint_idx = inConstraintIdxBegin; constraint_idx < inConstraintIdxEnd; ++constraint_idx)
1716
{
1717
ContactConstraint &constraint = mConstraints[*constraint_idx];
1718
1719
// Fetch bodies
1720
Body &body1 = *constraint.mBody1;
1721
Body &body2 = *constraint.mBody2;
1722
1723
// Get transforms
1724
RMat44 transform1 = body1.GetCenterOfMassTransform();
1725
RMat44 transform2 = body2.GetCenterOfMassTransform();
1726
1727
Vec3 ws_normal = constraint.GetWorldSpaceNormal();
1728
1729
for (WorldContactPoint &wcp : constraint.mContactPoints)
1730
{
1731
// Calculate new contact point positions in world space (the bodies may have moved)
1732
RVec3 p1 = transform1 * Vec3::sLoadFloat3Unsafe(wcp.mContactPoint->mPosition1);
1733
RVec3 p2 = transform2 * Vec3::sLoadFloat3Unsafe(wcp.mContactPoint->mPosition2);
1734
1735
// Calculate separation along the normal (negative if interpenetrating)
1736
// Allow a little penetration by default (PhysicsSettings::mPenetrationSlop) to avoid jittering between contact/no-contact which wipes out the contact cache and warm start impulses
1737
// Clamp penetration to a max PhysicsSettings::mMaxPenetrationDistance so that we don't apply a huge impulse if we're penetrating a lot
1738
float separation = max(Vec3(p2 - p1).Dot(ws_normal) + mPhysicsSettings.mPenetrationSlop, -mPhysicsSettings.mMaxPenetrationDistance);
1739
1740
// Only enforce constraint when separation < 0 (otherwise we're apart)
1741
if (separation < 0.0f)
1742
{
1743
// Update constraint properties (bodies may have moved)
1744
wcp.CalculateNonPenetrationConstraintProperties(body1, constraint.mInvMass1, constraint.mInvInertiaScale1, body2, constraint.mInvMass2, constraint.mInvInertiaScale2, p1, p2, ws_normal);
1745
1746
// Solve position errors
1747
if (wcp.mNonPenetrationConstraint.SolvePositionConstraintWithMassOverride(body1, constraint.mInvMass1, body2, constraint.mInvMass2, ws_normal, separation, mPhysicsSettings.mBaumgarte))
1748
any_impulse_applied = true;
1749
}
1750
}
1751
}
1752
1753
return any_impulse_applied;
1754
}
1755
1756
void ContactConstraintManager::RecycleConstraintBuffer()
1757
{
1758
// Reset constraint array
1759
mNumConstraints = 0;
1760
}
1761
1762
void ContactConstraintManager::FinishConstraintBuffer()
1763
{
1764
// Free constraints buffer
1765
mUpdateContext->mTempAllocator->Free(mConstraints, mMaxConstraints * sizeof(ContactConstraint));
1766
mConstraints = nullptr;
1767
mNumConstraints = 0;
1768
1769
// Reset update context
1770
mUpdateContext = nullptr;
1771
}
1772
1773
void ContactConstraintManager::SaveState(StateRecorder &inStream, const StateRecorderFilter *inFilter) const
1774
{
1775
mCache[mCacheWriteIdx ^ 1].SaveState(inStream, inFilter);
1776
}
1777
1778
bool ContactConstraintManager::RestoreState(StateRecorder &inStream, const StateRecorderFilter *inFilter)
1779
{
1780
bool success = mCache[mCacheWriteIdx].RestoreState(mCache[mCacheWriteIdx ^ 1], inStream, inFilter);
1781
1782
// If this is the last part, the cache is finalized
1783
if (inStream.IsLastPart())
1784
{
1785
mCacheWriteIdx ^= 1;
1786
mCache[mCacheWriteIdx].Clear();
1787
}
1788
1789
return success;
1790
}
1791
1792
JPH_NAMESPACE_END
1793
1794