Jolt Physics
A multi core friendly Game Physics Engine
|
#include <ContactConstraintManager.h>
Classes | |
class | ContactAllocator |
Contacts are allocated in a lock free hash map. More... | |
Public Types | |
using | CombineFunction = float(*)(const Body &inBody1, const SubShapeID &inSubShapeID1, const Body &inBody2, const SubShapeID &inSubShapeID2) |
using | BodyPairHandle = void * |
Handle used to keep track of the current body pair. | |
Public Member Functions | |
JPH_OVERRIDE_NEW_DELETE | ContactConstraintManager (const PhysicsSettings &inPhysicsSettings) |
Constructor. | |
~ContactConstraintManager () | |
void | Init (uint inMaxBodyPairs, uint inMaxContactConstraints) |
void | SetContactListener (ContactListener *inListener) |
Listener that is notified whenever a contact point between two bodies is added/updated/removed. | |
ContactListener * | GetContactListener () const |
void | SetCombineFriction (CombineFunction inCombineFriction) |
CombineFunction | GetCombineFriction () const |
void | SetCombineRestitution (CombineFunction inCombineRestitution) |
CombineFunction | GetCombineRestitution () const |
uint32 | GetMaxConstraints () const |
Get the max number of contact constraints that are allowed. | |
ValidateResult | ValidateContactPoint (const Body &inBody1, const Body &inBody2, RVec3Arg inBaseOffset, const CollideShapeResult &inCollisionResult) const |
Check with the listener if inBody1 and inBody2 could collide, returns false if not. | |
void | PrepareConstraintBuffer (PhysicsUpdateContext *inContext) |
Sets up the constraint buffer. Should be called before starting collision detection. | |
ContactAllocator | GetContactAllocator () |
Get a new allocator context for storing contacts. Note that you should call this once and then add multiple contacts using the context. | |
void | GetContactsFromCache (ContactAllocator &ioContactAllocator, Body &inBody1, Body &inBody2, bool &outPairHandled, bool &outConstraintCreated) |
BodyPairHandle | AddBodyPair (ContactAllocator &ioContactAllocator, const Body &inBody1, const Body &inBody2) |
bool | AddContactConstraint (ContactAllocator &ioContactAllocator, BodyPairHandle inBodyPair, Body &inBody1, Body &inBody2, const ContactManifold &inManifold) |
void | FinalizeContactCacheAndCallContactPointRemovedCallbacks (uint inExpectedNumBodyPairs, uint inExpectedNumManifolds) |
bool | WereBodiesInContact (const BodyID &inBody1ID, const BodyID &inBody2ID) const |
uint32 | GetNumConstraints () const |
Get the number of contact constraints that were found. | |
void | SortContacts (uint32 *inConstraintIdxBegin, uint32 *inConstraintIdxEnd) const |
Sort contact constraints deterministically. | |
void | GetAffectedBodies (uint32 inConstraintIdx, const Body *&outBody1, const Body *&outBody2) const |
Get the affected bodies for a given constraint. | |
template<class MotionPropertiesCallback > | |
void | WarmStartVelocityConstraints (const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inWarmStartImpulseRatio, MotionPropertiesCallback &ioCallback) |
Apply last frame's impulses as an initial guess for this frame's impulses. | |
bool | SolveVelocityConstraints (const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd) |
void | StoreAppliedImpulses (const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd) const |
Save back the lambdas to the contact cache for the next warm start. | |
bool | SolvePositionConstraints (const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd) |
void | RecycleConstraintBuffer () |
Recycle the constraint buffer. Should be called between collision simulation steps. | |
void | FinishConstraintBuffer () |
Terminate the constraint buffer. Should be called after simulation ends. | |
void | OnCCDContactAdded (ContactAllocator &ioContactAllocator, const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &outSettings) |
void | SaveState (StateRecorder &inStream, const StateRecorderFilter *inFilter) const |
Saving state for replay. | |
bool | RestoreState (StateRecorder &inStream, const StateRecorderFilter *inFilter) |
Restoring state for replay. Returns false when failed. | |
Public Member Functions inherited from NonCopyable | |
NonCopyable ()=default | |
NonCopyable (const NonCopyable &)=delete | |
void | operator= (const NonCopyable &)=delete |
Static Public Attributes | |
static const int | MaxContactPoints = 4 |
Max 4 contact points are needed for a stable manifold. | |
static bool | sDrawContactPoint = false |
static bool | sDrawSupportingFaces = false |
static bool | sDrawContactPointReduction = false |
static bool | sDrawContactManifolds = false |
using ContactConstraintManager::BodyPairHandle = void * |
Handle used to keep track of the current body pair.
using ContactConstraintManager::CombineFunction = float (*)(const Body &inBody1, const SubShapeID &inSubShapeID1, const Body &inBody2, const SubShapeID &inSubShapeID2) |
Callback function to combine the restitution or friction of two bodies Note that when merging manifolds (when PhysicsSettings::mUseManifoldReduction is true) you will only get a callback for the merged manifold. It is not possible in that case to get all sub shape ID pairs that were colliding, you'll get the first encountered pair.
|
explicit |
Constructor.
ContactConstraintManager::~ContactConstraintManager | ( | ) |
ContactConstraintManager::BodyPairHandle ContactConstraintManager::AddBodyPair | ( | ContactAllocator & | ioContactAllocator, |
const Body & | inBody1, | ||
const Body & | inBody2 | ||
) |
Create a handle for a colliding body pair so that contact constraints can be added between them. Needs to be called once per body pair per frame before calling AddContactConstraint.
bool ContactConstraintManager::AddContactConstraint | ( | ContactAllocator & | ioContactAllocator, |
BodyPairHandle | inBodyPair, | ||
Body & | inBody1, | ||
Body & | inBody2, | ||
const ContactManifold & | inManifold | ||
) |
Add a contact constraint for this frame.
ioContactAllocator | The allocator that reserves memory for the contacts |
inBodyPair | The handle for the contact cache for this body pair |
inBody1 | The first body that is colliding |
inBody2 | The second body that is colliding |
inManifold | The manifold that describes the collision |
This is using the approach described in 'Modeling and Solving Constraints' by Erin Catto presented at GDC 2009 (and later years with slight modifications). We're using the formulas from slide 50 - 53 combined.
Euler velocity integration:
v1' = v1 + M^-1 P
Impulse:
P = J^T lambda
Constraint force:
lambda = -K^-1 J v1
Inverse effective mass:
K = J M^-1 J^T
Constraint equation (limits movement in 1 axis):
C = (p2 - p1) . n
Jacobian (for position constraint)
J = [-n, -r1 x n, n, r2 x n]
n = contact normal (pointing away from body 1). p1, p2 = positions of collision on body 1 and 2. r1, r2 = contact point relative to center of mass of body 1 and body 2 (r1 = p1 - x1, r2 = p2 - x2). v1, v2 = (linear velocity, angular velocity): 6 vectors containing linear and angular velocity for body 1 and 2. M = mass matrix, a diagonal matrix of the mass and inertia with diagonal [m1, I1, m2, I2].
void ContactConstraintManager::FinalizeContactCacheAndCallContactPointRemovedCallbacks | ( | uint | inExpectedNumBodyPairs, |
uint | inExpectedNumManifolds | ||
) |
Finalizes the contact cache, the contact cache that was generated during the calls to AddContactConstraint in this update will be used from now on to read from. After finalizing the contact cache, the contact removed callbacks will be called. 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 in the next update.
void ContactConstraintManager::FinishConstraintBuffer | ( | ) |
Terminate the constraint buffer. Should be called after simulation ends.
|
inline |
Get the affected bodies for a given constraint.
|
inline |
|
inline |
|
inline |
Get a new allocator context for storing contacts. Note that you should call this once and then add multiple contacts using the context.
|
inline |
void ContactConstraintManager::GetContactsFromCache | ( | ContactAllocator & | ioContactAllocator, |
Body & | inBody1, | ||
Body & | inBody2, | ||
bool & | outPairHandled, | ||
bool & | outConstraintCreated | ||
) |
Check if the contact points from the previous frame are reusable and if so copy them. When the cache was usable and the pair has been handled: outPairHandled = true. When a contact constraint was produced: outConstraintCreated = true.
|
inline |
Get the max number of contact constraints that are allowed.
|
inline |
Get the number of contact constraints that were found.
Initialize the system.
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 |
inMaxContactConstraints | Maximum amount of contact constraints to process (anything else will fall through the world) |
void ContactConstraintManager::OnCCDContactAdded | ( | ContactAllocator & | ioContactAllocator, |
const Body & | inBody1, | ||
const Body & | inBody2, | ||
const ContactManifold & | inManifold, | ||
ContactSettings & | outSettings | ||
) |
Called by continuous collision detection to notify the contact listener that a contact was added
ioContactAllocator | The allocator that reserves memory for the contacts |
inBody1 | The first body that is colliding |
inBody2 | The second body that is colliding |
inManifold | The manifold that describes the collision |
outSettings | The calculated contact settings (may be overridden by the contact listener) |
void ContactConstraintManager::PrepareConstraintBuffer | ( | PhysicsUpdateContext * | inContext | ) |
Sets up the constraint buffer. Should be called before starting collision detection.
void ContactConstraintManager::RecycleConstraintBuffer | ( | ) |
Recycle the constraint buffer. Should be called between collision simulation steps.
bool ContactConstraintManager::RestoreState | ( | StateRecorder & | inStream, |
const StateRecorderFilter * | inFilter | ||
) |
Restoring state for replay. Returns false when failed.
void ContactConstraintManager::SaveState | ( | StateRecorder & | inStream, |
const StateRecorderFilter * | inFilter | ||
) | const |
Saving state for replay.
|
inline |
Set the function that combines the friction of two bodies and returns it Default method is the geometric mean: sqrt(friction1 * friction2).
|
inline |
Set the function that combines the restitution of two bodies and returns it Default method is max(restitution1, restitution1)
|
inline |
Listener that is notified whenever a contact point between two bodies is added/updated/removed.
bool ContactConstraintManager::SolvePositionConstraints | ( | const uint32 * | inConstraintIdxBegin, |
const uint32 * | inConstraintIdxEnd | ||
) |
Solve position constraints. This is using the approach described in 'Modeling and Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity integrate + a position integrate and then discard the velocity change.
Constraint force:
lambda = -K^-1 b
Baumgarte stabilization:
b = beta / dt C
beta = baumgarte stabilization factor. dt = delta time.
bool ContactConstraintManager::SolveVelocityConstraints | ( | const uint32 * | inConstraintIdxBegin, |
const uint32 * | inConstraintIdxEnd | ||
) |
Solve velocity constraints, when almost nothing changes this should only apply very small impulses since we're warm starting with the total impulse applied in the last frame above.
Friction wise we're using the Coulomb friction model which says that:
|F_T| <= mu |F_N|
Where F_T is the tangential force, F_N is the normal force and mu is the friction coefficient
In impulse terms this becomes:
|lambda_T| <= mu |lambda_N|
And the constraint that needs to be applied is exactly the same as a non penetration constraint except that we use a tangent instead of a normal. The tangent should point in the direction of the tangential velocity of the point:
J = [-T, -r1 x T, T, r2 x T]
Where T is the tangent.
See slide 42 and 43.
Restitution is implemented as a velocity bias (see slide 41):
b = e v_n^-
e = the restitution coefficient, v_n^- is the normal velocity prior to the collision
Restitution is only applied when v_n^- is large enough and the points are moving towards collision
void ContactConstraintManager::SortContacts | ( | uint32 * | inConstraintIdxBegin, |
uint32 * | inConstraintIdxEnd | ||
) | const |
Sort contact constraints deterministically.
void ContactConstraintManager::StoreAppliedImpulses | ( | const uint32 * | inConstraintIdxBegin, |
const uint32 * | inConstraintIdxEnd | ||
) | const |
Save back the lambdas to the contact cache for the next warm start.
|
inline |
Check with the listener if inBody1 and inBody2 could collide, returns false if not.
template void ContactConstraintManager::WarmStartVelocityConstraints< DummyCalculateSolverSteps > | ( | const uint32 * | inConstraintIdxBegin, |
const uint32 * | inConstraintIdxEnd, | ||
float | inWarmStartImpulseRatio, | ||
MotionPropertiesCallback & | ioCallback | ||
) |
Apply last frame's impulses as an initial guess for this frame's impulses.
bool ContactConstraintManager::WereBodiesInContact | ( | const BodyID & | inBody1ID, |
const BodyID & | inBody2ID | ||
) | const |
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. Uses the read collision cache to determine if 2 bodies are in contact.
|
static |
Max 4 contact points are needed for a stable manifold.
|
static |
|
static |
|
static |
|
static |