SpringPart.h 7.0 KB

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