SixDOFConstraint.cpp 33 KB

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