Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Collision/EstimateCollisionResponse.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/Collision/EstimateCollisionResponse.h>7#include <Jolt/Physics/Body/Body.h>89JPH_NAMESPACE_BEGIN1011void EstimateCollisionResponse(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, CollisionEstimationResult &outResult, float inCombinedFriction, float inCombinedRestitution, float inMinVelocityForRestitution, uint inNumIterations)12{13// Note this code is based on AxisConstraintPart, see that class for more comments on the math1415ContactPoints::size_type num_points = inManifold.mRelativeContactPointsOn1.size();16JPH_ASSERT(num_points == inManifold.mRelativeContactPointsOn2.size());1718// Start with zero impulses19outResult.mImpulses.resize(num_points);20memset(outResult.mImpulses.data(), 0, num_points * sizeof(CollisionEstimationResult::Impulse));2122// Calculate friction directions23outResult.mTangent1 = inManifold.mWorldSpaceNormal.GetNormalizedPerpendicular();24outResult.mTangent2 = inManifold.mWorldSpaceNormal.Cross(outResult.mTangent1);2526// Get body velocities27EMotionType motion_type1 = inBody1.GetMotionType();28const MotionProperties *motion_properties1 = inBody1.GetMotionPropertiesUnchecked();29if (motion_type1 != EMotionType::Static)30{31outResult.mLinearVelocity1 = motion_properties1->GetLinearVelocity();32outResult.mAngularVelocity1 = motion_properties1->GetAngularVelocity();33}34else35outResult.mLinearVelocity1 = outResult.mAngularVelocity1 = Vec3::sZero();3637EMotionType motion_type2 = inBody2.GetMotionType();38const MotionProperties *motion_properties2 = inBody2.GetMotionPropertiesUnchecked();39if (motion_type2 != EMotionType::Static)40{41outResult.mLinearVelocity2 = motion_properties2->GetLinearVelocity();42outResult.mAngularVelocity2 = motion_properties2->GetAngularVelocity();43}44else45outResult.mLinearVelocity2 = outResult.mAngularVelocity2 = Vec3::sZero();4647// Get inverse mass and inertia48float inv_m1, inv_m2;49Mat44 inv_i1, inv_i2;50if (motion_type1 == EMotionType::Dynamic)51{52inv_m1 = motion_properties1->GetInverseMass();53inv_i1 = inBody1.GetInverseInertia();54}55else56{57inv_m1 = 0.0f;58inv_i1 = Mat44::sZero();59}6061if (motion_type2 == EMotionType::Dynamic)62{63inv_m2 = motion_properties2->GetInverseMass();64inv_i2 = inBody2.GetInverseInertia();65}66else67{68inv_m2 = 0.0f;69inv_i2 = Mat44::sZero();70}7172// Get center of masses relative to the base offset73Vec3 com1 = Vec3(inBody1.GetCenterOfMassPosition() - inManifold.mBaseOffset);74Vec3 com2 = Vec3(inBody2.GetCenterOfMassPosition() - inManifold.mBaseOffset);7576struct AxisConstraint77{78inline void Initialize(Vec3Arg inR1, Vec3Arg inR2, Vec3Arg inWorldSpaceNormal, float inInvM1, float inInvM2, Mat44Arg inInvI1, Mat44Arg inInvI2)79{80// Calculate effective mass: K^-1 = (J M^-1 J^T)^-181mR1PlusUxAxis = inR1.Cross(inWorldSpaceNormal);82mR2xAxis = inR2.Cross(inWorldSpaceNormal);83mInvI1_R1PlusUxAxis = inInvI1.Multiply3x3(mR1PlusUxAxis);84mInvI2_R2xAxis = inInvI2.Multiply3x3(mR2xAxis);85mEffectiveMass = 1.0f / (inInvM1 + mInvI1_R1PlusUxAxis.Dot(mR1PlusUxAxis) + inInvM2 + mInvI2_R2xAxis.Dot(mR2xAxis));86mBias = 0.0f;87}8889inline float SolveGetLambda(Vec3Arg inWorldSpaceNormal, const CollisionEstimationResult &inResult) const90{91// Calculate jacobian multiplied by linear/angular velocity92float jv = inWorldSpaceNormal.Dot(inResult.mLinearVelocity1 - inResult.mLinearVelocity2) + mR1PlusUxAxis.Dot(inResult.mAngularVelocity1) - mR2xAxis.Dot(inResult.mAngularVelocity2);9394// Lagrange multiplier is:95//96// lambda = -K^-1 (J v + b)97return mEffectiveMass * (jv - mBias);98}99100inline void SolveApplyLambda(Vec3Arg inWorldSpaceNormal, float inInvM1, float inInvM2, float inLambda, CollisionEstimationResult &ioResult) const101{102// Apply impulse to body velocities103ioResult.mLinearVelocity1 -= (inLambda * inInvM1) * inWorldSpaceNormal;104ioResult.mAngularVelocity1 -= inLambda * mInvI1_R1PlusUxAxis;105ioResult.mLinearVelocity2 += (inLambda * inInvM2) * inWorldSpaceNormal;106ioResult.mAngularVelocity2 += inLambda * mInvI2_R2xAxis;107}108109inline void Solve(Vec3Arg inWorldSpaceNormal, float inInvM1, float inInvM2, float inMinLambda, float inMaxLambda, float &ioTotalLambda, CollisionEstimationResult &ioResult) const110{111// Calculate new total lambda112float total_lambda = ioTotalLambda + SolveGetLambda(inWorldSpaceNormal, ioResult);113114// Clamp impulse115total_lambda = Clamp(total_lambda, inMinLambda, inMaxLambda);116117SolveApplyLambda(inWorldSpaceNormal, inInvM1, inInvM2, total_lambda - ioTotalLambda, ioResult);118119ioTotalLambda = total_lambda;120}121122Vec3 mR1PlusUxAxis;123Vec3 mR2xAxis;124Vec3 mInvI1_R1PlusUxAxis;125Vec3 mInvI2_R2xAxis;126float mEffectiveMass;127float mBias;128};129130struct Constraint131{132AxisConstraint mContact;133AxisConstraint mFriction1;134AxisConstraint mFriction2;135};136137// Initialize the constraint properties138Constraint constraints[ContactPoints::Capacity];139for (uint c = 0; c < num_points; ++c)140{141Constraint &constraint = constraints[c];142143// Calculate contact points relative to body 1 and 2144Vec3 p = 0.5f * (inManifold.mRelativeContactPointsOn1[c] + inManifold.mRelativeContactPointsOn2[c]);145Vec3 r1 = p - com1;146Vec3 r2 = p - com2;147148// Initialize contact constraint149constraint.mContact.Initialize(r1, r2, inManifold.mWorldSpaceNormal, inv_m1, inv_m2, inv_i1, inv_i2);150151// Handle elastic collisions152if (inCombinedRestitution > 0.0f)153{154// Calculate velocity of contact point155Vec3 relative_velocity = outResult.mLinearVelocity2 + outResult.mAngularVelocity2.Cross(r2) - outResult.mLinearVelocity1 - outResult.mAngularVelocity1.Cross(r1);156float normal_velocity = relative_velocity.Dot(inManifold.mWorldSpaceNormal);157158// If it is big enough, apply restitution159if (normal_velocity < -inMinVelocityForRestitution)160constraint.mContact.mBias = inCombinedRestitution * normal_velocity;161}162163if (inCombinedFriction > 0.0f)164{165// Initialize friction constraints166constraint.mFriction1.Initialize(r1, r2, outResult.mTangent1, inv_m1, inv_m2, inv_i1, inv_i2);167constraint.mFriction2.Initialize(r1, r2, outResult.mTangent2, inv_m1, inv_m2, inv_i1, inv_i2);168}169}170171// If there's only 1 contact point, we only need 1 iteration172int num_iterations = inCombinedFriction <= 0.0f && num_points == 1? 1 : inNumIterations;173174// Solve iteratively175for (int iteration = 0; iteration < num_iterations; ++iteration)176{177// Solve friction constraints first178if (inCombinedFriction > 0.0f && iteration > 0) // For first iteration the contact impulse is zero so there's no point in applying friction179for (uint c = 0; c < num_points; ++c)180{181const Constraint &constraint = constraints[c];182CollisionEstimationResult::Impulse &impulse = outResult.mImpulses[c];183184float lambda1 = impulse.mFrictionImpulse1 + constraint.mFriction1.SolveGetLambda(outResult.mTangent1, outResult);185float lambda2 = impulse.mFrictionImpulse2 + constraint.mFriction2.SolveGetLambda(outResult.mTangent2, outResult);186187// Calculate max impulse based on contact impulse188float max_impulse = inCombinedFriction * impulse.mContactImpulse;189190// If the total lambda that we will apply is too large, scale it back191float total_lambda_sq = Square(lambda1) + Square(lambda2);192if (total_lambda_sq > Square(max_impulse))193{194float scale = max_impulse / sqrt(total_lambda_sq);195lambda1 *= scale;196lambda2 *= scale;197}198199constraint.mFriction1.SolveApplyLambda(outResult.mTangent1, inv_m1, inv_m2, lambda1 - impulse.mFrictionImpulse1, outResult);200constraint.mFriction2.SolveApplyLambda(outResult.mTangent2, inv_m1, inv_m2, lambda2 - impulse.mFrictionImpulse2, outResult);201202impulse.mFrictionImpulse1 = lambda1;203impulse.mFrictionImpulse2 = lambda2;204}205206// Solve contact constraints last207for (uint c = 0; c < num_points; ++c)208constraints[c].mContact.Solve(inManifold.mWorldSpaceNormal, inv_m1, inv_m2, 0.0f, FLT_MAX, outResult.mImpulses[c].mContactImpulse, outResult);209}210}211212JPH_NAMESPACE_END213214215