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