Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Constraints/ContactConstraintManager.h
22316 views
1
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
2
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
3
// SPDX-License-Identifier: MIT
4
5
#pragma once
6
7
#include <Jolt/Core/StaticArray.h>
8
#include <Jolt/Core/LockFreeHashMap.h>
9
#include <Jolt/Physics/EPhysicsUpdateError.h>
10
#include <Jolt/Physics/Body/BodyPair.h>
11
#include <Jolt/Physics/Collision/Shape/SubShapeIDPair.h>
12
#include <Jolt/Physics/Collision/ManifoldBetweenTwoFaces.h>
13
#include <Jolt/Physics/Constraints/ConstraintPart/AxisConstraintPart.h>
14
#include <Jolt/Physics/Constraints/ConstraintPart/DualAxisConstraintPart.h>
15
#include <Jolt/Core/HashCombine.h>
16
#include <Jolt/Core/NonCopyable.h>
17
18
JPH_SUPPRESS_WARNINGS_STD_BEGIN
19
#include <atomic>
20
JPH_SUPPRESS_WARNINGS_STD_END
21
22
JPH_NAMESPACE_BEGIN
23
24
struct PhysicsSettings;
25
class PhysicsUpdateContext;
26
27
/// A contact constraint manager manages all contacts between two bodies
28
///
29
/// WARNING: This class is an internal part of PhysicsSystem, it has no functions that can be called by users of the library.
30
class JPH_EXPORT ContactConstraintManager : public NonCopyable
31
{
32
public:
33
JPH_OVERRIDE_NEW_DELETE
34
35
/// Constructor
36
explicit ContactConstraintManager(const PhysicsSettings &inPhysicsSettings);
37
~ContactConstraintManager();
38
39
/// Initialize the system.
40
/// @param inMaxBodyPairs Maximum amount of body pairs to process (anything else will fall through the world), this number should generally be much higher than the max amount of contact points as there will be lots of bodies close that are not actually touching
41
/// @param inMaxContactConstraints Maximum amount of contact constraints to process (anything else will fall through the world)
42
void Init(uint inMaxBodyPairs, uint inMaxContactConstraints);
43
44
/// Listener that is notified whenever a contact point between two bodies is added/updated/removed
45
void SetContactListener(ContactListener *inListener) { mContactListener = inListener; }
46
ContactListener * GetContactListener() const { return mContactListener; }
47
48
/// Callback function to combine the restitution or friction of two bodies
49
/// Note that when merging manifolds (when PhysicsSettings::mUseManifoldReduction is true) you will only get a callback for the merged manifold.
50
/// It is not possible in that case to get all sub shape ID pairs that were colliding, you'll get the first encountered pair.
51
using CombineFunction = float (*)(const Body &inBody1, const SubShapeID &inSubShapeID1, const Body &inBody2, const SubShapeID &inSubShapeID2);
52
53
/// Set the function that combines the friction of two bodies and returns it
54
/// Default method is the geometric mean: sqrt(friction1 * friction2).
55
void SetCombineFriction(CombineFunction inCombineFriction) { mCombineFriction = inCombineFriction; }
56
CombineFunction GetCombineFriction() const { return mCombineFriction; }
57
58
/// Set the function that combines the restitution of two bodies and returns it
59
/// Default method is max(restitution1, restitution1)
60
void SetCombineRestitution(CombineFunction inCombineRestitution) { mCombineRestitution = inCombineRestitution; }
61
CombineFunction GetCombineRestitution() const { return mCombineRestitution; }
62
63
/// Get the max number of contact constraints that are allowed
64
uint32 GetMaxConstraints() const { return mMaxConstraints; }
65
66
/// Check with the listener if inBody1 and inBody2 could collide, returns false if not
67
inline ValidateResult ValidateContactPoint(const Body &inBody1, const Body &inBody2, RVec3Arg inBaseOffset, const CollideShapeResult &inCollisionResult) const
68
{
69
if (mContactListener == nullptr)
70
return ValidateResult::AcceptAllContactsForThisBodyPair;
71
72
return mContactListener->OnContactValidate(inBody1, inBody2, inBaseOffset, inCollisionResult);
73
}
74
75
/// Sets up the constraint buffer. Should be called before starting collision detection.
76
void PrepareConstraintBuffer(PhysicsUpdateContext *inContext);
77
78
/// Max 4 contact points are needed for a stable manifold
79
static const int MaxContactPoints = 4;
80
81
/// Contacts are allocated in a lock free hash map
82
class ContactAllocator : public LFHMAllocatorContext
83
{
84
public:
85
using LFHMAllocatorContext::LFHMAllocatorContext;
86
87
uint mNumBodyPairs = 0; ///< Total number of body pairs added using this allocator
88
uint mNumManifolds = 0; ///< Total number of manifolds added using this allocator
89
EPhysicsUpdateError mErrors = EPhysicsUpdateError::None; ///< Errors reported on this allocator
90
};
91
92
/// Get a new allocator context for storing contacts. Note that you should call this once and then add multiple contacts using the context.
93
ContactAllocator GetContactAllocator() { return mCache[mCacheWriteIdx].GetContactAllocator(); }
94
95
/// Check if the contact points from the previous frame are reusable and if so copy them.
96
/// When the cache was usable and the pair has been handled: outPairHandled = true.
97
/// When a contact constraint was produced: outConstraintCreated = true.
98
void GetContactsFromCache(ContactAllocator &ioContactAllocator, Body &inBody1, Body &inBody2, bool &outPairHandled, bool &outConstraintCreated);
99
100
/// Handle used to keep track of the current body pair
101
using BodyPairHandle = void *;
102
103
/// Create a handle for a colliding body pair so that contact constraints can be added between them.
104
/// Needs to be called once per body pair per frame before calling AddContactConstraint.
105
BodyPairHandle AddBodyPair(ContactAllocator &ioContactAllocator, const Body &inBody1, const Body &inBody2);
106
107
/// Add a contact constraint for this frame.
108
///
109
/// @param ioContactAllocator The allocator that reserves memory for the contacts
110
/// @param inBodyPair The handle for the contact cache for this body pair
111
/// @param inBody1 The first body that is colliding
112
/// @param inBody2 The second body that is colliding
113
/// @param inManifold The manifold that describes the collision
114
/// @return true if a contact constraint was created (can be false in the case of a sensor)
115
///
116
/// This is using the approach described in 'Modeling and Solving Constraints' by Erin Catto presented at GDC 2009 (and later years with slight modifications).
117
/// We're using the formulas from slide 50 - 53 combined.
118
///
119
/// Euler velocity integration:
120
///
121
/// v1' = v1 + M^-1 P
122
///
123
/// Impulse:
124
///
125
/// P = J^T lambda
126
///
127
/// Constraint force:
128
///
129
/// lambda = -K^-1 J v1
130
///
131
/// Inverse effective mass:
132
///
133
/// K = J M^-1 J^T
134
///
135
/// Constraint equation (limits movement in 1 axis):
136
///
137
/// C = (p2 - p1) . n
138
///
139
/// Jacobian (for position constraint)
140
///
141
/// J = [-n, -r1 x n, n, r2 x n]
142
///
143
/// n = contact normal (pointing away from body 1).
144
/// p1, p2 = positions of collision on body 1 and 2.
145
/// r1, r2 = contact point relative to center of mass of body 1 and body 2 (r1 = p1 - x1, r2 = p2 - x2).
146
/// v1, v2 = (linear velocity, angular velocity): 6 vectors containing linear and angular velocity for body 1 and 2.
147
/// M = mass matrix, a diagonal matrix of the mass and inertia with diagonal [m1, I1, m2, I2].
148
bool AddContactConstraint(ContactAllocator &ioContactAllocator, BodyPairHandle inBodyPair, Body &inBody1, Body &inBody2, const ContactManifold &inManifold);
149
150
/// Finalizes the contact cache, the contact cache that was generated during the calls to AddContactConstraint in this update
151
/// will be used from now on to read from. After finalizing the contact cache, the contact removed callbacks will be called.
152
/// inExpectedNumBodyPairs / inExpectedNumManifolds are the amount of body pairs / manifolds found in the previous step and is
153
/// used to determine the amount of buckets the contact cache hash map will use in the next update.
154
void FinalizeContactCacheAndCallContactPointRemovedCallbacks(uint inExpectedNumBodyPairs, uint inExpectedNumManifolds);
155
156
/// Check if 2 bodies were in contact during the last simulation step. Since contacts are only detected between active bodies, at least one of the bodies must be active.
157
/// Uses the read collision cache to determine if 2 bodies are in contact.
158
bool WereBodiesInContact(const BodyID &inBody1ID, const BodyID &inBody2ID) const;
159
160
/// Get the number of contact constraints that were found
161
uint32 GetNumConstraints() const { return min<uint32>(mNumConstraints, mMaxConstraints); }
162
163
/// Sort contact constraints deterministically
164
void SortContacts(uint32 *inConstraintIdxBegin, uint32 *inConstraintIdxEnd) const;
165
166
/// Get the affected bodies for a given constraint
167
inline void GetAffectedBodies(uint32 inConstraintIdx, const Body *&outBody1, const Body *&outBody2) const
168
{
169
const ContactConstraint &constraint = mConstraints[inConstraintIdx];
170
outBody1 = constraint.mBody1;
171
outBody2 = constraint.mBody2;
172
}
173
174
/// Apply last frame's impulses as an initial guess for this frame's impulses
175
template <class MotionPropertiesCallback>
176
void WarmStartVelocityConstraints(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inWarmStartImpulseRatio, MotionPropertiesCallback &ioCallback);
177
178
/// Solve velocity constraints, when almost nothing changes this should only apply very small impulses
179
/// since we're warm starting with the total impulse applied in the last frame above.
180
///
181
/// Friction wise we're using the Coulomb friction model which says that:
182
///
183
/// |F_T| <= mu |F_N|
184
///
185
/// Where F_T is the tangential force, F_N is the normal force and mu is the friction coefficient
186
///
187
/// In impulse terms this becomes:
188
///
189
/// |lambda_T| <= mu |lambda_N|
190
///
191
/// And the constraint that needs to be applied is exactly the same as a non penetration constraint
192
/// except that we use a tangent instead of a normal. The tangent should point in the direction of the
193
/// tangential velocity of the point:
194
///
195
/// J = [-T, -r1 x T, T, r2 x T]
196
///
197
/// Where T is the tangent.
198
///
199
/// See slide 42 and 43.
200
///
201
/// Restitution is implemented as a velocity bias (see slide 41):
202
///
203
/// b = e v_n^-
204
///
205
/// e = the restitution coefficient, v_n^- is the normal velocity prior to the collision
206
///
207
/// Restitution is only applied when v_n^- is large enough and the points are moving towards collision
208
bool SolveVelocityConstraints(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd);
209
210
/// Save back the lambdas to the contact cache for the next warm start
211
void StoreAppliedImpulses(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd) const;
212
213
/// Solve position constraints.
214
/// This is using the approach described in 'Modeling and Solving Constraints' by Erin Catto presented at GDC 2007.
215
/// On slide 78 it is suggested to split up the Baumgarte stabilization for positional drift so that it does not
216
/// actually add to the momentum. We combine an Euler velocity integrate + a position integrate and then discard the velocity
217
/// change.
218
///
219
/// Constraint force:
220
///
221
/// lambda = -K^-1 b
222
///
223
/// Baumgarte stabilization:
224
///
225
/// b = beta / dt C
226
///
227
/// beta = baumgarte stabilization factor.
228
/// dt = delta time.
229
bool SolvePositionConstraints(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd);
230
231
/// Recycle the constraint buffer. Should be called between collision simulation steps.
232
void RecycleConstraintBuffer();
233
234
/// Terminate the constraint buffer. Should be called after simulation ends.
235
void FinishConstraintBuffer();
236
237
/// Called by continuous collision detection to notify the contact listener that a contact was added
238
/// @param ioContactAllocator The allocator that reserves memory for the contacts
239
/// @param inBody1 The first body that is colliding
240
/// @param inBody2 The second body that is colliding
241
/// @param inManifold The manifold that describes the collision
242
/// @param outSettings The calculated contact settings (may be overridden by the contact listener)
243
void OnCCDContactAdded(ContactAllocator &ioContactAllocator, const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &outSettings);
244
245
#ifdef JPH_DEBUG_RENDERER
246
// Drawing properties
247
static bool sDrawContactPoint;
248
static bool sDrawSupportingFaces;
249
static bool sDrawContactPointReduction;
250
static bool sDrawContactManifolds;
251
#endif // JPH_DEBUG_RENDERER
252
253
/// Saving state for replay
254
void SaveState(StateRecorder &inStream, const StateRecorderFilter *inFilter) const;
255
256
/// Restoring state for replay. Returns false when failed.
257
bool RestoreState(StateRecorder &inStream, const StateRecorderFilter *inFilter);
258
259
private:
260
/// Local space contact point, used for caching impulses
261
class CachedContactPoint
262
{
263
public:
264
/// Saving / restoring state for replay
265
void SaveState(StateRecorder &inStream) const;
266
void RestoreState(StateRecorder &inStream);
267
268
/// Local space positions on body 1 and 2.
269
/// Note: these values are read through sLoadFloat3Unsafe.
270
Float3 mPosition1;
271
Float3 mPosition2;
272
273
/// Total applied impulse during the last update that it was used
274
float mNonPenetrationLambda;
275
Vector<2> mFrictionLambda;
276
};
277
278
static_assert(sizeof(CachedContactPoint) == 36, "Unexpected size");
279
static_assert(alignof(CachedContactPoint) == 4, "Assuming 4 byte aligned");
280
281
/// A single cached manifold
282
class CachedManifold
283
{
284
public:
285
/// Calculate size in bytes needed beyond the size of the class to store inNumContactPoints
286
static int sGetRequiredExtraSize(int inNumContactPoints) { return max(0, inNumContactPoints - 1) * sizeof(CachedContactPoint); }
287
288
/// Calculate total class size needed for storing inNumContactPoints
289
static int sGetRequiredTotalSize(int inNumContactPoints) { return sizeof(CachedManifold) + sGetRequiredExtraSize(inNumContactPoints); }
290
291
/// Saving / restoring state for replay
292
void SaveState(StateRecorder &inStream) const;
293
void RestoreState(StateRecorder &inStream);
294
295
/// Handle to next cached contact points in ManifoldCache::mCachedManifolds for the same body pair
296
uint32 mNextWithSameBodyPair;
297
298
/// Contact normal in the space of 2.
299
/// Note: this value is read through sLoadFloat3Unsafe.
300
Float3 mContactNormal;
301
302
/// Flags for this cached manifold
303
enum class EFlags : uint16
304
{
305
ContactPersisted = 1, ///< If this cache entry was reused in the next simulation update
306
CCDContact = 2 ///< This is a cached manifold reported by continuous collision detection and was only used to create a contact callback
307
};
308
309
/// @see EFlags
310
mutable atomic<uint16> mFlags { 0 };
311
312
/// Number of contact points in the array below
313
uint16 mNumContactPoints;
314
315
/// Contact points that this manifold consists of
316
CachedContactPoint mContactPoints[1];
317
};
318
319
static_assert(sizeof(CachedManifold) == 56, "This structure is expect to not contain any waste due to alignment");
320
static_assert(alignof(CachedManifold) == 4, "Assuming 4 byte aligned");
321
322
/// Define a map that maps SubShapeIDPair -> manifold
323
using ManifoldMap = LockFreeHashMap<SubShapeIDPair, CachedManifold>;
324
using MKeyValue = ManifoldMap::KeyValue;
325
using MKVAndCreated = std::pair<MKeyValue *, bool>;
326
327
/// Start of list of contact points for a particular pair of bodies
328
class CachedBodyPair
329
{
330
public:
331
/// Saving / restoring state for replay
332
void SaveState(StateRecorder &inStream) const;
333
void RestoreState(StateRecorder &inStream);
334
335
/// Local space position difference from Body A to Body B.
336
/// Note: this value is read through sLoadFloat3Unsafe
337
Float3 mDeltaPosition;
338
339
/// Local space rotation difference from Body A to Body B, fourth component of quaternion is not stored but is guaranteed >= 0.
340
/// Note: this value is read through sLoadFloat3Unsafe
341
Float3 mDeltaRotation;
342
343
/// Handle to first manifold in ManifoldCache::mCachedManifolds
344
uint32 mFirstCachedManifold;
345
};
346
347
static_assert(sizeof(CachedBodyPair) == 28, "Unexpected size");
348
static_assert(alignof(CachedBodyPair) == 4, "Assuming 4 byte aligned");
349
350
/// Define a map that maps BodyPair -> CachedBodyPair
351
using BodyPairMap = LockFreeHashMap<BodyPair, CachedBodyPair>;
352
using BPKeyValue = BodyPairMap::KeyValue;
353
354
/// Holds all caches that are needed to quickly find cached body pairs / manifolds
355
class ManifoldCache
356
{
357
public:
358
/// Initialize the cache
359
void Init(uint inMaxBodyPairs, uint inMaxContactConstraints, uint inCachedManifoldsSize);
360
361
/// Reset all entries from the cache
362
void Clear();
363
364
/// Prepare cache before creating new contacts.
365
/// inExpectedNumBodyPairs / inExpectedNumManifolds are the amount of body pairs / manifolds found in the previous step and is used to determine the amount of buckets the contact cache hash map will use.
366
void Prepare(uint inExpectedNumBodyPairs, uint inExpectedNumManifolds);
367
368
/// Get a new allocator context for storing contacts. Note that you should call this once and then add multiple contacts using the context.
369
ContactAllocator GetContactAllocator() { return ContactAllocator(mAllocator, cAllocatorBlockSize); }
370
371
/// Find / create cached entry for SubShapeIDPair -> CachedManifold
372
const MKeyValue * Find(const SubShapeIDPair &inKey, uint64 inKeyHash) const;
373
MKeyValue * Create(ContactAllocator &ioContactAllocator, const SubShapeIDPair &inKey, uint64 inKeyHash, int inNumContactPoints);
374
MKVAndCreated FindOrCreate(ContactAllocator &ioContactAllocator, const SubShapeIDPair &inKey, uint64 inKeyHash, int inNumContactPoints);
375
uint32 ToHandle(const MKeyValue *inKeyValue) const;
376
const MKeyValue * FromHandle(uint32 inHandle) const;
377
378
/// Find / create entry for BodyPair -> CachedBodyPair
379
const BPKeyValue * Find(const BodyPair &inKey, uint64 inKeyHash) const;
380
BPKeyValue * Create(ContactAllocator &ioContactAllocator, const BodyPair &inKey, uint64 inKeyHash);
381
void GetAllBodyPairsSorted(Array<const BPKeyValue *> &outAll) const;
382
void GetAllManifoldsSorted(const CachedBodyPair &inBodyPair, Array<const MKeyValue *> &outAll) const;
383
void GetAllCCDManifoldsSorted(Array<const MKeyValue *> &outAll) const;
384
void ContactPointRemovedCallbacks(ContactListener *inListener);
385
386
#ifdef JPH_ENABLE_ASSERTS
387
/// Get the amount of manifolds in the cache
388
uint GetNumManifolds() const { return mCachedManifolds.GetNumKeyValues(); }
389
390
/// Get the amount of body pairs in the cache
391
uint GetNumBodyPairs() const { return mCachedBodyPairs.GetNumKeyValues(); }
392
393
/// Before a cache is finalized you can only do Create(), after only Find() or Clear()
394
void Finalize();
395
#endif
396
397
/// Saving / restoring state for replay
398
void SaveState(StateRecorder &inStream, const StateRecorderFilter *inFilter) const;
399
bool RestoreState(const ManifoldCache &inReadCache, StateRecorder &inStream, const StateRecorderFilter *inFilter);
400
401
private:
402
/// Block size used when allocating new blocks in the contact cache
403
static constexpr uint32 cAllocatorBlockSize = 4096;
404
405
/// Allocator used by both mCachedManifolds and mCachedBodyPairs, this makes it more likely that a body pair and its manifolds are close in memory
406
LFHMAllocator mAllocator;
407
408
/// Simple hash map for SubShapeIDPair -> CachedManifold
409
ManifoldMap mCachedManifolds { mAllocator };
410
411
/// Simple hash map for BodyPair -> CachedBodyPair
412
BodyPairMap mCachedBodyPairs { mAllocator };
413
414
#ifdef JPH_ENABLE_ASSERTS
415
bool mIsFinalized = false; ///< Marks if this buffer is complete
416
#endif
417
};
418
419
ManifoldCache mCache[2]; ///< We have one cache to read from and one to write to
420
int mCacheWriteIdx = 0; ///< Which cache we're currently writing to
421
422
/// World space contact point, used for solving penetrations
423
class WorldContactPoint
424
{
425
public:
426
/// Calculate constraint properties below
427
void CalculateNonPenetrationConstraintProperties(const Body &inBody1, float inInvMass1, float inInvInertiaScale1, const Body &inBody2, float inInvMass2, float inInvInertiaScale2, RVec3Arg inWorldSpacePosition1, RVec3Arg inWorldSpacePosition2, Vec3Arg inWorldSpaceNormal);
428
429
template <EMotionType Type1, EMotionType Type2>
430
JPH_INLINE void 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);
431
432
/// The constraint parts
433
AxisConstraintPart mNonPenetrationConstraint;
434
AxisConstraintPart mFrictionConstraint1;
435
AxisConstraintPart mFrictionConstraint2;
436
437
/// Contact cache
438
CachedContactPoint * mContactPoint;
439
};
440
441
using WorldContactPoints = StaticArray<WorldContactPoint, MaxContactPoints>;
442
443
/// Contact constraint class, used for solving penetrations
444
class ContactConstraint
445
{
446
public:
447
#ifdef JPH_DEBUG_RENDERER
448
/// Draw the state of the contact constraint
449
void Draw(DebugRenderer *inRenderer, ColorArg inManifoldColor) const;
450
#endif // JPH_DEBUG_RENDERER
451
452
/// Convert the world space normal to a Vec3
453
JPH_INLINE Vec3 GetWorldSpaceNormal() const
454
{
455
return Vec3::sLoadFloat3Unsafe(mWorldSpaceNormal);
456
}
457
458
/// Get the tangents for this contact constraint
459
JPH_INLINE void GetTangents(Vec3 &outTangent1, Vec3 &outTangent2) const
460
{
461
Vec3 ws_normal = GetWorldSpaceNormal();
462
outTangent1 = ws_normal.GetNormalizedPerpendicular();
463
outTangent2 = ws_normal.Cross(outTangent1);
464
}
465
466
Body * mBody1;
467
Body * mBody2;
468
uint64 mSortKey;
469
Float3 mWorldSpaceNormal;
470
float mCombinedFriction;
471
float mInvMass1;
472
float mInvInertiaScale1;
473
float mInvMass2;
474
float mInvInertiaScale2;
475
WorldContactPoints mContactPoints;
476
};
477
478
public:
479
/// The maximum value that can be passed to Init for inMaxContactConstraints. Note you should really use a lower value, using this value will cost a lot of memory!
480
static constexpr uint cMaxContactConstraintsLimit = ~uint(0) / sizeof(ContactConstraint);
481
482
/// The maximum value that can be passed to Init for inMaxBodyPairs. Note you should really use a lower value, using this value will cost a lot of memory!
483
static constexpr uint cMaxBodyPairsLimit = ~uint(0) / sizeof(BodyPairMap::KeyValue);
484
485
private:
486
/// Internal helper function to calculate the friction and non-penetration constraint properties. Templated to the motion type to reduce the amount of branches and calculations.
487
template <EMotionType Type1, EMotionType Type2>
488
JPH_INLINE void TemplatedCalculateFrictionAndNonPenetrationConstraintProperties(ContactConstraint &ioConstraint, const ContactSettings &inSettings, float inDeltaTime, Vec3Arg inGravity, RMat44Arg inTransformBody1, RMat44Arg inTransformBody2, const Body &inBody1, const Body &inBody2);
489
490
/// Internal helper function to calculate the friction and non-penetration constraint properties.
491
inline void CalculateFrictionAndNonPenetrationConstraintProperties(ContactConstraint &ioConstraint, const ContactSettings &inSettings, float inDeltaTime, Vec3Arg inGravity, RMat44Arg inTransformBody1, RMat44Arg inTransformBody2, const Body &inBody1, const Body &inBody2);
492
493
/// Internal helper function to add a contact constraint. Templated to the motion type to reduce the amount of branches and calculations.
494
template <EMotionType Type1, EMotionType Type2>
495
bool TemplatedAddContactConstraint(ContactAllocator &ioContactAllocator, BodyPairHandle inBodyPairHandle, Body &inBody1, Body &inBody2, const ContactManifold &inManifold);
496
497
/// Internal helper function to warm start contact constraint. Templated to the motion type to reduce the amount of branches and calculations.
498
template <EMotionType Type1, EMotionType Type2>
499
JPH_INLINE static void sWarmStartConstraint(ContactConstraint &ioConstraint, MotionProperties *ioMotionProperties1, MotionProperties *ioMotionProperties2, float inWarmStartImpulseRatio);
500
501
/// Internal helper function to solve a single contact constraint. Templated to the motion type to reduce the amount of branches and calculations.
502
template <EMotionType Type1, EMotionType Type2>
503
JPH_INLINE static bool sSolveVelocityConstraint(ContactConstraint &ioConstraint, MotionProperties *ioMotionProperties1, MotionProperties *ioMotionProperties2);
504
505
/// The main physics settings instance
506
const PhysicsSettings & mPhysicsSettings;
507
508
/// Listener that is notified whenever a contact point between two bodies is added/updated/removed
509
ContactListener * mContactListener = nullptr;
510
511
/// Functions that are used to combine friction and restitution of 2 bodies
512
CombineFunction mCombineFriction = [](const Body &inBody1, const SubShapeID &, const Body &inBody2, const SubShapeID &) { return sqrt(inBody1.GetFriction() * inBody2.GetFriction()); };
513
CombineFunction mCombineRestitution = [](const Body &inBody1, const SubShapeID &, const Body &inBody2, const SubShapeID &) { return max(inBody1.GetRestitution(), inBody2.GetRestitution()); };
514
515
/// The constraints that were added this frame
516
ContactConstraint * mConstraints = nullptr;
517
uint32 mMaxConstraints = 0;
518
atomic<uint32> mNumConstraints { 0 };
519
520
/// Context used for this physics update
521
PhysicsUpdateContext * mUpdateContext;
522
};
523
524
JPH_NAMESPACE_END
525
526