EstimateCollisionResponse.cpp 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #include <Jolt/Jolt.h>
  4. #include <Jolt/Physics/Collision/EstimateCollisionResponse.h>
  5. #include <Jolt/Physics/Body/Body.h>
  6. JPH_NAMESPACE_BEGIN
  7. void EstimateCollisionResponse(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, Vec3 &outLinearVelocity1, Vec3 &outAngularVelocity1, Vec3 &outLinearVelocity2, Vec3 &outAngularVelocity2, ContactImpulses &outContactImpulses, float inCombinedRestitution, float inMinVelocityForRestitution, uint inNumIterations)
  8. {
  9. // Note this code is based on AxisConstraintPart, see that class for more comments on the math
  10. // Start with zero impulses
  11. outContactImpulses.resize(inManifold.mRelativeContactPointsOn1.size());
  12. for (float &impulse : outContactImpulses)
  13. impulse = 0.0f;
  14. // Get body velocities
  15. EMotionType motion_type1 = inBody1.GetMotionType();
  16. const MotionProperties *motion_properties1 = inBody1.GetMotionPropertiesUnchecked();
  17. if (motion_type1 != EMotionType::Static)
  18. {
  19. outLinearVelocity1 = motion_properties1->GetLinearVelocity();
  20. outAngularVelocity1 = motion_properties1->GetAngularVelocity();
  21. }
  22. else
  23. outLinearVelocity1 = outAngularVelocity1 = Vec3::sZero();
  24. EMotionType motion_type2 = inBody2.GetMotionType();
  25. const MotionProperties *motion_properties2 = inBody2.GetMotionPropertiesUnchecked();
  26. if (motion_type2 != EMotionType::Static)
  27. {
  28. outLinearVelocity2 = motion_properties2->GetLinearVelocity();
  29. outAngularVelocity2 = motion_properties2->GetAngularVelocity();
  30. }
  31. else
  32. outLinearVelocity2 = outAngularVelocity2 = Vec3::sZero();
  33. // Get inverse mass and inertia
  34. float inv_m1, inv_m2;
  35. Mat44 inv_i1, inv_i2;
  36. if (motion_type1 == EMotionType::Dynamic)
  37. {
  38. inv_m1 = motion_properties1->GetInverseMass();
  39. inv_i1 = inBody1.GetInverseInertia();
  40. }
  41. else
  42. {
  43. inv_m1 = 0.0f;
  44. inv_i1 = Mat44::sZero();
  45. }
  46. if (motion_type2 == EMotionType::Dynamic)
  47. {
  48. inv_m2 = motion_properties2->GetInverseMass();
  49. inv_i2 = inBody2.GetInverseInertia();
  50. }
  51. else
  52. {
  53. inv_m2 = 0.0f;
  54. inv_i2 = Mat44::sZero();
  55. }
  56. // Get center of masses relative to the base offset
  57. Vec3 com1 = Vec3(inBody1.GetCenterOfMassPosition() - inManifold.mBaseOffset);
  58. Vec3 com2 = Vec3(inBody2.GetCenterOfMassPosition() - inManifold.mBaseOffset);
  59. struct ContactConstraint
  60. {
  61. Vec3 mR1PlusUxAxis;
  62. Vec3 mR2xAxis;
  63. Vec3 mInvI1_R1PlusUxAxis;
  64. Vec3 mInvI2_R2xAxis;
  65. float mEffectiveMass;
  66. float mBias;
  67. };
  68. // Initialize the constraint properties
  69. ContactConstraint constraints[ContactPoints::capacity()];
  70. JPH_ASSERT(inManifold.mRelativeContactPointsOn1.size() == inManifold.mRelativeContactPointsOn2.size());
  71. for (uint c = 0; c < inManifold.mRelativeContactPointsOn1.size(); ++c)
  72. {
  73. ContactConstraint &contact = constraints[c];
  74. // Calculate contact points relative to body 1 and 2
  75. Vec3 p = 0.5f * (inManifold.mRelativeContactPointsOn1[c] + inManifold.mRelativeContactPointsOn2[c]);
  76. Vec3 r1 = p - com1;
  77. Vec3 r2 = p - com2;
  78. // Calculate effective mass: K^-1 = (J M^-1 J^T)^-1
  79. contact.mR1PlusUxAxis = r1.Cross(inManifold.mWorldSpaceNormal);
  80. contact.mR2xAxis = r2.Cross(inManifold.mWorldSpaceNormal);
  81. contact.mInvI1_R1PlusUxAxis = inv_i1.Multiply3x3(contact.mR1PlusUxAxis);
  82. contact.mInvI2_R2xAxis = inv_i2.Multiply3x3(contact.mR2xAxis);
  83. contact.mEffectiveMass = 1.0f / (inv_m1 + contact.mInvI1_R1PlusUxAxis.Dot(contact.mR1PlusUxAxis) + inv_m2 + contact.mInvI2_R2xAxis.Dot(contact.mR2xAxis));
  84. // Handle elastic collisions
  85. contact.mBias = 0.0f;
  86. if (inCombinedRestitution > 0.0f)
  87. {
  88. // Calculate velocity of contact point
  89. Vec3 relative_velocity = outLinearVelocity2 + outAngularVelocity2.Cross(r2) - outLinearVelocity1 - outAngularVelocity1.Cross(r1);
  90. float normal_velocity = relative_velocity.Dot(inManifold.mWorldSpaceNormal);
  91. // If it is big enough, apply restitution
  92. if (normal_velocity < -inMinVelocityForRestitution)
  93. contact.mBias = inCombinedRestitution * normal_velocity;
  94. }
  95. }
  96. // If there's only 1 contact point, we only need 1 iteration
  97. int num_iterations = inManifold.mRelativeContactPointsOn1.size() == 1? 1 : inNumIterations;
  98. // Calculate the impulse needed to resolve the contacts
  99. for (int iteration = 0; iteration < num_iterations; ++iteration)
  100. for (uint c = 0; c < inManifold.mRelativeContactPointsOn1.size(); ++c)
  101. {
  102. const ContactConstraint &contact = constraints[c];
  103. float &total_lambda = outContactImpulses[c];
  104. // Calculate jacobian multiplied by linear/angular velocity
  105. float jv = inManifold.mWorldSpaceNormal.Dot(outLinearVelocity1 - outLinearVelocity2) + contact.mR1PlusUxAxis.Dot(outAngularVelocity1) - contact.mR2xAxis.Dot(outAngularVelocity2);
  106. // Lagrange multiplier is:
  107. //
  108. // lambda = -K^-1 (J v + b)
  109. float lambda = contact.mEffectiveMass * (jv - contact.mBias);
  110. float new_lambda = max(total_lambda + lambda, 0.0f); // Clamp impulse
  111. lambda = new_lambda - total_lambda; // Lambda potentially got clamped, calculate the new impulse to apply
  112. total_lambda = new_lambda; // Store accumulated impulse
  113. // Apply impulse to body velocities
  114. outLinearVelocity1 -= (lambda * inv_m1) * inManifold.mWorldSpaceNormal;
  115. outAngularVelocity1 -= lambda * contact.mInvI1_R1PlusUxAxis;
  116. outLinearVelocity2 += (lambda * inv_m2) * inManifold.mWorldSpaceNormal;
  117. outAngularVelocity2 += lambda * contact.mInvI2_R2xAxis;
  118. }
  119. }
  120. JPH_NAMESPACE_END