SixDOFConstraint.cpp 29 KB

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