AxisConstraintPart.h 22 KB

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