AxisConstraintPart.h 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676
  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, float inInvMass1, MotionProperties *ioMotionProperties2, float inInvMass2, 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 * inInvMass1) * inWorldSpaceAxis);
  57. ioMotionProperties1->SubAngularVelocityStep(inLambda * Vec3::sLoadFloat3Unsafe(mInvI1_R1PlusUxAxis));
  58. }
  59. if constexpr (Type2 == EMotionType::Dynamic)
  60. {
  61. ioMotionProperties2->AddLinearVelocityStep((inLambda * inInvMass2) * 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. r1_plus_u_x_axis = inR1PlusU.Cross(inWorldSpaceAxis);
  78. r1_plus_u_x_axis.StoreFloat3(&mR1PlusUxAxis);
  79. }
  80. else
  81. {
  82. #ifdef _DEBUG
  83. Vec3::sNaN().StoreFloat3(&mR1PlusUxAxis);
  84. #endif
  85. }
  86. Vec3 r2_x_axis;
  87. if constexpr (Type2 != EMotionType::Static)
  88. {
  89. r2_x_axis = inR2.Cross(inWorldSpaceAxis);
  90. r2_x_axis.StoreFloat3(&mR2xAxis);
  91. }
  92. else
  93. {
  94. #ifdef _DEBUG
  95. Vec3::sNaN().StoreFloat3(&mR2xAxis);
  96. #endif
  97. }
  98. // Calculate inverse effective mass: K = J M^-1 J^T
  99. float inv_effective_mass;
  100. if constexpr (Type1 == EMotionType::Dynamic)
  101. {
  102. Vec3 invi1_r1_plus_u_x_axis = inInvI1.Multiply3x3(r1_plus_u_x_axis);
  103. invi1_r1_plus_u_x_axis.StoreFloat3(&mInvI1_R1PlusUxAxis);
  104. inv_effective_mass = inInvMass1 + invi1_r1_plus_u_x_axis.Dot(r1_plus_u_x_axis);
  105. }
  106. else
  107. {
  108. (void)r1_plus_u_x_axis; // Fix compiler warning: Not using this (it's not calculated either)
  109. JPH_IF_DEBUG(Vec3::sNaN().StoreFloat3(&mInvI1_R1PlusUxAxis);)
  110. inv_effective_mass = 0.0f;
  111. }
  112. if constexpr (Type2 == EMotionType::Dynamic)
  113. {
  114. Vec3 invi2_r2_x_axis = inInvI2.Multiply3x3(r2_x_axis);
  115. invi2_r2_x_axis.StoreFloat3(&mInvI2_R2xAxis);
  116. inv_effective_mass += inInvMass2 + invi2_r2_x_axis.Dot(r2_x_axis);
  117. }
  118. else
  119. {
  120. (void)r2_x_axis; // Fix compiler warning: Not using this (it's not calculated either)
  121. JPH_IF_DEBUG(Vec3::sNaN().StoreFloat3(&mInvI2_R2xAxis);)
  122. }
  123. return inv_effective_mass;
  124. }
  125. /// Internal helper function to calculate the inverse effective mass
  126. JPH_INLINE float CalculateInverseEffectiveMass(const Body &inBody1, Vec3Arg inR1PlusU, const Body &inBody2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis)
  127. {
  128. // Dispatch to the correct templated form
  129. switch (inBody1.GetMotionType())
  130. {
  131. case EMotionType::Dynamic:
  132. {
  133. const MotionProperties *mp1 = inBody1.GetMotionPropertiesUnchecked();
  134. float inv_m1 = mp1->GetInverseMass();
  135. Mat44 inv_i1 = inBody1.GetInverseInertia();
  136. switch (inBody2.GetMotionType())
  137. {
  138. case EMotionType::Dynamic:
  139. return TemplatedCalculateInverseEffectiveMass<EMotionType::Dynamic, EMotionType::Dynamic>(inv_m1, inv_i1, inR1PlusU, inBody2.GetMotionPropertiesUnchecked()->GetInverseMass(), inBody2.GetInverseInertia(), inR2, inWorldSpaceAxis);
  140. case EMotionType::Kinematic:
  141. return TemplatedCalculateInverseEffectiveMass<EMotionType::Dynamic, EMotionType::Kinematic>(inv_m1, inv_i1, inR1PlusU, 0 /* Will not be used */, Mat44() /* Will not be used */, inR2, inWorldSpaceAxis);
  142. case EMotionType::Static:
  143. return TemplatedCalculateInverseEffectiveMass<EMotionType::Dynamic, EMotionType::Static>(inv_m1, inv_i1, inR1PlusU, 0 /* Will not be used */, Mat44() /* Will not be used */, inR2, inWorldSpaceAxis);
  144. default:
  145. break;
  146. }
  147. break;
  148. }
  149. case EMotionType::Kinematic:
  150. JPH_ASSERT(inBody2.IsDynamic());
  151. return TemplatedCalculateInverseEffectiveMass<EMotionType::Kinematic, EMotionType::Dynamic>(0 /* Will not be used */, Mat44() /* Will not be used */, inR1PlusU, inBody2.GetMotionPropertiesUnchecked()->GetInverseMass(), inBody2.GetInverseInertia(), inR2, inWorldSpaceAxis);
  152. case EMotionType::Static:
  153. JPH_ASSERT(inBody2.IsDynamic());
  154. return TemplatedCalculateInverseEffectiveMass<EMotionType::Static, EMotionType::Dynamic>(0 /* Will not be used */, Mat44() /* Will not be used */, inR1PlusU, inBody2.GetMotionPropertiesUnchecked()->GetInverseMass(), inBody2.GetInverseInertia(), inR2, inWorldSpaceAxis);
  155. default:
  156. break;
  157. }
  158. JPH_ASSERT(false);
  159. return 0.0f;
  160. }
  161. /// Internal helper function to calculate the inverse effective mass, version that supports mass scaling
  162. JPH_INLINE float CalculateInverseEffectiveMassWithMassOverride(const Body &inBody1, float inInvMass1, float inInvInertiaScale1, Vec3Arg inR1PlusU, const Body &inBody2, float inInvMass2, float inInvInertiaScale2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis)
  163. {
  164. // Dispatch to the correct templated form
  165. switch (inBody1.GetMotionType())
  166. {
  167. case EMotionType::Dynamic:
  168. {
  169. Mat44 inv_i1 = inInvInertiaScale1 * inBody1.GetInverseInertia();
  170. switch (inBody2.GetMotionType())
  171. {
  172. case EMotionType::Dynamic:
  173. return TemplatedCalculateInverseEffectiveMass<EMotionType::Dynamic, EMotionType::Dynamic>(inInvMass1, inv_i1, inR1PlusU, inInvMass2, inInvInertiaScale2 * inBody2.GetInverseInertia(), inR2, inWorldSpaceAxis);
  174. case EMotionType::Kinematic:
  175. return TemplatedCalculateInverseEffectiveMass<EMotionType::Dynamic, EMotionType::Kinematic>(inInvMass1, inv_i1, inR1PlusU, 0 /* Will not be used */, Mat44() /* Will not be used */, inR2, inWorldSpaceAxis);
  176. case EMotionType::Static:
  177. return TemplatedCalculateInverseEffectiveMass<EMotionType::Dynamic, EMotionType::Static>(inInvMass1, inv_i1, inR1PlusU, 0 /* Will not be used */, Mat44() /* Will not be used */, inR2, inWorldSpaceAxis);
  178. default:
  179. break;
  180. }
  181. break;
  182. }
  183. case EMotionType::Kinematic:
  184. JPH_ASSERT(inBody2.IsDynamic());
  185. return TemplatedCalculateInverseEffectiveMass<EMotionType::Kinematic, EMotionType::Dynamic>(0 /* Will not be used */, Mat44() /* Will not be used */, inR1PlusU, inInvMass2, inInvInertiaScale2 * inBody2.GetInverseInertia(), inR2, inWorldSpaceAxis);
  186. case EMotionType::Static:
  187. JPH_ASSERT(inBody2.IsDynamic());
  188. return TemplatedCalculateInverseEffectiveMass<EMotionType::Static, EMotionType::Dynamic>(0 /* Will not be used */, Mat44() /* Will not be used */, inR1PlusU, inInvMass2, inInvInertiaScale2 * inBody2.GetInverseInertia(), inR2, inWorldSpaceAxis);
  189. default:
  190. break;
  191. }
  192. JPH_ASSERT(false);
  193. return 0.0f;
  194. }
  195. public:
  196. /// Templated form of CalculateConstraintProperties with the motion types baked in
  197. template <EMotionType Type1, EMotionType Type2>
  198. JPH_INLINE void TemplatedCalculateConstraintProperties(float inInvMass1, Mat44Arg inInvI1, Vec3Arg inR1PlusU, float inInvMass2, Mat44Arg inInvI2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias = 0.0f)
  199. {
  200. mEffectiveMass = 1.0f / TemplatedCalculateInverseEffectiveMass<Type1, Type2>(inInvMass1, inInvI1, inR1PlusU, inInvMass2, inInvI2, inR2, inWorldSpaceAxis);
  201. mSpringPart.CalculateSpringPropertiesWithBias(inBias);
  202. 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);
  203. }
  204. /// Calculate properties used during the functions below
  205. /// @param inBody1 The first body that this constraint is attached to
  206. /// @param inBody2 The second body that this constraint is attached to
  207. /// @param inR1PlusU See equations above (r1 + u)
  208. /// @param inR2 See equations above (r2)
  209. /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized, pointing from body 1 to 2)
  210. /// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b
  211. inline void CalculateConstraintProperties(const Body &inBody1, Vec3Arg inR1PlusU, const Body &inBody2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias = 0.0f)
  212. {
  213. float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inR1PlusU, inBody2, inR2, inWorldSpaceAxis);
  214. if (inv_effective_mass == 0.0f)
  215. Deactivate();
  216. else
  217. {
  218. mEffectiveMass = 1.0f / inv_effective_mass;
  219. mSpringPart.CalculateSpringPropertiesWithBias(inBias);
  220. }
  221. }
  222. /// Calculate properties used during the functions below, version that supports mass scaling
  223. /// @param inBody1 The first body that this constraint is attached to
  224. /// @param inBody2 The second body that this constraint is attached to
  225. /// @param inInvMass1 The inverse mass of body 1 (only used when body 1 is dynamic)
  226. /// @param inInvMass2 The inverse mass of body 2 (only used when body 2 is dynamic)
  227. /// @param inInvInertiaScale1 Scale factor for the inverse inertia of body 1
  228. /// @param inInvInertiaScale2 Scale factor for the inverse inertia of body 2
  229. /// @param inR1PlusU See equations above (r1 + u)
  230. /// @param inR2 See equations above (r2)
  231. /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized, pointing from body 1 to 2)
  232. /// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b
  233. inline void CalculateConstraintPropertiesWithMassOverride(const Body &inBody1, float inInvMass1, float inInvInertiaScale1, Vec3Arg inR1PlusU, const Body &inBody2, float inInvMass2, float inInvInertiaScale2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias = 0.0f)
  234. {
  235. float inv_effective_mass = CalculateInverseEffectiveMassWithMassOverride(inBody1, inInvMass1, inInvInertiaScale1, inR1PlusU, inBody2, inInvMass2, inInvInertiaScale2, inR2, inWorldSpaceAxis);
  236. if (inv_effective_mass == 0.0f)
  237. Deactivate();
  238. else
  239. {
  240. mEffectiveMass = 1.0f / inv_effective_mass;
  241. mSpringPart.CalculateSpringPropertiesWithBias(inBias);
  242. }
  243. }
  244. /// Calculate properties used during the functions below
  245. /// @param inDeltaTime Time step
  246. /// @param inBody1 The first body that this constraint is attached to
  247. /// @param inBody2 The second body that this constraint is attached to
  248. /// @param inR1PlusU See equations above (r1 + u)
  249. /// @param inR2 See equations above (r2)
  250. /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized, pointing from body 1 to 2)
  251. /// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b
  252. /// @param inC Value of the constraint equation (C).
  253. /// @param inFrequency Oscillation frequency (Hz).
  254. /// @param inDamping Damping factor (0 = no damping, 1 = critical damping).
  255. 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)
  256. {
  257. float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inR1PlusU, inBody2, inR2, inWorldSpaceAxis);
  258. if (inv_effective_mass == 0.0f)
  259. Deactivate();
  260. else
  261. mSpringPart.CalculateSpringPropertiesWithFrequencyAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inFrequency, inDamping, mEffectiveMass);
  262. }
  263. /// Calculate properties used during the functions below
  264. /// @param inDeltaTime Time step
  265. /// @param inBody1 The first body that this constraint is attached to
  266. /// @param inBody2 The second body that this constraint is attached to
  267. /// @param inR1PlusU See equations above (r1 + u)
  268. /// @param inR2 See equations above (r2)
  269. /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized, pointing from body 1 to 2)
  270. /// @param inBias Bias term (b) for the constraint impulse: lambda = J v + b
  271. /// @param inC Value of the constraint equation (C).
  272. /// @param inStiffness Spring stiffness k.
  273. /// @param inDamping Spring damping coefficient c.
  274. 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)
  275. {
  276. float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inR1PlusU, inBody2, inR2, inWorldSpaceAxis);
  277. if (inv_effective_mass == 0.0f)
  278. Deactivate();
  279. else
  280. mSpringPart.CalculateSpringPropertiesWithStiffnessAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inStiffness, inDamping, mEffectiveMass);
  281. }
  282. /// Selects one of the above functions based on the spring settings
  283. inline void CalculateConstraintPropertiesWithSettings(float inDeltaTime, const Body &inBody1, Vec3Arg inR1PlusU, const Body &inBody2, Vec3Arg inR2, Vec3Arg inWorldSpaceAxis, float inBias, float inC, const SpringSettings &inSpringSettings)
  284. {
  285. float inv_effective_mass = CalculateInverseEffectiveMass(inBody1, inR1PlusU, inBody2, inR2, inWorldSpaceAxis);
  286. if (inv_effective_mass == 0.0f)
  287. Deactivate();
  288. else if (inSpringSettings.mMode == ESpringMode::FrequencyAndDamping)
  289. mSpringPart.CalculateSpringPropertiesWithFrequencyAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inSpringSettings.mFrequency, inSpringSettings.mDamping, mEffectiveMass);
  290. else
  291. mSpringPart.CalculateSpringPropertiesWithStiffnessAndDamping(inDeltaTime, inv_effective_mass, inBias, inC, inSpringSettings.mStiffness, inSpringSettings.mDamping, mEffectiveMass);
  292. }
  293. /// Deactivate this constraint
  294. inline void Deactivate()
  295. {
  296. mEffectiveMass = 0.0f;
  297. mTotalLambda = 0.0f;
  298. }
  299. /// Check if constraint is active
  300. inline bool IsActive() const
  301. {
  302. return mEffectiveMass != 0.0f;
  303. }
  304. /// Templated form of WarmStart with the motion types baked in
  305. template <EMotionType Type1, EMotionType Type2>
  306. inline void TemplatedWarmStart(MotionProperties *ioMotionProperties1, float inInvMass1, MotionProperties *ioMotionProperties2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inWarmStartImpulseRatio)
  307. {
  308. mTotalLambda *= inWarmStartImpulseRatio;
  309. ApplyVelocityStep<Type1, Type2>(ioMotionProperties1, inInvMass1, ioMotionProperties2, inInvMass2, inWorldSpaceAxis, mTotalLambda);
  310. }
  311. /// Must be called from the WarmStartVelocityConstraint call to apply the previous frame's impulses
  312. /// @param ioBody1 The first body that this constraint is attached to
  313. /// @param ioBody2 The second body that this constraint is attached to
  314. /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized)
  315. /// @param inWarmStartImpulseRatio Ratio of new step to old time step (dt_new / dt_old) for scaling the lagrange multiplier of the previous frame
  316. inline void WarmStart(Body &ioBody1, Body &ioBody2, Vec3Arg inWorldSpaceAxis, float inWarmStartImpulseRatio)
  317. {
  318. EMotionType motion_type1 = ioBody1.GetMotionType();
  319. MotionProperties *motion_properties1 = ioBody1.GetMotionPropertiesUnchecked();
  320. EMotionType motion_type2 = ioBody2.GetMotionType();
  321. MotionProperties *motion_properties2 = ioBody2.GetMotionPropertiesUnchecked();
  322. // Dispatch to the correct templated form
  323. // Note: Warm starting doesn't differentiate between kinematic/static bodies so we handle both as static bodies
  324. if (motion_type1 == EMotionType::Dynamic)
  325. {
  326. if (motion_type2 == EMotionType::Dynamic)
  327. TemplatedWarmStart<EMotionType::Dynamic, EMotionType::Dynamic>(motion_properties1, motion_properties1->GetInverseMass(), motion_properties2, motion_properties2->GetInverseMass(), inWorldSpaceAxis, inWarmStartImpulseRatio);
  328. else
  329. TemplatedWarmStart<EMotionType::Dynamic, EMotionType::Static>(motion_properties1, motion_properties1->GetInverseMass(), motion_properties2, 0.0f /* Unused */, inWorldSpaceAxis, inWarmStartImpulseRatio);
  330. }
  331. else
  332. {
  333. JPH_ASSERT(motion_type2 == EMotionType::Dynamic);
  334. TemplatedWarmStart<EMotionType::Static, EMotionType::Dynamic>(motion_properties1, 0.0f /* Unused */, motion_properties2, motion_properties2->GetInverseMass(), inWorldSpaceAxis, inWarmStartImpulseRatio);
  335. }
  336. }
  337. /// Templated form of SolveVelocityConstraint with the motion types baked in, part 1: get the total lambda
  338. template <EMotionType Type1, EMotionType Type2>
  339. JPH_INLINE float TemplatedSolveVelocityConstraintGetTotalLambda(const MotionProperties *ioMotionProperties1, const MotionProperties *ioMotionProperties2, Vec3Arg inWorldSpaceAxis) const
  340. {
  341. // Calculate jacobian multiplied by linear velocity
  342. float jv;
  343. if constexpr (Type1 != EMotionType::Static && Type2 != EMotionType::Static)
  344. jv = inWorldSpaceAxis.Dot(ioMotionProperties1->GetLinearVelocity() - ioMotionProperties2->GetLinearVelocity());
  345. else if constexpr (Type1 != EMotionType::Static)
  346. jv = inWorldSpaceAxis.Dot(ioMotionProperties1->GetLinearVelocity());
  347. else if constexpr (Type2 != EMotionType::Static)
  348. jv = inWorldSpaceAxis.Dot(-ioMotionProperties2->GetLinearVelocity());
  349. else
  350. JPH_ASSERT(false); // Static vs static is nonsensical!
  351. // Calculate jacobian multiplied by angular velocity
  352. if constexpr (Type1 != EMotionType::Static)
  353. jv += Vec3::sLoadFloat3Unsafe(mR1PlusUxAxis).Dot(ioMotionProperties1->GetAngularVelocity());
  354. if constexpr (Type2 != EMotionType::Static)
  355. jv -= Vec3::sLoadFloat3Unsafe(mR2xAxis).Dot(ioMotionProperties2->GetAngularVelocity());
  356. // Lagrange multiplier is:
  357. //
  358. // lambda = -K^-1 (J v + b)
  359. float lambda = mEffectiveMass * (jv - mSpringPart.GetBias(mTotalLambda));
  360. // Return the total accumulated lambda
  361. return mTotalLambda + lambda;
  362. }
  363. /// Templated form of SolveVelocityConstraint with the motion types baked in, part 2: apply new lambda
  364. template <EMotionType Type1, EMotionType Type2>
  365. JPH_INLINE bool TemplatedSolveVelocityConstraintApplyLambda(MotionProperties *ioMotionProperties1, float inInvMass1, MotionProperties *ioMotionProperties2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inTotalLambda)
  366. {
  367. float delta_lambda = inTotalLambda - mTotalLambda; // Calculate change in lambda
  368. mTotalLambda = inTotalLambda; // Store accumulated impulse
  369. return ApplyVelocityStep<Type1, Type2>(ioMotionProperties1, inInvMass1, ioMotionProperties2, inInvMass2, inWorldSpaceAxis, delta_lambda);
  370. }
  371. /// Templated form of SolveVelocityConstraint with the motion types baked in
  372. template <EMotionType Type1, EMotionType Type2>
  373. inline bool TemplatedSolveVelocityConstraint(MotionProperties *ioMotionProperties1, float inInvMass1, MotionProperties *ioMotionProperties2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inMinLambda, float inMaxLambda)
  374. {
  375. float total_lambda = TemplatedSolveVelocityConstraintGetTotalLambda<Type1, Type2>(ioMotionProperties1, ioMotionProperties2, inWorldSpaceAxis);
  376. // Clamp impulse to specified range
  377. total_lambda = Clamp(total_lambda, inMinLambda, inMaxLambda);
  378. return TemplatedSolveVelocityConstraintApplyLambda<Type1, Type2>(ioMotionProperties1, inInvMass1, ioMotionProperties2, inInvMass2, inWorldSpaceAxis, total_lambda);
  379. }
  380. /// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation.
  381. /// @param ioBody1 The first body that this constraint is attached to
  382. /// @param ioBody2 The second body that this constraint is attached to
  383. /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized)
  384. /// @param inMinLambda Minimum value of constraint impulse to apply (N s)
  385. /// @param inMaxLambda Maximum value of constraint impulse to apply (N s)
  386. inline bool SolveVelocityConstraint(Body &ioBody1, Body &ioBody2, Vec3Arg inWorldSpaceAxis, float inMinLambda, float inMaxLambda)
  387. {
  388. EMotionType motion_type1 = ioBody1.GetMotionType();
  389. MotionProperties *motion_properties1 = ioBody1.GetMotionPropertiesUnchecked();
  390. EMotionType motion_type2 = ioBody2.GetMotionType();
  391. MotionProperties *motion_properties2 = ioBody2.GetMotionPropertiesUnchecked();
  392. // Dispatch to the correct templated form
  393. switch (motion_type1)
  394. {
  395. case EMotionType::Dynamic:
  396. switch (motion_type2)
  397. {
  398. case EMotionType::Dynamic:
  399. return TemplatedSolveVelocityConstraint<EMotionType::Dynamic, EMotionType::Dynamic>(motion_properties1, motion_properties1->GetInverseMass(), motion_properties2, motion_properties2->GetInverseMass(), inWorldSpaceAxis, inMinLambda, inMaxLambda);
  400. case EMotionType::Kinematic:
  401. return TemplatedSolveVelocityConstraint<EMotionType::Dynamic, EMotionType::Kinematic>(motion_properties1, motion_properties1->GetInverseMass(), motion_properties2, 0.0f /* Unused */, inWorldSpaceAxis, inMinLambda, inMaxLambda);
  402. case EMotionType::Static:
  403. return TemplatedSolveVelocityConstraint<EMotionType::Dynamic, EMotionType::Static>(motion_properties1, motion_properties1->GetInverseMass(), motion_properties2, 0.0f /* Unused */, inWorldSpaceAxis, inMinLambda, inMaxLambda);
  404. default:
  405. JPH_ASSERT(false);
  406. break;
  407. }
  408. break;
  409. case EMotionType::Kinematic:
  410. JPH_ASSERT(motion_type2 == EMotionType::Dynamic);
  411. return TemplatedSolveVelocityConstraint<EMotionType::Kinematic, EMotionType::Dynamic>(motion_properties1, 0.0f /* Unused */, motion_properties2, motion_properties2->GetInverseMass(), inWorldSpaceAxis, inMinLambda, inMaxLambda);
  412. case EMotionType::Static:
  413. JPH_ASSERT(motion_type2 == EMotionType::Dynamic);
  414. return TemplatedSolveVelocityConstraint<EMotionType::Static, EMotionType::Dynamic>(motion_properties1, 0.0f /* Unused */, motion_properties2, motion_properties2->GetInverseMass(), inWorldSpaceAxis, inMinLambda, inMaxLambda);
  415. default:
  416. JPH_ASSERT(false);
  417. break;
  418. }
  419. return false;
  420. }
  421. /// Iteratively update the velocity constraint. Makes sure d/dt C(...) = 0, where C is the constraint equation.
  422. /// @param ioBody1 The first body that this constraint is attached to
  423. /// @param ioBody2 The second body that this constraint is attached to
  424. /// @param inInvMass1 The inverse mass of body 1 (only used when body 1 is dynamic)
  425. /// @param inInvMass2 The inverse mass of body 2 (only used when body 2 is dynamic)
  426. /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized)
  427. /// @param inMinLambda Minimum value of constraint impulse to apply (N s)
  428. /// @param inMaxLambda Maximum value of constraint impulse to apply (N s)
  429. inline bool SolveVelocityConstraintWithMassOverride(Body &ioBody1, float inInvMass1, Body &ioBody2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inMinLambda, float inMaxLambda)
  430. {
  431. EMotionType motion_type1 = ioBody1.GetMotionType();
  432. MotionProperties *motion_properties1 = ioBody1.GetMotionPropertiesUnchecked();
  433. EMotionType motion_type2 = ioBody2.GetMotionType();
  434. MotionProperties *motion_properties2 = ioBody2.GetMotionPropertiesUnchecked();
  435. // Dispatch to the correct templated form
  436. switch (motion_type1)
  437. {
  438. case EMotionType::Dynamic:
  439. switch (motion_type2)
  440. {
  441. case EMotionType::Dynamic:
  442. return TemplatedSolveVelocityConstraint<EMotionType::Dynamic, EMotionType::Dynamic>(motion_properties1, inInvMass1, motion_properties2, inInvMass2, inWorldSpaceAxis, inMinLambda, inMaxLambda);
  443. case EMotionType::Kinematic:
  444. return TemplatedSolveVelocityConstraint<EMotionType::Dynamic, EMotionType::Kinematic>(motion_properties1, inInvMass1, motion_properties2, 0.0f /* Unused */, inWorldSpaceAxis, inMinLambda, inMaxLambda);
  445. case EMotionType::Static:
  446. return TemplatedSolveVelocityConstraint<EMotionType::Dynamic, EMotionType::Static>(motion_properties1, inInvMass1, motion_properties2, 0.0f /* Unused */, inWorldSpaceAxis, inMinLambda, inMaxLambda);
  447. default:
  448. JPH_ASSERT(false);
  449. break;
  450. }
  451. break;
  452. case EMotionType::Kinematic:
  453. JPH_ASSERT(motion_type2 == EMotionType::Dynamic);
  454. return TemplatedSolveVelocityConstraint<EMotionType::Kinematic, EMotionType::Dynamic>(motion_properties1, 0.0f /* Unused */, motion_properties2, inInvMass2, inWorldSpaceAxis, inMinLambda, inMaxLambda);
  455. case EMotionType::Static:
  456. JPH_ASSERT(motion_type2 == EMotionType::Dynamic);
  457. return TemplatedSolveVelocityConstraint<EMotionType::Static, EMotionType::Dynamic>(motion_properties1, 0.0f /* Unused */, motion_properties2, inInvMass2, inWorldSpaceAxis, inMinLambda, inMaxLambda);
  458. default:
  459. JPH_ASSERT(false);
  460. break;
  461. }
  462. return false;
  463. }
  464. /// Iteratively update the position constraint. Makes sure C(...) = 0.
  465. /// @param ioBody1 The first body that this constraint is attached to
  466. /// @param ioBody2 The second body that this constraint is attached to
  467. /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized)
  468. /// @param inC Value of the constraint equation (C)
  469. /// @param inBaumgarte Baumgarte constant (fraction of the error to correct)
  470. inline bool SolvePositionConstraint(Body &ioBody1, Body &ioBody2, Vec3Arg inWorldSpaceAxis, float inC, float inBaumgarte) const
  471. {
  472. // Only apply position constraint when the constraint is hard, otherwise the velocity bias will fix the constraint
  473. if (inC != 0.0f && !mSpringPart.IsActive())
  474. {
  475. // Calculate lagrange multiplier (lambda) for Baumgarte stabilization:
  476. //
  477. // lambda = -K^-1 * beta / dt * C
  478. //
  479. // We should divide by inDeltaTime, but we should multiply by inDeltaTime in the Euler step below so they're cancelled out
  480. float lambda = -mEffectiveMass * inBaumgarte * inC;
  481. // Directly integrate velocity change for one time step
  482. //
  483. // Euler velocity integration:
  484. // dv = M^-1 P
  485. //
  486. // Impulse:
  487. // P = J^T lambda
  488. //
  489. // Euler position integration:
  490. // x' = x + dv * dt
  491. //
  492. // Note we don't accumulate velocities for the stabilization. This is using the approach described in 'Modeling and
  493. // Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte
  494. // stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity
  495. // integrate + a position integrate and then discard the velocity change.
  496. if (ioBody1.IsDynamic())
  497. {
  498. ioBody1.SubPositionStep((lambda * ioBody1.GetMotionProperties()->GetInverseMass()) * inWorldSpaceAxis);
  499. ioBody1.SubRotationStep(lambda * Vec3::sLoadFloat3Unsafe(mInvI1_R1PlusUxAxis));
  500. }
  501. if (ioBody2.IsDynamic())
  502. {
  503. ioBody2.AddPositionStep((lambda * ioBody2.GetMotionProperties()->GetInverseMass()) * inWorldSpaceAxis);
  504. ioBody2.AddRotationStep(lambda * Vec3::sLoadFloat3Unsafe(mInvI2_R2xAxis));
  505. }
  506. return true;
  507. }
  508. return false;
  509. }
  510. /// Iteratively update the position constraint. Makes sure C(...) = 0.
  511. /// @param ioBody1 The first body that this constraint is attached to
  512. /// @param ioBody2 The second body that this constraint is attached to
  513. /// @param inInvMass1 The inverse mass of body 1 (only used when body 1 is dynamic)
  514. /// @param inInvMass2 The inverse mass of body 2 (only used when body 2 is dynamic)
  515. /// @param inWorldSpaceAxis Axis along which the constraint acts (normalized)
  516. /// @param inC Value of the constraint equation (C)
  517. /// @param inBaumgarte Baumgarte constant (fraction of the error to correct)
  518. inline bool SolvePositionConstraintWithMassOverride(Body &ioBody1, float inInvMass1, Body &ioBody2, float inInvMass2, Vec3Arg inWorldSpaceAxis, float inC, float inBaumgarte) const
  519. {
  520. // Only apply position constraint when the constraint is hard, otherwise the velocity bias will fix the constraint
  521. if (inC != 0.0f && !mSpringPart.IsActive())
  522. {
  523. // Calculate lagrange multiplier (lambda) for Baumgarte stabilization:
  524. //
  525. // lambda = -K^-1 * beta / dt * C
  526. //
  527. // We should divide by inDeltaTime, but we should multiply by inDeltaTime in the Euler step below so they're cancelled out
  528. float lambda = -mEffectiveMass * inBaumgarte * inC;
  529. // Directly integrate velocity change for one time step
  530. //
  531. // Euler velocity integration:
  532. // dv = M^-1 P
  533. //
  534. // Impulse:
  535. // P = J^T lambda
  536. //
  537. // Euler position integration:
  538. // x' = x + dv * dt
  539. //
  540. // Note we don't accumulate velocities for the stabilization. This is using the approach described in 'Modeling and
  541. // Solving Constraints' by Erin Catto presented at GDC 2007. On slide 78 it is suggested to split up the Baumgarte
  542. // stabilization for positional drift so that it does not actually add to the momentum. We combine an Euler velocity
  543. // integrate + a position integrate and then discard the velocity change.
  544. if (ioBody1.IsDynamic())
  545. {
  546. ioBody1.SubPositionStep((lambda * inInvMass1) * inWorldSpaceAxis);
  547. ioBody1.SubRotationStep(lambda * Vec3::sLoadFloat3Unsafe(mInvI1_R1PlusUxAxis));
  548. }
  549. if (ioBody2.IsDynamic())
  550. {
  551. ioBody2.AddPositionStep((lambda * inInvMass2) * inWorldSpaceAxis);
  552. ioBody2.AddRotationStep(lambda * Vec3::sLoadFloat3Unsafe(mInvI2_R2xAxis));
  553. }
  554. return true;
  555. }
  556. return false;
  557. }
  558. /// Override total lagrange multiplier, can be used to set the initial value for warm starting
  559. inline void SetTotalLambda(float inLambda)
  560. {
  561. mTotalLambda = inLambda;
  562. }
  563. /// Return lagrange multiplier
  564. inline float GetTotalLambda() const
  565. {
  566. return mTotalLambda;
  567. }
  568. /// Save state of this constraint part
  569. void SaveState(StateRecorder &inStream) const
  570. {
  571. inStream.Write(mTotalLambda);
  572. }
  573. /// Restore state of this constraint part
  574. void RestoreState(StateRecorder &inStream)
  575. {
  576. inStream.Read(mTotalLambda);
  577. }
  578. private:
  579. Float3 mR1PlusUxAxis;
  580. Float3 mR2xAxis;
  581. Float3 mInvI1_R1PlusUxAxis;
  582. Float3 mInvI2_R2xAxis;
  583. float mEffectiveMass = 0.0f;
  584. SpringPart mSpringPart;
  585. float mTotalLambda = 0.0f;
  586. };
  587. JPH_NAMESPACE_END