Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Constraints/ContactConstraintManager.cpp
9913 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, float inGravityDeltaTimeDotNormal, const Body &inBody1, const Body &inBody2, float inInvM1, float inInvM2, Mat44Arg inInvI1, Mat44Arg inInvI2, RVec3Arg inWorldSpacePosition1, RVec3Arg inWorldSpacePosition2, Vec3Arg inWorldSpaceNormal, Vec3Arg inWorldSpaceTangent1, Vec3Arg inWorldSpaceTangent2, const ContactSettings &inSettings, float inMinVelocityForRestitution)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());5960// The gravity is applied in the beginning of the time step. If we get here, there was a collision61// at the beginning of the time step, so we've applied too much gravity. This means that our62// calculated restitution can be too high, so when we apply restitution, we cancel the added63// velocity due to gravity.64float gravity_dt_dot_normal;6566// Calculate velocity of collision points67Vec3 relative_velocity;68if constexpr (Type1 != EMotionType::Static && Type2 != EMotionType::Static)69{70const MotionProperties *mp1 = inBody1.GetMotionPropertiesUnchecked();71const MotionProperties *mp2 = inBody2.GetMotionPropertiesUnchecked();72relative_velocity = mp2->GetPointVelocityCOM(r2) - mp1->GetPointVelocityCOM(r1);73gravity_dt_dot_normal = inGravityDeltaTimeDotNormal * (mp2->GetGravityFactor() - mp1->GetGravityFactor());74}75else if constexpr (Type1 != EMotionType::Static)76{77const MotionProperties *mp1 = inBody1.GetMotionPropertiesUnchecked();78relative_velocity = -mp1->GetPointVelocityCOM(r1);79gravity_dt_dot_normal = inGravityDeltaTimeDotNormal * mp1->GetGravityFactor();80}81else if constexpr (Type2 != EMotionType::Static)82{83const MotionProperties *mp2 = inBody2.GetMotionPropertiesUnchecked();84relative_velocity = mp2->GetPointVelocityCOM(r2);85gravity_dt_dot_normal = inGravityDeltaTimeDotNormal * mp2->GetGravityFactor();86}87else88{89JPH_ASSERT(false); // Static vs static makes no sense90relative_velocity = Vec3::sZero();91gravity_dt_dot_normal = 0.0f;92}93float normal_velocity = relative_velocity.Dot(inWorldSpaceNormal);9495// How much the shapes are penetrating (> 0 if penetrating, < 0 if separated)96float penetration = Vec3(inWorldSpacePosition1 - inWorldSpacePosition2).Dot(inWorldSpaceNormal);9798// If there is no penetration, this is a speculative contact and we will apply a bias to the contact constraint99// so that the constraint becomes relative_velocity . contact normal > -penetration / delta_time100// instead of relative_velocity . contact normal > 0101// See: GDC 2013: "Physics for Game Programmers; Continuous Collision" - Erin Catto102float speculative_contact_velocity_bias = max(0.0f, -penetration / inDeltaTime);103104// Determine if the velocity is big enough for restitution105float normal_velocity_bias;106if (inSettings.mCombinedRestitution > 0.0f && normal_velocity < -inMinVelocityForRestitution)107{108// We have a velocity that is big enough for restitution. This is where speculative contacts don't work109// great as we have to decide now if we're going to apply the restitution or not. If the relative110// velocity is big enough for a hit, we apply the restitution (in the end, due to other constraints,111// the objects may actually not collide and we will have applied restitution incorrectly). Another112// artifact that occurs because of this approximation is that the object will bounce from its current113// position rather than from a position where it is touching the other object. This causes the object114// to appear to move faster for 1 frame (the opposite of time stealing).115if (normal_velocity < -speculative_contact_velocity_bias)116normal_velocity_bias = inSettings.mCombinedRestitution * (normal_velocity - gravity_dt_dot_normal);117else118// In this case we have predicted that we don't hit the other object, but if we do (due to other constraints changing velocities)119// the speculative contact will prevent penetration but will not apply restitution leading to another artifact.120normal_velocity_bias = speculative_contact_velocity_bias;121}122else123{124// No restitution. We can safely apply our contact velocity bias.125normal_velocity_bias = speculative_contact_velocity_bias;126}127128mNonPenetrationConstraint.TemplatedCalculateConstraintProperties<Type1, Type2>(inInvM1, inInvI1, r1, inInvM2, inInvI2, r2, inWorldSpaceNormal, normal_velocity_bias);129130// Calculate friction part131if (inSettings.mCombinedFriction > 0.0f)132{133// Get surface velocity relative to tangents134Vec3 ws_surface_velocity = inSettings.mRelativeLinearSurfaceVelocity + inSettings.mRelativeAngularSurfaceVelocity.Cross(r1);135float surface_velocity1 = inWorldSpaceTangent1.Dot(ws_surface_velocity);136float surface_velocity2 = inWorldSpaceTangent2.Dot(ws_surface_velocity);137138// Implement friction as 2 AxisConstraintParts139mFrictionConstraint1.TemplatedCalculateConstraintProperties<Type1, Type2>(inInvM1, inInvI1, r1, inInvM2, inInvI2, r2, inWorldSpaceTangent1, surface_velocity1);140mFrictionConstraint2.TemplatedCalculateConstraintProperties<Type1, Type2>(inInvM1, inInvI1, r1, inInvM2, inInvI2, r2, inWorldSpaceTangent2, surface_velocity2);141}142else143{144// Turn off friction constraint145mFrictionConstraint1.Deactivate();146mFrictionConstraint2.Deactivate();147}148}149150////////////////////////////////////////////////////////////////////////////////////////////////////////151// ContactConstraintManager::ContactConstraint152////////////////////////////////////////////////////////////////////////////////////////////////////////153154#ifdef JPH_DEBUG_RENDERER155void ContactConstraintManager::ContactConstraint::Draw(DebugRenderer *inRenderer, ColorArg inManifoldColor) const156{157if (mContactPoints.empty())158return;159160// Get body transforms161RMat44 transform_body1 = mBody1->GetCenterOfMassTransform();162RMat44 transform_body2 = mBody2->GetCenterOfMassTransform();163164RVec3 prev_point = transform_body1 * Vec3::sLoadFloat3Unsafe(mContactPoints.back().mContactPoint->mPosition1);165for (const WorldContactPoint &wcp : mContactPoints)166{167// Test if any lambda from the previous frame was transferred168float radius = wcp.mNonPenetrationConstraint.GetTotalLambda() == 0.0f169&& wcp.mFrictionConstraint1.GetTotalLambda() == 0.0f170&& wcp.mFrictionConstraint2.GetTotalLambda() == 0.0f? 0.1f : 0.2f;171172RVec3 next_point = transform_body1 * Vec3::sLoadFloat3Unsafe(wcp.mContactPoint->mPosition1);173inRenderer->DrawMarker(next_point, Color::sCyan, radius);174inRenderer->DrawMarker(transform_body2 * Vec3::sLoadFloat3Unsafe(wcp.mContactPoint->mPosition2), Color::sPurple, radius);175176// Draw edge177inRenderer->DrawArrow(prev_point, next_point, inManifoldColor, 0.05f);178prev_point = next_point;179}180181// Draw normal182RVec3 wp = transform_body1 * Vec3::sLoadFloat3Unsafe(mContactPoints[0].mContactPoint->mPosition1);183inRenderer->DrawArrow(wp, wp + GetWorldSpaceNormal(), Color::sRed, 0.05f);184185// Get tangents186Vec3 t1, t2;187GetTangents(t1, t2);188189// Draw tangents190inRenderer->DrawLine(wp, wp + t1, Color::sGreen);191inRenderer->DrawLine(wp, wp + t2, Color::sBlue);192}193#endif // JPH_DEBUG_RENDERER194195////////////////////////////////////////////////////////////////////////////////////////////////////////196// ContactConstraintManager::CachedContactPoint197////////////////////////////////////////////////////////////////////////////////////////////////////////198199void ContactConstraintManager::CachedContactPoint::SaveState(StateRecorder &inStream) const200{201inStream.Write(mPosition1);202inStream.Write(mPosition2);203inStream.Write(mNonPenetrationLambda);204inStream.Write(mFrictionLambda);205}206207void ContactConstraintManager::CachedContactPoint::RestoreState(StateRecorder &inStream)208{209inStream.Read(mPosition1);210inStream.Read(mPosition2);211inStream.Read(mNonPenetrationLambda);212inStream.Read(mFrictionLambda);213}214215////////////////////////////////////////////////////////////////////////////////////////////////////////216// ContactConstraintManager::CachedManifold217////////////////////////////////////////////////////////////////////////////////////////////////////////218219void ContactConstraintManager::CachedManifold::SaveState(StateRecorder &inStream) const220{221inStream.Write(mContactNormal);222}223224void ContactConstraintManager::CachedManifold::RestoreState(StateRecorder &inStream)225{226inStream.Read(mContactNormal);227}228229////////////////////////////////////////////////////////////////////////////////////////////////////////230// ContactConstraintManager::CachedBodyPair231////////////////////////////////////////////////////////////////////////////////////////////////////////232233void ContactConstraintManager::CachedBodyPair::SaveState(StateRecorder &inStream) const234{235inStream.Write(mDeltaPosition);236inStream.Write(mDeltaRotation);237}238239void ContactConstraintManager::CachedBodyPair::RestoreState(StateRecorder &inStream)240{241inStream.Read(mDeltaPosition);242inStream.Read(mDeltaRotation);243}244245////////////////////////////////////////////////////////////////////////////////////////////////////////246// ContactConstraintManager::ManifoldCache247////////////////////////////////////////////////////////////////////////////////////////////////////////248249void ContactConstraintManager::ManifoldCache::Init(uint inMaxBodyPairs, uint inMaxContactConstraints, uint inCachedManifoldsSize)250{251uint max_body_pairs = min(inMaxBodyPairs, cMaxBodyPairsLimit);252JPH_ASSERT(max_body_pairs == inMaxBodyPairs, "Cannot support this many body pairs!");253JPH_ASSERT(inMaxContactConstraints <= cMaxContactConstraintsLimit); // Should have been enforced by caller254255mAllocator.Init(uint(min(uint64(max_body_pairs) * sizeof(BodyPairMap::KeyValue) + inCachedManifoldsSize, uint64(~uint(0)))));256257mCachedManifolds.Init(GetNextPowerOf2(inMaxContactConstraints));258mCachedBodyPairs.Init(GetNextPowerOf2(max_body_pairs));259}260261void ContactConstraintManager::ManifoldCache::Clear()262{263JPH_PROFILE_FUNCTION();264265mCachedManifolds.Clear();266mCachedBodyPairs.Clear();267mAllocator.Clear();268269#ifdef JPH_ENABLE_ASSERTS270// Mark as incomplete271mIsFinalized = false;272#endif273}274275void ContactConstraintManager::ManifoldCache::Prepare(uint inExpectedNumBodyPairs, uint inExpectedNumManifolds)276{277// Minimum amount of buckets to use in the hash map278constexpr uint32 cMinBuckets = 1024;279280// 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 frame281mCachedManifolds.SetNumBuckets(min(max(cMinBuckets, GetNextPowerOf2(inExpectedNumManifolds)), mCachedManifolds.GetMaxBuckets()));282mCachedBodyPairs.SetNumBuckets(min(max(cMinBuckets, GetNextPowerOf2(inExpectedNumBodyPairs)), mCachedBodyPairs.GetMaxBuckets()));283}284285const ContactConstraintManager::MKeyValue *ContactConstraintManager::ManifoldCache::Find(const SubShapeIDPair &inKey, uint64 inKeyHash) const286{287JPH_ASSERT(mIsFinalized);288return mCachedManifolds.Find(inKey, inKeyHash);289}290291ContactConstraintManager::MKeyValue *ContactConstraintManager::ManifoldCache::Create(ContactAllocator &ioContactAllocator, const SubShapeIDPair &inKey, uint64 inKeyHash, int inNumContactPoints)292{293JPH_ASSERT(!mIsFinalized);294MKeyValue *kv = mCachedManifolds.Create(ioContactAllocator, inKey, inKeyHash, CachedManifold::sGetRequiredExtraSize(inNumContactPoints));295if (kv == nullptr)296{297ioContactAllocator.mErrors |= EPhysicsUpdateError::ManifoldCacheFull;298return nullptr;299}300kv->GetValue().mNumContactPoints = uint16(inNumContactPoints);301++ioContactAllocator.mNumManifolds;302return kv;303}304305ContactConstraintManager::MKVAndCreated ContactConstraintManager::ManifoldCache::FindOrCreate(ContactAllocator &ioContactAllocator, const SubShapeIDPair &inKey, uint64 inKeyHash, int inNumContactPoints)306{307MKeyValue *kv = const_cast<MKeyValue *>(mCachedManifolds.Find(inKey, inKeyHash));308if (kv != nullptr)309return { kv, false };310311return { Create(ioContactAllocator, inKey, inKeyHash, inNumContactPoints), true };312}313314uint32 ContactConstraintManager::ManifoldCache::ToHandle(const MKeyValue *inKeyValue) const315{316JPH_ASSERT(!mIsFinalized);317return mCachedManifolds.ToHandle(inKeyValue);318}319320const ContactConstraintManager::MKeyValue *ContactConstraintManager::ManifoldCache::FromHandle(uint32 inHandle) const321{322JPH_ASSERT(mIsFinalized);323return mCachedManifolds.FromHandle(inHandle);324}325326const ContactConstraintManager::BPKeyValue *ContactConstraintManager::ManifoldCache::Find(const BodyPair &inKey, uint64 inKeyHash) const327{328JPH_ASSERT(mIsFinalized);329return mCachedBodyPairs.Find(inKey, inKeyHash);330}331332ContactConstraintManager::BPKeyValue *ContactConstraintManager::ManifoldCache::Create(ContactAllocator &ioContactAllocator, const BodyPair &inKey, uint64 inKeyHash)333{334JPH_ASSERT(!mIsFinalized);335BPKeyValue *kv = mCachedBodyPairs.Create(ioContactAllocator, inKey, inKeyHash, 0);336if (kv == nullptr)337{338ioContactAllocator.mErrors |= EPhysicsUpdateError::BodyPairCacheFull;339return nullptr;340}341++ioContactAllocator.mNumBodyPairs;342return kv;343}344345void ContactConstraintManager::ManifoldCache::GetAllBodyPairsSorted(Array<const BPKeyValue *> &outAll) const346{347JPH_ASSERT(mIsFinalized);348mCachedBodyPairs.GetAllKeyValues(outAll);349350// Sort by key351QuickSort(outAll.begin(), outAll.end(), [](const BPKeyValue *inLHS, const BPKeyValue *inRHS) {352return inLHS->GetKey() < inRHS->GetKey();353});354}355356void ContactConstraintManager::ManifoldCache::GetAllManifoldsSorted(const CachedBodyPair &inBodyPair, Array<const MKeyValue *> &outAll) const357{358JPH_ASSERT(mIsFinalized);359360// Iterate through the attached manifolds361for (uint32 handle = inBodyPair.mFirstCachedManifold; handle != ManifoldMap::cInvalidHandle; handle = FromHandle(handle)->GetValue().mNextWithSameBodyPair)362{363const MKeyValue *kv = mCachedManifolds.FromHandle(handle);364outAll.push_back(kv);365}366367// Sort by key368QuickSort(outAll.begin(), outAll.end(), [](const MKeyValue *inLHS, const MKeyValue *inRHS) {369return inLHS->GetKey() < inRHS->GetKey();370});371}372373void ContactConstraintManager::ManifoldCache::GetAllCCDManifoldsSorted(Array<const MKeyValue *> &outAll) const374{375mCachedManifolds.GetAllKeyValues(outAll);376377for (int i = (int)outAll.size() - 1; i >= 0; --i)378if ((outAll[i]->GetValue().mFlags & (uint16)CachedManifold::EFlags::CCDContact) == 0)379{380outAll[i] = outAll.back();381outAll.pop_back();382}383384// Sort by key385QuickSort(outAll.begin(), outAll.end(), [](const MKeyValue *inLHS, const MKeyValue *inRHS) {386return inLHS->GetKey() < inRHS->GetKey();387});388}389390void ContactConstraintManager::ManifoldCache::ContactPointRemovedCallbacks(ContactListener *inListener)391{392JPH_PROFILE_FUNCTION();393394for (MKeyValue &kv : mCachedManifolds)395if ((kv.GetValue().mFlags & uint16(CachedManifold::EFlags::ContactPersisted)) == 0)396inListener->OnContactRemoved(kv.GetKey());397}398399#ifdef JPH_ENABLE_ASSERTS400401void ContactConstraintManager::ManifoldCache::Finalize()402{403mIsFinalized = true;404405#ifdef JPH_MANIFOLD_CACHE_DEBUG406Trace("ManifoldMap:");407mCachedManifolds.TraceStats();408Trace("BodyPairMap:");409mCachedBodyPairs.TraceStats();410#endif // JPH_MANIFOLD_CACHE_DEBUG411}412413#endif414415void ContactConstraintManager::ManifoldCache::SaveState(StateRecorder &inStream, const StateRecorderFilter *inFilter) const416{417JPH_ASSERT(mIsFinalized);418419// Get contents of cache420Array<const BPKeyValue *> all_bp;421GetAllBodyPairsSorted(all_bp);422423// Determine which ones to save424Array<const BPKeyValue *> selected_bp;425if (inFilter == nullptr)426selected_bp = std::move(all_bp);427else428{429selected_bp.reserve(all_bp.size());430for (const BPKeyValue *bp_kv : all_bp)431if (inFilter->ShouldSaveContact(bp_kv->GetKey().mBodyA, bp_kv->GetKey().mBodyB))432selected_bp.push_back(bp_kv);433}434435// Write body pairs436uint32 num_body_pairs = uint32(selected_bp.size());437inStream.Write(num_body_pairs);438for (const BPKeyValue *bp_kv : selected_bp)439{440// Write body pair key441inStream.Write(bp_kv->GetKey());442443// Write body pair444const CachedBodyPair &bp = bp_kv->GetValue();445bp.SaveState(inStream);446447// Get attached manifolds448Array<const MKeyValue *> all_m;449GetAllManifoldsSorted(bp, all_m);450451// Write num manifolds452uint32 num_manifolds = uint32(all_m.size());453inStream.Write(num_manifolds);454455// Write all manifolds456for (const MKeyValue *m_kv : all_m)457{458// Write key459inStream.Write(m_kv->GetKey());460const CachedManifold &cm = m_kv->GetValue();461JPH_ASSERT((cm.mFlags & (uint16)CachedManifold::EFlags::CCDContact) == 0);462463// Write amount of contacts464inStream.Write(cm.mNumContactPoints);465466// Write manifold467cm.SaveState(inStream);468469// Write contact points470for (uint32 i = 0; i < cm.mNumContactPoints; ++i)471cm.mContactPoints[i].SaveState(inStream);472}473}474475// Get CCD manifolds476Array<const MKeyValue *> all_m;477GetAllCCDManifoldsSorted(all_m);478479// Determine which ones to save480Array<const MKeyValue *> selected_m;481if (inFilter == nullptr)482selected_m = std::move(all_m);483else484{485selected_m.reserve(all_m.size());486for (const MKeyValue *m_kv : all_m)487if (inFilter->ShouldSaveContact(m_kv->GetKey().GetBody1ID(), m_kv->GetKey().GetBody2ID()))488selected_m.push_back(m_kv);489}490491// Write all CCD manifold keys492uint32 num_manifolds = uint32(selected_m.size());493inStream.Write(num_manifolds);494for (const MKeyValue *m_kv : selected_m)495inStream.Write(m_kv->GetKey());496}497498bool ContactConstraintManager::ManifoldCache::RestoreState(const ManifoldCache &inReadCache, StateRecorder &inStream, const StateRecorderFilter *inFilter)499{500JPH_ASSERT(!mIsFinalized);501502bool success = true;503504// Create a contact allocator for restoring the contact cache505ContactAllocator contact_allocator(GetContactAllocator());506507// When validating, get all existing body pairs508Array<const BPKeyValue *> all_bp;509if (inStream.IsValidating())510inReadCache.GetAllBodyPairsSorted(all_bp);511512// Read amount of body pairs513uint32 num_body_pairs;514if (inStream.IsValidating())515num_body_pairs = uint32(all_bp.size());516inStream.Read(num_body_pairs);517518// Read entire cache519for (uint32 i = 0; i < num_body_pairs; ++i)520{521// Read key522BodyPair body_pair_key;523if (inStream.IsValidating() && i < all_bp.size())524body_pair_key = all_bp[i]->GetKey();525inStream.Read(body_pair_key);526527// Check if we want to restore this contact528if (inFilter == nullptr || inFilter->ShouldRestoreContact(body_pair_key.mBodyA, body_pair_key.mBodyB))529{530// Create new entry for this body pair531uint64 body_pair_hash = body_pair_key.GetHash();532BPKeyValue *bp_kv = Create(contact_allocator, body_pair_key, body_pair_hash);533if (bp_kv == nullptr)534{535// Out of cache space536success = false;537break;538}539CachedBodyPair &bp = bp_kv->GetValue();540541// Read body pair542if (inStream.IsValidating() && i < all_bp.size())543memcpy(&bp, &all_bp[i]->GetValue(), sizeof(CachedBodyPair));544bp.RestoreState(inStream);545546// When validating, get all existing manifolds547Array<const MKeyValue *> all_m;548if (inStream.IsValidating())549inReadCache.GetAllManifoldsSorted(all_bp[i]->GetValue(), all_m);550551// Read amount of manifolds552uint32 num_manifolds = 0;553if (inStream.IsValidating())554num_manifolds = uint32(all_m.size());555inStream.Read(num_manifolds);556557uint32 handle = ManifoldMap::cInvalidHandle;558for (uint32 j = 0; j < num_manifolds; ++j)559{560// Read key561SubShapeIDPair sub_shape_key;562if (inStream.IsValidating() && j < all_m.size())563sub_shape_key = all_m[j]->GetKey();564inStream.Read(sub_shape_key);565uint64 sub_shape_key_hash = sub_shape_key.GetHash();566567// Read amount of contact points568uint16 num_contact_points = 0;569if (inStream.IsValidating() && j < all_m.size())570num_contact_points = all_m[j]->GetValue().mNumContactPoints;571inStream.Read(num_contact_points);572573// Read manifold574MKeyValue *m_kv = Create(contact_allocator, sub_shape_key, sub_shape_key_hash, num_contact_points);575if (m_kv == nullptr)576{577// Out of cache space578success = false;579break;580}581CachedManifold &cm = m_kv->GetValue();582if (inStream.IsValidating() && j < all_m.size())583{584memcpy(&cm, &all_m[j]->GetValue(), CachedManifold::sGetRequiredTotalSize(num_contact_points));585cm.mNumContactPoints = uint16(num_contact_points); // Restore num contact points586}587cm.RestoreState(inStream);588cm.mNextWithSameBodyPair = handle;589handle = ToHandle(m_kv);590591// Read contact points592for (uint32 k = 0; k < num_contact_points; ++k)593cm.mContactPoints[k].RestoreState(inStream);594}595bp.mFirstCachedManifold = handle;596}597else598{599// Skip the contact600CachedBodyPair bp;601bp.RestoreState(inStream);602uint32 num_manifolds = 0;603inStream.Read(num_manifolds);604for (uint32 j = 0; j < num_manifolds; ++j)605{606SubShapeIDPair sub_shape_key;607inStream.Read(sub_shape_key);608uint16 num_contact_points;609inStream.Read(num_contact_points);610CachedManifold cm;611cm.RestoreState(inStream);612for (uint32 k = 0; k < num_contact_points; ++k)613cm.mContactPoints[0].RestoreState(inStream);614}615}616}617618// When validating, get all existing CCD manifolds619Array<const MKeyValue *> all_m;620if (inStream.IsValidating())621inReadCache.GetAllCCDManifoldsSorted(all_m);622623// Read amount of CCD manifolds624uint32 num_manifolds;625if (inStream.IsValidating())626num_manifolds = uint32(all_m.size());627inStream.Read(num_manifolds);628629for (uint32 j = 0; j < num_manifolds; ++j)630{631// Read key632SubShapeIDPair sub_shape_key;633if (inStream.IsValidating() && j < all_m.size())634sub_shape_key = all_m[j]->GetKey();635inStream.Read(sub_shape_key);636637// Check if we want to restore this contact638if (inFilter == nullptr || inFilter->ShouldRestoreContact(sub_shape_key.GetBody1ID(), sub_shape_key.GetBody2ID()))639{640// Create CCD manifold641uint64 sub_shape_key_hash = sub_shape_key.GetHash();642MKeyValue *m_kv = Create(contact_allocator, sub_shape_key, sub_shape_key_hash, 0);643if (m_kv == nullptr)644{645// Out of cache space646success = false;647break;648}649CachedManifold &cm = m_kv->GetValue();650cm.mFlags |= (uint16)CachedManifold::EFlags::CCDContact;651}652}653654#ifdef JPH_ENABLE_ASSERTS655// We don't finalize until the last part is restored656if (inStream.IsLastPart())657mIsFinalized = true;658#endif659660return success;661}662663////////////////////////////////////////////////////////////////////////////////////////////////////////664// ContactConstraintManager665////////////////////////////////////////////////////////////////////////////////////////////////////////666667ContactConstraintManager::ContactConstraintManager(const PhysicsSettings &inPhysicsSettings) :668mPhysicsSettings(inPhysicsSettings)669{670#ifdef JPH_ENABLE_ASSERTS671// For the first frame mark this empty buffer as finalized672mCache[mCacheWriteIdx ^ 1].Finalize();673#endif674}675676ContactConstraintManager::~ContactConstraintManager()677{678JPH_ASSERT(mConstraints == nullptr);679}680681void ContactConstraintManager::Init(uint inMaxBodyPairs, uint inMaxContactConstraints)682{683// Limit the number of constraints so that the allocation size fits in an unsigned integer684mMaxConstraints = min(inMaxContactConstraints, cMaxContactConstraintsLimit);685JPH_ASSERT(mMaxConstraints == inMaxContactConstraints, "Cannot support this many contact constraints!");686687// Calculate worst case cache usage688constexpr uint cMaxManifoldSizePerConstraint = sizeof(CachedManifold) + (MaxContactPoints - 1) * sizeof(CachedContactPoint);689static_assert(cMaxManifoldSizePerConstraint < sizeof(ContactConstraint)); // If not true, then the next line can overflow690uint cached_manifolds_size = mMaxConstraints * cMaxManifoldSizePerConstraint;691692// Init the caches693mCache[0].Init(inMaxBodyPairs, mMaxConstraints, cached_manifolds_size);694mCache[1].Init(inMaxBodyPairs, mMaxConstraints, cached_manifolds_size);695}696697void ContactConstraintManager::PrepareConstraintBuffer(PhysicsUpdateContext *inContext)698{699// Store context700mUpdateContext = inContext;701702// Allocate temporary constraint buffer703JPH_ASSERT(mConstraints == nullptr);704mConstraints = (ContactConstraint *)inContext->mTempAllocator->Allocate(mMaxConstraints * sizeof(ContactConstraint));705}706707template <EMotionType Type1, EMotionType Type2>708JPH_INLINE void ContactConstraintManager::TemplatedCalculateFrictionAndNonPenetrationConstraintProperties(ContactConstraint &ioConstraint, const ContactSettings &inSettings, float inDeltaTime, Vec3Arg inGravityDeltaTime, RMat44Arg inTransformBody1, RMat44Arg inTransformBody2, const Body &inBody1, const Body &inBody2)709{710// Calculate scaled mass and inertia711Mat44 inv_i1;712if constexpr (Type1 == EMotionType::Dynamic)713{714const MotionProperties *mp1 = inBody1.GetMotionPropertiesUnchecked();715inv_i1 = inSettings.mInvInertiaScale1 * mp1->GetInverseInertiaForRotation(inTransformBody1.GetRotation());716}717else718{719inv_i1 = Mat44::sZero();720}721722Mat44 inv_i2;723if constexpr (Type2 == EMotionType::Dynamic)724{725const MotionProperties *mp2 = inBody2.GetMotionPropertiesUnchecked();726inv_i2 = inSettings.mInvInertiaScale2 * mp2->GetInverseInertiaForRotation(inTransformBody2.GetRotation());727}728else729{730inv_i2 = Mat44::sZero();731}732733// Calculate tangents734Vec3 t1, t2;735ioConstraint.GetTangents(t1, t2);736737Vec3 ws_normal = ioConstraint.GetWorldSpaceNormal();738739// Calculate value for restitution correction740float gravity_dt_dot_normal = inGravityDeltaTime.Dot(ws_normal);741742// Setup velocity constraint properties743float min_velocity_for_restitution = mPhysicsSettings.mMinVelocityForRestitution;744for (WorldContactPoint &wcp : ioConstraint.mContactPoints)745{746RVec3 p1 = inTransformBody1 * Vec3::sLoadFloat3Unsafe(wcp.mContactPoint->mPosition1);747RVec3 p2 = inTransformBody2 * Vec3::sLoadFloat3Unsafe(wcp.mContactPoint->mPosition2);748wcp.TemplatedCalculateFrictionAndNonPenetrationConstraintProperties<Type1, Type2>(inDeltaTime, gravity_dt_dot_normal, inBody1, inBody2, ioConstraint.mInvMass1, ioConstraint.mInvMass2, inv_i1, inv_i2, p1, p2, ws_normal, t1, t2, inSettings, min_velocity_for_restitution);749}750}751752inline void ContactConstraintManager::CalculateFrictionAndNonPenetrationConstraintProperties(ContactConstraint &ioConstraint, const ContactSettings &inSettings, float inDeltaTime, Vec3Arg inGravityDeltaTime, RMat44Arg inTransformBody1, RMat44Arg inTransformBody2, const Body &inBody1, const Body &inBody2)753{754// Dispatch to the correct templated form755switch (inBody1.GetMotionType())756{757case EMotionType::Dynamic:758switch (inBody2.GetMotionType())759{760case EMotionType::Dynamic:761TemplatedCalculateFrictionAndNonPenetrationConstraintProperties<EMotionType::Dynamic, EMotionType::Dynamic>(ioConstraint, inSettings, inDeltaTime, inGravityDeltaTime, inTransformBody1, inTransformBody2, inBody1, inBody2);762break;763764case EMotionType::Kinematic:765TemplatedCalculateFrictionAndNonPenetrationConstraintProperties<EMotionType::Dynamic, EMotionType::Kinematic>(ioConstraint, inSettings, inDeltaTime, inGravityDeltaTime, inTransformBody1, inTransformBody2, inBody1, inBody2);766break;767768case EMotionType::Static:769TemplatedCalculateFrictionAndNonPenetrationConstraintProperties<EMotionType::Dynamic, EMotionType::Static>(ioConstraint, inSettings, inDeltaTime, inGravityDeltaTime, inTransformBody1, inTransformBody2, inBody1, inBody2);770break;771772default:773JPH_ASSERT(false);774break;775}776break;777778case EMotionType::Kinematic:779JPH_ASSERT(inBody2.IsDynamic());780TemplatedCalculateFrictionAndNonPenetrationConstraintProperties<EMotionType::Kinematic, EMotionType::Dynamic>(ioConstraint, inSettings, inDeltaTime, inGravityDeltaTime, inTransformBody1, inTransformBody2, inBody1, inBody2);781break;782783case EMotionType::Static:784JPH_ASSERT(inBody2.IsDynamic());785TemplatedCalculateFrictionAndNonPenetrationConstraintProperties<EMotionType::Static, EMotionType::Dynamic>(ioConstraint, inSettings, inDeltaTime, inGravityDeltaTime, inTransformBody1, inTransformBody2, inBody1, inBody2);786break;787788default:789JPH_ASSERT(false);790break;791}792}793794void ContactConstraintManager::GetContactsFromCache(ContactAllocator &ioContactAllocator, Body &inBody1, Body &inBody2, bool &outPairHandled, bool &outConstraintCreated)795{796JPH_PROFILE_FUNCTION();797798// Start with nothing found and not handled799outConstraintCreated = false;800outPairHandled = false;801802// Swap bodies so that body 1 id < body 2 id803Body *body1, *body2;804if (inBody1.GetID() < inBody2.GetID())805{806body1 = &inBody1;807body2 = &inBody2;808}809else810{811body1 = &inBody2;812body2 = &inBody1;813}814815// Find the cached body pair816BodyPair body_pair_key(body1->GetID(), body2->GetID());817uint64 body_pair_hash = body_pair_key.GetHash();818const ManifoldCache &read_cache = mCache[mCacheWriteIdx ^ 1];819const BPKeyValue *kv = read_cache.Find(body_pair_key, body_pair_hash);820if (kv == nullptr)821return;822const CachedBodyPair &input_cbp = kv->GetValue();823824// Get relative translation825Quat inv_r1 = body1->GetRotation().Conjugated();826Vec3 delta_position = inv_r1 * Vec3(body2->GetCenterOfMassPosition() - body1->GetCenterOfMassPosition());827828// Get old position delta829Vec3 old_delta_position = Vec3::sLoadFloat3Unsafe(input_cbp.mDeltaPosition);830831// Check if bodies are still roughly in the same relative position832if ((delta_position - old_delta_position).LengthSq() > mPhysicsSettings.mBodyPairCacheMaxDeltaPositionSq)833return;834835// Determine relative orientation836Quat delta_rotation = inv_r1 * body2->GetRotation();837838// Reconstruct old quaternion delta839Quat old_delta_rotation = Quat::sLoadFloat3Unsafe(input_cbp.mDeltaRotation);840841// Check if bodies are still roughly in the same relative orientation842// The delta between 2 quaternions p and q is: p q^* = [rotation_axis * sin(angle / 2), cos(angle / 2)]843// From the W component we can extract the angle: cos(angle / 2) = px * qx + py * qy + pz * qz + pw * qw = p . q844// 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)845if (abs(delta_rotation.Dot(old_delta_rotation)) < mPhysicsSettings.mBodyPairCacheCosMaxDeltaRotationDiv2)846return;847848// The cache is valid, return that we've handled this body pair849outPairHandled = true;850851// Copy the cached body pair to this frame852ManifoldCache &write_cache = mCache[mCacheWriteIdx];853BPKeyValue *output_bp_kv = write_cache.Create(ioContactAllocator, body_pair_key, body_pair_hash);854if (output_bp_kv == nullptr)855return; // Out of cache space856CachedBodyPair *output_cbp = &output_bp_kv->GetValue();857memcpy(output_cbp, &input_cbp, sizeof(CachedBodyPair));858859// If there were no contacts, we have handled the contact860if (input_cbp.mFirstCachedManifold == ManifoldMap::cInvalidHandle)861return;862863// Get body transforms864RMat44 transform_body1 = body1->GetCenterOfMassTransform();865RMat44 transform_body2 = body2->GetCenterOfMassTransform();866867// Get time step868float delta_time = mUpdateContext->mStepDeltaTime;869870// Calculate value for restitution correction871Vec3 gravity_dt = mUpdateContext->mPhysicsSystem->GetGravity() * delta_time;872873// Copy manifolds874uint32 output_handle = ManifoldMap::cInvalidHandle;875uint32 input_handle = input_cbp.mFirstCachedManifold;876do877{878JPH_PROFILE("Add Constraint From Cached Manifold");879880// Find the existing manifold881const MKeyValue *input_kv = read_cache.FromHandle(input_handle);882const SubShapeIDPair &input_key = input_kv->GetKey();883const CachedManifold &input_cm = input_kv->GetValue();884JPH_ASSERT(input_cm.mNumContactPoints > 0); // There should be contact points in this manifold!885886// Create room for manifold in write buffer and copy data887uint64 input_hash = input_key.GetHash();888MKeyValue *output_kv = write_cache.Create(ioContactAllocator, input_key, input_hash, input_cm.mNumContactPoints);889if (output_kv == nullptr)890break; // Out of cache space891CachedManifold *output_cm = &output_kv->GetValue();892memcpy(output_cm, &input_cm, CachedManifold::sGetRequiredTotalSize(input_cm.mNumContactPoints));893894// Link the object under the body pairs895output_cm->mNextWithSameBodyPair = output_handle;896output_handle = write_cache.ToHandle(output_kv);897898// Calculate default contact settings899ContactSettings settings;900settings.mCombinedFriction = mCombineFriction(*body1, input_key.GetSubShapeID1(), *body2, input_key.GetSubShapeID2());901settings.mCombinedRestitution = mCombineRestitution(*body1, input_key.GetSubShapeID1(), *body2, input_key.GetSubShapeID2());902settings.mIsSensor = body1->IsSensor() || body2->IsSensor();903904// Calculate world space contact normal905Vec3 world_space_normal = transform_body2.Multiply3x3(Vec3::sLoadFloat3Unsafe(output_cm->mContactNormal)).Normalized();906907// Call contact listener to update settings908if (mContactListener != nullptr)909{910// Convert constraint to manifold structure for callback911ContactManifold manifold;912manifold.mWorldSpaceNormal = world_space_normal;913manifold.mSubShapeID1 = input_key.GetSubShapeID1();914manifold.mSubShapeID2 = input_key.GetSubShapeID2();915manifold.mBaseOffset = transform_body1.GetTranslation();916manifold.mRelativeContactPointsOn1.resize(output_cm->mNumContactPoints);917manifold.mRelativeContactPointsOn2.resize(output_cm->mNumContactPoints);918Mat44 local_transform_body2 = transform_body2.PostTranslated(-manifold.mBaseOffset).ToMat44();919float penetration_depth = -FLT_MAX;920for (uint32 i = 0; i < output_cm->mNumContactPoints; ++i)921{922const CachedContactPoint &ccp = output_cm->mContactPoints[i];923manifold.mRelativeContactPointsOn1[i] = transform_body1.Multiply3x3(Vec3::sLoadFloat3Unsafe(ccp.mPosition1));924manifold.mRelativeContactPointsOn2[i] = local_transform_body2 * Vec3::sLoadFloat3Unsafe(ccp.mPosition2);925penetration_depth = max(penetration_depth, (manifold.mRelativeContactPointsOn1[0] - manifold.mRelativeContactPointsOn2[0]).Dot(world_space_normal));926}927manifold.mPenetrationDepth = penetration_depth; // We don't have the penetration depth anymore, estimate it928929// Notify callback930mContactListener->OnContactPersisted(*body1, *body2, manifold, settings);931}932933JPH_ASSERT(settings.mIsSensor || !(body1->IsSensor() || body2->IsSensor()), "Sensors cannot be converted into regular bodies by a contact callback!");934if (!settings.mIsSensor // If one of the bodies is a sensor, don't actually create the constraint935&& ((body1->IsDynamic() && settings.mInvMassScale1 != 0.0f) // One of the bodies must have mass to be able to create a contact constraint936|| (body2->IsDynamic() && settings.mInvMassScale2 != 0.0f)))937{938// Add contact constraint in world space for the solver939uint32 constraint_idx = mNumConstraints++;940if (constraint_idx >= mMaxConstraints)941{942ioContactAllocator.mErrors |= EPhysicsUpdateError::ContactConstraintsFull;943break;944}945946// A constraint will be created947outConstraintCreated = true;948949ContactConstraint &constraint = mConstraints[constraint_idx];950new (&constraint) ContactConstraint();951constraint.mBody1 = body1;952constraint.mBody2 = body2;953constraint.mSortKey = input_hash;954world_space_normal.StoreFloat3(&constraint.mWorldSpaceNormal);955constraint.mCombinedFriction = settings.mCombinedFriction;956constraint.mInvMass1 = body1->GetMotionPropertiesUnchecked() != nullptr? settings.mInvMassScale1 * body1->GetMotionPropertiesUnchecked()->GetInverseMassUnchecked() : 0.0f;957constraint.mInvInertiaScale1 = settings.mInvInertiaScale1;958constraint.mInvMass2 = body2->GetMotionPropertiesUnchecked() != nullptr? settings.mInvMassScale2 * body2->GetMotionPropertiesUnchecked()->GetInverseMassUnchecked() : 0.0f;959constraint.mInvInertiaScale2 = settings.mInvInertiaScale2;960constraint.mContactPoints.resize(output_cm->mNumContactPoints);961for (uint32 i = 0; i < output_cm->mNumContactPoints; ++i)962{963CachedContactPoint &ccp = output_cm->mContactPoints[i];964WorldContactPoint &wcp = constraint.mContactPoints[i];965wcp.mNonPenetrationConstraint.SetTotalLambda(ccp.mNonPenetrationLambda);966wcp.mFrictionConstraint1.SetTotalLambda(ccp.mFrictionLambda[0]);967wcp.mFrictionConstraint2.SetTotalLambda(ccp.mFrictionLambda[1]);968wcp.mContactPoint = &ccp;969}970971JPH_DET_LOG("GetContactsFromCache: id1: " << constraint.mBody1->GetID() << " id2: " << constraint.mBody2->GetID() << " key: " << constraint.mSortKey);972973// Calculate friction and non-penetration constraint properties for all contact points974CalculateFrictionAndNonPenetrationConstraintProperties(constraint, settings, delta_time, gravity_dt, transform_body1, transform_body2, *body1, *body2);975976// Notify island builder977mUpdateContext->mIslandBuilder->LinkContact(constraint_idx, body1->GetIndexInActiveBodiesInternal(), body2->GetIndexInActiveBodiesInternal());978979#ifdef JPH_DEBUG_RENDERER980// Draw the manifold981if (sDrawContactManifolds)982constraint.Draw(DebugRenderer::sInstance, Color::sYellow);983#endif // JPH_DEBUG_RENDERER984}985986// Mark contact as persisted so that we won't fire OnContactRemoved callbacks987input_cm.mFlags |= (uint16)CachedManifold::EFlags::ContactPersisted;988989// Fetch the next manifold990input_handle = input_cm.mNextWithSameBodyPair;991}992while (input_handle != ManifoldMap::cInvalidHandle);993output_cbp->mFirstCachedManifold = output_handle;994}995996ContactConstraintManager::BodyPairHandle ContactConstraintManager::AddBodyPair(ContactAllocator &ioContactAllocator, const Body &inBody1, const Body &inBody2)997{998JPH_PROFILE_FUNCTION();9991000// Swap bodies so that body 1 id < body 2 id1001const Body *body1, *body2;1002if (inBody1.GetID() < inBody2.GetID())1003{1004body1 = &inBody1;1005body2 = &inBody2;1006}1007else1008{1009body1 = &inBody2;1010body2 = &inBody1;1011}10121013// Add an entry1014BodyPair body_pair_key(body1->GetID(), body2->GetID());1015uint64 body_pair_hash = body_pair_key.GetHash();1016BPKeyValue *body_pair_kv = mCache[mCacheWriteIdx].Create(ioContactAllocator, body_pair_key, body_pair_hash);1017if (body_pair_kv == nullptr)1018return nullptr; // Out of cache space1019CachedBodyPair *cbp = &body_pair_kv->GetValue();1020cbp->mFirstCachedManifold = ManifoldMap::cInvalidHandle;10211022// Get relative translation1023Quat inv_r1 = body1->GetRotation().Conjugated();1024Vec3 delta_position = inv_r1 * Vec3(body2->GetCenterOfMassPosition() - body1->GetCenterOfMassPosition());10251026// Store it1027delta_position.StoreFloat3(&cbp->mDeltaPosition);10281029// Determine relative orientation1030Quat delta_rotation = inv_r1 * body2->GetRotation();10311032// Store it1033delta_rotation.StoreFloat3(&cbp->mDeltaRotation);10341035return cbp;1036}10371038template <EMotionType Type1, EMotionType Type2>1039bool ContactConstraintManager::TemplatedAddContactConstraint(ContactAllocator &ioContactAllocator, BodyPairHandle inBodyPairHandle, Body &inBody1, Body &inBody2, const ContactManifold &inManifold)1040{1041// Calculate hash1042SubShapeIDPair key { inBody1.GetID(), inManifold.mSubShapeID1, inBody2.GetID(), inManifold.mSubShapeID2 };1043uint64 key_hash = key.GetHash();10441045// Determine number of contact points1046int num_contact_points = (int)inManifold.mRelativeContactPointsOn1.size();1047JPH_ASSERT(num_contact_points <= MaxContactPoints);1048JPH_ASSERT(num_contact_points == (int)inManifold.mRelativeContactPointsOn2.size());10491050// Reserve space for new contact cache entry1051// Note that for dynamic vs dynamic we always require the first body to have a lower body id to get a consistent key1052// under which to look up the contact1053ManifoldCache &write_cache = mCache[mCacheWriteIdx];1054MKeyValue *new_manifold_kv = write_cache.Create(ioContactAllocator, key, key_hash, num_contact_points);1055if (new_manifold_kv == nullptr)1056return false; // Out of cache space1057CachedManifold *new_manifold = &new_manifold_kv->GetValue();10581059// Transform the world space normal to the space of body 2 (this is usually the static body)1060RMat44 inverse_transform_body2 = inBody2.GetInverseCenterOfMassTransform();1061inverse_transform_body2.Multiply3x3(inManifold.mWorldSpaceNormal).Normalized().StoreFloat3(&new_manifold->mContactNormal);10621063// Settings object that gets passed to the callback1064ContactSettings settings;1065settings.mCombinedFriction = mCombineFriction(inBody1, inManifold.mSubShapeID1, inBody2, inManifold.mSubShapeID2);1066settings.mCombinedRestitution = mCombineRestitution(inBody1, inManifold.mSubShapeID1, inBody2, inManifold.mSubShapeID2);1067settings.mIsSensor = inBody1.IsSensor() || inBody2.IsSensor();10681069// Get the contact points for the old cache entry1070const ManifoldCache &read_cache = mCache[mCacheWriteIdx ^ 1];1071const MKeyValue *old_manifold_kv = read_cache.Find(key, key_hash);1072const CachedContactPoint *ccp_start;1073const CachedContactPoint *ccp_end;1074if (old_manifold_kv != nullptr)1075{1076// Call point persisted listener1077if (mContactListener != nullptr)1078mContactListener->OnContactPersisted(inBody1, inBody2, inManifold, settings);10791080// Fetch the contact points from the old manifold1081const CachedManifold *old_manifold = &old_manifold_kv->GetValue();1082ccp_start = old_manifold->mContactPoints;1083ccp_end = ccp_start + old_manifold->mNumContactPoints;10841085// Mark contact as persisted so that we won't fire OnContactRemoved callbacks1086old_manifold->mFlags |= (uint16)CachedManifold::EFlags::ContactPersisted;1087}1088else1089{1090// Call point added listener1091if (mContactListener != nullptr)1092mContactListener->OnContactAdded(inBody1, inBody2, inManifold, settings);10931094// No contact points available from old manifold1095ccp_start = nullptr;1096ccp_end = nullptr;1097}10981099// Get inverse transform for body 11100RMat44 inverse_transform_body1 = inBody1.GetInverseCenterOfMassTransform();11011102bool contact_constraint_created = false;11031104// If one of the bodies is a sensor, don't actually create the constraint1105JPH_ASSERT(settings.mIsSensor || !(inBody1.IsSensor() || inBody2.IsSensor()), "Sensors cannot be converted into regular bodies by a contact callback!");1106if (!settings.mIsSensor1107&& ((inBody1.IsDynamic() && settings.mInvMassScale1 != 0.0f) // One of the bodies must have mass to be able to create a contact constraint1108|| (inBody2.IsDynamic() && settings.mInvMassScale2 != 0.0f)))1109{1110// Add contact constraint1111uint32 constraint_idx = mNumConstraints++;1112if (constraint_idx >= mMaxConstraints)1113{1114ioContactAllocator.mErrors |= EPhysicsUpdateError::ContactConstraintsFull;11151116// Manifold has been created already, we're not filling it in, so we need to reset the contact number of points.1117// 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.1118new_manifold->mNumContactPoints = 0;1119return false;1120}11211122// We will create a contact constraint1123contact_constraint_created = true;11241125ContactConstraint &constraint = mConstraints[constraint_idx];1126new (&constraint) ContactConstraint();1127constraint.mBody1 = &inBody1;1128constraint.mBody2 = &inBody2;1129constraint.mSortKey = key_hash;1130inManifold.mWorldSpaceNormal.StoreFloat3(&constraint.mWorldSpaceNormal);1131constraint.mCombinedFriction = settings.mCombinedFriction;1132constraint.mInvMass1 = inBody1.GetMotionPropertiesUnchecked() != nullptr? settings.mInvMassScale1 * inBody1.GetMotionPropertiesUnchecked()->GetInverseMassUnchecked() : 0.0f;1133constraint.mInvInertiaScale1 = settings.mInvInertiaScale1;1134constraint.mInvMass2 = inBody2.GetMotionPropertiesUnchecked() != nullptr? settings.mInvMassScale2 * inBody2.GetMotionPropertiesUnchecked()->GetInverseMassUnchecked() : 0.0f;1135constraint.mInvInertiaScale2 = settings.mInvInertiaScale2;11361137JPH_DET_LOG("TemplatedAddContactConstraint: id1: " << constraint.mBody1->GetID() << " id2: " << constraint.mBody2->GetID() << " key: " << constraint.mSortKey);11381139// Notify island builder1140mUpdateContext->mIslandBuilder->LinkContact(constraint_idx, inBody1.GetIndexInActiveBodiesInternal(), inBody2.GetIndexInActiveBodiesInternal());11411142// Get time step1143float delta_time = mUpdateContext->mStepDeltaTime;11441145// Calculate value for restitution correction1146float gravity_dt_dot_normal = inManifold.mWorldSpaceNormal.Dot(mUpdateContext->mPhysicsSystem->GetGravity() * delta_time);11471148// Calculate scaled mass and inertia1149float inv_m1;1150Mat44 inv_i1;1151if constexpr (Type1 == EMotionType::Dynamic)1152{1153const MotionProperties *mp1 = inBody1.GetMotionPropertiesUnchecked();1154inv_m1 = settings.mInvMassScale1 * mp1->GetInverseMass();1155inv_i1 = settings.mInvInertiaScale1 * mp1->GetInverseInertiaForRotation(inverse_transform_body1.Transposed3x3());1156}1157else1158{1159inv_m1 = 0.0f;1160inv_i1 = Mat44::sZero();1161}11621163float inv_m2;1164Mat44 inv_i2;1165if constexpr (Type2 == EMotionType::Dynamic)1166{1167const MotionProperties *mp2 = inBody2.GetMotionPropertiesUnchecked();1168inv_m2 = settings.mInvMassScale2 * mp2->GetInverseMass();1169inv_i2 = settings.mInvInertiaScale2 * mp2->GetInverseInertiaForRotation(inverse_transform_body2.Transposed3x3());1170}1171else1172{1173inv_m2 = 0.0f;1174inv_i2 = Mat44::sZero();1175}11761177// Calculate tangents1178Vec3 t1, t2;1179constraint.GetTangents(t1, t2);11801181constraint.mContactPoints.resize(num_contact_points);1182for (int i = 0; i < num_contact_points; ++i)1183{1184// Convert to world space and set positions1185WorldContactPoint &wcp = constraint.mContactPoints[i];1186RVec3 p1_ws = inManifold.mBaseOffset + inManifold.mRelativeContactPointsOn1[i];1187RVec3 p2_ws = inManifold.mBaseOffset + inManifold.mRelativeContactPointsOn2[i];11881189// Convert to local space to the body1190Vec3 p1_ls = Vec3(inverse_transform_body1 * p1_ws);1191Vec3 p2_ls = Vec3(inverse_transform_body2 * p2_ws);11921193// Check if we have a close contact point from last update1194bool lambda_set = false;1195for (const CachedContactPoint *ccp = ccp_start; ccp < ccp_end; ccp++)1196if (Vec3::sLoadFloat3Unsafe(ccp->mPosition1).IsClose(p1_ls, mPhysicsSettings.mContactPointPreserveLambdaMaxDistSq)1197&& Vec3::sLoadFloat3Unsafe(ccp->mPosition2).IsClose(p2_ls, mPhysicsSettings.mContactPointPreserveLambdaMaxDistSq))1198{1199// Get lambdas from previous frame1200wcp.mNonPenetrationConstraint.SetTotalLambda(ccp->mNonPenetrationLambda);1201wcp.mFrictionConstraint1.SetTotalLambda(ccp->mFrictionLambda[0]);1202wcp.mFrictionConstraint2.SetTotalLambda(ccp->mFrictionLambda[1]);1203lambda_set = true;1204break;1205}1206if (!lambda_set)1207{1208wcp.mNonPenetrationConstraint.SetTotalLambda(0.0f);1209wcp.mFrictionConstraint1.SetTotalLambda(0.0f);1210wcp.mFrictionConstraint2.SetTotalLambda(0.0f);1211}12121213// Create new contact point1214CachedContactPoint &cp = new_manifold->mContactPoints[i];1215p1_ls.StoreFloat3(&cp.mPosition1);1216p2_ls.StoreFloat3(&cp.mPosition2);1217wcp.mContactPoint = &cp;12181219// Setup velocity constraint1220wcp.TemplatedCalculateFrictionAndNonPenetrationConstraintProperties<Type1, Type2>(delta_time, gravity_dt_dot_normal, inBody1, inBody2, inv_m1, inv_m2, inv_i1, inv_i2, p1_ws, p2_ws, inManifold.mWorldSpaceNormal, t1, t2, settings, mPhysicsSettings.mMinVelocityForRestitution);1221}12221223#ifdef JPH_DEBUG_RENDERER1224// Draw the manifold1225if (sDrawContactManifolds)1226constraint.Draw(DebugRenderer::sInstance, Color::sOrange);1227#endif // JPH_DEBUG_RENDERER1228}1229else1230{1231// Store the contact manifold in the cache1232for (int i = 0; i < num_contact_points; ++i)1233{1234// Convert to local space to the body1235Vec3 p1 = Vec3(inverse_transform_body1 * (inManifold.mBaseOffset + inManifold.mRelativeContactPointsOn1[i]));1236Vec3 p2 = Vec3(inverse_transform_body2 * (inManifold.mBaseOffset + inManifold.mRelativeContactPointsOn2[i]));12371238// Create new contact point1239CachedContactPoint &cp = new_manifold->mContactPoints[i];1240p1.StoreFloat3(&cp.mPosition1);1241p2.StoreFloat3(&cp.mPosition2);12421243// Reset contact impulses, we haven't applied any1244cp.mNonPenetrationLambda = 0.0f;1245cp.mFrictionLambda[0] = 0.0f;1246cp.mFrictionLambda[1] = 0.0f;1247}1248}12491250// Store cached contact point in body pair cache1251CachedBodyPair *cbp = reinterpret_cast<CachedBodyPair *>(inBodyPairHandle);1252new_manifold->mNextWithSameBodyPair = cbp->mFirstCachedManifold;1253cbp->mFirstCachedManifold = write_cache.ToHandle(new_manifold_kv);12541255// A contact constraint was added1256return contact_constraint_created;1257}12581259bool ContactConstraintManager::AddContactConstraint(ContactAllocator &ioContactAllocator, BodyPairHandle inBodyPairHandle, Body &inBody1, Body &inBody2, const ContactManifold &inManifold)1260{1261JPH_PROFILE_FUNCTION();12621263JPH_DET_LOG("AddContactConstraint: id1: " << inBody1.GetID() << " id2: " << inBody2.GetID()1264<< " subshape1: " << inManifold.mSubShapeID1 << " subshape2: " << inManifold.mSubShapeID21265<< " normal: " << inManifold.mWorldSpaceNormal << " pendepth: " << inManifold.mPenetrationDepth);12661267JPH_ASSERT(inManifold.mWorldSpaceNormal.IsNormalized());12681269// Swap bodies so that body 1 id < body 2 id1270const ContactManifold *manifold;1271Body *body1, *body2;1272ContactManifold temp;1273if (inBody2.GetID() < inBody1.GetID())1274{1275body1 = &inBody2;1276body2 = &inBody1;1277temp = inManifold.SwapShapes();1278manifold = &temp;1279}1280else1281{1282body1 = &inBody1;1283body2 = &inBody2;1284manifold = &inManifold;1285}12861287// Dispatch to the correct templated form1288// 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 here1289switch (body1->GetMotionType())1290{1291case EMotionType::Dynamic:1292{1293switch (body2->GetMotionType())1294{1295case EMotionType::Dynamic:1296return TemplatedAddContactConstraint<EMotionType::Dynamic, EMotionType::Dynamic>(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold);12971298case EMotionType::Kinematic:1299return TemplatedAddContactConstraint<EMotionType::Dynamic, EMotionType::Kinematic>(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold);13001301case EMotionType::Static:1302return TemplatedAddContactConstraint<EMotionType::Dynamic, EMotionType::Static>(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold);13031304default:1305JPH_ASSERT(false);1306break;1307}1308break;1309}13101311case EMotionType::Kinematic:1312switch (body2->GetMotionType())1313{1314case EMotionType::Dynamic:1315return TemplatedAddContactConstraint<EMotionType::Kinematic, EMotionType::Dynamic>(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold);13161317case EMotionType::Kinematic:1318return TemplatedAddContactConstraint<EMotionType::Kinematic, EMotionType::Kinematic>(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold);13191320case EMotionType::Static:1321return TemplatedAddContactConstraint<EMotionType::Kinematic, EMotionType::Static>(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold);13221323default:1324JPH_ASSERT(false);1325break;1326}1327break;13281329case EMotionType::Static:1330switch (body2->GetMotionType())1331{1332case EMotionType::Dynamic:1333return TemplatedAddContactConstraint<EMotionType::Static, EMotionType::Dynamic>(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold);13341335case EMotionType::Kinematic:1336return TemplatedAddContactConstraint<EMotionType::Static, EMotionType::Kinematic>(ioContactAllocator, inBodyPairHandle, *body1, *body2, *manifold);13371338case EMotionType::Static: // Static vs static not possible1339default:1340JPH_ASSERT(false);1341break;1342}1343break;13441345default:1346JPH_ASSERT(false);1347break;1348}13491350return false;1351}13521353void ContactConstraintManager::OnCCDContactAdded(ContactAllocator &ioContactAllocator, const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &outSettings)1354{1355JPH_ASSERT(inManifold.mWorldSpaceNormal.IsNormalized());13561357// Calculate contact settings1358outSettings.mCombinedFriction = mCombineFriction(inBody1, inManifold.mSubShapeID1, inBody2, inManifold.mSubShapeID2);1359outSettings.mCombinedRestitution = mCombineRestitution(inBody1, inManifold.mSubShapeID1, inBody2, inManifold.mSubShapeID2);1360outSettings.mIsSensor = false; // For now, no sensors are supported during CCD13611362// 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 work1363if (mContactListener != nullptr)1364{1365// Swap bodies so that body 1 id < body 2 id1366const ContactManifold *manifold;1367const Body *body1, *body2;1368ContactManifold temp;1369if (inBody2.GetID() < inBody1.GetID())1370{1371body1 = &inBody2;1372body2 = &inBody1;1373temp = inManifold.SwapShapes();1374manifold = &temp;1375}1376else1377{1378body1 = &inBody1;1379body2 = &inBody2;1380manifold = &inManifold;1381}13821383// Calculate hash1384SubShapeIDPair key { body1->GetID(), manifold->mSubShapeID1, body2->GetID(), manifold->mSubShapeID2 };1385uint64 key_hash = key.GetHash();13861387// Check if we already created this contact this physics update1388ManifoldCache &write_cache = mCache[mCacheWriteIdx];1389MKVAndCreated new_manifold_kv = write_cache.FindOrCreate(ioContactAllocator, key, key_hash, 0);1390if (new_manifold_kv.second)1391{1392// This contact is new for this physics update, check if previous update we already had this contact.1393const ManifoldCache &read_cache = mCache[mCacheWriteIdx ^ 1];1394const MKeyValue *old_manifold_kv = read_cache.Find(key, key_hash);1395if (old_manifold_kv == nullptr)1396{1397// New contact1398mContactListener->OnContactAdded(*body1, *body2, *manifold, outSettings);1399}1400else1401{1402// Existing contact1403mContactListener->OnContactPersisted(*body1, *body2, *manifold, outSettings);14041405// Mark contact as persisted so that we won't fire OnContactRemoved callbacks1406old_manifold_kv->GetValue().mFlags |= (uint16)CachedManifold::EFlags::ContactPersisted;1407}14081409// Check if the cache is full1410if (new_manifold_kv.first != nullptr)1411{1412// 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 created1413CachedManifold &new_manifold = new_manifold_kv.first->GetValue();1414new_manifold.mContactNormal = { 0, 0, 0 };1415new_manifold.mFlags |= (uint16)CachedManifold::EFlags::CCDContact;1416}1417}1418else1419{1420// Already found this contact this physics update.1421// Note that we can trigger OnContactPersisted multiple times per physics update, but otherwise we have no way of obtaining the settings1422mContactListener->OnContactPersisted(*body1, *body2, *manifold, outSettings);1423}14241425// If we swapped body1 and body2 we need to swap the mass scales back1426if (manifold == &temp)1427{1428std::swap(outSettings.mInvMassScale1, outSettings.mInvMassScale2);1429std::swap(outSettings.mInvInertiaScale1, outSettings.mInvInertiaScale2);1430// Note we do not need to negate the relative surface velocity as it is not applied by the CCD collision constraint1431}1432}14331434JPH_ASSERT(outSettings.mIsSensor || !(inBody1.IsSensor() || inBody2.IsSensor()), "Sensors cannot be converted into regular bodies by a contact callback!");1435}14361437void ContactConstraintManager::SortContacts(uint32 *inConstraintIdxBegin, uint32 *inConstraintIdxEnd) const1438{1439JPH_PROFILE_FUNCTION();14401441QuickSort(inConstraintIdxBegin, inConstraintIdxEnd, [this](uint32 inLHS, uint32 inRHS) {1442const ContactConstraint &lhs = mConstraints[inLHS];1443const ContactConstraint &rhs = mConstraints[inRHS];14441445// Most of the time the sort key will be different so we sort on that1446if (lhs.mSortKey != rhs.mSortKey)1447return lhs.mSortKey < rhs.mSortKey;14481449// If they're equal we use the IDs of body 1 to order1450if (lhs.mBody1 != rhs.mBody1)1451return lhs.mBody1->GetID() < rhs.mBody1->GetID();14521453// If they're still equal we use the IDs of body 2 to order1454if (lhs.mBody2 != rhs.mBody2)1455return lhs.mBody2->GetID() < rhs.mBody2->GetID();14561457JPH_ASSERT(inLHS == inRHS, "Hash collision, ordering will be inconsistent");1458return false;1459});1460}14611462void ContactConstraintManager::FinalizeContactCacheAndCallContactPointRemovedCallbacks(uint inExpectedNumBodyPairs, uint inExpectedNumManifolds)1463{1464JPH_PROFILE_FUNCTION();14651466#ifdef JPH_ENABLE_ASSERTS1467// Mark cache as finalized1468ManifoldCache &old_write_cache = mCache[mCacheWriteIdx];1469old_write_cache.Finalize();14701471// Check that the count of body pairs and manifolds that we tracked outside of the cache (to avoid contention on an atomic) is correct1472JPH_ASSERT(old_write_cache.GetNumBodyPairs() == inExpectedNumBodyPairs);1473JPH_ASSERT(old_write_cache.GetNumManifolds() == inExpectedNumManifolds);1474#endif14751476// Buffers are now complete, make write buffer the read buffer1477mCacheWriteIdx ^= 1;14781479// Get the old read cache / new write cache1480ManifoldCache &old_read_cache = mCache[mCacheWriteIdx];14811482// Call the contact point removal callbacks1483if (mContactListener != nullptr)1484old_read_cache.ContactPointRemovedCallbacks(mContactListener);14851486// We're done with the old read cache now1487old_read_cache.Clear();14881489// Use the amount of contacts from the last iteration to determine the amount of buckets to use in the hash map for the next iteration1490old_read_cache.Prepare(inExpectedNumBodyPairs, inExpectedNumManifolds);1491}14921493bool ContactConstraintManager::WereBodiesInContact(const BodyID &inBody1ID, const BodyID &inBody2ID) const1494{1495// 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)1496const ManifoldCache &read_cache = mCache[mCacheWriteIdx ^ 1];1497BodyPair key;1498if (inBody1ID < inBody2ID)1499key = BodyPair(inBody1ID, inBody2ID);1500else1501key = BodyPair(inBody2ID, inBody1ID);1502uint64 key_hash = key.GetHash();1503const BPKeyValue *kv = read_cache.Find(key, key_hash);1504return kv != nullptr && kv->GetValue().mFirstCachedManifold != ManifoldMap::cInvalidHandle;1505}15061507template <EMotionType Type1, EMotionType Type2>1508JPH_INLINE void ContactConstraintManager::sWarmStartConstraint(ContactConstraint &ioConstraint, MotionProperties *ioMotionProperties1, MotionProperties *ioMotionProperties2, float inWarmStartImpulseRatio)1509{1510// Calculate tangents1511Vec3 t1, t2;1512ioConstraint.GetTangents(t1, t2);15131514Vec3 ws_normal = ioConstraint.GetWorldSpaceNormal();15151516for (WorldContactPoint &wcp : ioConstraint.mContactPoints)1517{1518// Warm starting: Apply impulse from last frame1519if (wcp.mFrictionConstraint1.IsActive() || wcp.mFrictionConstraint2.IsActive())1520{1521wcp.mFrictionConstraint1.TemplatedWarmStart<Type1, Type2>(ioMotionProperties1, ioConstraint.mInvMass1, ioMotionProperties2, ioConstraint.mInvMass2, t1, inWarmStartImpulseRatio);1522wcp.mFrictionConstraint2.TemplatedWarmStart<Type1, Type2>(ioMotionProperties1, ioConstraint.mInvMass1, ioMotionProperties2, ioConstraint.mInvMass2, t2, inWarmStartImpulseRatio);1523}1524wcp.mNonPenetrationConstraint.TemplatedWarmStart<Type1, Type2>(ioMotionProperties1, ioConstraint.mInvMass1, ioMotionProperties2, ioConstraint.mInvMass2, ws_normal, inWarmStartImpulseRatio);1525}1526}15271528template <class MotionPropertiesCallback>1529void ContactConstraintManager::WarmStartVelocityConstraints(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inWarmStartImpulseRatio, MotionPropertiesCallback &ioCallback)1530{1531JPH_PROFILE_FUNCTION();15321533for (const uint32 *constraint_idx = inConstraintIdxBegin; constraint_idx < inConstraintIdxEnd; ++constraint_idx)1534{1535ContactConstraint &constraint = mConstraints[*constraint_idx];15361537// Fetch bodies1538Body &body1 = *constraint.mBody1;1539EMotionType motion_type1 = body1.GetMotionType();1540MotionProperties *motion_properties1 = body1.GetMotionPropertiesUnchecked();15411542Body &body2 = *constraint.mBody2;1543EMotionType motion_type2 = body2.GetMotionType();1544MotionProperties *motion_properties2 = body2.GetMotionPropertiesUnchecked();15451546// Dispatch to the correct templated form1547// Note: Warm starting doesn't differentiate between kinematic/static bodies so we handle both as static bodies1548if (motion_type1 == EMotionType::Dynamic)1549{1550if (motion_type2 == EMotionType::Dynamic)1551{1552sWarmStartConstraint<EMotionType::Dynamic, EMotionType::Dynamic>(constraint, motion_properties1, motion_properties2, inWarmStartImpulseRatio);15531554ioCallback(motion_properties2);1555}1556else1557sWarmStartConstraint<EMotionType::Dynamic, EMotionType::Static>(constraint, motion_properties1, motion_properties2, inWarmStartImpulseRatio);15581559ioCallback(motion_properties1);1560}1561else1562{1563JPH_ASSERT(motion_type2 == EMotionType::Dynamic);15641565sWarmStartConstraint<EMotionType::Static, EMotionType::Dynamic>(constraint, motion_properties1, motion_properties2, inWarmStartImpulseRatio);15661567ioCallback(motion_properties2);1568}1569}1570}15711572// Specialize for the two body callback types1573template void ContactConstraintManager::WarmStartVelocityConstraints<CalculateSolverSteps>(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inWarmStartImpulseRatio, CalculateSolverSteps &ioCallback);1574template void ContactConstraintManager::WarmStartVelocityConstraints<DummyCalculateSolverSteps>(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd, float inWarmStartImpulseRatio, DummyCalculateSolverSteps &ioCallback);15751576template <EMotionType Type1, EMotionType Type2>1577JPH_INLINE bool ContactConstraintManager::sSolveVelocityConstraint(ContactConstraint &ioConstraint, MotionProperties *ioMotionProperties1, MotionProperties *ioMotionProperties2)1578{1579bool any_impulse_applied = false;15801581// Calculate tangents1582Vec3 t1, t2;1583ioConstraint.GetTangents(t1, t2);15841585// First apply all friction constraints (non-penetration is more important than friction)1586for (WorldContactPoint &wcp : ioConstraint.mContactPoints)1587{1588// Check if friction is enabled1589if (wcp.mFrictionConstraint1.IsActive() || wcp.mFrictionConstraint2.IsActive())1590{1591// Calculate impulse to stop motion in tangential direction1592float lambda1 = wcp.mFrictionConstraint1.TemplatedSolveVelocityConstraintGetTotalLambda<Type1, Type2>(ioMotionProperties1, ioMotionProperties2, t1);1593float lambda2 = wcp.mFrictionConstraint2.TemplatedSolveVelocityConstraintGetTotalLambda<Type1, Type2>(ioMotionProperties1, ioMotionProperties2, t2);1594float total_lambda_sq = Square(lambda1) + Square(lambda2);15951596// Calculate max impulse that can be applied. Note that we're using the non-penetration impulse from the previous iteration here.1597// We do this because non-penetration is more important so is solved last (the last things that are solved in an iterative solver1598// contribute the most).1599float max_lambda_f = ioConstraint.mCombinedFriction * wcp.mNonPenetrationConstraint.GetTotalLambda();16001601// If the total lambda that we will apply is too large, scale it back1602if (total_lambda_sq > Square(max_lambda_f))1603{1604float scale = max_lambda_f / sqrt(total_lambda_sq);1605lambda1 *= scale;1606lambda2 *= scale;1607}16081609// Apply the friction impulse1610if (wcp.mFrictionConstraint1.TemplatedSolveVelocityConstraintApplyLambda<Type1, Type2>(ioMotionProperties1, ioConstraint.mInvMass1, ioMotionProperties2, ioConstraint.mInvMass2, t1, lambda1))1611any_impulse_applied = true;1612if (wcp.mFrictionConstraint2.TemplatedSolveVelocityConstraintApplyLambda<Type1, Type2>(ioMotionProperties1, ioConstraint.mInvMass1, ioMotionProperties2, ioConstraint.mInvMass2, t2, lambda2))1613any_impulse_applied = true;1614}1615}16161617Vec3 ws_normal = ioConstraint.GetWorldSpaceNormal();16181619// Then apply all non-penetration constraints1620for (WorldContactPoint &wcp : ioConstraint.mContactPoints)1621{1622// Solve non penetration velocities1623if (wcp.mNonPenetrationConstraint.TemplatedSolveVelocityConstraint<Type1, Type2>(ioMotionProperties1, ioConstraint.mInvMass1, ioMotionProperties2, ioConstraint.mInvMass2, ws_normal, 0.0f, FLT_MAX))1624any_impulse_applied = true;1625}16261627return any_impulse_applied;1628}16291630bool ContactConstraintManager::SolveVelocityConstraints(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd)1631{1632JPH_PROFILE_FUNCTION();16331634bool any_impulse_applied = false;16351636for (const uint32 *constraint_idx = inConstraintIdxBegin; constraint_idx < inConstraintIdxEnd; ++constraint_idx)1637{1638ContactConstraint &constraint = mConstraints[*constraint_idx];16391640// Fetch bodies1641Body &body1 = *constraint.mBody1;1642EMotionType motion_type1 = body1.GetMotionType();1643MotionProperties *motion_properties1 = body1.GetMotionPropertiesUnchecked();16441645Body &body2 = *constraint.mBody2;1646EMotionType motion_type2 = body2.GetMotionType();1647MotionProperties *motion_properties2 = body2.GetMotionPropertiesUnchecked();16481649// Dispatch to the correct templated form1650switch (motion_type1)1651{1652case EMotionType::Dynamic:1653switch (motion_type2)1654{1655case EMotionType::Dynamic:1656any_impulse_applied |= sSolveVelocityConstraint<EMotionType::Dynamic, EMotionType::Dynamic>(constraint, motion_properties1, motion_properties2);1657break;16581659case EMotionType::Kinematic:1660any_impulse_applied |= sSolveVelocityConstraint<EMotionType::Dynamic, EMotionType::Kinematic>(constraint, motion_properties1, motion_properties2);1661break;16621663case EMotionType::Static:1664any_impulse_applied |= sSolveVelocityConstraint<EMotionType::Dynamic, EMotionType::Static>(constraint, motion_properties1, motion_properties2);1665break;16661667default:1668JPH_ASSERT(false);1669break;1670}1671break;16721673case EMotionType::Kinematic:1674JPH_ASSERT(motion_type2 == EMotionType::Dynamic);1675any_impulse_applied |= sSolveVelocityConstraint<EMotionType::Kinematic, EMotionType::Dynamic>(constraint, motion_properties1, motion_properties2);1676break;16771678case EMotionType::Static:1679JPH_ASSERT(motion_type2 == EMotionType::Dynamic);1680any_impulse_applied |= sSolveVelocityConstraint<EMotionType::Static, EMotionType::Dynamic>(constraint, motion_properties1, motion_properties2);1681break;16821683default:1684JPH_ASSERT(false);1685break;1686}1687}16881689return any_impulse_applied;1690}16911692void ContactConstraintManager::StoreAppliedImpulses(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd) const1693{1694// Copy back total applied impulse to cache for the next frame1695for (const uint32 *constraint_idx = inConstraintIdxBegin; constraint_idx < inConstraintIdxEnd; ++constraint_idx)1696{1697const ContactConstraint &constraint = mConstraints[*constraint_idx];16981699for (const WorldContactPoint &wcp : constraint.mContactPoints)1700{1701wcp.mContactPoint->mNonPenetrationLambda = wcp.mNonPenetrationConstraint.GetTotalLambda();1702wcp.mContactPoint->mFrictionLambda[0] = wcp.mFrictionConstraint1.GetTotalLambda();1703wcp.mContactPoint->mFrictionLambda[1] = wcp.mFrictionConstraint2.GetTotalLambda();1704}1705}1706}17071708bool ContactConstraintManager::SolvePositionConstraints(const uint32 *inConstraintIdxBegin, const uint32 *inConstraintIdxEnd)1709{1710JPH_PROFILE_FUNCTION();17111712bool any_impulse_applied = false;17131714for (const uint32 *constraint_idx = inConstraintIdxBegin; constraint_idx < inConstraintIdxEnd; ++constraint_idx)1715{1716ContactConstraint &constraint = mConstraints[*constraint_idx];17171718// Fetch bodies1719Body &body1 = *constraint.mBody1;1720Body &body2 = *constraint.mBody2;17211722// Get transforms1723RMat44 transform1 = body1.GetCenterOfMassTransform();1724RMat44 transform2 = body2.GetCenterOfMassTransform();17251726Vec3 ws_normal = constraint.GetWorldSpaceNormal();17271728for (WorldContactPoint &wcp : constraint.mContactPoints)1729{1730// Calculate new contact point positions in world space (the bodies may have moved)1731RVec3 p1 = transform1 * Vec3::sLoadFloat3Unsafe(wcp.mContactPoint->mPosition1);1732RVec3 p2 = transform2 * Vec3::sLoadFloat3Unsafe(wcp.mContactPoint->mPosition2);17331734// Calculate separation along the normal (negative if interpenetrating)1735// Allow a little penetration by default (PhysicsSettings::mPenetrationSlop) to avoid jittering between contact/no-contact which wipes out the contact cache and warm start impulses1736// Clamp penetration to a max PhysicsSettings::mMaxPenetrationDistance so that we don't apply a huge impulse if we're penetrating a lot1737float separation = max(Vec3(p2 - p1).Dot(ws_normal) + mPhysicsSettings.mPenetrationSlop, -mPhysicsSettings.mMaxPenetrationDistance);17381739// Only enforce constraint when separation < 0 (otherwise we're apart)1740if (separation < 0.0f)1741{1742// Update constraint properties (bodies may have moved)1743wcp.CalculateNonPenetrationConstraintProperties(body1, constraint.mInvMass1, constraint.mInvInertiaScale1, body2, constraint.mInvMass2, constraint.mInvInertiaScale2, p1, p2, ws_normal);17441745// Solve position errors1746if (wcp.mNonPenetrationConstraint.SolvePositionConstraintWithMassOverride(body1, constraint.mInvMass1, body2, constraint.mInvMass2, ws_normal, separation, mPhysicsSettings.mBaumgarte))1747any_impulse_applied = true;1748}1749}1750}17511752return any_impulse_applied;1753}17541755void ContactConstraintManager::RecycleConstraintBuffer()1756{1757// Reset constraint array1758mNumConstraints = 0;1759}17601761void ContactConstraintManager::FinishConstraintBuffer()1762{1763// Free constraints buffer1764mUpdateContext->mTempAllocator->Free(mConstraints, mMaxConstraints * sizeof(ContactConstraint));1765mConstraints = nullptr;1766mNumConstraints = 0;17671768// Reset update context1769mUpdateContext = nullptr;1770}17711772void ContactConstraintManager::SaveState(StateRecorder &inStream, const StateRecorderFilter *inFilter) const1773{1774mCache[mCacheWriteIdx ^ 1].SaveState(inStream, inFilter);1775}17761777bool ContactConstraintManager::RestoreState(StateRecorder &inStream, const StateRecorderFilter *inFilter)1778{1779bool success = mCache[mCacheWriteIdx].RestoreState(mCache[mCacheWriteIdx ^ 1], inStream, inFilter);17801781// If this is the last part, the cache is finalized1782if (inStream.IsLastPart())1783{1784mCacheWriteIdx ^= 1;1785mCache[mCacheWriteIdx].Clear();1786}17871788return success;1789}17901791JPH_NAMESPACE_END179217931794