SpringPart.h 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #pragma once
  4. JPH_NAMESPACE_BEGIN
  5. #ifndef JPH_PLATFORM_DOXYGEN // Somehow Doxygen gets confused and thinks the parameters to CalculateSpringProperties belong to this macro
  6. JPH_MSVC_SUPPRESS_WARNING(4723) // potential divide by 0 - caused by line: outEffectiveMass = 1.0f / inInvEffectiveMass, note that JPH_NAMESPACE_BEGIN already pushes the warning state
  7. #endif // !JPH_PLATFORM_DOXYGEN
  8. /// Class used in other constraint parts to calculate the required bias factor in the lagrange multiplier for creating springs
  9. class SpringPart
  10. {
  11. public:
  12. /// Calculate spring properties
  13. ///
  14. /// @param inDeltaTime Time step
  15. /// @param inInvEffectiveMass Inverse effective mass K
  16. /// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b
  17. /// @param inC Value of the constraint equation (C). Set to zero if you don't want to drive the constraint to zero with a spring.
  18. /// @param inFrequency Oscillation frequency (Hz). Set to zero if you don't want to drive the constraint to zero with a spring.
  19. /// @param inDamping Damping factor (0 = no damping, 1 = critical damping). Set to zero if you don't want to drive the constraint to zero with a spring.
  20. /// @param outEffectiveMass On return, this contains the new effective mass K^-1
  21. inline void CalculateSpringProperties(float inDeltaTime, float inInvEffectiveMass, float inBias, float inC, float inFrequency, float inDamping, float &outEffectiveMass)
  22. {
  23. outEffectiveMass = 1.0f / inInvEffectiveMass;
  24. // Soft constraints as per: Soft Contraints: Reinventing The Spring - Erin Catto - GDC 2011
  25. if (inFrequency > 0.0f)
  26. {
  27. // Calculate angular frequency
  28. float omega = 2.0f * JPH_PI * inFrequency;
  29. // Calculate spring constant k and drag constant c (page 45)
  30. float k = outEffectiveMass * Square(omega);
  31. float c = 2.0f * outEffectiveMass * inDamping * omega;
  32. // Note that the calculation of beta and gamma below are based on the solution of an implicit Euler integration scheme
  33. // This scheme is unconditionally stable but has built in damping, so even when you set the damping ratio to 0 there will still
  34. // be damping. See page 16 and 32.
  35. // Calculate softness (gamma in the slides)
  36. // See page 34 and note that the gamma needs to be divided by delta time since we're working with impulses rather than forces:
  37. // softness = 1 / (dt * (c + dt * k))
  38. mSoftness = 1.0f / (inDeltaTime * (c + inDeltaTime * k));
  39. // Calculate bias factor (baumgarte stabilization):
  40. // beta = dt * k / (c + dt * k) = dt * k^2 * softness
  41. // b = beta / dt * C = dt * k * softness * C;
  42. mBias = inBias + inDeltaTime * k * mSoftness * inC;
  43. // Update the effective mass, see post by Erin Catto: http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?f=4&t=1354
  44. //
  45. // Newton's Law:
  46. // M * (v2 - v1) = J^T * lambda
  47. //
  48. // Velocity constraint with softness and Baumgarte:
  49. // J * v2 + softness * lambda + b = 0
  50. //
  51. // where b = beta * C / dt
  52. //
  53. // We know everything except v2 and lambda.
  54. //
  55. // First solve Newton's law for v2 in terms of lambda:
  56. //
  57. // v2 = v1 + M^-1 * J^T * lambda
  58. //
  59. // Substitute this expression into the velocity constraint:
  60. //
  61. // J * (v1 + M^-1 * J^T * lambda) + softness * lambda + b = 0
  62. //
  63. // Now collect coefficients of lambda:
  64. //
  65. // (J * M^-1 * J^T + softness) * lambda = - J * v1 - b
  66. //
  67. // Now we define:
  68. //
  69. // K = J * M^-1 * J^T + softness
  70. //
  71. // So our new effective mass is K^-1
  72. outEffectiveMass = 1.0f / (inInvEffectiveMass + mSoftness);
  73. }
  74. else
  75. {
  76. mSoftness = 0.0f;
  77. mBias = inBias;
  78. }
  79. }
  80. /// Returns if this spring is active
  81. inline bool IsActive() const
  82. {
  83. return mSoftness != 0.0f;
  84. }
  85. /// Get total bias b, including supplied bias and bias for spring: lambda = J v + b
  86. inline float GetBias(float inTotalLambda) const
  87. {
  88. // Remainder of post by Erin Catto: http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?f=4&t=1354
  89. //
  90. // Each iteration we are not computing the whole impulse, we are computing an increment to the impulse and we are updating the velocity.
  91. // Also, as we solve each constraint we get a perfect v2, but then some other constraint will come along and mess it up.
  92. // So we want to patch up the constraint while acknowledging the accumulated impulse and the damaged velocity.
  93. // To help with that we use P for the accumulated impulse and lambda as the update. Mathematically we have:
  94. //
  95. // M * (v2new - v2damaged) = J^T * lambda
  96. // J * v2new + softness * (total_lambda + lambda) + b = 0
  97. //
  98. // If we solve this we get:
  99. //
  100. // v2new = v2damaged + M^-1 * J^T * lambda
  101. // J * (v2damaged + M^-1 * J^T * lambda) + softness * total_lambda + softness * lambda + b = 0
  102. //
  103. // (J * M^-1 * J^T + softness) * lambda = -(J * v2damaged + softness * total_lambda + b)
  104. //
  105. // So our lagrange multiplier becomes:
  106. //
  107. // lambda = -K^-1 (J v + softness * total_lambda + b)
  108. //
  109. // So we return the bias: softness * total_lambda + b
  110. return mSoftness * inTotalLambda + mBias;
  111. }
  112. private:
  113. float mBias = 0.0f;
  114. float mSoftness = 0.0f;
  115. };
  116. JPH_NAMESPACE_END