AxisConstraintPart.h 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #pragma once
  5. #include <Jolt/Physics/Body/Body.h>
  6. #include <Jolt/Physics/Constraints/ConstraintPart/SpringPart.h>
  7. #include <Jolt/Physics/Constraints/SpringSettings.h>
  8. #include <Jolt/Physics/StateRecorder.h>
  9. #include <Jolt/Physics/DeterminismLog.h>
  10. JPH_NAMESPACE_BEGIN
  11. /// Constraint that constrains motion along 1 axis
  12. ///
  13. /// @see "Constraints Derivation for Rigid Body Simulation in 3D" - Daniel Chappuis, section 2.1.1
  14. /// (we're not using the approximation of eq 27 but instead add the U term as in eq 55)
  15. ///
  16. /// Constraint equation (eq 25):
  17. ///
  18. /// \f[C = (p_2 - p_1) \cdot n\f]
  19. ///
  20. /// Jacobian (eq 28):
  21. ///
  22. /// \f[J = \begin{bmatrix} -n^T & (-(r_1 + u) \times n)^T & n^T & (r_2 \times n)^T \end{bmatrix}\f]
  23. ///
  24. /// Used terms (here and below, everything in world space):\n
  25. /// n = constraint axis (normalized).\n
  26. /// p1, p2 = constraint points.\n
  27. /// r1 = p1 - x1.\n
  28. /// r2 = p2 - x2.\n
  29. /// u = x2 + r2 - x1 - r1 = p2 - p1.\n
  30. /// x1, x2 = center of mass for the bodies.\n
  31. /// v = [v1, w1, v2, w2].\n
  32. /// v1, v2 = linear velocity of body 1 and 2.\n
  33. /// w1, w2 = angular velocity of body 1 and 2.\n
  34. /// M = mass matrix, a diagonal matrix of the mass and inertia with diagonal [m1, I1, m2, I2].\n
  35. /// \f$K^{-1} = \left( J M^{-1} J^T \right)^{-1}\f$ = effective mass.\n
  36. /// b = velocity bias.\n
  37. /// \f$\beta\f$ = baumgarte constant.
  38. class AxisConstraintPart
  39. {
  40. /// Internal helper function to update velocities of bodies after Lagrange multiplier is calculated
  41. template <EMotionType Type1, EMotionType Type2>
  42. JPH_INLINE bool ApplyVelocityStep(MotionProperties *ioMotionProperties1, MotionProperties *ioMotionProperties2, Vec3Arg inWorldSpaceAxis, float inLambda) const
  43. {
  44. // Apply impulse if delta is not zero
  45. if (inLambda != 0.0f)
  46. {
  47. // Calculate velocity change due to constraint
  48. //
  49. // Impulse:
  50. // P = J^T lambda
  51. //
  52. // Euler velocity integration:
  53. // v' = v + M^-1 P
  54. if constexpr (Type1 == EMotionType::Dynamic)
  55. {
  56. ioMotionProperties1->SubLinearVelocityStep((inLambda * mInverseMass1) * inWorldSpaceAxis);
  57. ioMotionProperties1->SubAngularVelocityStep(inLambda * Vec3::sLoadFloat3Unsafe(mInvI1_R1PlusUxAxis));
  58. }
  59. if constexpr (Type2 == EMotionType::Dynamic)
  60. {
  61. ioMotionProperties2->AddLinearVelocityStep((inLambda * mInverseMass2) * inWorldSpaceAxis);
  62. ioMotionProperties2->AddAngularVelocityStep(inLambda * Vec3::sLoadFloat3Unsafe(mInvI2_R2xAxis));
  63. }
  64. return true;
  65. }
  66. return false;
  67. }
  68. /// Internal helper function to calculate the inverse effective mass
  69. template <EMotionType Type1, EMotionType Type2>
  70. JPH_INLINE float TemplatedCalculateInverseEffectiveMass(float inInvMass1, Mat44Arg inInvI1, Vec3Arg inR1PlusU, float inInvMass2, Mat44Arg inInvI2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis)
  71. {
  72. JPH_ASSERT(inWorldSpaceAxis.IsNormalized(1.0e-5f));
  73. // Calculate properties used below
  74. Vec3 r1_plus_u_x_axis;
  75. if constexpr (Type1 != EMotionType::Static)
  76. {
  77. mInverseMass1 = inInvMass1;
  78. r1_plus_u_x_axis = inR1PlusU.Cross(inWorldSpaceAxis);
  79. r1_plus_u_x_axis.StoreFloat3(&mR1PlusUxAxis);
  80. }
  81. else
  82. {
  83. #ifdef _DEBUG
  84. Vec3::sNaN().StoreFloat3(&mR1PlusUxAxis);
  85. mInverseMass1 = numeric_limits<float>::quiet_NaN();
  86. #endif
  87. }
  88. Vec3 r2_x_axis;
  89. if constexpr (Type2 != EMotionType::Static)
  90. {
  91. mInverseMass2 = inInvMass2;
  92. r2_x_axis = inR2.Cross(inWorldSpaceAxis);
  93. r2_x_axis.StoreFloat3(&mR2xAxis);
  94. }
  95. else
  96. {
  97. #ifdef _DEBUG
  98. Vec3::sNaN().StoreFloat3(&mR2xAxis);
  99. mInverseMass2 = numeric_limits<float>::quiet_NaN();
  100. #endif
  101. }
  102. // Calculate inverse effective mass: K = J M^-1 J^T
  103. float inv_effective_mass;
  104. if constexpr (Type1 == EMotionType::Dynamic)
  105. {
  106. Vec3 invi1_r1_plus_u_x_axis = inInvI1.Multiply3x3(r1_plus_u_x_axis);
  107. invi1_r1_plus_u_x_axis.StoreFloat3(&mInvI1_R1PlusUxAxis);
  108. inv_effective_mass = mInverseMass1 + invi1_r1_plus_u_x_axis.Dot(r1_plus_u_x_axis);
  109. }
  110. else
  111. {
  112. (void)r1_plus_u_x_axis; // Fix compiler warning: Not using this (it's not calculated either)
  113. JPH_IF_DEBUG(Vec3::sNaN().StoreFloat3(&mInvI1_R1PlusUxAxis);)
  114. inv_effective_mass = 0.0f;
  115. }
  116. if constexpr (Type2 == EMotionType::Dynamic)
  117. {
  118. Vec3 invi2_r2_x_axis = inInvI2.Multiply3x3(r2_x_axis);
  119. invi2_r2_x_axis.StoreFloat3(&mInvI2_R2xAxis);
  120. inv_effective_mass += mInverseMass2 + invi2_r2_x_axis.Dot(r2_x_axis);
  121. }
  122. else
  123. {
  124. (void)r2_x_axis; // Fix compiler warning: Not using this (it's not calculated either)
  125. JPH_IF_DEBUG(Vec3::sNaN().StoreFloat3(&mInvI2_R2xAxis);)
  126. }
  127. return inv_effective_mass;
  128. }
  129. /// Internal helper function to calculate the inverse effective mass
  130. JPH_INLINE float CalculateInverseEffectiveMass(const Body &inBody1, Vec3Arg inR1PlusU, const Body &inBody2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis)
  131. {
  132. // Dispatch to the correct templated form
  133. switch (inBody1.GetMotionType())
  134. {
  135. case EMotionType::Dynamic:
  136. {
  137. const MotionProperties *mp1 = inBody1.GetMotionPropertiesUnchecked();
  138. Mat44 invi1 = inBody1.GetInverseInertia();
  139. switch (inBody2.GetMotionType())
  140. {
  141. case EMotionType::Dynamic:
  142. return TemplatedCalculateInverseEffectiveMass<EMotionType::Dynamic, EMotionType::Dynamic>(mp1->GetInverseMass(), invi1, inR1PlusU, inBody2.GetMotionPropertiesUnchecked()->GetInverseMass(), inBody2.GetInverseInertia(), inR2, inWorldSpaceAxis);
  143. case EMotionType::Kinematic:
  144. return TemplatedCalculateInverseEffectiveMass<EMotionType::Dynamic, EMotionType::Kinematic>(mp1->GetInverseMass(), invi1, inR1PlusU, 0 /* Will not be used */, Mat44() /* Will not be used */, inR2, inWorldSpaceAxis);
  145. case EMotionType::Static:
  146. return TemplatedCalculateInverseEffectiveMass<EMotionType::Dynamic, EMotionType::Static>(mp1->GetInverseMass(), invi1, inR1PlusU, 0 /* Will not be used */, Mat44() /* Will not be used */, inR2, inWorldSpaceAxis);
  147. default:
  148. break;
  149. }
  150. break;
  151. }
  152. case EMotionType::Kinematic:
  153. JPH_ASSERT(inBody2.IsDynamic());
  154. return TemplatedCalculateInverseEffectiveMass<EMotionType::Kinematic, EMotionType::Dynamic>(0 /* Will not be used */, Mat44() /* Will not be used */, inR1PlusU, inBody2.GetMotionPropertiesUnchecked()->GetInverseMass(), inBody2.GetInverseInertia(), inR2, inWorldSpaceAxis);
  155. case EMotionType::Static:
  156. JPH_ASSERT(inBody2.IsDynamic());
  157. return TemplatedCalculateInverseEffectiveMass<EMotionType::Static, EMotionType::Dynamic>(0 /* Will not be used */, Mat44() /* Will not be used */, inR1PlusU, inBody2.GetMotionPropertiesUnchecked()->GetInverseMass(), inBody2.GetInverseInertia(), inR2, inWorldSpaceAxis);
  158. default:
  159. break;
  160. }
  161. JPH_ASSERT(false);
  162. return 0.0f;
  163. }
  164. public:
  165. /// Templated form of CalculateConstraintProperties with the motion types baked in
  166. template <EMotionType Type1, EMotionType Type2>
  167. JPH_INLINE void TemplatedCalculateConstraintProperties(float inInvMass1, Mat44Arg inInvI1, Vec3Arg inR1PlusU, float inInvMass2, Mat44Arg inInvI2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias = 0.0f)
  168. {
  169. mEffectiveMass = 1.0f / TemplatedCalculateInverseEffectiveMass<Type1, Type2>(inInvMass1, inInvI1, inR1PlusU, inInvMass2, inInvI2, inR2, inWorldSpaceAxis);
  170. mSpringPart.CalculateSpringPropertiesWithBias(inBias);
  171. JPH_DET_LOG("TemplatedCalculateConstraintProperties: invM1: " << inInvMass1 << " invI1: " << inInvI1 << " r1PlusU: " << inR1PlusU << " invM2: " << inInvMass2 << " invI2: " << inInvI2 << " r2: " << inR2 << " bias: " << inBias << " r1PlusUxAxis: " << mR1PlusUxAxis << " r2xAxis: " << mR2xAxis << " invI1_R1PlusUxAxis: " << mInvI1_R1PlusUxAxis << " invI2_R2xAxis: " << mInvI2_R2xAxis << " effectiveMass: " << mEffectiveMass << " totalLambda: " << mTotalLambda);
  172. }
  173. /// Calculate properties used during the functions below
  174. /// @param inBody1 The first body that this constraint is attached to
  175. /// @param inBody2 The second body that this constraint is attached to
  176. /// @param inR1PlusU See equations above (r1 + u)
  177. /// @param inR2 See equations above (r2)
  178. /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized, pointing from body 1 to 2)
  179. /// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b
  180. inline void CalculateConstraintProperties(const Body &inBody1, Vec3Arg inR1PlusU, const Body &inBody2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias = 0.0f)
  181. {
  182. mEffectiveMass = 1.0f / CalculateInverseEffectiveMass(inBody1, inR1PlusU, inBody2, inR2, inWorldSpaceAxis);
  183. mSpringPart.CalculateSpringPropertiesWithBias(inBias);
  184. }
  185. /// Calculate properties used during the functions below
  186. /// @param inDeltaTime Time step
  187. /// @param inBody1 The first body that this constraint is attached to
  188. /// @param inBody2 The second body that this constraint is attached to
  189. /// @param inR1PlusU See equations above (r1 + u)
  190. /// @param inR2 See equations above (r2)
  191. /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized, pointing from body 1 to 2)
  192. /// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b
  193. /// @param inC Value of the constraint equation (C).
  194. /// @param inFrequency Oscillation frequency (Hz).
  195. /// @param inDamping Damping factor (0 = no damping, 1 = critical damping).
  196. inline void CalculateConstraintPropertiesWithFrequencyAndDamping(float inDeltaTime, const Body &inBody1, Vec3Arg inR1PlusU, const Body &inBody2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias, float inC, float inFrequency, float inDamping)
  197. {
  198. float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inR1PlusU, inBody2, inR2, inWorldSpaceAxis);
  199. mSpringPart.CalculateSpringPropertiesWithFrequencyAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inFrequency, inDamping, mEffectiveMass);
  200. }
  201. /// Calculate properties used during the functions below
  202. /// @param inDeltaTime Time step
  203. /// @param inBody1 The first body that this constraint is attached to
  204. /// @param inBody2 The second body that this constraint is attached to
  205. /// @param inR1PlusU See equations above (r1 + u)
  206. /// @param inR2 See equations above (r2)
  207. /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized, pointing from body 1 to 2)
  208. /// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b
  209. /// @param inC Value of the constraint equation (C).
  210. /// @param inStiffness Spring stiffness k.
  211. /// @param inDamping Spring damping coefficient c.
  212. inline void CalculateConstraintPropertiesWithStiffnessAndDamping(float inDeltaTime, const Body &inBody1, Vec3Arg inR1PlusU, const Body &inBody2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias, float inC, float inStiffness, float inDamping)
  213. {
  214. float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inR1PlusU, inBody2, inR2, inWorldSpaceAxis);
  215. mSpringPart.CalculateSpringPropertiesWithStiffnessAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inStiffness, inDamping, mEffectiveMass);
  216. }
  217. /// Selects one of the above functions based on the spring settings
  218. inline void CalculateConstraintPropertiesWithSettings(float inDeltaTime, const Body &inBody1, Vec3Arg inR1PlusU, const Body &inBody2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias, float inC, const SpringSettings &inSpringSettings)
  219. {
  220. float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inR1PlusU, inBody2, inR2, inWorldSpaceAxis);
  221. if (inSpringSettings.mMode == ESpringMode::FrequencyAndDamping)
  222. mSpringPart.CalculateSpringPropertiesWithFrequencyAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inSpringSettings.mFrequency, inSpringSettings.mDamping, mEffectiveMass);
  223. else
  224. mSpringPart.CalculateSpringPropertiesWithStiffnessAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inSpringSettings.mStiffness, inSpringSettings.mDamping, mEffectiveMass);
  225. }
  226. /// Deactivate this constraint
  227. inline void Deactivate()
  228. {
  229. mEffectiveMass = 0.0f;
  230. mTotalLambda = 0.0f;
  231. }
  232. /// Check if constraint is active
  233. inline bool IsActive() const
  234. {
  235. return mEffectiveMass != 0.0f;
  236. }
  237. /// Templated form of WarmStart with the motion types baked in
  238. template <EMotionType Type1, EMotionType Type2>
  239. inline void TemplatedWarmStart(MotionProperties *ioMotionProperties1, MotionProperties *ioMotionProperties2, Vec3Arg inWorldSpaceAxis, float inWarmStartImpulseRatio)
  240. {
  241. mTotalLambda *= inWarmStartImpulseRatio;
  242. ApplyVelocityStep<Type1, Type2>(ioMotionProperties1, ioMotionProperties2, inWorldSpaceAxis, mTotalLambda);
  243. }
  244. /// Must be called from the WarmStartVelocityConstraint call to apply the previous frame's impulses
  245. /// @param ioBody1 The first body that this constraint is attached to
  246. /// @param ioBody2 The second body that this constraint is attached to
  247. /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized)
  248. /// @param inWarmStartImpulseRatio Ratio of new step to old time step (dt_new / dt_old) for scaling the lagrange multiplier of the previous frame
  249. inline void WarmStart(Body &ioBody1, Body &ioBody2, Vec3Arg inWorldSpaceAxis, float inWarmStartImpulseRatio)
  250. {
  251. EMotionType motion_type1 = ioBody1.GetMotionType();
  252. MotionProperties *motion_properties1 = ioBody1.GetMotionPropertiesUnchecked();
  253. EMotionType motion_type2 = ioBody2.GetMotionType();
  254. MotionProperties *motion_properties2 = ioBody2.GetMotionPropertiesUnchecked();
  255. // Dispatch to the correct templated form
  256. // Note: Warm starting doesn't differentiate between kinematic/static bodies so we handle both as static bodies
  257. if (motion_type1 == EMotionType::Dynamic)
  258. {
  259. if (motion_type2 == EMotionType::Dynamic)
  260. TemplatedWarmStart<EMotionType::Dynamic, EMotionType::Dynamic>(motion_properties1, motion_properties2, inWorldSpaceAxis, inWarmStartImpulseRatio);
  261. else
  262. TemplatedWarmStart<EMotionType::Dynamic, EMotionType::Static>(motion_properties1, motion_properties2, inWorldSpaceAxis, inWarmStartImpulseRatio);
  263. }
  264. else
  265. {
  266. JPH_ASSERT(motion_type2 == EMotionType::Dynamic);
  267. TemplatedWarmStart<EMotionType::Static, EMotionType::Dynamic>(motion_properties1, motion_properties2, inWorldSpaceAxis, inWarmStartImpulseRatio);
  268. }
  269. }
  270. /// Templated form of SolveVelocityConstraint with the motion types baked in, part 1: get the total lambda
  271. template <EMotionType Type1, EMotionType Type2>
  272. JPH_INLINE float TemplatedSolveVelocityConstraintGetTotalLambda(const MotionProperties *ioMotionProperties1, const MotionProperties *ioMotionProperties2, Vec3Arg inWorldSpaceAxis) const
  273. {
  274. // Calculate jacobian multiplied by linear velocity
  275. float jv;
  276. if constexpr (Type1 != EMotionType::Static && Type2 != EMotionType::Static)
  277. jv = inWorldSpaceAxis.Dot(ioMotionProperties1->GetLinearVelocity() - ioMotionProperties2->GetLinearVelocity());
  278. else if constexpr (Type1 != EMotionType::Static)
  279. jv = inWorldSpaceAxis.Dot(ioMotionProperties1->GetLinearVelocity());
  280. else if constexpr (Type2 != EMotionType::Static)
  281. jv = inWorldSpaceAxis.Dot(-ioMotionProperties2->GetLinearVelocity());
  282. else
  283. JPH_ASSERT(false); // Static vs static is nonsensical!
  284. // Calculate jacobian multiplied by angular velocity
  285. if constexpr (Type1 != EMotionType::Static)
  286. jv += Vec3::sLoadFloat3Unsafe(mR1PlusUxAxis).Dot(ioMotionProperties1->GetAngularVelocity());
  287. if constexpr (Type2 != EMotionType::Static)
  288. jv -= Vec3::sLoadFloat3Unsafe(mR2xAxis).Dot(ioMotionProperties2->GetAngularVelocity());
  289. // Lagrange multiplier is:
  290. //
  291. // lambda = -K^-1 (J v + b)
  292. float lambda = mEffectiveMass * (jv - mSpringPart.GetBias(mTotalLambda));
  293. // Return the total accumulated lambda
  294. return mTotalLambda + lambda;
  295. }
  296. /// Templated form of SolveVelocityConstraint with the motion types baked in, part 2: apply new lambda
  297. template <EMotionType Type1, EMotionType Type2>
  298. JPH_INLINE bool TemplatedSolveVelocityConstraintApplyLambda(MotionProperties *ioMotionProperties1, MotionProperties *ioMotionProperties2, Vec3Arg inWorldSpaceAxis, float inTotalLambda)
  299. {
  300. float delta_lambda = inTotalLambda - mTotalLambda; // Calculate change in lambda
  301. mTotalLambda = inTotalLambda; // Store accumulated impulse
  302. return ApplyVelocityStep<Type1, Type2>(ioMotionProperties1, ioMotionProperties2, inWorldSpaceAxis, delta_lambda);
  303. }
  304. /// Templated form of SolveVelocityConstraint with the motion types baked in
  305. template <EMotionType Type1, EMotionType Type2>
  306. inline bool TemplatedSolveVelocityConstraint(MotionProperties *ioMotionProperties1, MotionProperties *ioMotionProperties2, Vec3Arg inWorldSpaceAxis, float inMinLambda, float inMaxLambda)
  307. {
  308. float total_lambda = TemplatedSolveVelocityConstraintGetTotalLambda<Type1, Type2>(ioMotionProperties1, ioMotionProperties2, inWorldSpaceAxis);
  309. // Clamp impulse to specified range
  310. total_lambda = Clamp(total_lambda, inMinLambda, inMaxLambda);
  311. return TemplatedSolveVelocityConstraintApplyLambda<Type1, Type2>(ioMotionProperties1, ioMotionProperties2, inWorldSpaceAxis, total_lambda);
  312. }
  313. /// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation.
  314. /// @param ioBody1 The first body that this constraint is attached to
  315. /// @param ioBody2 The second body that this constraint is attached to
  316. /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized)
  317. /// @param inMinLambda Minimum value of constraint impulse to apply (N s)
  318. /// @param inMaxLambda Maximum value of constraint impulse to apply (N s)
  319. inline bool SolveVelocityConstraint(Body &ioBody1, Body &ioBody2, Vec3Arg inWorldSpaceAxis, float inMinLambda, float inMaxLambda)
  320. {
  321. EMotionType motion_type1 = ioBody1.GetMotionType();
  322. MotionProperties *motion_properties1 = ioBody1.GetMotionPropertiesUnchecked();
  323. EMotionType motion_type2 = ioBody2.GetMotionType();
  324. MotionProperties *motion_properties2 = ioBody2.GetMotionPropertiesUnchecked();
  325. // Dispatch to the correct templated form
  326. switch (motion_type1)
  327. {
  328. case EMotionType::Dynamic:
  329. switch (motion_type2)
  330. {
  331. case EMotionType::Dynamic:
  332. return TemplatedSolveVelocityConstraint<EMotionType::Dynamic, EMotionType::Dynamic>(motion_properties1, motion_properties2, inWorldSpaceAxis, inMinLambda, inMaxLambda);
  333. case EMotionType::Kinematic:
  334. return TemplatedSolveVelocityConstraint<EMotionType::Dynamic, EMotionType::Kinematic>(motion_properties1, motion_properties2, inWorldSpaceAxis, inMinLambda, inMaxLambda);
  335. case EMotionType::Static:
  336. return TemplatedSolveVelocityConstraint<EMotionType::Dynamic, EMotionType::Static>(motion_properties1, motion_properties2, inWorldSpaceAxis, inMinLambda, inMaxLambda);
  337. default:
  338. JPH_ASSERT(false);
  339. break;
  340. }
  341. break;
  342. case EMotionType::Kinematic:
  343. JPH_ASSERT(motion_type2 == EMotionType::Dynamic);
  344. return TemplatedSolveVelocityConstraint<EMotionType::Kinematic, EMotionType::Dynamic>(motion_properties1, motion_properties2, inWorldSpaceAxis, inMinLambda, inMaxLambda);
  345. case EMotionType::Static:
  346. JPH_ASSERT(motion_type2 == EMotionType::Dynamic);
  347. return TemplatedSolveVelocityConstraint<EMotionType::Static, EMotionType::Dynamic>(motion_properties1, motion_properties2, inWorldSpaceAxis, inMinLambda, inMaxLambda);
  348. default:
  349. JPH_ASSERT(false);
  350. break;
  351. }
  352. return false;
  353. }
  354. /// Iteratively update the position constraint. Makes sure C(...) = 0.
  355. /// @param ioBody1 The first body that this constraint is attached to
  356. /// @param ioBody2 The second body that this constraint is attached to
  357. /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized)
  358. /// @param inC Value of the constraint equation (C)
  359. /// @param inBaumgarte Baumgarte constant (fraction of the error to correct)
  360. inline bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, Vec3Arg inWorldSpaceAxis, float inC, float inBaumgarte) const
  361. {
  362. // Only apply position constraint when the constraint is hard, otherwise the velocity bias will fix the constraint
  363. if (inC != 0.0f && !mSpringPart.IsActive())
  364. {
  365. // Calculate lagrange multiplier (lambda) for Baumgarte stabilization:
  366. //
  367. // lambda = -K^-1 * beta / dt * C
  368. //
  369. // We should divide by inDeltaTime, but we should multiply by inDeltaTime in the Euler step below so they're cancelled out
  370. float lambda = -mEffectiveMass * inBaumgarte * inC;
  371. // Directly integrate velocity change for one time step
  372. //
  373. // Euler velocity integration:
  374. // dv = M^-1 P
  375. //
  376. // Impulse:
  377. // P = J^T lambda
  378. //
  379. // Euler position integration:
  380. // x' = x + dv * dt
  381. //
  382. // Note we don't accumulate velocities for the stabilization. This is using the approach described in 'Modeling and
  383. // Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte
  384. // stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity
  385. // integrate + a position integrate and then discard the velocity change.
  386. if (ioBody1.IsDynamic())
  387. {
  388. ioBody1.SubPositionStep((lambda * mInverseMass1) * inWorldSpaceAxis);
  389. ioBody1.SubRotationStep(lambda * Vec3::sLoadFloat3Unsafe(mInvI1_R1PlusUxAxis));
  390. }
  391. if (ioBody2.IsDynamic())
  392. {
  393. ioBody2.AddPositionStep((lambda * mInverseMass2) * inWorldSpaceAxis);
  394. ioBody2.AddRotationStep(lambda * Vec3::sLoadFloat3Unsafe(mInvI2_R2xAxis));
  395. }
  396. return true;
  397. }
  398. return false;
  399. }
  400. /// Override total lagrange multiplier, can be used to set the initial value for warm starting
  401. inline void SetTotalLambda(float inLambda)
  402. {
  403. mTotalLambda = inLambda;
  404. }
  405. /// Return lagrange multiplier
  406. inline float GetTotalLambda() const
  407. {
  408. return mTotalLambda;
  409. }
  410. /// Save state of this constraint part
  411. void SaveState(StateRecorder &inStream) const
  412. {
  413. inStream.Write(mTotalLambda);
  414. }
  415. /// Restore state of this constraint part
  416. void RestoreState(StateRecorder &inStream)
  417. {
  418. inStream.Read(mTotalLambda);
  419. }
  420. private:
  421. Float3 mR1PlusUxAxis;
  422. Float3 mR2xAxis;
  423. Float3 mInvI1_R1PlusUxAxis;
  424. Float3 mInvI2_R2xAxis;
  425. float mInverseMass1;
  426. float mInverseMass2;
  427. float mEffectiveMass = 0.0f;
  428. SpringPart mSpringPart;
  429. float mTotalLambda = 0.0f;
  430. };
  431. JPH_NAMESPACE_END