SixDOFConstraint.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #include <Jolt.h>
  4. #include <Physics/Constraints/SixDOFConstraint.h>
  5. #include <Physics/Body/Body.h>
  6. #include <Geometry/Ellipse.h>
  7. #include <ObjectStream/TypeDeclarations.h>
  8. #include <Core/StreamIn.h>
  9. #include <Core/StreamOut.h>
  10. #ifdef JPH_DEBUG_RENDERER
  11. #include <Renderer/DebugRenderer.h>
  12. #endif // JPH_DEBUG_RENDERER
  13. namespace JPH {
  14. JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(SixDOFConstraintSettings)
  15. {
  16. JPH_ADD_BASE_CLASS(SixDOFConstraintSettings, TwoBodyConstraintSettings)
  17. JPH_ADD_ENUM_ATTRIBUTE(SixDOFConstraintSettings, mSpace)
  18. JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mPosition1)
  19. JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mAxisX1)
  20. JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mAxisY1)
  21. JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mPosition2)
  22. JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mAxisX2)
  23. JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mAxisY2)
  24. JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mMaxFriction)
  25. JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mLimitMin)
  26. JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mLimitMax)
  27. JPH_ADD_ATTRIBUTE(SixDOFConstraintSettings, mMotorSettings)
  28. }
  29. void SixDOFConstraintSettings::SaveBinaryState(StreamOut &inStream) const
  30. {
  31. ConstraintSettings::SaveBinaryState(inStream);
  32. inStream.Write(mSpace);
  33. inStream.Write(mPosition1);
  34. inStream.Write(mAxisX1);
  35. inStream.Write(mAxisY1);
  36. inStream.Write(mPosition2);
  37. inStream.Write(mAxisX2);
  38. inStream.Write(mAxisY2);
  39. inStream.Write(mMaxFriction);
  40. inStream.Write(mLimitMin);
  41. inStream.Write(mLimitMax);
  42. for (const MotorSettings &m : mMotorSettings)
  43. m.SaveBinaryState(inStream);
  44. }
  45. void SixDOFConstraintSettings::RestoreBinaryState(StreamIn &inStream)
  46. {
  47. ConstraintSettings::RestoreBinaryState(inStream);
  48. inStream.Read(mSpace);
  49. inStream.Read(mPosition1);
  50. inStream.Read(mAxisX1);
  51. inStream.Read(mAxisY1);
  52. inStream.Read(mPosition2);
  53. inStream.Read(mAxisX2);
  54. inStream.Read(mAxisY2);
  55. inStream.Read(mMaxFriction);
  56. inStream.Read(mLimitMin);
  57. inStream.Read(mLimitMax);
  58. for (MotorSettings &m : mMotorSettings)
  59. m.RestoreBinaryState(inStream);
  60. }
  61. TwoBodyConstraint *SixDOFConstraintSettings::Create(Body &inBody1, Body &inBody2) const
  62. {
  63. return new SixDOFConstraint(inBody1, inBody2, *this);
  64. }
  65. void SixDOFConstraint::UpdateRotationLimits()
  66. {
  67. // Make values sensible
  68. for (int i = 3; i < 6; ++i)
  69. if (IsAxisFixed((EAxis)i))
  70. mLimitMin[i] = mLimitMax[i] = 0.0f;
  71. else
  72. {
  73. mLimitMin[i] = max(-JPH_PI, mLimitMin[i]);
  74. mLimitMax[i] = min(JPH_PI, mLimitMax[i]);
  75. }
  76. // The swing twist constraint part requires symmetrical rotations around Y and Z
  77. JPH_ASSERT(mLimitMin[EAxis::RotationY] == -mLimitMax[EAxis::RotationY]);
  78. JPH_ASSERT(mLimitMin[EAxis::RotationZ] == -mLimitMax[EAxis::RotationZ]);
  79. // Pass limits on to constraint part
  80. mSwingTwistConstraintPart.SetLimits(mLimitMin[EAxis::RotationX], mLimitMax[EAxis::RotationX], mLimitMax[EAxis::RotationY], mLimitMax[EAxis::RotationZ]);
  81. }
  82. SixDOFConstraint::SixDOFConstraint(Body &inBody1, Body &inBody2, const SixDOFConstraintSettings &inSettings) :
  83. TwoBodyConstraint(inBody1, inBody2, inSettings),
  84. mLocalSpacePosition1(inSettings.mPosition1),
  85. mLocalSpacePosition2(inSettings.mPosition2)
  86. {
  87. // Assert that input adheres to the limitations of this class
  88. JPH_ASSERT(inSettings.mLimitMin[EAxis::RotationY] == -inSettings.mLimitMax[EAxis::RotationY]);
  89. JPH_ASSERT(inSettings.mLimitMin[EAxis::RotationZ] == -inSettings.mLimitMax[EAxis::RotationZ]);
  90. // Calculate rotation needed to go from constraint space to body1 local space
  91. Vec3 axis_z1 = inSettings.mAxisX1.Cross(inSettings.mAxisY1);
  92. Mat44 c_to_b1(Vec4(inSettings.mAxisX1, 0), Vec4(inSettings.mAxisY1, 0), Vec4(axis_z1, 0), Vec4(0, 0, 0, 1));
  93. mConstraintToBody1 = c_to_b1.GetQuaternion();
  94. // Calculate rotation needed to go from constraint space to body2 local space
  95. Vec3 axis_z2 = inSettings.mAxisX2.Cross(inSettings.mAxisY2);
  96. Mat44 c_to_b2(Vec4(inSettings.mAxisX2, 0), Vec4(inSettings.mAxisY2, 0), Vec4(axis_z2, 0), Vec4(0, 0, 0, 1));
  97. mConstraintToBody2 = c_to_b2.GetQuaternion();
  98. if (inSettings.mSpace == EConstraintSpace::WorldSpace)
  99. {
  100. // If all properties were specified in world space, take them to local space now
  101. mLocalSpacePosition1 = inBody1.GetInverseCenterOfMassTransform() * mLocalSpacePosition1;
  102. mConstraintToBody1 = inBody1.GetRotation().Conjugated() * mConstraintToBody1;
  103. mLocalSpacePosition2 = inBody2.GetInverseCenterOfMassTransform() * mLocalSpacePosition2;
  104. mConstraintToBody2 = inBody2.GetRotation().Conjugated() * mConstraintToBody2;
  105. }
  106. // Cache which axis are fixed and which ones are free
  107. mFreeAxis = 0;
  108. mFixedAxis = 0;
  109. for (int a = 0; a < EAxis::Num; ++a)
  110. {
  111. if (inSettings.IsFixedAxis((EAxis)a))
  112. mFixedAxis |= 1 << a;
  113. if (inSettings.IsFreeAxis((EAxis)a))
  114. mFreeAxis |= 1 << a;
  115. }
  116. // Copy translation and rotation limits
  117. memcpy(mLimitMin, inSettings.mLimitMin, sizeof(mLimitMin));
  118. memcpy(mLimitMax, inSettings.mLimitMax, sizeof(mLimitMax));
  119. UpdateRotationLimits();
  120. // Store friction settings
  121. memcpy(mMaxFriction, inSettings.mMaxFriction, sizeof(mMaxFriction));
  122. // Store motor settings
  123. for (int i = 0; i < EAxis::Num; ++i)
  124. mMotorSettings[i] = inSettings.mMotorSettings[i];
  125. }
  126. void SixDOFConstraint::SetTranslationLimits(Vec3Arg inLimitMin, Vec3Arg inLimitMax)
  127. {
  128. mLimitMin[EAxis::TranslationX] = inLimitMin.GetX();
  129. mLimitMin[EAxis::TranslationY] = inLimitMin.GetY();
  130. mLimitMin[EAxis::TranslationZ] = inLimitMin.GetZ();
  131. mLimitMax[EAxis::TranslationX] = inLimitMax.GetX();
  132. mLimitMax[EAxis::TranslationY] = inLimitMax.GetY();
  133. mLimitMax[EAxis::TranslationZ] = inLimitMax.GetZ();
  134. }
  135. void SixDOFConstraint::SetRotationLimits(Vec3Arg inLimitMin, Vec3Arg inLimitMax)
  136. {
  137. mLimitMin[EAxis::RotationX] = inLimitMin.GetX();
  138. mLimitMin[EAxis::RotationY] = inLimitMin.GetY();
  139. mLimitMin[EAxis::RotationZ] = inLimitMin.GetZ();
  140. mLimitMax[EAxis::RotationX] = inLimitMax.GetX();
  141. mLimitMax[EAxis::RotationY] = inLimitMax.GetY();
  142. mLimitMax[EAxis::RotationZ] = inLimitMax.GetZ();
  143. UpdateRotationLimits();
  144. }
  145. void SixDOFConstraint::GetPositionConstraintProperties(Vec3 &outR1PlusU, Vec3 &outR2, Vec3 &outU) const
  146. {
  147. Vec3 p1 = mBody1->GetCenterOfMassTransform() * mLocalSpacePosition1;
  148. Vec3 p2 = mBody2->GetCenterOfMassTransform() * mLocalSpacePosition2;
  149. outR1PlusU = p2 - mBody1->GetCenterOfMassPosition(); // r1 + u = (p1 - x1) + (p2 - p1) = p2 - x1
  150. outR2 = p2 - mBody2->GetCenterOfMassPosition();
  151. outU = p2 - p1;
  152. }
  153. Quat SixDOFConstraint::GetRotationInConstraintSpace() const
  154. {
  155. // Let b1, b2 be the center of mass transform of body1 and body2 (For body1 this is mBody1->GetCenterOfMassTransform())
  156. // Let c1, c2 be the transform that takes a vector from constraint space to local space of body1 and body2 (For body1 this is Mat44::sRotationTranslation(mConstraintToBody1, mLocalSpacePosition1))
  157. // Let q be the rotation of the constraint in constraint space
  158. // b2 takes a vector from the local space of body2 to world space
  159. // To express this in terms of b1: b2 = b1 * c1 * q * c2^-1
  160. // c2^-1 goes from local body 2 space to constraint space
  161. // q rotates the constraint
  162. // c1 goes from constraint space to body 1 local space
  163. // b1 goes from body 1 local space to world space
  164. // So when the body rotations are given, q = (b1 * c1)^-1 * b2 c2
  165. // Or: q = (q1 * c1)^-1 * (q2 * c2) if we're only interested in rotations
  166. return (mBody1->GetRotation() * mConstraintToBody1).Conjugated() * mBody2->GetRotation() * mConstraintToBody2;
  167. }
  168. void SixDOFConstraint::SetMotorState(EAxis inAxis, EMotorState inState)
  169. {
  170. JPH_ASSERT(inState == EMotorState::Off || mMotorSettings[inAxis].IsValid());
  171. if (mMotorState[inAxis] != inState)
  172. {
  173. mMotorState[inAxis] = inState;
  174. // Ensure that warm starting next frame doesn't apply any impulses (motor parts are repurposed for different modes)
  175. if (inAxis >= EAxis::TranslationX && inAxis <= EAxis::TranslationZ)
  176. {
  177. mMotorTranslationConstraintPart[inAxis - EAxis::TranslationX].Deactivate();
  178. // Test if any of the motors or friction are active
  179. mTranslationMotorActive = mMotorState[EAxis::TranslationX] != EMotorState::Off
  180. || mMotorState[EAxis::TranslationY] != EMotorState::Off
  181. || mMotorState[EAxis::TranslationZ] != EMotorState::Off
  182. || mMaxFriction[EAxis::TranslationX] > 0.0f
  183. || mMaxFriction[EAxis::TranslationY] > 0.0f
  184. || mMaxFriction[EAxis::TranslationZ] > 0.0f;
  185. }
  186. else
  187. {
  188. JPH_ASSERT(inAxis >= EAxis::RotationX && inAxis <= EAxis::RotationZ);
  189. mMotorRotationConstraintPart[inAxis - EAxis::RotationX].Deactivate();
  190. mRotationMotorActive = mMotorState[EAxis::RotationX] != EMotorState::Off
  191. || mMotorState[EAxis::RotationY] != EMotorState::Off
  192. || mMotorState[EAxis::RotationZ] != EMotorState::Off
  193. || mMaxFriction[EAxis::RotationX] > 0.0f
  194. || mMaxFriction[EAxis::RotationY] > 0.0f
  195. || mMaxFriction[EAxis::RotationZ] > 0.0f;
  196. mRotationPositionMotorActive = 0;
  197. for (int i = 0; i < 3; ++i)
  198. if (mMotorState[EAxis::RotationX + i] == EMotorState::Position)
  199. mRotationPositionMotorActive |= 1 << i;
  200. }
  201. }
  202. }
  203. void SixDOFConstraint::SetTargetOrientationCS(QuatArg inOrientation)
  204. {
  205. Quat q_swing, q_twist;
  206. inOrientation.GetSwingTwist(q_swing, q_twist);
  207. bool twist_clamped, swing_y_clamped, swing_z_clamped;
  208. mSwingTwistConstraintPart.ClampSwingTwist(q_swing, swing_y_clamped, swing_z_clamped, q_twist, twist_clamped);
  209. if (twist_clamped || swing_y_clamped || swing_z_clamped)
  210. mTargetOrientation = q_swing * q_twist;
  211. else
  212. mTargetOrientation = inOrientation;
  213. }
  214. void SixDOFConstraint::SetupVelocityConstraint(float inDeltaTime)
  215. {
  216. // Get body rotations
  217. Quat rotation1 = mBody1->GetRotation();
  218. Quat rotation2 = mBody2->GetRotation();
  219. // Quaternion that rotates from body1's constraint space to world space
  220. Quat constraint_body1_to_world = rotation1 * mConstraintToBody1;
  221. // Store world space axis of constraint space
  222. Mat44 translation_axis_mat = Mat44::sRotation(constraint_body1_to_world);
  223. for (int i = 0; i < 3; ++i)
  224. mTranslationAxis[i] = translation_axis_mat.GetColumn3(i);
  225. if (IsTranslationFullyConstrained())
  226. {
  227. // All translation locked: Setup point constraint
  228. mPointConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(rotation1), mLocalSpacePosition1, *mBody2, Mat44::sRotation(rotation2), mLocalSpacePosition2);
  229. }
  230. else if (IsTranslationConstrained() || mTranslationMotorActive)
  231. {
  232. // Update world space positions (the bodies may have moved)
  233. Vec3 r1_plus_u, r2, u;
  234. GetPositionConstraintProperties(r1_plus_u, r2, u);
  235. // Setup axis constraint parts
  236. for (int i = 0; i < 3; ++i)
  237. {
  238. EAxis axis = EAxis(EAxis::TranslationX + i);
  239. Vec3 translation_axis = mTranslationAxis[i];
  240. // Setup limit constraint
  241. bool constraint_active = false;
  242. if (IsAxisFixed(axis))
  243. {
  244. // When constraint is fixed it is always active
  245. constraint_active = true;
  246. }
  247. else if (!IsAxisFree(axis))
  248. {
  249. // When constraint is limited, it is only active when outside of the allowed range
  250. float d = translation_axis.Dot(u);
  251. constraint_active = d <= mLimitMin[i] || d >= mLimitMax[i];
  252. mDisplacement[i] = d; // Store for SolveVelocityConstraint
  253. }
  254. if (constraint_active)
  255. mTranslationConstraintPart[i].CalculateConstraintProperties(inDeltaTime, *mBody1, r1_plus_u, *mBody2, r2, translation_axis);
  256. else
  257. mTranslationConstraintPart[i].Deactivate();
  258. // Setup motor constraint
  259. switch (mMotorState[i])
  260. {
  261. case EMotorState::Off:
  262. if (mMaxFriction[i] > 0.0f)
  263. mMotorTranslationConstraintPart[i].CalculateConstraintProperties(inDeltaTime, *mBody1, r1_plus_u, *mBody2, r2, translation_axis);
  264. else
  265. mMotorTranslationConstraintPart[i].Deactivate();
  266. break;
  267. case EMotorState::Velocity:
  268. mMotorTranslationConstraintPart[i].CalculateConstraintProperties(inDeltaTime, *mBody1, r1_plus_u, *mBody2, r2, translation_axis, -mTargetVelocity[i]);
  269. break;
  270. case EMotorState::Position:
  271. mMotorTranslationConstraintPart[i].CalculateConstraintProperties(inDeltaTime, *mBody1, r1_plus_u, *mBody2, r2, translation_axis, 0.0f, translation_axis.Dot(u) - mTargetPosition[i], mMotorSettings[i].mFrequency, mMotorSettings[i].mDamping);
  272. break;
  273. }
  274. }
  275. }
  276. // Setup rotation constraints
  277. if (IsRotationFullyConstrained())
  278. {
  279. // All rotation locked: Setup rotation contraint
  280. mRotationConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(mBody1->GetRotation()), *mBody2, Mat44::sRotation(mBody2->GetRotation()));
  281. }
  282. else if (IsRotationConstrained() || mRotationMotorActive)
  283. {
  284. // GetRotationInConstraintSpace without redoing the calculation of constraint_body1_to_world
  285. Quat constraint_body2_to_world = mBody2->GetRotation() * mConstraintToBody2;
  286. Quat q = constraint_body1_to_world.Conjugated() * constraint_body2_to_world;
  287. // Use swing twist constraint part
  288. if (IsRotationConstrained())
  289. mSwingTwistConstraintPart.CalculateConstraintProperties(inDeltaTime, *mBody1, *mBody2, q, constraint_body1_to_world);
  290. else
  291. mSwingTwistConstraintPart.Deactivate();
  292. if (mRotationMotorActive)
  293. {
  294. // Calculate rotation motor axis
  295. Mat44 ws_axis = Mat44::sRotation(constraint_body2_to_world);
  296. for (int i = 0; i < 3; ++i)
  297. mRotationAxis[i] = ws_axis.GetColumn3(i);
  298. // Get target orientation along the shortest path from q
  299. Quat target_orientation = q.Dot(mTargetOrientation) > 0.0f? mTargetOrientation : -mTargetOrientation;
  300. // The definition of the constraint rotation q:
  301. // R2 * ConstraintToBody2 = R1 * ConstraintToBody1 * q (1)
  302. //
  303. // R2' is the rotation of body 2 when reaching the target_orientation:
  304. // R2' * ConstraintToBody2 = R1 * ConstraintToBody1 * target_orientation (2)
  305. //
  306. // The difference in body 2 space:
  307. // R2' = R2 * diff_body2 (3)
  308. //
  309. // We want to specify the difference in the constraint space of body 2:
  310. // diff_body2 = ConstraintToBody2 * diff * ConstraintToBody2^* (4)
  311. //
  312. // Extracting R2' from 2: R2' = R1 * ConstraintToBody1 * target_orientation * ConstraintToBody2^* (5)
  313. // Combining 3 & 4: R2' = R2 * ConstraintToBody2 * diff * ConstraintToBody2^* (6)
  314. // Combining 1 & 6: R2' = R1 * ConstraintToBody1 * q * diff * ConstraintToBody2^* (7)
  315. // Combining 5 & 7: R1 * ConstraintToBody1 * target_orientation * ConstraintToBody2^* = R1 * ConstraintToBody1 * q * diff * ConstraintToBody2^*
  316. // <=> target_orientation = q * diff
  317. // <=> diff = q^* * target_orientation
  318. Quat diff = q.Conjugated() * target_orientation;
  319. // Project diff so that only rotation around axis that have a position motor are remaining
  320. Quat projected_diff;
  321. switch (mRotationPositionMotorActive)
  322. {
  323. case 0b001:
  324. // Keep only rotation around X
  325. projected_diff = diff.GetTwist(Vec3::sAxisX());
  326. break;
  327. case 0b010:
  328. // Keep only rotation around Y
  329. projected_diff = diff.GetTwist(Vec3::sAxisY());
  330. break;
  331. case 0b100:
  332. // Keep only rotation around Z
  333. projected_diff = diff.GetTwist(Vec3::sAxisZ());
  334. break;
  335. case 0b011:
  336. // Remove rotation around Z
  337. // q = swing_xy * twist_z <=> swing_xy = q * twist_z^*
  338. projected_diff = diff * diff.GetTwist(Vec3::sAxisZ()).Conjugated();
  339. break;
  340. case 0b101:
  341. // Remove rotation around Y
  342. // q = swing_xz * twist_y <=> swing_xz = q * twist_y^*
  343. projected_diff = diff * diff.GetTwist(Vec3::sAxisY()).Conjugated();
  344. break;
  345. case 0b110:
  346. // Remove rotation around X
  347. // q = swing_yz * twist_x <=> swing_yz = q * twist_x^*
  348. projected_diff = diff * diff.GetTwist(Vec3::sAxisX()).Conjugated();
  349. break;
  350. case 0b111:
  351. // Keep entire rotation
  352. projected_diff = diff;
  353. break;
  354. }
  355. // Approximate error angles
  356. // The imaginary part of a quaternion is rotation_axis * sin(angle / 2)
  357. // If angle is small, sin(x) = x so angle[i] ~ 2.0f * rotation_axis[i]
  358. // We'll be making small time steps, so if the angle is not small at least the sign will be correct and we'll move in the right direction
  359. Vec3 rotation_error = -2.0f * projected_diff.GetXYZ();
  360. // Setup motors
  361. for (int i = 0; i < 3; ++i)
  362. {
  363. EAxis axis = EAxis(EAxis::RotationX + i);
  364. Vec3 rotation_axis = mRotationAxis[i];
  365. switch (mMotorState[axis])
  366. {
  367. case EMotorState::Off:
  368. if (mMaxFriction[axis] > 0.0f)
  369. mMotorRotationConstraintPart[i].CalculateConstraintProperties(inDeltaTime, *mBody1, *mBody2, rotation_axis);
  370. else
  371. mMotorRotationConstraintPart[i].Deactivate();
  372. break;
  373. case EMotorState::Velocity:
  374. mMotorRotationConstraintPart[i].CalculateConstraintProperties(inDeltaTime, *mBody1, *mBody2, rotation_axis, -mTargetAngularVelocity[i]);
  375. break;
  376. case EMotorState::Position:
  377. mMotorRotationConstraintPart[i].CalculateConstraintProperties(inDeltaTime, *mBody1, *mBody2, rotation_axis, 0.0f, rotation_error[i], mMotorSettings[axis].mFrequency, mMotorSettings[axis].mDamping);
  378. break;
  379. }
  380. }
  381. }
  382. }
  383. }
  384. void SixDOFConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio)
  385. {
  386. // Warm start translation motors
  387. if (mTranslationMotorActive)
  388. for (int i = 0; i < 3; ++i)
  389. if (mMotorTranslationConstraintPart[i].IsActive())
  390. mMotorTranslationConstraintPart[i].WarmStart(*mBody1, *mBody2, mTranslationAxis[i], inWarmStartImpulseRatio);
  391. // Warm start rotation motors
  392. if (mRotationMotorActive)
  393. for (int i = 0; i < 3; ++i)
  394. if (mMotorRotationConstraintPart[i].IsActive())
  395. mMotorRotationConstraintPart[i].WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio);
  396. // Warm start rotation constraints
  397. if (IsRotationFullyConstrained())
  398. mRotationConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio);
  399. else if (IsRotationConstrained())
  400. mSwingTwistConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio);
  401. // Warm start translation constraints
  402. if (IsTranslationFullyConstrained())
  403. mPointConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio);
  404. else if (IsTranslationConstrained())
  405. for (int i = 0; i < 3; ++i)
  406. if (mTranslationConstraintPart[i].IsActive())
  407. mTranslationConstraintPart[i].WarmStart(*mBody1, *mBody2, mTranslationAxis[i], inWarmStartImpulseRatio);
  408. }
  409. bool SixDOFConstraint::SolveVelocityConstraint(float inDeltaTime)
  410. {
  411. bool impulse = false;
  412. // Solve translation motor
  413. if (mTranslationMotorActive)
  414. for (int i = 0; i < 3; ++i)
  415. if (mMotorTranslationConstraintPart[i].IsActive())
  416. switch (mMotorState[i])
  417. {
  418. case EMotorState::Off:
  419. {
  420. // Apply friction only
  421. float max_lambda = mMaxFriction[i] * inDeltaTime;
  422. impulse |= mMotorTranslationConstraintPart[i].SolveVelocityConstraint(*mBody1, *mBody2, mTranslationAxis[i], -max_lambda, max_lambda);
  423. break;
  424. }
  425. case EMotorState::Velocity:
  426. case EMotorState::Position:
  427. // Drive motor
  428. impulse |= mMotorTranslationConstraintPart[i].SolveVelocityConstraint(*mBody1, *mBody2, mTranslationAxis[i], inDeltaTime * mMotorSettings[i].mMinForceLimit, inDeltaTime * mMotorSettings[i].mMaxForceLimit);
  429. break;
  430. }
  431. // Solve rotation motor
  432. if (mRotationMotorActive)
  433. for (int i = 0; i < 3; ++i)
  434. {
  435. EAxis axis = EAxis(EAxis::RotationX + i);
  436. if (mMotorRotationConstraintPart[i].IsActive())
  437. switch (mMotorState[axis])
  438. {
  439. case EMotorState::Off:
  440. {
  441. // Apply friction only
  442. float max_lambda = mMaxFriction[axis] * inDeltaTime;
  443. impulse |= mMotorRotationConstraintPart[i].SolveVelocityConstraint(*mBody1, *mBody2, mRotationAxis[i], -max_lambda, max_lambda);
  444. break;
  445. }
  446. case EMotorState::Velocity:
  447. case EMotorState::Position:
  448. // Drive motor
  449. impulse |= mMotorRotationConstraintPart[i].SolveVelocityConstraint(*mBody1, *mBody2, mRotationAxis[i], inDeltaTime * mMotorSettings[axis].mMinTorqueLimit, inDeltaTime * mMotorSettings[axis].mMaxTorqueLimit);
  450. break;
  451. }
  452. }
  453. // Solve rotation constraint
  454. if (IsRotationFullyConstrained())
  455. impulse |= mRotationConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2);
  456. else if (IsRotationConstrained())
  457. impulse |= mSwingTwistConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2);
  458. // Solve position constraint
  459. if (IsTranslationFullyConstrained())
  460. impulse |= mPointConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2);
  461. else if (IsTranslationConstrained())
  462. for (int i = 0; i < 3; ++i)
  463. if (mTranslationConstraintPart[i].IsActive())
  464. {
  465. // If the axis is not fixed it must be limited (or else the constraint would not be active)
  466. // Calculate the min and max constraint force based on on which side we're limited
  467. float limit_min = -FLT_MAX, limit_max = FLT_MAX;
  468. if (!IsAxisFixed(EAxis(EAxis::TranslationX + i)))
  469. {
  470. JPH_ASSERT(!IsAxisFree(EAxis(EAxis::TranslationX + i)));
  471. if (mDisplacement[i] <= mLimitMin[i])
  472. limit_min = 0;
  473. else if (mDisplacement[i] >= mLimitMax[i])
  474. limit_max = 0;
  475. }
  476. impulse |= mTranslationConstraintPart[i].SolveVelocityConstraint(*mBody1, *mBody2, mTranslationAxis[i], limit_min, limit_max);
  477. }
  478. return impulse;
  479. }
  480. bool SixDOFConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte)
  481. {
  482. bool impulse = false;
  483. if (IsRotationFullyConstrained())
  484. {
  485. // Rotation locked: Solve rotation constraint
  486. // Inverse of initial rotation from body 1 to body 2 in body 1 space
  487. // Definition of initial orientation r0: q2 = q1 r0
  488. // Initial rotation (see: GetRotationInConstraintSpace): q2 = q1 c1 c2^-1
  489. // So: r0^-1 = (c1 c2^-1)^-1 = c2 * c1^-1
  490. Quat inv_initial_orientation = mConstraintToBody2 * mConstraintToBody1.Conjugated();
  491. // Solve rotation violations
  492. mRotationConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(mBody1->GetRotation()), *mBody2, Mat44::sRotation(mBody2->GetRotation()));
  493. impulse |= mRotationConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, inv_initial_orientation, inBaumgarte);
  494. }
  495. else if (IsRotationConstrained())
  496. {
  497. // Rotation partially constraint
  498. // Solve rotation violations
  499. Quat q = GetRotationInConstraintSpace();
  500. impulse |= mSwingTwistConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, q, mConstraintToBody1, mConstraintToBody2, inBaumgarte);
  501. }
  502. // Solve position violations
  503. if (IsTranslationFullyConstrained())
  504. {
  505. // Translation locked: Solve point constraint
  506. mPointConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(mBody1->GetRotation()), mLocalSpacePosition1, *mBody2, Mat44::sRotation(mBody2->GetRotation()), mLocalSpacePosition2);
  507. impulse |= mPointConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, inBaumgarte);
  508. }
  509. else if (IsTranslationConstrained())
  510. {
  511. // Translation partially locked: Solve per axis
  512. for (int i = 0; i < 3; ++i)
  513. {
  514. // Update world space positions (the bodies may have moved)
  515. Vec3 r1_plus_u, r2, u;
  516. GetPositionConstraintProperties(r1_plus_u, r2, u);
  517. // Quaternion that rotates from body1's constraint space to world space
  518. Quat constraint_body1_to_world = mBody1->GetRotation() * mConstraintToBody1;
  519. // Calculate axis
  520. Vec3 translation_axis;
  521. switch (i)
  522. {
  523. case 0: translation_axis = constraint_body1_to_world.RotateAxisX(); break;
  524. case 1: translation_axis = constraint_body1_to_world.RotateAxisY(); break;
  525. default: JPH_ASSERT(i == 2); translation_axis = constraint_body1_to_world.RotateAxisZ(); break;
  526. }
  527. // Determine position error
  528. float error = 0.0f;
  529. EAxis axis(EAxis(EAxis::TranslationX + i));
  530. if (IsAxisFixed(axis))
  531. error = u.Dot(translation_axis);
  532. else if (!IsAxisFree(axis))
  533. {
  534. float displacement = u.Dot(translation_axis);
  535. if (displacement <= mLimitMin[axis])
  536. error = displacement - mLimitMin[axis];
  537. else if (displacement >= mLimitMax[axis])
  538. error = displacement - mLimitMax[axis];
  539. }
  540. if (error != 0.0f)
  541. {
  542. // Setup axis constraint part and solve it
  543. mTranslationConstraintPart[i].CalculateConstraintProperties(inDeltaTime, *mBody1, r1_plus_u, *mBody2, r2, translation_axis);
  544. impulse |= mTranslationConstraintPart[i].SolvePositionConstraint(*mBody1, *mBody2, translation_axis, error, inBaumgarte);
  545. }
  546. }
  547. }
  548. return impulse;
  549. }
  550. #ifdef JPH_DEBUG_RENDERER
  551. void SixDOFConstraint::DrawConstraint(DebugRenderer *inRenderer) const
  552. {
  553. // Get constraint properties in world space
  554. Vec3 position1 = mBody1->GetCenterOfMassTransform() * mLocalSpacePosition1;
  555. Quat rotation1 = mBody1->GetRotation() * mConstraintToBody1;
  556. Quat rotation2 = mBody2->GetRotation() * mConstraintToBody2;
  557. // Draw constraint orientation
  558. inRenderer->DrawCoordinateSystem(Mat44::sRotationTranslation(rotation1, position1), mDrawConstraintSize);
  559. if ((IsRotationConstrained() || mRotationPositionMotorActive != 0) && !IsRotationFullyConstrained())
  560. {
  561. // Draw current swing and twist
  562. Quat q = GetRotationInConstraintSpace();
  563. Quat q_swing, q_twist;
  564. q.GetSwingTwist(q_swing, q_twist);
  565. inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * q_twist).RotateAxisY(), Color::sWhite);
  566. inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * q_swing).RotateAxisX(), Color::sWhite);
  567. }
  568. // Draw target rotation
  569. Quat m_swing, m_twist;
  570. mTargetOrientation.GetSwingTwist(m_swing, m_twist);
  571. if (mMotorState[EAxis::RotationX] == EMotorState::Position)
  572. inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * m_twist).RotateAxisY(), Color::sYellow);
  573. if (mMotorState[EAxis::RotationY] == EMotorState::Position || mMotorState[EAxis::RotationZ] == EMotorState::Position)
  574. inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * m_swing).RotateAxisX(), Color::sYellow);
  575. // Draw target angular velocity
  576. Vec3 target_angular_velocity = Vec3::sZero();
  577. for (int i = 0; i < 3; ++i)
  578. if (mMotorState[EAxis::RotationX + i] == EMotorState::Velocity)
  579. target_angular_velocity.SetComponent(i, mTargetAngularVelocity[i]);
  580. if (target_angular_velocity != Vec3::sZero())
  581. inRenderer->DrawArrow(position1, position1 + rotation2 * target_angular_velocity, Color::sRed, 0.1f);
  582. }
  583. void SixDOFConstraint::DrawConstraintLimits(DebugRenderer *inRenderer) const
  584. {
  585. // Get matrix that transforms from constraint space to world space
  586. Mat44 constraint_body1_to_world = Mat44::sRotationTranslation(mBody1->GetRotation() * mConstraintToBody1, mBody1->GetCenterOfMassTransform() * mLocalSpacePosition1);
  587. // Draw limits
  588. inRenderer->DrawSwingLimits(constraint_body1_to_world, mLimitMax[EAxis::RotationY], mLimitMax[EAxis::RotationZ], mDrawConstraintSize, Color::sGreen, DebugRenderer::ECastShadow::Off);
  589. inRenderer->DrawPie(constraint_body1_to_world.GetTranslation(), mDrawConstraintSize, constraint_body1_to_world.GetAxisX(), constraint_body1_to_world.GetAxisY(), mLimitMin[EAxis::RotationX], mLimitMax[EAxis::RotationX], Color::sPurple, DebugRenderer::ECastShadow::Off);
  590. }
  591. #endif // JPH_DEBUG_RENDERER
  592. void SixDOFConstraint::SaveState(StateRecorder &inStream) const
  593. {
  594. TwoBodyConstraint::SaveState(inStream);
  595. for (int i = 0; i < 3; ++i)
  596. mTranslationConstraintPart[i].SaveState(inStream);
  597. mPointConstraintPart.SaveState(inStream);
  598. mSwingTwistConstraintPart.SaveState(inStream);
  599. mRotationConstraintPart.SaveState(inStream);
  600. for (int i = 0; i < 3; ++i)
  601. mMotorTranslationConstraintPart[i].SaveState(inStream);
  602. for (int i = 0; i < 3; ++i)
  603. mMotorRotationConstraintPart[i].SaveState(inStream);
  604. inStream.Write(mMotorState);
  605. inStream.Write(mTargetVelocity);
  606. inStream.Write(mTargetAngularVelocity);
  607. inStream.Write(mTargetPosition);
  608. inStream.Write(mTargetOrientation);
  609. }
  610. void SixDOFConstraint::RestoreState(StateRecorder &inStream)
  611. {
  612. TwoBodyConstraint::RestoreState(inStream);
  613. for (int i = 0; i < 3; ++i)
  614. mTranslationConstraintPart[i].RestoreState(inStream);
  615. mPointConstraintPart.RestoreState(inStream);
  616. mSwingTwistConstraintPart.RestoreState(inStream);
  617. mRotationConstraintPart.RestoreState(inStream);
  618. for (int i = 0; i < 3; ++i)
  619. mMotorTranslationConstraintPart[i].RestoreState(inStream);
  620. for (int i = 0; i < 3; ++i)
  621. mMotorRotationConstraintPart[i].RestoreState(inStream);
  622. inStream.Read(mMotorState);
  623. inStream.Read(mTargetVelocity);
  624. inStream.Read(mTargetAngularVelocity);
  625. inStream.Read(mTargetPosition);
  626. inStream.Read(mTargetOrientation);
  627. }
  628. } // JPH