AxisConstraintPart.h 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  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. float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inR1PlusU, inBody2, inR2, inWorldSpaceAxis);
  183. if (inv_effective_mass == 0.0f)
  184. Deactivate();
  185. else
  186. {
  187. mEffectiveMass = 1.0f / inv_effective_mass;
  188. mSpringPart.CalculateSpringPropertiesWithBias(inBias);
  189. }
  190. }
  191. /// Calculate properties used during the functions below
  192. /// @param inDeltaTime Time step
  193. /// @param inBody1 The first body that this constraint is attached to
  194. /// @param inBody2 The second body that this constraint is attached to
  195. /// @param inR1PlusU See equations above (r1 + u)
  196. /// @param inR2 See equations above (r2)
  197. /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized, pointing from body 1 to 2)
  198. /// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b
  199. /// @param inC Value of the constraint equation (C).
  200. /// @param inFrequency Oscillation frequency (Hz).
  201. /// @param inDamping Damping factor (0 = no damping, 1 = critical damping).
  202. 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)
  203. {
  204. float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inR1PlusU, inBody2, inR2, inWorldSpaceAxis);
  205. if (inv_effective_mass == 0.0f)
  206. Deactivate();
  207. else
  208. mSpringPart.CalculateSpringPropertiesWithFrequencyAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inFrequency, inDamping, mEffectiveMass);
  209. }
  210. /// Calculate properties used during the functions below
  211. /// @param inDeltaTime Time step
  212. /// @param inBody1 The first body that this constraint is attached to
  213. /// @param inBody2 The second body that this constraint is attached to
  214. /// @param inR1PlusU See equations above (r1 + u)
  215. /// @param inR2 See equations above (r2)
  216. /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized, pointing from body 1 to 2)
  217. /// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b
  218. /// @param inC Value of the constraint equation (C).
  219. /// @param inStiffness Spring stiffness k.
  220. /// @param inDamping Spring damping coefficient c.
  221. 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)
  222. {
  223. float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inR1PlusU, inBody2, inR2, inWorldSpaceAxis);
  224. if (inv_effective_mass == 0.0f)
  225. Deactivate();
  226. else
  227. mSpringPart.CalculateSpringPropertiesWithStiffnessAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inStiffness, inDamping, mEffectiveMass);
  228. }
  229. /// Selects one of the above functions based on the spring settings
  230. inline void CalculateConstraintPropertiesWithSettings(float inDeltaTime, const Body &inBody1, Vec3Arg inR1PlusU, const Body &inBody2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias, float inC, const SpringSettings &inSpringSettings)
  231. {
  232. float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inR1PlusU, inBody2, inR2, inWorldSpaceAxis);
  233. if (inv_effective_mass == 0.0f)
  234. Deactivate();
  235. else if (inSpringSettings.mMode == ESpringMode::FrequencyAndDamping)
  236. mSpringPart.CalculateSpringPropertiesWithFrequencyAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inSpringSettings.mFrequency, inSpringSettings.mDamping, mEffectiveMass);
  237. else
  238. mSpringPart.CalculateSpringPropertiesWithStiffnessAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inSpringSettings.mStiffness, inSpringSettings.mDamping, mEffectiveMass);
  239. }
  240. /// Deactivate this constraint
  241. inline void Deactivate()
  242. {
  243. mEffectiveMass = 0.0f;
  244. mTotalLambda = 0.0f;
  245. }
  246. /// Check if constraint is active
  247. inline bool IsActive() const
  248. {
  249. return mEffectiveMass != 0.0f;
  250. }
  251. /// Templated form of WarmStart with the motion types baked in
  252. template <EMotionType Type1, EMotionType Type2>
  253. inline void TemplatedWarmStart(MotionProperties *ioMotionProperties1, MotionProperties *ioMotionProperties2, Vec3Arg inWorldSpaceAxis, float inWarmStartImpulseRatio)
  254. {
  255. mTotalLambda *= inWarmStartImpulseRatio;
  256. ApplyVelocityStep<Type1, Type2>(ioMotionProperties1, ioMotionProperties2, inWorldSpaceAxis, mTotalLambda);
  257. }
  258. /// Must be called from the WarmStartVelocityConstraint call to apply the previous frame's impulses
  259. /// @param ioBody1 The first body that this constraint is attached to
  260. /// @param ioBody2 The second body that this constraint is attached to
  261. /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized)
  262. /// @param inWarmStartImpulseRatio Ratio of new step to old time step (dt_new / dt_old) for scaling the lagrange multiplier of the previous frame
  263. inline void WarmStart(Body &ioBody1, Body &ioBody2, Vec3Arg inWorldSpaceAxis, float inWarmStartImpulseRatio)
  264. {
  265. EMotionType motion_type1 = ioBody1.GetMotionType();
  266. MotionProperties *motion_properties1 = ioBody1.GetMotionPropertiesUnchecked();
  267. EMotionType motion_type2 = ioBody2.GetMotionType();
  268. MotionProperties *motion_properties2 = ioBody2.GetMotionPropertiesUnchecked();
  269. // Dispatch to the correct templated form
  270. // Note: Warm starting doesn't differentiate between kinematic/static bodies so we handle both as static bodies
  271. if (motion_type1 == EMotionType::Dynamic)
  272. {
  273. if (motion_type2 == EMotionType::Dynamic)
  274. TemplatedWarmStart<EMotionType::Dynamic, EMotionType::Dynamic>(motion_properties1, motion_properties2, inWorldSpaceAxis, inWarmStartImpulseRatio);
  275. else
  276. TemplatedWarmStart<EMotionType::Dynamic, EMotionType::Static>(motion_properties1, motion_properties2, inWorldSpaceAxis, inWarmStartImpulseRatio);
  277. }
  278. else
  279. {
  280. JPH_ASSERT(motion_type2 == EMotionType::Dynamic);
  281. TemplatedWarmStart<EMotionType::Static, EMotionType::Dynamic>(motion_properties1, motion_properties2, inWorldSpaceAxis, inWarmStartImpulseRatio);
  282. }
  283. }
  284. /// Templated form of SolveVelocityConstraint with the motion types baked in, part 1: get the total lambda
  285. template <EMotionType Type1, EMotionType Type2>
  286. JPH_INLINE float TemplatedSolveVelocityConstraintGetTotalLambda(const MotionProperties *ioMotionProperties1, const MotionProperties *ioMotionProperties2, Vec3Arg inWorldSpaceAxis) const
  287. {
  288. // Calculate jacobian multiplied by linear velocity
  289. float jv;
  290. if constexpr (Type1 != EMotionType::Static && Type2 != EMotionType::Static)
  291. jv = inWorldSpaceAxis.Dot(ioMotionProperties1->GetLinearVelocity() - ioMotionProperties2->GetLinearVelocity());
  292. else if constexpr (Type1 != EMotionType::Static)
  293. jv = inWorldSpaceAxis.Dot(ioMotionProperties1->GetLinearVelocity());
  294. else if constexpr (Type2 != EMotionType::Static)
  295. jv = inWorldSpaceAxis.Dot(-ioMotionProperties2->GetLinearVelocity());
  296. else
  297. JPH_ASSERT(false); // Static vs static is nonsensical!
  298. // Calculate jacobian multiplied by angular velocity
  299. if constexpr (Type1 != EMotionType::Static)
  300. jv += Vec3::sLoadFloat3Unsafe(mR1PlusUxAxis).Dot(ioMotionProperties1->GetAngularVelocity());
  301. if constexpr (Type2 != EMotionType::Static)
  302. jv -= Vec3::sLoadFloat3Unsafe(mR2xAxis).Dot(ioMotionProperties2->GetAngularVelocity());
  303. // Lagrange multiplier is:
  304. //
  305. // lambda = -K^-1 (J v + b)
  306. float lambda = mEffectiveMass * (jv - mSpringPart.GetBias(mTotalLambda));
  307. // Return the total accumulated lambda
  308. return mTotalLambda + lambda;
  309. }
  310. /// Templated form of SolveVelocityConstraint with the motion types baked in, part 2: apply new lambda
  311. template <EMotionType Type1, EMotionType Type2>
  312. JPH_INLINE bool TemplatedSolveVelocityConstraintApplyLambda(MotionProperties *ioMotionProperties1, MotionProperties *ioMotionProperties2, Vec3Arg inWorldSpaceAxis, float inTotalLambda)
  313. {
  314. float delta_lambda = inTotalLambda - mTotalLambda; // Calculate change in lambda
  315. mTotalLambda = inTotalLambda; // Store accumulated impulse
  316. return ApplyVelocityStep<Type1, Type2>(ioMotionProperties1, ioMotionProperties2, inWorldSpaceAxis, delta_lambda);
  317. }
  318. /// Templated form of SolveVelocityConstraint with the motion types baked in
  319. template <EMotionType Type1, EMotionType Type2>
  320. inline bool TemplatedSolveVelocityConstraint(MotionProperties *ioMotionProperties1, MotionProperties *ioMotionProperties2, Vec3Arg inWorldSpaceAxis, float inMinLambda, float inMaxLambda)
  321. {
  322. float total_lambda = TemplatedSolveVelocityConstraintGetTotalLambda<Type1, Type2>(ioMotionProperties1, ioMotionProperties2, inWorldSpaceAxis);
  323. // Clamp impulse to specified range
  324. total_lambda = Clamp(total_lambda, inMinLambda, inMaxLambda);
  325. return TemplatedSolveVelocityConstraintApplyLambda<Type1, Type2>(ioMotionProperties1, ioMotionProperties2, inWorldSpaceAxis, total_lambda);
  326. }
  327. /// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation.
  328. /// @param ioBody1 The first body that this constraint is attached to
  329. /// @param ioBody2 The second body that this constraint is attached to
  330. /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized)
  331. /// @param inMinLambda Minimum value of constraint impulse to apply (N s)
  332. /// @param inMaxLambda Maximum value of constraint impulse to apply (N s)
  333. inline bool SolveVelocityConstraint(Body &ioBody1, Body &ioBody2, Vec3Arg inWorldSpaceAxis, float inMinLambda, float inMaxLambda)
  334. {
  335. EMotionType motion_type1 = ioBody1.GetMotionType();
  336. MotionProperties *motion_properties1 = ioBody1.GetMotionPropertiesUnchecked();
  337. EMotionType motion_type2 = ioBody2.GetMotionType();
  338. MotionProperties *motion_properties2 = ioBody2.GetMotionPropertiesUnchecked();
  339. // Dispatch to the correct templated form
  340. switch (motion_type1)
  341. {
  342. case EMotionType::Dynamic:
  343. switch (motion_type2)
  344. {
  345. case EMotionType::Dynamic:
  346. return TemplatedSolveVelocityConstraint<EMotionType::Dynamic, EMotionType::Dynamic>(motion_properties1, motion_properties2, inWorldSpaceAxis, inMinLambda, inMaxLambda);
  347. case EMotionType::Kinematic:
  348. return TemplatedSolveVelocityConstraint<EMotionType::Dynamic, EMotionType::Kinematic>(motion_properties1, motion_properties2, inWorldSpaceAxis, inMinLambda, inMaxLambda);
  349. case EMotionType::Static:
  350. return TemplatedSolveVelocityConstraint<EMotionType::Dynamic, EMotionType::Static>(motion_properties1, motion_properties2, inWorldSpaceAxis, inMinLambda, inMaxLambda);
  351. default:
  352. JPH_ASSERT(false);
  353. break;
  354. }
  355. break;
  356. case EMotionType::Kinematic:
  357. JPH_ASSERT(motion_type2 == EMotionType::Dynamic);
  358. return TemplatedSolveVelocityConstraint<EMotionType::Kinematic, EMotionType::Dynamic>(motion_properties1, motion_properties2, inWorldSpaceAxis, inMinLambda, inMaxLambda);
  359. case EMotionType::Static:
  360. JPH_ASSERT(motion_type2 == EMotionType::Dynamic);
  361. return TemplatedSolveVelocityConstraint<EMotionType::Static, EMotionType::Dynamic>(motion_properties1, motion_properties2, inWorldSpaceAxis, inMinLambda, inMaxLambda);
  362. default:
  363. JPH_ASSERT(false);
  364. break;
  365. }
  366. return false;
  367. }
  368. /// Iteratively update the position constraint. Makes sure C(...) = 0.
  369. /// @param ioBody1 The first body that this constraint is attached to
  370. /// @param ioBody2 The second body that this constraint is attached to
  371. /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized)
  372. /// @param inC Value of the constraint equation (C)
  373. /// @param inBaumgarte Baumgarte constant (fraction of the error to correct)
  374. inline bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, Vec3Arg inWorldSpaceAxis, float inC, float inBaumgarte) const
  375. {
  376. // Only apply position constraint when the constraint is hard, otherwise the velocity bias will fix the constraint
  377. if (inC != 0.0f && !mSpringPart.IsActive())
  378. {
  379. // Calculate lagrange multiplier (lambda) for Baumgarte stabilization:
  380. //
  381. // lambda = -K^-1 * beta / dt * C
  382. //
  383. // We should divide by inDeltaTime, but we should multiply by inDeltaTime in the Euler step below so they're cancelled out
  384. float lambda = -mEffectiveMass * inBaumgarte * inC;
  385. // Directly integrate velocity change for one time step
  386. //
  387. // Euler velocity integration:
  388. // dv = M^-1 P
  389. //
  390. // Impulse:
  391. // P = J^T lambda
  392. //
  393. // Euler position integration:
  394. // x' = x + dv * dt
  395. //
  396. // Note we don't accumulate velocities for the stabilization. This is using the approach described in 'Modeling and
  397. // Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte
  398. // stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity
  399. // integrate + a position integrate and then discard the velocity change.
  400. if (ioBody1.IsDynamic())
  401. {
  402. ioBody1.SubPositionStep((lambda * mInverseMass1) * inWorldSpaceAxis);
  403. ioBody1.SubRotationStep(lambda * Vec3::sLoadFloat3Unsafe(mInvI1_R1PlusUxAxis));
  404. }
  405. if (ioBody2.IsDynamic())
  406. {
  407. ioBody2.AddPositionStep((lambda * mInverseMass2) * inWorldSpaceAxis);
  408. ioBody2.AddRotationStep(lambda * Vec3::sLoadFloat3Unsafe(mInvI2_R2xAxis));
  409. }
  410. return true;
  411. }
  412. return false;
  413. }
  414. /// Override total lagrange multiplier, can be used to set the initial value for warm starting
  415. inline void SetTotalLambda(float inLambda)
  416. {
  417. mTotalLambda = inLambda;
  418. }
  419. /// Return lagrange multiplier
  420. inline float GetTotalLambda() const
  421. {
  422. return mTotalLambda;
  423. }
  424. /// Save state of this constraint part
  425. void SaveState(StateRecorder &inStream) const
  426. {
  427. inStream.Write(mTotalLambda);
  428. }
  429. /// Restore state of this constraint part
  430. void RestoreState(StateRecorder &inStream)
  431. {
  432. inStream.Read(mTotalLambda);
  433. }
  434. private:
  435. Float3 mR1PlusUxAxis;
  436. Float3 mR2xAxis;
  437. Float3 mInvI1_R1PlusUxAxis;
  438. Float3 mInvI2_R2xAxis;
  439. float mInverseMass1;
  440. float mInverseMass2;
  441. float mEffectiveMass = 0.0f;
  442. SpringPart mSpringPart;
  443. float mTotalLambda = 0.0f;
  444. };
  445. JPH_NAMESPACE_END