Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Constraints/ContactConstraintManager.cpp
21504 views
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)1// SPDX-FileCopyrightText: 2021 Jorrit Rouwe2// SPDX-License-Identifier: MIT34#include <Jolt/Jolt.h>56#include <Jolt/Physics/Constraints/ContactConstraintManager.h>7#include <Jolt/Physics/Constraints/CalculateSolverSteps.h>8#include <Jolt/Physics/Body/Body.h>9#include <Jolt/Physics/PhysicsUpdateContext.h>10#include <Jolt/Physics/PhysicsSettings.h>11#include <Jolt/Physics/PhysicsSystem.h>12#include <Jolt/Physics/IslandBuilder.h>13#include <Jolt/Physics/DeterminismLog.h>14#include <Jolt/Core/TempAllocator.h>15#include <Jolt/Core/QuickSort.h>16#ifdef JPH_DEBUG_RENDERER17#include <Jolt/Renderer/DebugRenderer.h>18#endif // JPH_DEBUG_RENDERER1920JPH_NAMESPACE_BEGIN2122using namespace literals;2324#ifdef JPH_DEBUG_RENDERER25bool ContactConstraintManager::sDrawContactPoint = false;26bool ContactConstraintManager::sDrawSupportingFaces = false;27bool ContactConstraintManager::sDrawContactPointReduction = false;28bool ContactConstraintManager::sDrawContactManifolds = false;29#endif // JPH_DEBUG_RENDERER3031//#define JPH_MANIFOLD_CACHE_DEBUG3233////////////////////////////////////////////////////////////////////////////////////////////////////////34// ContactConstraintManager::WorldContactPoint35////////////////////////////////////////////////////////////////////////////////////////////////////////3637void ContactConstraintManager::WorldContactPoint::CalculateNonPenetrationConstraintProperties(const Body &inBody1, float inInvMass1, float inInvInertiaScale1, const Body &inBody2, float inInvMass2, float inInvInertiaScale2, RVec3Arg inWorldSpacePosition1, RVec3Arg inWorldSpacePosition2, Vec3Arg inWorldSpaceNormal)38{39// Calculate collision points relative to body40RVec3 p = 0.5_r * (inWorldSpacePosition1 + inWorldSpacePosition2);41Vec3 r1 = Vec3(p - inBody1.GetCenterOfMassPosition());42Vec3 r2 = Vec3(p - inBody2.GetCenterOfMassPosition());4344mNonPenetrationConstraint.CalculateConstraintPropertiesWithMassOverride(inBody1, inInvMass1, inInvInertiaScale1, r1, inBody2, inInvMass2, inInvInertiaScale2, r2, inWorldSpaceNormal);45}4647template <EMotionType Type1, EMotionType Type2>48JPH_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)49{50JPH_DET_LOG("TemplatedCalculateFrictionAndNonPenetrationConstraintProperties: p1: " << inWorldSpacePosition1 << " p2: " << inWorldSpacePosition251<< " normal: " << inWorldSpaceNormal << " tangent1: " << inWorldSpaceTangent1 << " tangent2: " << inWorldSpaceTangent252<< " restitution: " << inSettings.mCombinedRestitution << " friction: " << inSettings.mCombinedFriction << " minv: " << inMinVelocityForRestitution53<< " surface_vel: " << inSettings.mRelativeLinearSurfaceVelocity << " surface_ang: " << inSettings.mRelativeAngularSurfaceVelocity);5455// Calculate collision points relative to body56RVec3 p = 0.5_r * (inWorldSpacePosition1 + inWorldSpacePosition2);57Vec3 r1 = Vec3(p - inBody1.GetCenterOfMassPosition());58Vec3 r2 = Vec3(p - inBody2.GetCenterOfMassPosition());5960const MotionProperties *mp1 = inBody1.GetMotionPropertiesUnchecked();61const MotionProperties *mp2 = inBody2.GetMotionPropertiesUnchecked();6263// Calculate velocity of collision points64Vec3 relative_velocity;65if constexpr (Type1 != EMotionType::Static && Type2 != EMotionType::Static)66relative_velocity = mp2->GetPointVelocityCOM(r2) - mp1->GetPointVelocityCOM(r1);67else if constexpr (Type1 != EMotionType::Static)68relative_velocity = -mp1->GetPointVelocityCOM(r1);69else if constexpr (Type2 != EMotionType::Static)70relative_velocity = mp2->GetPointVelocityCOM(r2);71else72{73JPH_ASSERT(false, "Static vs static makes no sense");74relative_velocity = Vec3::sZero();75}76float normal_velocity = relative_velocity.Dot(inWorldSpaceNormal);7778// How much the shapes are penetrating (> 0 if penetrating, < 0 if separated)79float penetration = Vec3(inWorldSpacePosition1 - inWorldSpacePosition2).Dot(inWorldSpaceNormal);8081// If there is no penetration, this is a speculative contact and we will apply a bias to the contact constraint82// so that the constraint becomes relative_velocity . contact normal > -penetration / delta_time83// instead of relative_velocity . contact normal > 084// See: GDC 2013: "Physics for Game Programmers; Continuous Collision" - Erin Catto85float speculative_contact_velocity_bias = max(0.0f, -penetration / inDeltaTime);8687// Determine if the velocity is big enough for restitution88float normal_velocity_bias;89if (inSettings.mCombinedRestitution > 0.0f && normal_velocity < -inMinVelocityForRestitution)90{91// We have a velocity that is big enough for restitution. This is where speculative contacts don't work92// great as we have to decide now if we're going to apply the restitution or not. If the relative93// velocity is big enough for a hit, we apply the restitution (in the end, due to other constraints,94// the objects may actually not collide and we will have applied restitution incorrectly). Another95// artifact that occurs because of this approximation is that the object will bounce from its current96// position rather than from a position where it is touching the other object. This causes the object97// to appear to move faster for 1 frame (the opposite of time stealing).98if (normal_velocity < -speculative_contact_velocity_bias)99{100// The gravity / constant forces are applied in the beginning of the time step.101// If we get here, there was a collision at the beginning of the time step, so we've applied too much force.102// This means that our calculated restitution can be too high resulting in an increase in energy.103// So, when we apply restitution, we cancel the added velocity due to these forces.104Vec3 relative_acceleration;105106// Calculate effect of gravity107if constexpr (Type1 != EMotionType::Static && Type2 != EMotionType::Static)108relative_acceleration = inGravity * (mp2->GetGravityFactor() - mp1->GetGravityFactor());109else if constexpr (Type1 != EMotionType::Static)110relative_acceleration = -inGravity * mp1->GetGravityFactor();111else if constexpr (Type2 != EMotionType::Static)112relative_acceleration = inGravity * mp2->GetGravityFactor();113else114{115JPH_ASSERT(false, "Static vs static makes no sense");116relative_acceleration = Vec3::sZero();117}118119// Calculate effect of accumulated forces120if constexpr (Type1 == EMotionType::Dynamic)121relative_acceleration -= mp1->GetAccumulatedForce() * mp1->GetInverseMass();122if constexpr (Type2 == EMotionType::Dynamic)123relative_acceleration += mp2->GetAccumulatedForce() * mp2->GetInverseMass();124125// We only compensate forces towards the contact normal.126float force_delta_velocity = min(0.0f, relative_acceleration.Dot(inWorldSpaceNormal) * inDeltaTime);127128normal_velocity_bias = inSettings.mCombinedRestitution * (normal_velocity - force_delta_velocity);129}130else131{132// In this case we have predicted that we don't hit the other object, but if we do (due to other constraints changing velocities)133// the speculative contact will prevent penetration but will not apply restitution leading to another artifact.134normal_velocity_bias = speculative_contact_velocity_bias;135}136}137else138{139// No restitution. We can safely apply our contact velocity bias.140normal_velocity_bias = speculative_contact_velocity_bias;141}142143mNonPenetrationConstraint.TemplatedCalculateConstraintProperties<Type1, Type2>(inInvM1, inInvI1, r1, inInvM2, inInvI2, r2, inWorldSpaceNormal, normal_velocity_bias);144145// Calculate friction part146if (inSettings.mCombinedFriction > 0.0f)147{148// Get surface velocity relative to tangents149Vec3 ws_surface_velocity = inSettings.mRelativeLinearSurfaceVelocity + inSettings.mRelativeAngularSurfaceVelocity.Cross(r1);150float surface_velocity1 = inWorldSpaceTangent1.Dot(ws_surface_velocity);151float surface_velocity2 = inWorldSpaceTangent2.Dot(ws_surface_velocity);152153// Implement friction as 2 AxisConstraintParts154mFrictionConstraint1.TemplatedCalculateConstraintProperties<Type1, Type2>(inInvM1, inInvI1, r1, inInvM2, inInvI2, r2, inWorldSpaceTangent1, surface_velocity1);155mFrictionConstraint2.TemplatedCalculateConstraintProperties<Type1, Type2>(inInvM1, inInvI1, r1, inInvM2, inInvI2, r2, inWorldSpaceTangent2, surface_velocity2);156}157else158{159// Turn off friction constraint160mFrictionConstraint1.Deactivate();161mFrictionConstraint2.Deactivate();162}163}164165////////////////////////////////////////////////////////////////////////////////////////////////////////166// ContactConstraintManager::ContactConstraint167////////////////////////////////////////////////////////////////////////////////////////////////////////168169#ifdef JPH_DEBUG_RENDERER170void ContactConstraintManager::ContactConstraint::Draw(DebugRenderer *inRenderer, ColorArg inManifoldColor) const171{172if (mContactPoints.empty())173return;174175// Get body transforms176RMat44 transform_body1 = mBody1->GetCenterOfMassTransform();177RMat44 transform_body2 = mBody2->GetCenterOfMassTransform();178179RVec3 prev_point = transform_body1 * Vec3::sLoadFloat3Unsafe(mContactPoints.back().mContactPoint->mPosition1);180for (const WorldContactPoint &wcp : mContactPoints)181{182// Test if any lambda from the previous frame was transferred183float radius = wcp.mNonPenetrationConstraint.GetTotalLambda() == 0.0f184&& wcp.mFrictionConstraint1.GetTotalLambda() == 0.0f185&& wcp.mFrictionConstraint2.GetTotalLambda() == 0.0f? 0.1f : 0.2f;186187RVec3 next_point = transform_body1 * Vec3::sLoadFloat3Unsafe(wcp.mContactPoint->mPosition1);188inRenderer->DrawMarker(next_point, Color::sCyan, radius);189inRenderer->DrawMarker(transform_body2 * Vec3::sLoadFloat3Unsafe(wcp.mContactPoint->mPosition2), Color::sPurple, radius);190191// Draw edge192inRenderer->DrawArrow(prev_point, next_point, inManifoldColor, 0.05f);193prev_point = next_point;194}195196// Draw normal197RVec3 wp = transform_body1 * Vec3::sLoadFloat3Unsafe(mContactPoints[0].mContactPoint->mPosition1);198inRenderer->DrawArrow(wp, wp + GetWorldSpaceNormal(), Color::sRed, 0.05f);199200// Get tangents201Vec3 t1, t2;202GetTangents(t1, t2);203204// Draw tangents205inRenderer->DrawLine(wp, wp + t1, Color::sGreen);206inRenderer->DrawLine(wp, wp + t2, Color::sBlue);207}208#endif // JPH_DEBUG_RENDERER209210////////////////////////////////////////////////////////////////////////////////////////////////////////211// ContactConstraintManager::CachedContactPoint212////////////////////////////////////////////////////////////////////////////////////////////////////////213214void ContactConstraintManager::CachedContactPoint::SaveState(StateRecorder &inStream) const215{216inStream.Write(mPosition1);217inStream.Write(mPosition2);218inStream.Write(mNonPenetrationLambda);219inStream.Write(mFrictionLambda);220}221222void ContactConstraintManager::CachedContactPoint::RestoreState(StateRecorder &inStream)223{224inStream.Read(mPosition1);225inStream.Read(mPosition2);226inStream.Read(mNonPenetrationLambda);227inStream.Read(mFrictionLambda);228}229230////////////////////////////////////////////////////////////////////////////////////////////////////////231// ContactConstraintManager::CachedManifold232////////////////////////////////////////////////////////////////////////////////////////////////////////233234void ContactConstraintManager::CachedManifold::SaveState(StateRecorder &inStream) const235{236inStream.Write(mContactNormal);237}238239void ContactConstraintManager::CachedManifold::RestoreState(StateRecorder &inStream)240{241inStream.Read(mContactNormal);242}243244////////////////////////////////////////////////////////////////////////////////////////////////////////245// ContactConstraintManager::CachedBodyPair246////////////////////////////////////////////////////////////////////////////////////////////////////////247248void ContactConstraintManager::CachedBodyPair::SaveState(StateRecorder &inStream) const249{250inStream.Write(mDeltaPosition);251inStream.Write(mDeltaRotation);252}253254void ContactConstraintManager::CachedBodyPair::RestoreState(StateRecorder &inStream)255{256inStream.Read(mDeltaPosition);257inStream.Read(mDeltaRotation);258}259260////////////////////////////////////////////////////////////////////////////////////////////////////////261// ContactConstraintManager::ManifoldCache262////////////////////////////////////////////////////////////////////////////////////////////////////////263264void ContactConstraintManager::ManifoldCache::Init(uint inMaxBodyPairs, uint inMaxContactConstraints, uint inCachedManifoldsSize)265{266uint max_body_pairs = min(inMaxBodyPairs, cMaxBodyPairsLimit);267JPH_ASSERT(max_body_pairs == inMaxBodyPairs, "Cannot support this many body pairs!");268JPH_ASSERT(inMaxContactConstraints <= cMaxContactConstraintsLimit); // Should have been enforced by caller269270mAllocator.Init(uint(min(uint64(max_body_pairs) * sizeof(BodyPairMap::KeyValue) + inCachedManifoldsSize, uint64(~uint(0)))));271272mCachedManifolds.Init(GetNextPowerOf2(inMaxContactConstraints));273mCachedBodyPairs.Init(GetNextPowerOf2(max_body_pairs));274}275276void ContactConstraintManager::ManifoldCache::Clear()277{278JPH_PROFILE_FUNCTION();279280mCachedManifolds.Clear();281mCachedBodyPairs.Clear();282mAllocator.Clear();283284#ifdef JPH_ENABLE_ASSERTS285// Mark as incomplete286mIsFinalized = false;287#endif288}289290void ContactConstraintManager::ManifoldCache::Prepare(uint inExpectedNumBodyPairs, uint inExpectedNumManifolds)291{292// Minimum amount of buckets to use in the hash map293constexpr uint32 cMinBuckets = 1024;294295// 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 frame296mCachedManifolds.SetNumBuckets(min(max(cMinBuckets, GetNextPowerOf2(inExpectedNumManifolds)), mCachedManifolds.GetMaxBuckets()));297mCachedBodyPairs.SetNumBuckets(min(max(cMinBuckets, GetNextPowerOf2(inExpectedNumBodyPairs)), mCachedBodyPairs.GetMaxBuckets()));298}299300const ContactConstraintManager::MKeyValue *ContactConstraintManager::ManifoldCache::Find(const SubShapeIDPair &inKey, uint64 inKeyHash) const301{302JPH_ASSERT(mIsFinalized);303return mCachedManifolds.Find(inKey, inKeyHash);304}305306ContactConstraintManager::MKeyValue *ContactConstraintManager::ManifoldCache::Create(ContactAllocator &ioContactAllocator, const SubShapeIDPair &inKey, uint64 inKeyHash, int inNumContactPoints)307{308JPH_ASSERT(!mIsFinalized);309MKeyValue *kv = mCachedManifolds.Create(ioContactAllocator, inKey, inKeyHash, CachedManifold::sGetRequiredExtraSize(inNumContactPoints));310if (kv == nullptr)311{312ioContactAllocator.mErrors |= EPhysicsUpdateError::ManifoldCacheFull;313return nullptr;314}315kv->GetValue().mNumContactPoints = uint16(inNumContactPoints);316++ioContactAllocator.mNumManifolds;317return kv;318}319320ContactConstraintManager::MKVAndCreated ContactConstraintManager::ManifoldCache::FindOrCreate(ContactAllocator &ioContactAllocator, const SubShapeIDPair &inKey, uint64 inKeyHash, int inNumContactPoints)321{322MKeyValue *kv = const_cast<MKeyValue *>(mCachedManifolds.Find(inKey, inKeyHash));323if (kv != nullptr)324return { kv, false };325326return { Create(ioContactAllocator, inKey, inKeyHash, inNumContactPoints), true };327}328329uint32 ContactConstraintManager::ManifoldCache::ToHandle(const MKeyValue *inKeyValue) const330{331JPH_ASSERT(!mIsFinalized);332return mCachedManifolds.ToHandle(inKeyValue);333}334335const ContactConstraintManager::MKeyValue *ContactConstraintManager::ManifoldCache::FromHandle(uint32 inHandle) const336{337JPH_ASSERT(mIsFinalized);338return mCachedManifolds.FromHandle(inHandle);339}340341const ContactConstraintManager::BPKeyValue *ContactConstraintManager::ManifoldCache::Find(const BodyPair &inKey, uint64 inKeyHash) const342{343JPH_ASSERT(mIsFinalized);344return mCachedBodyPairs.Find(inKey, inKeyHash);345}346347ContactConstraintManager::BPKeyValue *ContactConstraintManager::ManifoldCache::Create(ContactAllocator &ioContactAllocator, const BodyPair &inKey, uint64 inKeyHash)348{349JPH_ASSERT(!mIsFinalized);350BPKeyValue *kv = mCachedBodyPairs.Create(ioContactAllocator, inKey, inKeyHash, 0);351if (kv == nullptr)352{353ioContactAllocator.mErrors |= EPhysicsUpdateError::BodyPairCacheFull;354return nullptr;355}356++ioContactAllocator.mNumBodyPairs;357return kv;358}359360void ContactConstraintManager::ManifoldCache::GetAllBodyPairsSorted(Array<const BPKeyValue *> &outAll) const361{362JPH_ASSERT(mIsFinalized);363mCachedBodyPairs.GetAllKeyValues(outAll);364365// Sort by key366QuickSort(outAll.begin(), outAll.end(), [](const BPKeyValue *inLHS, const BPKeyValue *inRHS) {367return inLHS->GetKey() < inRHS->GetKey();368});369}370371void ContactConstraintManager::ManifoldCache::GetAllManifoldsSorted(const CachedBodyPair &inBodyPair, Array<const MKeyValue *> &outAll) const372{373JPH_ASSERT(mIsFinalized);374375// Iterate through the attached manifolds376for (uint32 handle = inBodyPair.mFirstCachedManifold; handle != ManifoldMap::cInvalidHandle; handle = FromHandle(handle)->GetValue().mNextWithSameBodyPair)377{378const MKeyValue *kv = mCachedManifolds.FromHandle(handle);379outAll.push_back(kv);380}381382// Sort by key383QuickSort(outAll.begin(), outAll.end(), [](const MKeyValue *inLHS, const MKeyValue *inRHS) {384return inLHS->GetKey() < inRHS->GetKey();385});386}387388void ContactConstraintManager::ManifoldCache::GetAllCCDManifoldsSorted(Array<const MKeyValue *> &outAll) const389{390mCachedManifolds.GetAllKeyValues(outAll);391392for (int i = (int)outAll.size() - 1; i >= 0; --i)393if ((outAll[i]->GetValue().mFlags & (uint16)CachedManifold::EFlags::CCDContact) == 0)394{395outAll[i] = outAll.back();396outAll.pop_back();397}398399// Sort by key400QuickSort(outAll.begin(), outAll.end(), [](const MKeyValue *inLHS, const MKeyValue *inRHS) {401return inLHS->GetKey() < inRHS->GetKey();402});403}404405void ContactConstraintManager::ManifoldCache::ContactPointRemovedCallbacks(ContactListener *inListener)406{407JPH_PROFILE_FUNCTION();408409for (MKeyValue &kv : mCachedManifolds)410if ((kv.GetValue().mFlags & uint16(CachedManifold::EFlags::ContactPersisted)) == 0)411inListener->OnContactRemoved(kv.GetKey());412}413414#ifdef JPH_ENABLE_ASSERTS415416void ContactConstraintManager::ManifoldCache::Finalize()417{418mIsFinalized = true;419420#ifdef JPH_MANIFOLD_CACHE_DEBUG421Trace("ManifoldMap:");422mCachedManifolds.TraceStats();423Trace("BodyPairMap:");424mCachedBodyPairs.TraceStats();425#endif // JPH_MANIFOLD_CACHE_DEBUG426}427428#endif429430void ContactConstraintManager::ManifoldCache::SaveState(StateRecorder &inStream, const StateRecorderFilter *inFilter) const431{432JPH_ASSERT(mIsFinalized);433434// Get contents of cache435Array<const BPKeyValue *> all_bp;436GetAllBodyPairsSorted(all_bp);437438// Determine which ones to save439Array<const BPKeyValue *> selected_bp;440if (inFilter == nullptr)441selected_bp = std::move(all_bp);442else443{444selected_bp.reserve(all_bp.size());445for (const BPKeyValue *bp_kv : all_bp)446if (inFilter->ShouldSaveContact(bp_kv->GetKey().mBodyA, bp_kv->GetKey().mBodyB))447selected_bp.push_back(bp_kv);448}449450// Write body pairs451uint32 num_body_pairs = uint32(selected_bp.size());452inStream.Write(num_body_pairs);453for (const BPKeyValue *bp_kv : selected_bp)454{455// Write body pair key456inStream.Write(bp_kv->GetKey());457458// Write body pair459const CachedBodyPair &bp = bp_kv->GetValue();460bp.SaveState(inStream);461462// Get attached manifolds463Array<const MKeyValue *> all_m;464GetAllManifoldsSorted(bp, all_m);465466// Write num manifolds467uint32 num_manifolds = uint32(all_m.size());468inStream.Write(num_manifolds);469470// Write all manifolds471for (const MKeyValue *m_kv : all_m)472{473// Write key474inStream.Write(m_kv->GetKey());475const CachedManifold &cm = m_kv->GetValue();476JPH_ASSERT((cm.mFlags & (uint16)CachedManifold::EFlags::CCDContact) == 0);477478// Write amount of contacts479inStream.Write(cm.mNumContactPoints);480481// Write manifold482cm.SaveState(inStream);483484// Write contact points485for (uint32 i = 0; i < cm.mNumContactPoints; ++i)486cm.mContactPoints[i].SaveState(inStream);487}488}489490// Get CCD manifolds491Array<const MKeyValue *> all_m;492GetAllCCDManifoldsSorted(all_m);493494// Determine which ones to save495Array<const MKeyValue *> selected_m;496if (inFilter == nullptr)497selected_m = std::move(all_m);498else499{500selected_m.reserve(all_m.size());501for (const MKeyValue *m_kv : all_m)502if (inFilter->ShouldSaveContact(m_kv->GetKey().GetBody1ID(), m_kv->GetKey().GetBody2ID()))503selected_m.push_back(m_kv);504}505506// Write all CCD manifold keys507uint32 num_manifolds = uint32(selected_m.size());508inStream.Write(num_manifolds);509for (const MKeyValue *m_kv : selected_m)510inStream.Write(m_kv->GetKey());511}512513bool ContactConstraintManager::ManifoldCache::RestoreState(const ManifoldCache &inReadCache, StateRecorder &inStream, const StateRecorderFilter *inFilter)514{515JPH_ASSERT(!mIsFinalized);516517bool success = true;518519// Create a contact allocator for restoring the contact cache520ContactAllocator contact_allocator(GetContactAllocator());521522// When validating, get all existing body pairs523Array<const BPKeyValue *> all_bp;524if (inStream.IsValidating())525inReadCache.GetAllBodyPairsSorted(all_bp);526527// Read amount of body pairs528uint32 num_body_pairs;529if (inStream.IsValidating())530num_body_pairs = uint32(all_bp.size());531inStream.Read(num_body_pairs);532533// Read entire cache534for (uint32 i = 0; i < num_body_pairs; ++i)535{536// Read key537BodyPair body_pair_key;538if (inStream.IsValidating() && i < all_bp.size())539body_pair_key = all_bp[i]->GetKey();540inStream.Read(body_pair_key);541542// Check if we want to restore this contact543if (inFilter == nullptr || inFilter->ShouldRestoreContact(body_pair_key.mBodyA, body_pair_key.mBodyB))544{545// Create new entry for this body pair546uint64 body_pair_hash = body_pair_key.GetHash();547BPKeyValue *bp_kv = Create(contact_allocator, body_pair_key, body_pair_hash);548if (bp_kv == nullptr)549{550// Out of cache space551success = false;552break;553}554CachedBodyPair &bp = bp_kv->GetValue();555556// Read body pair557if (inStream.IsValidating() && i < all_bp.size())558memcpy(&bp, &all_bp[i]->GetValue(), sizeof(CachedBodyPair));559bp.RestoreState(inStream);560561// When validating, get all existing manifolds562Array<const MKeyValue *> all_m;563if (inStream.IsValidating())564inReadCache.GetAllManifoldsSorted(all_bp[i]->GetValue(), all_m);565566// Read amount of manifolds567uint32 num_manifolds = 0;568if (inStream.IsValidating())569num_manifolds = uint32(all_m.size());570inStream.Read(num_manifolds);571572uint32 handle = ManifoldMap::cInvalidHandle;573for (uint32 j = 0; j < num_manifolds; ++j)574{575// Read key576SubShapeIDPair sub_shape_key;577if (inStream.IsValidating() && j < all_m.size())578sub_shape_key = all_m[j]->GetKey();579inStream.Read(sub_shape_key);580uint64 sub_shape_key_hash = sub_shape_key.GetHash();581582// Read amount of contact points583uint16 num_contact_points = 0;584if (inStream.IsValidating() && j < all_m.size())585num_contact_points = all_m[j]->GetValue().mNumContactPoints;586inStream.Read(num_contact_points);587588// Read manifold589MKeyValue *m_kv = Create(contact_allocator, sub_shape_key, sub_shape_key_hash, num_contact_points);590if (m_kv == nullptr)591{592// Out of cache space593success = false;594break;595}596CachedManifold &cm = m_kv->GetValue();597if (inStream.IsValidating() && j < all_m.size())598{599memcpy(&cm, &all_m[j]->GetValue(), CachedManifold::sGetRequiredTotalSize(num_contact_points));600cm.mNumContactPoints = uint16(num_contact_points); // Restore num contact points601}602cm.RestoreState(inStream);603cm.mNextWithSameBodyPair = handle;604handle = ToHandle(m_kv);605606// Read contact points607for (uint32 k = 0; k < num_contact_points; ++k)608cm.mContactPoints[k].RestoreState(inStream);609}610bp.mFirstCachedManifold = handle;611}612else613{614// Skip the contact615CachedBodyPair bp;616bp.RestoreState(inStream);617uint32 num_manifolds = 0;618inStream.Read(num_manifolds);619for (uint32 j = 0; j < num_manifolds; ++j)620{621SubShapeIDPair sub_shape_key;622inStream.Read(sub_shape_key);623uint16 num_contact_points;624inStream.Read(num_contact_points);625CachedManifold cm;626cm.RestoreState(inStream);627for (uint32 k = 0; k < num_contact_points; ++k)628cm.mContactPoints[0].RestoreState(inStream);629}630}631}632633// When validating, get all existing CCD manifolds634Array<const MKeyValue *> all_m;635if (inStream.IsValidating())636inReadCache.GetAllCCDManifoldsSorted(all_m);637638// Read amount of CCD manifolds639uint32 num_manifolds;640if (inStream.IsValidating())641num_manifolds = uint32(all_m.size());642inStream.Read(num_manifolds);643644for (uint32 j = 0; j < num_manifolds; ++j)645{646// Read key647SubShapeIDPair sub_shape_key;648if (inStream.IsValidating() && j < all_m.size())649sub_shape_key = all_m[j]->GetKey();650inStream.Read(sub_shape_key);651652// Check if we want to restore this contact653if (inFilter == nullptr || inFilter->ShouldRestoreContact(sub_shape_key.GetBody1ID(), sub_shape_key.GetBody2ID()))654{655// Create CCD manifold656uint64 sub_shape_key_hash = sub_shape_key.GetHash();657MKeyValue *m_kv = Create(contact_allocator, sub_shape_key, sub_shape_key_hash, 0);658if (m_kv == nullptr)659{660// Out of cache space661success = false;662break;663}664CachedManifold &cm = m_kv->GetValue();665cm.mFlags |= (uint16)CachedManifold::EFlags::CCDContact;666}667}668669#ifdef JPH_ENABLE_ASSERTS670// We don't finalize until the last part is restored671if (inStream.IsLastPart())672mIsFinalized = true;673#endif674675return success;676}677678////////////////////////////////////////////////////////////////////////////////////////////////////////679// ContactConstraintManager680////////////////////////////////////////////////////////////////////////////////////////////////////////681682ContactConstraintManager::ContactConstraintManager(const PhysicsSettings &inPhysicsSettings) :683mPhysicsSettings(inPhysicsSettings)684{685#ifdef JPH_ENABLE_ASSERTS686// For the first frame mark this empty buffer as finalized687mCache[mCacheWriteIdx ^ 1].Finalize();688#endif689}690691ContactConstraintManager::~ContactConstraintManager()692{693JPH_ASSERT(mConstraints == nullptr);694}695696void ContactConstraintManager::Init(uint inMaxBodyPairs, uint inMaxContactConstraints)697{698// Limit the number of constraints so that the allocation size fits in an unsigned integer699mMaxConstraints = min(inMaxContactConstraints, cMaxContactConstraintsLimit);700JPH_ASSERT(mMaxConstraints == inMaxContactConstraints, "Cannot support this many contact constraints!");701702// Calculate worst case cache usage703constexpr uint cMaxManifoldSizePerConstraint = sizeof(CachedManifold) + (MaxContactPoints - 1) * sizeof(CachedContactPoint);704static_assert(cMaxManifoldSizePerConstraint < sizeof(ContactConstraint)); // If not true, then the next line can overflow705uint cached_manifolds_size = mMaxConstraints * cMaxManifoldSizePerConstraint;706707// Init the caches708mCache[0].Init(inMaxBodyPairs, mMaxConstraints, cached_manifolds_size);709mCache[1].Init(inMaxBodyPairs, mMaxConstraints, cached_manifolds_size);710}711712void ContactConstraintManager::PrepareConstraintBuffer(PhysicsUpdateContext *inContext)713{714// Store context715mUpdateContext = inContext;716717// Allocate temporary constraint buffer718JPH_ASSERT(mConstraints == nullptr);719mConstraints = (ContactConstraint *)inContext->mTempAllocator->Allocate(mMaxConstraints * sizeof(ContactConstraint));720}721722template <EMotionType Type1, EMotionType Type2>723JPH_INLINE void ContactConstraintManager::TemplatedCalculateFrictionAndNonPenetrationConstraintProperties(ContactConstraint &ioConstraint, const ContactSettings &inSettings, float inDeltaTime, Vec3Arg inGravity, RMat44Arg inTransformBody1, RMat44Arg inTransformBody2, const Body &inBody1, const Body &inBody2)724{725// Calculate scaled mass and inertia726Mat44 inv_i1;727if constexpr (Type1 == EMotionType::Dynamic)728{729const MotionProperties *mp1 = inBody1.GetMotionPropertiesUnchecked();730inv_i1 = inSettings.mInvInertiaScale1 * mp1->GetInverseInertiaForRotation(inTransformBody1.GetRotation());731}732else733{734inv_i1 = Mat44::sZero();735}736737Mat44 inv_i2;738if constexpr (Type2 == EMotionType::Dynamic)739{740const MotionProperties *mp2 = inBody2.GetMotionPropertiesUnchecked();741inv_i2 = inSettings.mInvInertiaScale2 * mp2->GetInverseInertiaForRotation(inTransformBody2.GetRotation());742}743else744{745inv_i2 = Mat44::sZero();746}747748// Calculate tangents749Vec3 t1, t2;750ioConstraint.GetTangents(t1, t2);751752Vec3 ws_normal = ioConstraint.GetWorldSpaceNormal();753754// Setup velocity constraint properties755float min_velocity_for_restitution = mPhysicsSettings.mMinVelocityForRestitution;756for (WorldContactPoint &wcp : ioConstraint.mContactPoints)757{758RVec3 p1 = inTransformBody1 * Vec3::sLoadFloat3Unsafe(wcp.mContactPoint->mPosition1);759RVec3 p2 = inTransformBody2 * Vec3::sLoadFloat3Unsafe(wcp.mContactPoint->mPosition2);760wcp.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);761}762}763764inline void ContactConstraintManager::CalculateFrictionAndNonPenetrationConstraintProperties(ContactConstraint &ioConstraint, const ContactSettings &inSettings, float inDeltaTime, Vec3Arg inGravity, RMat44Arg inTransformBody1, RMat44Arg inTransformBody2, const Body &inBody1, const Body &inBody2)765{766// Dispatch to the correct templated form767switch (inBody1.GetMotionType())768{769case EMotionType::Dynamic:770switch (inBody2.GetMotionType())771{772case EMotionType::Dynamic:773TemplatedCalculateFrictionAndNonPenetrationConstraintProperties<EMotionType::Dynamic, EMotionType::Dynamic>(ioConstraint, inSettings, inDeltaTime, inGravity, inTransformBody1, inTransformBody2, inBody1, inBody2);774break;775776case EMotionType::Kinematic:777TemplatedCalculateFrictionAndNonPenetrationConstraintProperties<EMotionType::Dynamic, EMotionType::Kinematic>(ioConstraint, inSettings, inDeltaTime, inGravity, inTransformBody1, inTransformBody2, inBody1, inBody2);778break;779780case EMotionType::Static:781TemplatedCalculateFrictionAndNonPenetrationConstraintProperties<EMotionType::Dynamic, EMotionType::Static>(ioConstraint, inSettings, inDeltaTime, inGravity, inTransformBody1, inTransformBody2, inBody1, inBody2);782break;783784default:785JPH_ASSERT(false);786break;787}788break;789790case EMotionType::Kinematic:791JPH_ASSERT(inBody2.IsDynamic());792TemplatedCalculateFrictionAndNonPenetrationConstraintProperties<EMotionType::Kinematic, EMotionType::Dynamic>(ioConstraint, inSettings, inDeltaTime, inGravity, inTransformBody1, inTransformBody2, inBody1, inBody2);793break;794795case EMotionType::Static:796JPH_ASSERT(inBody2.IsDynamic());797TemplatedCalculateFrictionAndNonPenetrationConstraintProperties<EMotionType::Static, EMotionType::Dynamic>(ioConstraint, inSettings, inDeltaTime, inGravity, inTransformBody1, inTransformBody2, inBody1, inBody2);798break;799800default:801JPH_ASSERT(false);802break;803}804}805806void ContactConstraintManager::GetContactsFromCache(ContactAllocator &ioContactAllocator, Body &inBody1, Body &inBody2, bool &outPairHandled, bool &outConstraintCreated)807{808// Start with nothing found and not handled809outConstraintCreated = false;810outPairHandled = false;811812// Swap bodies so that body 1 id < body 2 id813Body *body1, *body2;814if (inBody1.GetID() < inBody2.GetID())815{816body1 = &inBody1;817body2 = &inBody2;818}819else820{821body1 = &inBody2;822body2 = &inBody1;823}824825// Find the cached body pair826BodyPair body_pair_key(body1->GetID(), body2->GetID());827uint64 body_pair_hash = body_pair_key.GetHash();828const ManifoldCache &read_cache = mCache[mCacheWriteIdx ^ 1];829const BPKeyValue *kv = read_cache.Find(body_pair_key, body_pair_hash);830if (kv == nullptr)831return;832const CachedBodyPair &input_cbp = kv->GetValue();833834// Get relative translation835Quat inv_r1 = body1->GetRotation().Conjugated();836Vec3 delta_position = inv_r1 * Vec3(body2->GetCenterOfMassPosition() - body1->GetCenterOfMassPosition());837838// Get old position delta839Vec3 old_delta_position = Vec3::sLoadFloat3Unsafe(input_cbp.mDeltaPosition);840841// Check if bodies are still roughly in the same relative position842if ((delta_position - old_delta_position).LengthSq() > mPhysicsSettings.mBodyPairCacheMaxDeltaPositionSq)843return;844845// Determine relative orientation846Quat delta_rotation = inv_r1 * body2->GetRotation();847848// Reconstruct old quaternion delta849Quat old_delta_rotation = Quat::sLoadFloat3Unsafe(input_cbp.mDeltaRotation);850851// Check if bodies are still roughly in the same relative orientation852// The delta between 2 quaternions p and q is: p q^* = [rotation_axis * sin(angle / 2), cos(angle / 2)]853// From the W component we can extract the angle: cos(angle / 2) = px * qx + py * qy + pz * qz + pw * qw = p . q854// 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)855if (abs(delta_rotation.Dot(old_delta_rotation)) < mPhysicsSettings.mBodyPairCacheCosMaxDeltaRotationDiv2)856return;857858// The cache is valid, return that we've handled this body pair859outPairHandled = true;860861// Copy the cached body pair to this frame862ManifoldCache &write_cache = mCache[mCacheWriteIdx];863BPKeyValue *output_bp_kv = write_cache.Create(ioContactAllocator, body_pair_key, body_pair_hash);864if (output_bp_kv == nullptr)865return; // Out of cache space866CachedBodyPair *output_cbp = &output_bp_kv->GetValue();867memcpy(output_cbp, &input_cbp, sizeof(CachedBodyPair));868869// If there were no contacts, we have handled the contact870if (input_cbp.mFirstCachedManifold == ManifoldMap::cInvalidHandle)871return;872873// Get body transforms874RMat44 transform_body1 = body1->GetCenterOfMassTransform();875RMat44 transform_body2 = body2->GetCenterOfMassTransform();876877// Get time step and gravity878float delta_time = mUpdateContext->mStepDeltaTime;879Vec3 gravity = mUpdateContext->mPhysicsSystem->GetGravity();880881// Copy manifolds882uint32 output_handle = ManifoldMap::cInvalidHandle;883uint32 input_handle = input_cbp.mFirstCachedManifold;884do885{886JPH_PROFILE("Add Constraint From Cached Manifold");887888// Find the existing manifold889const MKeyValue *input_kv = read_cache.FromHandle(input_handle);890const SubShapeIDPair &input_key = input_kv->GetKey();891const CachedManifold &input_cm = input_kv->GetValue();892JPH_ASSERT(input_cm.mNumContactPoints > 0); // There should be contact points in this manifold!893894// Create room for manifold in write buffer and copy data895uint64 input_hash = input_key.GetHash();896MKeyValue *output_kv = write_cache.Create(ioContactAllocator, input_key, input_hash, input_cm.mNumContactPoints);897if (output_kv == nullptr)898break; // Out of cache space899CachedManifold *output_cm = &output_kv->GetValue();900memcpy(output_cm, &input_cm, CachedManifold::sGetRequiredTotalSize(input_cm.mNumContactPoints));901902// Link the object under the body pairs903output_cm->mNextWithSameBodyPair = output_handle;904output_handle = write_cache.ToHandle(output_kv);905906// Calculate default contact settings907ContactSettings settings;908settings.mCombinedFriction = mCombineFriction(*body1, input_key.GetSubShapeID1(), *body2, input_key.GetSubShapeID2());909settings.mCombinedRestitution = mCombineRestitution(*body1, input_key.GetSubShapeID1(), *body2, input_key.GetSubShapeID2());910settings.mIsSensor = body1->IsSensor() || body2->IsSensor();911912// Calculate world space contact normal913Vec3 world_space_normal = transform_body2.Multiply3x3(Vec3::sLoadFloat3Unsafe(output_cm->mContactNormal)).Normalized();914915// Call contact listener to update settings916if (mContactListener != nullptr)917{918// Convert constraint to manifold structure for callback919ContactManifold manifold;920manifold.mWorldSpaceNormal = world_space_normal;921manifold.mSubShapeID1 = input_key.GetSubShapeID1();922manifold.mSubShapeID2 = input_key.GetSubShapeID2();923manifold.mBaseOffset = transform_body1.GetTranslation();924manifold.mRelativeContactPointsOn1.resize(output_cm->mNumContactPoints);925manifold.mRelativeContactPointsOn2.resize(output_cm->mNumContactPoints);926Mat44 local_transform_body2 = transform_body2.PostTranslated(-manifold.mBaseOffset).ToMat44();927float penetration_depth = -FLT_MAX;928for (uint32 i = 0; i < output_cm->mNumContactPoints; ++i)929{930const CachedContactPoint &ccp = output_cm->mContactPoints[i];931manifold.mRelativeContactPointsOn1[i] = transform_body1.Multiply3x3(Vec3::sLoadFloat3Unsafe(ccp.mPosition1));932manifold.mRelativeContactPointsOn2[i] = local_transform_body2 * Vec3::sLoadFloat3Unsafe(ccp.mPosition2);933penetration_depth = max(penetration_depth, (manifold.mRelativeContactPointsOn1[i] - manifold.mRelativeContactPointsOn2[i]).Dot(world_space_normal));934}935manifold.mPenetrationDepth = penetration_depth; // We don't have the penetration depth anymore, estimate it936937// Notify callback938mContactListener->OnContactPersisted(*body1, *body2, manifold, settings);939}940941JPH_ASSERT(settings.mIsSensor || !(body1->IsSensor() || body2->IsSensor()), "Sensors cannot be converted into regular bodies by a contact callback!");942if (!settings.mIsSensor // If one of the bodies is a sensor, don't actually create the constraint943&& ((body1->IsDynamic() && settings.mInvMassScale1 != 0.0f) // One of the bodies must have mass to be able to create a contact constraint944|| (body2->IsDynamic() && settings.mInvMassScale2 != 0.0f)))945{946// Add contact constraint in world space for the solver947uint32 constraint_idx = mNumConstraints++;948if (constraint_idx >= mMaxConstraints)949{950ioContactAllocator.mErrors |= EPhysicsUpdateError::ContactConstraintsFull;951break;952}953954// A constraint will be created955outConstraintCreated = true;956957ContactConstraint &constraint = mConstraints[constraint_idx];958new (&constraint) ContactConstraint();959constraint.mBody1 = body1;960constraint.mBody2 = body2;961constraint.mSortKey = input_hash;962world_space_normal.StoreFloat3(&constraint.mWorldSpaceNormal);963constraint.mCombinedFriction = settings.mCombinedFriction;964constraint.mInvMass1 = body1->GetMotionPropertiesUnchecked() != nullptr? settings.mInvMassScale1 * body1->GetMotionPropertiesUnchecked()->GetInverseMassUnchecked() : 0.0f;965constraint.mInvInertiaScale1 = settings.mInvInertiaScale1;966constraint.mInvMass2 = body2->GetMotionPropertiesUnchecked() != nullptr? settings.mInvMassScale2 * body2->GetMotionPropertiesUnchecked()->GetInverseMassUnchecked() : 0.0f;967constraint.mInvInertiaScale2 = settings.mInvInertiaScale2;968constraint.mContactPoints.resize(output_cm->mNumContactPoints);969for (uint32 i = 0; i < output_cm->mNumContactPoints; ++i)970{971CachedContactPoint &ccp = output_cm->mContactPoints[i];972WorldContactPoint &wcp = constraint.mContactPoints[i];973wcp.mNonPenetrationConstraint.SetTotalLambda(ccp.mNonPenetrationLambda);974wcp.mFrictionConstraint1.SetTotalLambda(ccp.mFrictionLambda[0]);975wcp.mFrictionConstraint2.SetTotalLambda(ccp.mFrictionLambda[1]);976wcp.mContactPoint = &ccp;977}978979JPH_DET_LOG("GetContactsFromCache: id1: " << constraint.mBody1->GetID() << " id2: " << constraint.mBody2->GetID() << " key: " << constraint.mSortKey);980981// Calculate friction and non-penetration constraint properties for all contact points982CalculateFrictionAndNonPenetrationConstraintProperties(constraint, settings, delta_time, gravity, transform_body1, transform_body2, *body1, *body2);983984// Notify island builder985mUpdateContext->mIslandBuilder->LinkContact(constraint_idx, body1->GetIndexInActiveBodiesInternal(), body2->GetIndexInActiveBodiesInternal());986987#ifdef JPH_DEBUG_RENDERER988// Draw the manifold989if (sDrawContactManifolds)990constraint.Draw(DebugRenderer::sInstance, Color::sYellow);991#endif // JPH_DEBUG_RENDERER992}993994// Mark contact as persisted so that we won't fire OnContactRemoved callbacks995input_cm.mFlags |= (uint16)CachedManifold::EFlags::ContactPersisted;996997// Fetch the next manifold998input_handle = input_cm.mNextWithSameBodyPair;999}1000while (input_handle != ManifoldMap::cInvalidHandle);1001output_cbp->mFirstCachedManifold = output_handle;1002}10031004ContactConstraintManager::BodyPairHandle ContactConstraintManager::AddBodyPair(ContactAllocator &ioContactAllocator, const Body &inBody1, const Body &inBody2)1005{1006// Swap bodies so that body 1 id < body 2 id1007const Body *body1, *body2;1008if (inBody1.GetID() < inBody2.GetID())1009{1010body1 = &inBody1;1011body2 = &inBody2;1012}1013else1014{1015body1 = &inBody2;1016body2 = &inBody1;1017}10181019// Add an entry1020BodyPair body_pair_key(body1->GetID(), body2->GetID());1021uint64 body_pair_hash = body_pair_key.GetHash();1022BPKeyValue *body_pair_kv = mCache[mCacheWriteIdx].Create(ioContactAllocator, body_pair_key, body_pair_hash);1023if (body_pair_kv == nullptr)1024return nullptr; // Out of cache space1025CachedBodyPair *cbp = &body_pair_kv->GetValue();1026cbp->mFirstCachedManifold = ManifoldMap::cInvalidHandle;10271028// Get relative translation1029Quat inv_r1 = body1->GetRotation().Conjugated();1030Vec3 delta_position = inv_r1 * Vec3(body2->GetCenterOfMassPosition() - body1->GetCenterOfMassPosition());10311032// Store it1033delta_position.StoreFloat3(&cbp->mDeltaPosition);10341035// Determine relative orientation1036Quat delta_rotation = inv_r1 * body2->GetRotation();10371038// Store it1039delta_rotation.StoreFloat3(&cbp->mDeltaRotation);10401041return cbp;1042}10431044template <EMotionType Type1, EMotionType Type2>1045bool ContactConstraintManager::TemplatedAddContactConstraint(ContactAllocator &ioContactAllocator, BodyPairHandle inBodyPairHandle, Body &inBody1, Body &inBody2, const ContactManifold &inManifold)1046{1047// Calculate hash1048SubShapeIDPair key { inBody1.GetID(), inManifold.mSubShapeID1, inBody2.GetID(), inManifold.mSubShapeID2 };1049uint64 key_hash = key.GetHash();10501051// Determine number of contact points1052int num_contact_points = (int)inManifold.mRelativeContactPointsOn1.size();1053JPH_ASSERT(num_contact_points <= MaxContactPoints);1054JPH_ASSERT(num_contact_points == (int)inManifold.mRelativeContactPointsOn2.size());10551056// Reserve space for new contact cache entry1057// Note that for dynamic vs dynamic we always require the first body to have a lower body id to get a consistent key1058// under which to look up the contact1059ManifoldCache &write_cache = mCache[mCacheWriteIdx];1060MKeyValue *new_manifold_kv = write_cache.Create(ioContactAllocator, key, key_hash, num_contact_points);1061if (new_manifold_kv == nullptr)1062return false; // Out of cache space1063CachedManifold *new_manifold = &new_manifold_kv->GetValue();10641065// Transform the world space normal to the space of body 2 (this is usually the static body)1066RMat44 inverse_transform_body2 = inBody2.GetInverseCenterOfMassTransform();1067inverse_transform_body2.Multiply3x3(inManifold.mWorldSpaceNormal).Normalized().StoreFloat3(&new_manifold->mContactNormal);10681069// Settings object that gets passed to the callback1070ContactSettings settings;1071settings.mCombinedFriction = mCombineFriction(inBody1, inManifold.mSubShapeID1, inBody2, inManifold.mSubShapeID2);1072settings.mCombinedRestitution = mCombineRestitution(inBody1, inManifold.mSubShapeID1, inBody2, inManifold.mSubShapeID2);1073settings.mIsSensor = inBody1.IsSensor() || inBody2.IsSensor();10741075// Get the contact points for the old cache entry1076const ManifoldCache &read_cache = mCache[mCacheWriteIdx ^ 1];1077const MKeyValue *old_manifold_kv = read_cache.Find(key, key_hash);1078const CachedContactPoint *ccp_start;1079const CachedContactPoint *ccp_end;1080if (old_manifold_kv != nullptr)1081{1082// Call point persisted listener1083if (mContactListener != nullptr)1084mContactListener->OnContactPersisted(inBody1, inBody2, inManifold, settings);10851086// Fetch the contact points from the old manifold1087const CachedManifold *old_manifold = &old_manifold_kv->GetValue();1088ccp_start = old_manifold->mContactPoints;1089ccp_end = ccp_start + old_manifold->mNumContactPoints;10901091// Mark contact as persisted so that we won't fire OnContactRemoved callbacks1092old_manifold->mFlags |= (uint16)CachedManifold::EFlags::ContactPersisted;1093}1094else1095{1096// Call point added listener1097if (mContactListener != nullptr)1098mContactListener->OnContactAdded(inBody1, inBody2, inManifold, settings);10991100// No contact points available from old manifold1101ccp_start = nullptr;1102ccp_end = nullptr;1103}11041105// Get inverse transform for body 11106RMat44 inverse_transform_body1 = inBody1.GetInverseCenterOfMassTransform();11071108bool contact_constraint_created = false;11091110// If one of the bodies is a sensor, don't actually create the constraint1111JPH_ASSERT(settings.mIsSensor || !(inBody1.IsSensor() || inBody2.IsSensor()), "Sensors cannot be converted into regular bodies by a contact callback!");1112if (!settings.mIsSensor1113&& ((inBody1.IsDynamic() && settings.mInvMassScale1 != 0.0f) // One of the bodies must have mass to be able to create a contact constraint1114|| (inBody2.IsDynamic() && settings.mInvMassScale2 != 0.0f)))1115{1116// Add contact constraint1117uint32 constraint_idx = mNumConstraints++;1118if (constraint_idx >= mMaxConstraints)1119{1120ioContactAllocator.mErrors |= EPhysicsUpdateError::ContactConstraintsFull;11211122// Manifold has been created already, we're not filling it in, so we need to reset the contact number of points.1123// 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.1124new_manifold->mNumContactPoints = 0;1125return false;1126}11271128// We will create a contact constraint1129contact_constraint_created = true;11301131ContactConstraint &constraint = mConstraints[constraint_idx];1132new (&constraint) ContactConstraint();1133constraint.mBody1 = &inBody1;1134constraint.mBody2 = &inBody2;1135constraint.mSortKey = key_hash;1136inManifold.mWorldSpaceNormal.StoreFloat3(&constraint.mWorldSpaceNormal);1137constraint.mCombinedFriction = settings.mCombinedFriction;1138constraint.mInvMass1 = inBody1.GetMotionPropertiesUnchecked() != nullptr? settings.mInvMassScale1 * inBody1.GetMotionPropertiesUnchecked()->GetInverseMassUnchecked() : 0.0f;1139constraint.mInvInertiaScale1 = settings.mInvInertiaScale1;1140constraint.mInvMass2 = inBody2.GetMotionPropertiesUnchecked() != nullptr? settings.mInvMassScale2 * inBody2.GetMotionPropertiesUnchecked()->GetInverseMassUnchecked() : 0.0f;1141constraint.mInvInertiaScale2 = settings.mInvInertiaScale2;11421143JPH_DET_LOG("TemplatedAddContactConstraint: id1: " << constraint.mBody1->GetID() << " id2: " << constraint.mBody2->GetID() << " key: " << constraint.mSortKey);11441145// Notify island builder1146mUpdateContext->mIslandBuilder->LinkContact(constraint_idx, inBody1.GetIndexInActiveBodiesInternal(), inBody2.GetIndexInActiveBodiesInternal());11471148// Get time step and gravity1149float delta_time = mUpdateContext->mStepDeltaTime;1150Vec3 gravity = mUpdateContext->mPhysicsSystem->GetGravity();11511152// Calculate scaled mass and inertia1153float inv_m1;1154Mat44 inv_i1;1155if constexpr (Type1 == EMotionType::Dynamic)1156{1157const MotionProperties *mp1 = inBody1.GetMotionPropertiesUnchecked();1158inv_m1 = settings.mInvMassScale1 * mp1->GetInverseMass();1159inv_i1 = settings.mInvInertiaScale1 * mp1->GetInverseInertiaForRotation(inverse_transform_body1.Transposed3x3());1160}1161else1162{1163inv_m1 = 0.0f;1164inv_i1 = Mat44::sZero();1165}11661167float inv_m2;1168Mat44 inv_i2;1169if constexpr (Type2 == EMotionType::Dynamic)1170{1171const MotionProperties *mp2 = inBody2.GetMotionPropertiesUnchecked();1172inv_m2 = settings.mInvMassScale2 * mp2->GetInverseMass();1173inv_i2 = settings.mInvInertiaScale2 * mp2->GetInverseInertiaForRotation(inverse_transform_body2.Transposed3x3());1174}1175else1176{1177inv_m2 = 0.0f;1178inv_i2 = Mat44::sZero();1179}11801181// Calculate tangents1182Vec3 t1, t2;1183constraint.GetTangents(t1, t2);11841185constraint.mContactPoints.resize(num_contact_points);1186for (int i = 0; i < num_contact_points; ++i)1187{1188// Convert to world space and set positions1189WorldContactPoint &wcp = constraint.mContactPoints[i];1190RVec3 p1_ws = inManifold.mBaseOffset + inManifold.mRelativeContactPointsOn1[i];1191RVec3 p2_ws = inManifold.mBaseOffset + inManifold.mRelativeContactPointsOn2[i];11921193// Convert to local space to the body1194Vec3 p1_ls = Vec3(inverse_transform_body1 * p1_ws);1195Vec3 p2_ls = Vec3(inverse_transform_body2 * p2_ws);11961197// Check if we have a close contact point from last update1198bool lambda_set = false;1199for (const CachedContactPoint *ccp = ccp_start; ccp < ccp_end; ccp++)1200if (Vec3::sLoadFloat3Unsafe(ccp->mPosition1).IsClose(p1_ls, mPhysicsSettings.mContactPointPreserveLambdaMaxDistSq)1201&& Vec3::sLoadFloat3Unsafe(ccp->mPosition2).IsClose(p2_ls, mPhysicsSettings.mContactPointPreserveLambdaMaxDistSq))1202{1203// Get lambdas from previous frame1204wcp.mNonPenetrationConstraint.SetTotalLambda(ccp->mNonPenetrationLambda);1205wcp.mFrictionConstraint1.SetTotalLambda(ccp->mFrictionLambda[0]);1206wcp.mFrictionConstraint2.SetTotalLambda(ccp->mFrictionLambda[1]);1207lambda_set = true;1208break;1209}1210if (!lambda_set)1211{1212wcp.mNonPenetrationConstraint.SetTotalLambda(0.0f);1213wcp.mFrictionConstraint1.SetTotalLambda(0.0f);1214wcp.mFrictionConstraint2.SetTotalLambda(0.0f);1215}12161217// Create new contact point1218CachedContactPoint &cp = new_manifold->mContactPoints[i];1219p1_ls.StoreFloat3(&cp.mPosition1);1220p2_ls.StoreFloat3(&cp.mPosition2);1221wcp.mContactPoint = &cp;12221223// Setup velocity constraint1224wcp.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);1225}12261227#ifdef JPH_DEBUG_RENDERER1228// Draw the manifold1229if (sDrawContactManifolds)1230constraint.Draw(DebugRenderer::sInstance, Color::sOrange);1231#endif // JPH_DEBUG_RENDERER1232}1233else1234{1235// Store the contact manifold in the cache1236for (int i = 0; i < num_contact_points; ++i)1237{1238// Convert to local space to the body1239Vec3 p1 = Vec3(inverse_transform_body1 * (inManifold.mBaseOffset + inManifold.mRelativeContactPointsOn1[i]));1240Vec3 p2 = Vec3(inverse_transform_body2 * (inManifold.mBaseOffset + inManifold.mRelativeContactPointsOn2[i]));12411242// Create new contact point1243CachedContactPoint &cp = new_manifold->mContactPoints[i];1244p1.StoreFloat3(&cp.mPosition1);1245p2.StoreFloat3(&cp.mPosition2);12461247// Reset contact impulses, we haven't applied any1248cp.mNonPenetrationLambda = 0.0f;1249cp.mFrictionLambda[0] = 0.0f;1250cp.mFrictionLambda[1] = 0.0f;1251}1252}12531254// Store cached contact point in body pair cache1255CachedBodyPair *cbp = reinterpret_cast<CachedBodyPair *>(inBodyPairHandle);1256new_manifold->mNextWithSameBodyPair = cbp->mFirstCachedManifold;1257cbp->mFirstCachedManifold = write_cache.ToHandle(new_manifold_kv);12581259// A contact constraint was added1260return contact_constraint_created;1261}12621263bool ContactConstraintManager::AddContactConstraint(ContactAllocator &ioContactAllocator, BodyPairHandle inBodyPairHandle, Body &inBody1, Body &inBody2, const ContactManifold &inManifold)1264{1265JPH_PROFILE_FUNCTION();12661267JPH_DET_LOG("AddContactConstraint: id1: " << inBody1.GetID() << " id2: " << inBody2.GetID()1268<< " subshape1: " << inManifold.mSubShapeID1 << " subshape2: " << inManifold.mSubShapeID21269<< " normal: " << inManifold.mWorldSpaceNormal << " pendepth: " << inManifold.mPenetrationDepth);12701271JPH_ASSERT(inManifold.mWorldSpaceNormal.IsNormalized());12721273// Swap bodies so that body 1 id < body 2 id1274const ContactManifold *manifold;1275Body *body1, *body2;1276ContactManifold temp;1277if (inBody2.GetID() < inBody1.GetID())1278{1279body1 = &inBody2;1280body2 = &inBody1;1281temp = inManifold.SwapShapes();1282manifold = &temp;1283}1284else1285{1286body1 = &inBody1;1287body2 = &inBody2;1288manifold = &inManifold;1289}12901291// Dispatch to the correct templated form1292// 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 here1293switch (body1->GetMotionType())1294{1295case EMotionType::Dynamic:1296{1297switch (body2->GetMotionType())1298{1299case EMotionType::Dynamic:1300return TemplatedAddContactConstraint<EMotionType::Dynamic, EMotionType::Dynamic>(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold);13011302case EMotionType::Kinematic:1303return TemplatedAddContactConstraint<EMotionType::Dynamic, EMotionType::Kinematic>(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold);13041305case EMotionType::Static:1306return TemplatedAddContactConstraint<EMotionType::Dynamic, EMotionType::Static>(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold);13071308default:1309JPH_ASSERT(false);1310break;1311}1312break;1313}13141315case EMotionType::Kinematic:1316switch (body2->GetMotionType())1317{1318case EMotionType::Dynamic:1319return TemplatedAddContactConstraint<EMotionType::Kinematic, EMotionType::Dynamic>(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold);13201321case EMotionType::Kinematic:1322return TemplatedAddContactConstraint<EMotionType::Kinematic, EMotionType::Kinematic>(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold);13231324case EMotionType::Static:1325return TemplatedAddContactConstraint<EMotionType::Kinematic, EMotionType::Static>(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold);13261327default:1328JPH_ASSERT(false);1329break;1330}1331break;13321333case EMotionType::Static:1334switch (body2->GetMotionType())1335{1336case EMotionType::Dynamic:1337return TemplatedAddContactConstraint<EMotionType::Static, EMotionType::Dynamic>(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold);13381339case EMotionType::Kinematic:1340return TemplatedAddContactConstraint<EMotionType::Static, EMotionType::Kinematic>(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold);13411342case EMotionType::Static: // Static vs static not possible1343default:1344JPH_ASSERT(false);1345break;1346}1347break;13481349default:1350JPH_ASSERT(false);1351break;1352}13531354return false;1355}13561357void ContactConstraintManager::OnCCDContactAdded(ContactAllocator &ioContactAllocator, const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &outSettings)1358{1359JPH_ASSERT(inManifold.mWorldSpaceNormal.IsNormalized());13601361// Calculate contact settings1362outSettings.mCombinedFriction = mCombineFriction(inBody1, inManifold.mSubShapeID1, inBody2, inManifold.mSubShapeID2);1363outSettings.mCombinedRestitution = mCombineRestitution(inBody1, inManifold.mSubShapeID1, inBody2, inManifold.mSubShapeID2);1364outSettings.mIsSensor = false; // For now, no sensors are supported during CCD13651366// 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 work1367if (mContactListener != nullptr)1368{1369// Swap bodies so that body 1 id < body 2 id1370const ContactManifold *manifold;1371const Body *body1, *body2;1372ContactManifold temp;1373if (inBody2.GetID() < inBody1.GetID())1374{1375body1 = &inBody2;1376body2 = &inBody1;1377temp = inManifold.SwapShapes();1378manifold = &temp;1379}1380else1381{1382body1 = &inBody1;1383body2 = &inBody2;1384manifold = &inManifold;1385}13861387// Calculate hash1388SubShapeIDPair key { body1->GetID(), manifold->mSubShapeID1, body2->GetID(), manifold->mSubShapeID2 };1389uint64 key_hash = key.GetHash();13901391// Check if we already created this contact this physics update1392ManifoldCache &write_cache = mCache[mCacheWriteIdx];1393MKVAndCreated new_manifold_kv = write_cache.FindOrCreate(ioContactAllocator, key, key_hash, 0);1394if (new_manifold_kv.second)1395{1396// This contact is new for this physics update, check if previous update we already had this contact.1397const ManifoldCache &read_cache = mCache[mCacheWriteIdx ^ 1];1398const MKeyValue *old_manifold_kv = read_cache.Find(key, key_hash);1399if (old_manifold_kv == nullptr)1400{1401// New contact1402mContactListener->OnContactAdded(*body1, *body2, *manifold, outSettings);1403}1404else1405{1406// Existing contact1407mContactListener->OnContactPersisted(*body1, *body2, *manifold, outSettings);14081409// Mark contact as persisted so that we won't fire OnContactRemoved callbacks1410old_manifold_kv->GetValue().mFlags |= (uint16)CachedManifold::EFlags::ContactPersisted;1411}14121413// Check if the cache is full1414if (new_manifold_kv.first != nullptr)1415{1416// 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 created1417CachedManifold &new_manifold = new_manifold_kv.first->GetValue();1418new_manifold.mContactNormal = { 0, 0, 0 };1419new_manifold.mFlags |= (uint16)CachedManifold::EFlags::CCDContact;1420}1421}1422else1423{1424// Already found this contact this physics update.1425// Note that we can trigger OnContactPersisted multiple times per physics update, but otherwise we have no way of obtaining the settings1426mContactListener->OnContactPersisted(*body1, *body2, *manifold, outSettings);1427}14281429// If we swapped body1 and body2 we need to swap the mass scales back1430if (manifold == &temp)1431{1432std::swap(outSettings.mInvMassScale1, outSettings.mInvMassScale2);1433std::swap(outSettings.mInvInertiaScale1, outSettings.mInvInertiaScale2);1434// Note we do not need to negate the relative surface velocity as it is not applied by the CCD collision constraint1435}1436}14371438JPH_ASSERT(outSettings.mIsSensor || !(inBody1.IsSensor() || inBody2.IsSensor()), "Sensors cannot be converted into regular bodies by a contact callback!");1439}14401441void ContactConstraintManager::SortContacts(uint32 *inConstraintIdxBegin, uint32 *inConstraintIdxEnd) const1442{1443JPH_PROFILE_FUNCTION();14441445QuickSort(inConstraintIdxBegin, inConstraintIdxEnd, [this](uint32 inLHS, uint32 inRHS) {1446const ContactConstraint &lhs = mConstraints[inLHS];1447const ContactConstraint &rhs = mConstraints[inRHS];14481449// Most of the time the sort key will be different so we sort on that1450if (lhs.mSortKey != rhs.mSortKey)1451return lhs.mSortKey < rhs.mSortKey;14521453// If they're equal we use the IDs of body 1 to order1454if (lhs.mBody1 != rhs.mBody1)1455return lhs.mBody1->GetID() < rhs.mBody1->GetID();14561457// If they're still equal we use the IDs of body 2 to order1458if (lhs.mBody2 != rhs.mBody2)1459return lhs.mBody2->GetID() < rhs.mBody2->GetID();14601461JPH_ASSERT(inLHS == inRHS, "Hash collision, ordering will be inconsistent");1462return false;1463});1464}14651466void ContactConstraintManager::FinalizeContactCacheAndCallContactPointRemovedCallbacks(uint inExpectedNumBodyPairs, uint inExpectedNumManifolds)1467{1468JPH_PROFILE_FUNCTION();14691470#ifdef JPH_ENABLE_ASSERTS1471// Mark cache as finalized1472ManifoldCache &old_write_cache = mCache[mCacheWriteIdx];1473old_write_cache.Finalize();14741475// Check that the count of body pairs and manifolds that we tracked outside of the cache (to avoid contention on an atomic) is correct1476JPH_ASSERT(old_write_cache.GetNumBodyPairs() == inExpectedNumBodyPairs);1477JPH_ASSERT(old_write_cache.GetNumManifolds() == inExpectedNumManifolds);1478#endif14791480// Buffers are now complete, make write buffer the read buffer1481mCacheWriteIdx ^= 1;14821483// Get the old read cache / new write cache1484ManifoldCache &old_read_cache = mCache[mCacheWriteIdx];14851486// Call the contact point removal callbacks1487if (mContactListener != nullptr)1488old_read_cache.ContactPointRemovedCallbacks(mContactListener);14891490// We're done with the old read cache now1491old_read_cache.Clear();14921493// Use the amount of contacts from the last iteration to determine the amount of buckets to use in the hash map for the next iteration1494old_read_cache.Prepare(inExpectedNumBodyPairs, inExpectedNumManifolds);1495}14961497bool ContactConstraintManager::WereBodiesInContact(const BodyID &inBody1ID, const BodyID &inBody2ID) const1498{1499// 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)1500const ManifoldCache &read_cache = mCache[mCacheWriteIdx ^ 1];1501BodyPair key;1502if (inBody1ID < inBody2ID)1503key = BodyPair(inBody1ID, inBody2ID);1504else1505key = BodyPair(inBody2ID, inBody1ID);1506uint64 key_hash = key.GetHash();1507const BPKeyValue *kv = read_cache.Find(key, key_hash);1508return kv != nullptr && kv->GetValue().mFirstCachedManifold != ManifoldMap::cInvalidHandle;1509}15101511template <EMotionType Type1, EMotionType Type2>1512JPH_INLINE void ContactConstraintManager::sWarmStartConstraint(ContactConstraint &ioConstraint, MotionProperties *ioMotionProperties1, MotionProperties *ioMotionProperties2, float inWarmStartImpulseRatio)1513{1514// Calculate tangents1515Vec3 t1, t2;1516ioConstraint.GetTangents(t1, t2);15171518Vec3 ws_normal = ioConstraint.GetWorldSpaceNormal();15191520for (WorldContactPoint &wcp : ioConstraint.mContactPoints)1521{1522// Warm starting: Apply impulse from last frame1523if (wcp.mFrictionConstraint1.IsActive() || wcp.mFrictionConstraint2.IsActive())1524{1525wcp.mFrictionConstraint1.TemplatedWarmStart<Type1, Type2>(ioMotionProperties1, ioConstraint.mInvMass1, ioMotionProperties2, ioConstraint.mInvMass2, t1, inWarmStartImpulseRatio);1526wcp.mFrictionConstraint2.TemplatedWarmStart<Type1, Type2>(ioMotionProperties1, ioConstraint.mInvMass1, ioMotionProperties2, ioConstraint.mInvMass2, t2, inWarmStartImpulseRatio);1527}1528wcp.mNonPenetrationConstraint.TemplatedWarmStart<Type1, Type2>(ioMotionProperties1, ioConstraint.mInvMass1, ioMotionProperties2, ioConstraint.mInvMass2, ws_normal, inWarmStartImpulseRatio);1529}1530}15311532template <class MotionPropertiesCallback>1533void ContactConstraintManager::WarmStartVelocityConstraints(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inWarmStartImpulseRatio, MotionPropertiesCallback &ioCallback)1534{1535JPH_PROFILE_FUNCTION();15361537for (const uint32 *constraint_idx = inConstraintIdxBegin; constraint_idx < inConstraintIdxEnd; ++constraint_idx)1538{1539ContactConstraint &constraint = mConstraints[*constraint_idx];15401541// Fetch bodies1542Body &body1 = *constraint.mBody1;1543EMotionType motion_type1 = body1.GetMotionType();1544MotionProperties *motion_properties1 = body1.GetMotionPropertiesUnchecked();15451546Body &body2 = *constraint.mBody2;1547EMotionType motion_type2 = body2.GetMotionType();1548MotionProperties *motion_properties2 = body2.GetMotionPropertiesUnchecked();15491550// Dispatch to the correct templated form1551// Note: Warm starting doesn't differentiate between kinematic/static bodies so we handle both as static bodies1552if (motion_type1 == EMotionType::Dynamic)1553{1554if (motion_type2 == EMotionType::Dynamic)1555{1556sWarmStartConstraint<EMotionType::Dynamic, EMotionType::Dynamic>(constraint, motion_properties1, motion_properties2, inWarmStartImpulseRatio);15571558ioCallback(motion_properties2);1559}1560else1561sWarmStartConstraint<EMotionType::Dynamic, EMotionType::Static>(constraint, motion_properties1, motion_properties2, inWarmStartImpulseRatio);15621563ioCallback(motion_properties1);1564}1565else1566{1567JPH_ASSERT(motion_type2 == EMotionType::Dynamic);15681569sWarmStartConstraint<EMotionType::Static, EMotionType::Dynamic>(constraint, motion_properties1, motion_properties2, inWarmStartImpulseRatio);15701571ioCallback(motion_properties2);1572}1573}1574}15751576// Specialize for the two body callback types1577template void ContactConstraintManager::WarmStartVelocityConstraints<CalculateSolverSteps>(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inWarmStartImpulseRatio, CalculateSolverSteps &ioCallback);1578template void ContactConstraintManager::WarmStartVelocityConstraints<DummyCalculateSolverSteps>(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inWarmStartImpulseRatio, DummyCalculateSolverSteps &ioCallback);15791580template <EMotionType Type1, EMotionType Type2>1581JPH_INLINE bool ContactConstraintManager::sSolveVelocityConstraint(ContactConstraint &ioConstraint, MotionProperties *ioMotionProperties1, MotionProperties *ioMotionProperties2)1582{1583bool any_impulse_applied = false;15841585// Calculate tangents1586Vec3 t1, t2;1587ioConstraint.GetTangents(t1, t2);15881589// First apply all friction constraints (non-penetration is more important than friction)1590for (WorldContactPoint &wcp : ioConstraint.mContactPoints)1591{1592// Check if friction is enabled1593if (wcp.mFrictionConstraint1.IsActive() || wcp.mFrictionConstraint2.IsActive())1594{1595// Calculate impulse to stop motion in tangential direction1596float lambda1 = wcp.mFrictionConstraint1.TemplatedSolveVelocityConstraintGetTotalLambda<Type1, Type2>(ioMotionProperties1, ioMotionProperties2, t1);1597float lambda2 = wcp.mFrictionConstraint2.TemplatedSolveVelocityConstraintGetTotalLambda<Type1, Type2>(ioMotionProperties1, ioMotionProperties2, t2);1598float total_lambda_sq = Square(lambda1) + Square(lambda2);15991600// Calculate max impulse that can be applied. Note that we're using the non-penetration impulse from the previous iteration here.1601// We do this because non-penetration is more important so is solved last (the last things that are solved in an iterative solver1602// contribute the most).1603float max_lambda_f = ioConstraint.mCombinedFriction * wcp.mNonPenetrationConstraint.GetTotalLambda();16041605// If the total lambda that we will apply is too large, scale it back1606if (total_lambda_sq > Square(max_lambda_f))1607{1608float scale = max_lambda_f / sqrt(total_lambda_sq);1609lambda1 *= scale;1610lambda2 *= scale;1611}16121613// Apply the friction impulse1614if (wcp.mFrictionConstraint1.TemplatedSolveVelocityConstraintApplyLambda<Type1, Type2>(ioMotionProperties1, ioConstraint.mInvMass1, ioMotionProperties2, ioConstraint.mInvMass2, t1, lambda1))1615any_impulse_applied = true;1616if (wcp.mFrictionConstraint2.TemplatedSolveVelocityConstraintApplyLambda<Type1, Type2>(ioMotionProperties1, ioConstraint.mInvMass1, ioMotionProperties2, ioConstraint.mInvMass2, t2, lambda2))1617any_impulse_applied = true;1618}1619}16201621Vec3 ws_normal = ioConstraint.GetWorldSpaceNormal();16221623// Then apply all non-penetration constraints1624for (WorldContactPoint &wcp : ioConstraint.mContactPoints)1625{1626// Solve non penetration velocities1627if (wcp.mNonPenetrationConstraint.TemplatedSolveVelocityConstraint<Type1, Type2>(ioMotionProperties1, ioConstraint.mInvMass1, ioMotionProperties2, ioConstraint.mInvMass2, ws_normal, 0.0f, FLT_MAX))1628any_impulse_applied = true;1629}16301631return any_impulse_applied;1632}16331634bool ContactConstraintManager::SolveVelocityConstraints(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd)1635{1636JPH_PROFILE_FUNCTION();16371638bool any_impulse_applied = false;16391640for (const uint32 *constraint_idx = inConstraintIdxBegin; constraint_idx < inConstraintIdxEnd; ++constraint_idx)1641{1642ContactConstraint &constraint = mConstraints[*constraint_idx];16431644// Fetch bodies1645Body &body1 = *constraint.mBody1;1646EMotionType motion_type1 = body1.GetMotionType();1647MotionProperties *motion_properties1 = body1.GetMotionPropertiesUnchecked();16481649Body &body2 = *constraint.mBody2;1650EMotionType motion_type2 = body2.GetMotionType();1651MotionProperties *motion_properties2 = body2.GetMotionPropertiesUnchecked();16521653// Dispatch to the correct templated form1654switch (motion_type1)1655{1656case EMotionType::Dynamic:1657switch (motion_type2)1658{1659case EMotionType::Dynamic:1660any_impulse_applied |= sSolveVelocityConstraint<EMotionType::Dynamic, EMotionType::Dynamic>(constraint, motion_properties1, motion_properties2);1661break;16621663case EMotionType::Kinematic:1664any_impulse_applied |= sSolveVelocityConstraint<EMotionType::Dynamic, EMotionType::Kinematic>(constraint, motion_properties1, motion_properties2);1665break;16661667case EMotionType::Static:1668any_impulse_applied |= sSolveVelocityConstraint<EMotionType::Dynamic, EMotionType::Static>(constraint, motion_properties1, motion_properties2);1669break;16701671default:1672JPH_ASSERT(false);1673break;1674}1675break;16761677case EMotionType::Kinematic:1678JPH_ASSERT(motion_type2 == EMotionType::Dynamic);1679any_impulse_applied |= sSolveVelocityConstraint<EMotionType::Kinematic, EMotionType::Dynamic>(constraint, motion_properties1, motion_properties2);1680break;16811682case EMotionType::Static:1683JPH_ASSERT(motion_type2 == EMotionType::Dynamic);1684any_impulse_applied |= sSolveVelocityConstraint<EMotionType::Static, EMotionType::Dynamic>(constraint, motion_properties1, motion_properties2);1685break;16861687default:1688JPH_ASSERT(false);1689break;1690}1691}16921693return any_impulse_applied;1694}16951696void ContactConstraintManager::StoreAppliedImpulses(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd) const1697{1698// Copy back total applied impulse to cache for the next frame1699for (const uint32 *constraint_idx = inConstraintIdxBegin; constraint_idx < inConstraintIdxEnd; ++constraint_idx)1700{1701const ContactConstraint &constraint = mConstraints[*constraint_idx];17021703for (const WorldContactPoint &wcp : constraint.mContactPoints)1704{1705wcp.mContactPoint->mNonPenetrationLambda = wcp.mNonPenetrationConstraint.GetTotalLambda();1706wcp.mContactPoint->mFrictionLambda[0] = wcp.mFrictionConstraint1.GetTotalLambda();1707wcp.mContactPoint->mFrictionLambda[1] = wcp.mFrictionConstraint2.GetTotalLambda();1708}1709}1710}17111712bool ContactConstraintManager::SolvePositionConstraints(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd)1713{1714JPH_PROFILE_FUNCTION();17151716bool any_impulse_applied = false;17171718for (const uint32 *constraint_idx = inConstraintIdxBegin; constraint_idx < inConstraintIdxEnd; ++constraint_idx)1719{1720ContactConstraint &constraint = mConstraints[*constraint_idx];17211722// Fetch bodies1723Body &body1 = *constraint.mBody1;1724Body &body2 = *constraint.mBody2;17251726// Get transforms1727RMat44 transform1 = body1.GetCenterOfMassTransform();1728RMat44 transform2 = body2.GetCenterOfMassTransform();17291730Vec3 ws_normal = constraint.GetWorldSpaceNormal();17311732for (WorldContactPoint &wcp : constraint.mContactPoints)1733{1734// Calculate new contact point positions in world space (the bodies may have moved)1735RVec3 p1 = transform1 * Vec3::sLoadFloat3Unsafe(wcp.mContactPoint->mPosition1);1736RVec3 p2 = transform2 * Vec3::sLoadFloat3Unsafe(wcp.mContactPoint->mPosition2);17371738// Calculate separation along the normal (negative if interpenetrating)1739// Allow a little penetration by default (PhysicsSettings::mPenetrationSlop) to avoid jittering between contact/no-contact which wipes out the contact cache and warm start impulses1740// Clamp penetration to a max PhysicsSettings::mMaxPenetrationDistance so that we don't apply a huge impulse if we're penetrating a lot1741float separation = max(Vec3(p2 - p1).Dot(ws_normal) + mPhysicsSettings.mPenetrationSlop, -mPhysicsSettings.mMaxPenetrationDistance);17421743// Only enforce constraint when separation < 0 (otherwise we're apart)1744if (separation < 0.0f)1745{1746// Update constraint properties (bodies may have moved)1747wcp.CalculateNonPenetrationConstraintProperties(body1, constraint.mInvMass1, constraint.mInvInertiaScale1, body2, constraint.mInvMass2, constraint.mInvInertiaScale2, p1, p2, ws_normal);17481749// Solve position errors1750if (wcp.mNonPenetrationConstraint.SolvePositionConstraintWithMassOverride(body1, constraint.mInvMass1, body2, constraint.mInvMass2, ws_normal, separation, mPhysicsSettings.mBaumgarte))1751any_impulse_applied = true;1752}1753}1754}17551756return any_impulse_applied;1757}17581759void ContactConstraintManager::RecycleConstraintBuffer()1760{1761// Reset constraint array1762mNumConstraints = 0;1763}17641765void ContactConstraintManager::FinishConstraintBuffer()1766{1767// Free constraints buffer1768mUpdateContext->mTempAllocator->Free(mConstraints, mMaxConstraints * sizeof(ContactConstraint));1769mConstraints = nullptr;1770mNumConstraints = 0;17711772// Reset update context1773mUpdateContext = nullptr;1774}17751776void ContactConstraintManager::SaveState(StateRecorder &inStream, const StateRecorderFilter *inFilter) const1777{1778mCache[mCacheWriteIdx ^ 1].SaveState(inStream, inFilter);1779}17801781bool ContactConstraintManager::RestoreState(StateRecorder &inStream, const StateRecorderFilter *inFilter)1782{1783bool success = mCache[mCacheWriteIdx].RestoreState(mCache[mCacheWriteIdx ^ 1], inStream, inFilter);17841785// If this is the last part, the cache is finalized1786if (inStream.IsLastPart())1787{1788mCacheWriteIdx ^= 1;1789mCache[mCacheWriteIdx].Clear();1790}17911792return success;1793}17941795JPH_NAMESPACE_END179617971798