EstimateCollisionResponse.cpp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #include <Jolt/Jolt.h>
  5. #include <Jolt/Physics/Collision/EstimateCollisionResponse.h>
  6. #include <Jolt/Physics/Body/Body.h>
  7. JPH_NAMESPACE_BEGIN
  8. void EstimateCollisionResponse(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, CollisionEstimationResult &outResult, float inCombinedFriction, float inCombinedRestitution, float inMinVelocityForRestitution, uint inNumIterations)
  9. {
  10. // Note this code is based on AxisConstraintPart, see that class for more comments on the math
  11. ContactPoints::size_type num_points = inManifold.mRelativeContactPointsOn1.size();
  12. JPH_ASSERT(num_points == inManifold.mRelativeContactPointsOn2.size());
  13. // Start with zero impulses
  14. outResult.mImpulses.resize(num_points);
  15. memset(outResult.mImpulses.data(), 0, num_points * sizeof(CollisionEstimationResult::Impulse));
  16. // Calculate friction directions
  17. outResult.mTangent1 = inManifold.mWorldSpaceNormal.GetNormalizedPerpendicular();
  18. outResult.mTangent2 = inManifold.mWorldSpaceNormal.Cross(outResult.mTangent1);
  19. // Get body velocities
  20. EMotionType motion_type1 = inBody1.GetMotionType();
  21. const MotionProperties *motion_properties1 = inBody1.GetMotionPropertiesUnchecked();
  22. if (motion_type1 != EMotionType::Static)
  23. {
  24. outResult.mLinearVelocity1 = motion_properties1->GetLinearVelocity();
  25. outResult.mAngularVelocity1 = motion_properties1->GetAngularVelocity();
  26. }
  27. else
  28. outResult.mLinearVelocity1 = outResult.mAngularVelocity1 = Vec3::sZero();
  29. EMotionType motion_type2 = inBody2.GetMotionType();
  30. const MotionProperties *motion_properties2 = inBody2.GetMotionPropertiesUnchecked();
  31. if (motion_type2 != EMotionType::Static)
  32. {
  33. outResult.mLinearVelocity2 = motion_properties2->GetLinearVelocity();
  34. outResult.mAngularVelocity2 = motion_properties2->GetAngularVelocity();
  35. }
  36. else
  37. outResult.mLinearVelocity2 = outResult.mAngularVelocity2 = Vec3::sZero();
  38. // Get inverse mass and inertia
  39. float inv_m1, inv_m2;
  40. Mat44 inv_i1, inv_i2;
  41. if (motion_type1 == EMotionType::Dynamic)
  42. {
  43. inv_m1 = motion_properties1->GetInverseMass();
  44. inv_i1 = inBody1.GetInverseInertia();
  45. }
  46. else
  47. {
  48. inv_m1 = 0.0f;
  49. inv_i1 = Mat44::sZero();
  50. }
  51. if (motion_type2 == EMotionType::Dynamic)
  52. {
  53. inv_m2 = motion_properties2->GetInverseMass();
  54. inv_i2 = inBody2.GetInverseInertia();
  55. }
  56. else
  57. {
  58. inv_m2 = 0.0f;
  59. inv_i2 = Mat44::sZero();
  60. }
  61. // Get center of masses relative to the base offset
  62. Vec3 com1 = Vec3(inBody1.GetCenterOfMassPosition() - inManifold.mBaseOffset);
  63. Vec3 com2 = Vec3(inBody2.GetCenterOfMassPosition() - inManifold.mBaseOffset);
  64. struct AxisConstraint
  65. {
  66. inline void Initialize(Vec3Arg inR1, Vec3Arg inR2, Vec3Arg inWorldSpaceNormal, float inInvM1, float inInvM2, Mat44Arg inInvI1, Mat44Arg inInvI2)
  67. {
  68. // Calculate effective mass: K^-1 = (J M^-1 J^T)^-1
  69. mR1PlusUxAxis = inR1.Cross(inWorldSpaceNormal);
  70. mR2xAxis = inR2.Cross(inWorldSpaceNormal);
  71. mInvI1_R1PlusUxAxis = inInvI1.Multiply3x3(mR1PlusUxAxis);
  72. mInvI2_R2xAxis = inInvI2.Multiply3x3(mR2xAxis);
  73. mEffectiveMass = 1.0f / (inInvM1 + mInvI1_R1PlusUxAxis.Dot(mR1PlusUxAxis) + inInvM2 + mInvI2_R2xAxis.Dot(mR2xAxis));
  74. mBias = 0.0f;
  75. }
  76. inline float SolveGetLambda(Vec3Arg inWorldSpaceNormal, const CollisionEstimationResult &inResult) const
  77. {
  78. // Calculate jacobian multiplied by linear/angular velocity
  79. float jv = inWorldSpaceNormal.Dot(inResult.mLinearVelocity1 - inResult.mLinearVelocity2) + mR1PlusUxAxis.Dot(inResult.mAngularVelocity1) - mR2xAxis.Dot(inResult.mAngularVelocity2);
  80. // Lagrange multiplier is:
  81. //
  82. // lambda = -K^-1 (J v + b)
  83. return mEffectiveMass * (jv - mBias);
  84. }
  85. inline void SolveApplyLambda(Vec3Arg inWorldSpaceNormal, float inInvM1, float inInvM2, float inLambda, CollisionEstimationResult &ioResult) const
  86. {
  87. // Apply impulse to body velocities
  88. ioResult.mLinearVelocity1 -= (inLambda * inInvM1) * inWorldSpaceNormal;
  89. ioResult.mAngularVelocity1 -= inLambda * mInvI1_R1PlusUxAxis;
  90. ioResult.mLinearVelocity2 += (inLambda * inInvM2) * inWorldSpaceNormal;
  91. ioResult.mAngularVelocity2 += inLambda * mInvI2_R2xAxis;
  92. }
  93. inline void Solve(Vec3Arg inWorldSpaceNormal, float inInvM1, float inInvM2, float inMinLambda, float inMaxLambda, float &ioTotalLambda, CollisionEstimationResult &ioResult) const
  94. {
  95. // Calculate new total lambda
  96. float total_lambda = ioTotalLambda + SolveGetLambda(inWorldSpaceNormal, ioResult);
  97. // Clamp impulse
  98. total_lambda = Clamp(total_lambda, inMinLambda, inMaxLambda);
  99. SolveApplyLambda(inWorldSpaceNormal, inInvM1, inInvM2, total_lambda - ioTotalLambda, ioResult);
  100. ioTotalLambda = total_lambda;
  101. }
  102. Vec3 mR1PlusUxAxis;
  103. Vec3 mR2xAxis;
  104. Vec3 mInvI1_R1PlusUxAxis;
  105. Vec3 mInvI2_R2xAxis;
  106. float mEffectiveMass;
  107. float mBias;
  108. };
  109. struct Constraint
  110. {
  111. AxisConstraint mContact;
  112. AxisConstraint mFriction1;
  113. AxisConstraint mFriction2;
  114. };
  115. // Initialize the constraint properties
  116. Constraint constraints[ContactPoints::Capacity];
  117. for (uint c = 0; c < num_points; ++c)
  118. {
  119. Constraint &constraint = constraints[c];
  120. // Calculate contact points relative to body 1 and 2
  121. Vec3 p = 0.5f * (inManifold.mRelativeContactPointsOn1[c] + inManifold.mRelativeContactPointsOn2[c]);
  122. Vec3 r1 = p - com1;
  123. Vec3 r2 = p - com2;
  124. // Initialize contact constraint
  125. constraint.mContact.Initialize(r1, r2, inManifold.mWorldSpaceNormal, inv_m1, inv_m2, inv_i1, inv_i2);
  126. // Handle elastic collisions
  127. if (inCombinedRestitution > 0.0f)
  128. {
  129. // Calculate velocity of contact point
  130. Vec3 relative_velocity = outResult.mLinearVelocity2 + outResult.mAngularVelocity2.Cross(r2) - outResult.mLinearVelocity1 - outResult.mAngularVelocity1.Cross(r1);
  131. float normal_velocity = relative_velocity.Dot(inManifold.mWorldSpaceNormal);
  132. // If it is big enough, apply restitution
  133. if (normal_velocity < -inMinVelocityForRestitution)
  134. constraint.mContact.mBias = inCombinedRestitution * normal_velocity;
  135. }
  136. if (inCombinedFriction > 0.0f)
  137. {
  138. // Initialize friction constraints
  139. constraint.mFriction1.Initialize(r1, r2, outResult.mTangent1, inv_m1, inv_m2, inv_i1, inv_i2);
  140. constraint.mFriction2.Initialize(r1, r2, outResult.mTangent2, inv_m1, inv_m2, inv_i1, inv_i2);
  141. }
  142. }
  143. // If there's only 1 contact point, we only need 1 iteration
  144. int num_iterations = inCombinedFriction <= 0.0f && num_points == 1? 1 : inNumIterations;
  145. // Solve iteratively
  146. for (int iteration = 0; iteration < num_iterations; ++iteration)
  147. {
  148. // Solve friction constraints first
  149. if (inCombinedFriction > 0.0f && iteration > 0) // For first iteration the contact impulse is zero so there's no point in applying friction
  150. for (uint c = 0; c < num_points; ++c)
  151. {
  152. const Constraint &constraint = constraints[c];
  153. CollisionEstimationResult::Impulse &impulse = outResult.mImpulses[c];
  154. float lambda1 = impulse.mFrictionImpulse1 + constraint.mFriction1.SolveGetLambda(outResult.mTangent1, outResult);
  155. float lambda2 = impulse.mFrictionImpulse2 + constraint.mFriction2.SolveGetLambda(outResult.mTangent2, outResult);
  156. // Calculate max impulse based on contact impulse
  157. float max_impulse = inCombinedFriction * impulse.mContactImpulse;
  158. // If the total lambda that we will apply is too large, scale it back
  159. float total_lambda_sq = Square(lambda1) + Square(lambda2);
  160. if (total_lambda_sq > Square(max_impulse))
  161. {
  162. float scale = max_impulse / sqrt(total_lambda_sq);
  163. lambda1 *= scale;
  164. lambda2 *= scale;
  165. }
  166. constraint.mFriction1.SolveApplyLambda(outResult.mTangent1, inv_m1, inv_m2, lambda1 - impulse.mFrictionImpulse1, outResult);
  167. constraint.mFriction2.SolveApplyLambda(outResult.mTangent2, inv_m1, inv_m2, lambda2 - impulse.mFrictionImpulse2, outResult);
  168. impulse.mFrictionImpulse1 = lambda1;
  169. impulse.mFrictionImpulse2 = lambda2;
  170. }
  171. // Solve contact constraints last
  172. for (uint c = 0; c < num_points; ++c)
  173. constraints[c].mContact.Solve(inManifold.mWorldSpaceNormal, inv_m1, inv_m2, 0.0f, FLT_MAX, outResult.mImpulses[c].mContactImpulse, outResult);
  174. }
  175. }
  176. JPH_NAMESPACE_END