SwingTwistConstraint.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  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/SwingTwistConstraint.h>
  6. #include <Jolt/Physics/Body/Body.h>
  7. #include <Jolt/ObjectStream/TypeDeclarations.h>
  8. #include <Jolt/Core/StreamIn.h>
  9. #include <Jolt/Core/StreamOut.h>
  10. #ifdef JPH_DEBUG_RENDERER
  11. #include <Jolt/Renderer/DebugRenderer.h>
  12. #endif // JPH_DEBUG_RENDERER
  13. JPH_NAMESPACE_BEGIN
  14. JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(SwingTwistConstraintSettings)
  15. {
  16. JPH_ADD_BASE_CLASS(SwingTwistConstraintSettings, TwoBodyConstraintSettings)
  17. JPH_ADD_ENUM_ATTRIBUTE(SwingTwistConstraintSettings, mSpace)
  18. JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mPosition1)
  19. JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mTwistAxis1)
  20. JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mPlaneAxis1)
  21. JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mPosition2)
  22. JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mTwistAxis2)
  23. JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mPlaneAxis2)
  24. JPH_ADD_ENUM_ATTRIBUTE(SwingTwistConstraintSettings, mSwingType)
  25. JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mNormalHalfConeAngle)
  26. JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mPlaneHalfConeAngle)
  27. JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mTwistMinAngle)
  28. JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mTwistMaxAngle)
  29. JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mMaxFrictionTorque)
  30. JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mSwingMotorSettings)
  31. JPH_ADD_ATTRIBUTE(SwingTwistConstraintSettings, mTwistMotorSettings)
  32. }
  33. void SwingTwistConstraintSettings::SaveBinaryState(StreamOut &inStream) const
  34. {
  35. ConstraintSettings::SaveBinaryState(inStream);
  36. inStream.Write(mSpace);
  37. inStream.Write(mPosition1);
  38. inStream.Write(mTwistAxis1);
  39. inStream.Write(mPlaneAxis1);
  40. inStream.Write(mPosition2);
  41. inStream.Write(mTwistAxis2);
  42. inStream.Write(mPlaneAxis2);
  43. inStream.Write(mSwingType);
  44. inStream.Write(mNormalHalfConeAngle);
  45. inStream.Write(mPlaneHalfConeAngle);
  46. inStream.Write(mTwistMinAngle);
  47. inStream.Write(mTwistMaxAngle);
  48. inStream.Write(mMaxFrictionTorque);
  49. mSwingMotorSettings.SaveBinaryState(inStream);
  50. mTwistMotorSettings.SaveBinaryState(inStream);
  51. }
  52. void SwingTwistConstraintSettings::RestoreBinaryState(StreamIn &inStream)
  53. {
  54. ConstraintSettings::RestoreBinaryState(inStream);
  55. inStream.Read(mSpace);
  56. inStream.Read(mPosition1);
  57. inStream.Read(mTwistAxis1);
  58. inStream.Read(mPlaneAxis1);
  59. inStream.Read(mPosition2);
  60. inStream.Read(mTwistAxis2);
  61. inStream.Read(mPlaneAxis2);
  62. inStream.Read(mSwingType);
  63. inStream.Read(mNormalHalfConeAngle);
  64. inStream.Read(mPlaneHalfConeAngle);
  65. inStream.Read(mTwistMinAngle);
  66. inStream.Read(mTwistMaxAngle);
  67. inStream.Read(mMaxFrictionTorque);
  68. mSwingMotorSettings.RestoreBinaryState(inStream);
  69. mTwistMotorSettings.RestoreBinaryState(inStream);
  70. }
  71. TwoBodyConstraint *SwingTwistConstraintSettings::Create(Body &inBody1, Body &inBody2) const
  72. {
  73. return new SwingTwistConstraint(inBody1, inBody2, *this);
  74. }
  75. void SwingTwistConstraint::UpdateLimits()
  76. {
  77. // Pass limits on to swing twist constraint part
  78. mSwingTwistConstraintPart.SetLimits(mTwistMinAngle, mTwistMaxAngle, -mPlaneHalfConeAngle, mPlaneHalfConeAngle, -mNormalHalfConeAngle, mNormalHalfConeAngle);
  79. }
  80. SwingTwistConstraint::SwingTwistConstraint(Body &inBody1, Body &inBody2, const SwingTwistConstraintSettings &inSettings) :
  81. TwoBodyConstraint(inBody1, inBody2, inSettings),
  82. mNormalHalfConeAngle(inSettings.mNormalHalfConeAngle),
  83. mPlaneHalfConeAngle(inSettings.mPlaneHalfConeAngle),
  84. mTwistMinAngle(inSettings.mTwistMinAngle),
  85. mTwistMaxAngle(inSettings.mTwistMaxAngle),
  86. mMaxFrictionTorque(inSettings.mMaxFrictionTorque),
  87. mSwingMotorSettings(inSettings.mSwingMotorSettings),
  88. mTwistMotorSettings(inSettings.mTwistMotorSettings)
  89. {
  90. // Override swing type
  91. mSwingTwistConstraintPart.SetSwingType(inSettings.mSwingType);
  92. // Calculate rotation needed to go from constraint space to body1 local space
  93. Vec3 normal_axis1 = inSettings.mPlaneAxis1.Cross(inSettings.mTwistAxis1);
  94. Mat44 c_to_b1(Vec4(inSettings.mTwistAxis1, 0), Vec4(normal_axis1, 0), Vec4(inSettings.mPlaneAxis1, 0), Vec4(0, 0, 0, 1));
  95. mConstraintToBody1 = c_to_b1.GetQuaternion();
  96. // Calculate rotation needed to go from constraint space to body2 local space
  97. Vec3 normal_axis2 = inSettings.mPlaneAxis2.Cross(inSettings.mTwistAxis2);
  98. Mat44 c_to_b2(Vec4(inSettings.mTwistAxis2, 0), Vec4(normal_axis2, 0), Vec4(inSettings.mPlaneAxis2, 0), Vec4(0, 0, 0, 1));
  99. mConstraintToBody2 = c_to_b2.GetQuaternion();
  100. if (inSettings.mSpace == EConstraintSpace::WorldSpace)
  101. {
  102. // If all properties were specified in world space, take them to local space now
  103. mLocalSpacePosition1 = Vec3(inBody1.GetInverseCenterOfMassTransform() * inSettings.mPosition1);
  104. mConstraintToBody1 = inBody1.GetRotation().Conjugated() * mConstraintToBody1;
  105. mLocalSpacePosition2 = Vec3(inBody2.GetInverseCenterOfMassTransform() * inSettings.mPosition2);
  106. mConstraintToBody2 = inBody2.GetRotation().Conjugated() * mConstraintToBody2;
  107. }
  108. else
  109. {
  110. mLocalSpacePosition1 = Vec3(inSettings.mPosition1);
  111. mLocalSpacePosition2 = Vec3(inSettings.mPosition2);
  112. }
  113. UpdateLimits();
  114. }
  115. void SwingTwistConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM)
  116. {
  117. if (mBody1->GetID() == inBodyID)
  118. mLocalSpacePosition1 -= inDeltaCOM;
  119. else if (mBody2->GetID() == inBodyID)
  120. mLocalSpacePosition2 -= inDeltaCOM;
  121. }
  122. Quat SwingTwistConstraint::GetRotationInConstraintSpace() const
  123. {
  124. // Let b1, b2 be the center of mass transform of body1 and body2 (For body1 this is mBody1->GetCenterOfMassTransform())
  125. // 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))
  126. // Let q be the rotation of the constraint in constraint space
  127. // b2 takes a vector from the local space of body2 to world space
  128. // To express this in terms of b1: b2 = b1 * c1 * q * c2^-1
  129. // c2^-1 goes from local body 2 space to constraint space
  130. // q rotates the constraint
  131. // c1 goes from constraint space to body 1 local space
  132. // b1 goes from body 1 local space to world space
  133. // So when the body rotations are given, q = (b1 * c1)^-1 * b2 c2
  134. // Or: q = (q1 * c1)^-1 * (q2 * c2) if we're only interested in rotations
  135. Quat constraint_body1_to_world = mBody1->GetRotation() * mConstraintToBody1;
  136. Quat constraint_body2_to_world = mBody2->GetRotation() * mConstraintToBody2;
  137. return constraint_body1_to_world.Conjugated() * constraint_body2_to_world;
  138. }
  139. void SwingTwistConstraint::SetSwingMotorState(EMotorState inState)
  140. {
  141. JPH_ASSERT(inState == EMotorState::Off || mSwingMotorSettings.IsValid());
  142. if (mSwingMotorState != inState)
  143. {
  144. mSwingMotorState = inState;
  145. // Ensure that warm starting next frame doesn't apply any impulses (motor parts are repurposed for different modes)
  146. for (AngleConstraintPart &c : mMotorConstraintPart)
  147. c.Deactivate();
  148. }
  149. }
  150. void SwingTwistConstraint::SetTwistMotorState(EMotorState inState)
  151. {
  152. JPH_ASSERT(inState == EMotorState::Off || mTwistMotorSettings.IsValid());
  153. if (mTwistMotorState != inState)
  154. {
  155. mTwistMotorState = inState;
  156. // Ensure that warm starting next frame doesn't apply any impulses (motor parts are repurposed for different modes)
  157. mMotorConstraintPart[0].Deactivate();
  158. }
  159. }
  160. void SwingTwistConstraint::SetTargetOrientationCS(QuatArg inOrientation)
  161. {
  162. Quat q_swing, q_twist;
  163. inOrientation.GetSwingTwist(q_swing, q_twist);
  164. uint clamped_axis;
  165. mSwingTwistConstraintPart.ClampSwingTwist(q_swing, q_twist, clamped_axis);
  166. if (clamped_axis != 0)
  167. mTargetOrientation = q_swing * q_twist;
  168. else
  169. mTargetOrientation = inOrientation;
  170. }
  171. void SwingTwistConstraint::SetupVelocityConstraint(float inDeltaTime)
  172. {
  173. // Setup point constraint
  174. Mat44 rotation1 = Mat44::sRotation(mBody1->GetRotation());
  175. Mat44 rotation2 = Mat44::sRotation(mBody2->GetRotation());
  176. mPointConstraintPart.CalculateConstraintProperties(*mBody1, rotation1, mLocalSpacePosition1, *mBody2, rotation2, mLocalSpacePosition2);
  177. // GetRotationInConstraintSpace written out since we reuse the sub expressions
  178. Quat constraint_body1_to_world = mBody1->GetRotation() * mConstraintToBody1;
  179. Quat constraint_body2_to_world = mBody2->GetRotation() * mConstraintToBody2;
  180. Quat q = constraint_body1_to_world.Conjugated() * constraint_body2_to_world;
  181. // Calculate constraint properties for the swing twist limit
  182. mSwingTwistConstraintPart.CalculateConstraintProperties(*mBody1, *mBody2, q, constraint_body1_to_world);
  183. if (mSwingMotorState != EMotorState::Off || mTwistMotorState != EMotorState::Off || mMaxFrictionTorque > 0.0f)
  184. {
  185. // Calculate rotation motor axis
  186. Mat44 ws_axis = Mat44::sRotation(constraint_body2_to_world);
  187. for (int i = 0; i < 3; ++i)
  188. mWorldSpaceMotorAxis[i] = ws_axis.GetColumn3(i);
  189. Vec3 rotation_error;
  190. if (mSwingMotorState == EMotorState::Position || mTwistMotorState == EMotorState::Position)
  191. {
  192. // Get target orientation along the shortest path from q
  193. Quat target_orientation = q.Dot(mTargetOrientation) > 0.0f? mTargetOrientation : -mTargetOrientation;
  194. // The definition of the constraint rotation q:
  195. // R2 * ConstraintToBody2 = R1 * ConstraintToBody1 * q (1)
  196. //
  197. // R2' is the rotation of body 2 when reaching the target_orientation:
  198. // R2' * ConstraintToBody2 = R1 * ConstraintToBody1 * target_orientation (2)
  199. //
  200. // The difference in body 2 space:
  201. // R2' = R2 * diff_body2 (3)
  202. //
  203. // We want to specify the difference in the constraint space of body 2:
  204. // diff_body2 = ConstraintToBody2 * diff * ConstraintToBody2^* (4)
  205. //
  206. // Extracting R2' from 2: R2' = R1 * ConstraintToBody1 * target_orientation * ConstraintToBody2^* (5)
  207. // Combining 3 & 4: R2' = R2 * ConstraintToBody2 * diff * ConstraintToBody2^* (6)
  208. // Combining 1 & 6: R2' = R1 * ConstraintToBody1 * q * diff * ConstraintToBody2^* (7)
  209. // Combining 5 & 7: R1 * ConstraintToBody1 * target_orientation * ConstraintToBody2^* = R1 * ConstraintToBody1 * q * diff * ConstraintToBody2^*
  210. // <=> target_orientation = q * diff
  211. // <=> diff = q^* * target_orientation
  212. Quat diff = q.Conjugated() * target_orientation;
  213. // Approximate error angles
  214. // The imaginary part of a quaternion is rotation_axis * sin(angle / 2)
  215. // If angle is small, sin(x) = x so angle[i] ~ 2.0f * rotation_axis[i]
  216. // 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
  217. rotation_error = -2.0f * diff.GetXYZ();
  218. }
  219. // Swing motor
  220. switch (mSwingMotorState)
  221. {
  222. case EMotorState::Off:
  223. if (mMaxFrictionTorque > 0.0f)
  224. {
  225. // Enable friction
  226. for (int i = 1; i < 3; ++i)
  227. mMotorConstraintPart[i].CalculateConstraintProperties(*mBody1, *mBody2, mWorldSpaceMotorAxis[i], 0.0f);
  228. }
  229. else
  230. {
  231. // Disable friction
  232. for (AngleConstraintPart &c : mMotorConstraintPart)
  233. c.Deactivate();
  234. }
  235. break;
  236. case EMotorState::Velocity:
  237. // Use motor to create angular velocity around desired axis
  238. for (int i = 1; i < 3; ++i)
  239. mMotorConstraintPart[i].CalculateConstraintProperties(*mBody1, *mBody2, mWorldSpaceMotorAxis[i], -mTargetAngularVelocity[i]);
  240. break;
  241. case EMotorState::Position:
  242. // Use motor to drive rotation error to zero
  243. if (mSwingMotorSettings.mSpringSettings.HasStiffness())
  244. {
  245. for (int i = 1; i < 3; ++i)
  246. mMotorConstraintPart[i].CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, *mBody2, mWorldSpaceMotorAxis[i], 0.0f, rotation_error[i], mSwingMotorSettings.mSpringSettings);
  247. }
  248. else
  249. {
  250. for (int i = 1; i < 3; ++i)
  251. mMotorConstraintPart[i].Deactivate();
  252. }
  253. break;
  254. }
  255. // Twist motor
  256. switch (mTwistMotorState)
  257. {
  258. case EMotorState::Off:
  259. if (mMaxFrictionTorque > 0.0f)
  260. {
  261. // Enable friction
  262. mMotorConstraintPart[0].CalculateConstraintProperties(*mBody1, *mBody2, mWorldSpaceMotorAxis[0], 0.0f);
  263. }
  264. else
  265. {
  266. // Disable friction
  267. mMotorConstraintPart[0].Deactivate();
  268. }
  269. break;
  270. case EMotorState::Velocity:
  271. // Use motor to create angular velocity around desired axis
  272. mMotorConstraintPart[0].CalculateConstraintProperties(*mBody1, *mBody2, mWorldSpaceMotorAxis[0], -mTargetAngularVelocity[0]);
  273. break;
  274. case EMotorState::Position:
  275. // Use motor to drive rotation error to zero
  276. if (mTwistMotorSettings.mSpringSettings.HasStiffness())
  277. mMotorConstraintPart[0].CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, *mBody2, mWorldSpaceMotorAxis[0], 0.0f, rotation_error[0], mTwistMotorSettings.mSpringSettings);
  278. else
  279. mMotorConstraintPart[0].Deactivate();
  280. break;
  281. }
  282. }
  283. else
  284. {
  285. // Disable rotation motor
  286. for (AngleConstraintPart &c : mMotorConstraintPart)
  287. c.Deactivate();
  288. }
  289. }
  290. void SwingTwistConstraint::ResetWarmStart()
  291. {
  292. for (AngleConstraintPart &c : mMotorConstraintPart)
  293. c.Deactivate();
  294. mSwingTwistConstraintPart.Deactivate();
  295. mPointConstraintPart.Deactivate();
  296. }
  297. void SwingTwistConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio)
  298. {
  299. // Warm starting: Apply previous frame impulse
  300. for (AngleConstraintPart &c : mMotorConstraintPart)
  301. c.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio);
  302. mSwingTwistConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio);
  303. mPointConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio);
  304. }
  305. bool SwingTwistConstraint::SolveVelocityConstraint(float inDeltaTime)
  306. {
  307. bool impulse = false;
  308. // Solve twist rotation motor
  309. if (mMotorConstraintPart[0].IsActive())
  310. {
  311. // Twist limits
  312. float min_twist_limit, max_twist_limit;
  313. if (mTwistMotorState == EMotorState::Off)
  314. {
  315. max_twist_limit = inDeltaTime * mMaxFrictionTorque;
  316. min_twist_limit = -max_twist_limit;
  317. }
  318. else
  319. {
  320. min_twist_limit = inDeltaTime * mTwistMotorSettings.mMinTorqueLimit;
  321. max_twist_limit = inDeltaTime * mTwistMotorSettings.mMaxTorqueLimit;
  322. }
  323. impulse |= mMotorConstraintPart[0].SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceMotorAxis[0], min_twist_limit, max_twist_limit);
  324. }
  325. // Solve swing rotation motor
  326. if (mMotorConstraintPart[1].IsActive())
  327. {
  328. // Swing parts should turn on / off together
  329. JPH_ASSERT(mMotorConstraintPart[2].IsActive());
  330. // Swing limits
  331. float min_swing_limit, max_swing_limit;
  332. if (mSwingMotorState == EMotorState::Off)
  333. {
  334. max_swing_limit = inDeltaTime * mMaxFrictionTorque;
  335. min_swing_limit = -max_swing_limit;
  336. }
  337. else
  338. {
  339. min_swing_limit = inDeltaTime * mSwingMotorSettings.mMinTorqueLimit;
  340. max_swing_limit = inDeltaTime * mSwingMotorSettings.mMaxTorqueLimit;
  341. }
  342. for (int i = 1; i < 3; ++i)
  343. impulse |= mMotorConstraintPart[i].SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceMotorAxis[i], min_swing_limit, max_swing_limit);
  344. }
  345. else
  346. {
  347. // Swing parts should turn on / off together
  348. JPH_ASSERT(!mMotorConstraintPart[2].IsActive());
  349. }
  350. // Solve rotation limits
  351. impulse |= mSwingTwistConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2);
  352. // Solve position constraint
  353. impulse |= mPointConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2);
  354. return impulse;
  355. }
  356. bool SwingTwistConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte)
  357. {
  358. bool impulse = false;
  359. // Solve rotation violations
  360. Quat q = GetRotationInConstraintSpace();
  361. impulse |= mSwingTwistConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, q, mConstraintToBody1, mConstraintToBody2, inBaumgarte);
  362. // Solve position violations
  363. mPointConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(mBody1->GetRotation()), mLocalSpacePosition1, *mBody2, Mat44::sRotation(mBody2->GetRotation()), mLocalSpacePosition2);
  364. impulse |= mPointConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, inBaumgarte);
  365. return impulse;
  366. }
  367. #ifdef JPH_DEBUG_RENDERER
  368. void SwingTwistConstraint::DrawConstraint(DebugRenderer *inRenderer) const
  369. {
  370. // Get constraint properties in world space
  371. RMat44 transform1 = mBody1->GetCenterOfMassTransform();
  372. RVec3 position1 = transform1 * mLocalSpacePosition1;
  373. Quat rotation1 = mBody1->GetRotation() * mConstraintToBody1;
  374. Quat rotation2 = mBody2->GetRotation() * mConstraintToBody2;
  375. // Draw constraint orientation
  376. inRenderer->DrawCoordinateSystem(RMat44::sRotationTranslation(rotation1, position1), mDrawConstraintSize);
  377. // Draw current swing and twist
  378. Quat q = GetRotationInConstraintSpace();
  379. Quat q_swing, q_twist;
  380. q.GetSwingTwist(q_swing, q_twist);
  381. inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * q_twist).RotateAxisY(), Color::sWhite);
  382. inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * q_swing).RotateAxisX(), Color::sWhite);
  383. if (mSwingMotorState == EMotorState::Velocity || mTwistMotorState == EMotorState::Velocity)
  384. {
  385. // Draw target angular velocity
  386. inRenderer->DrawArrow(position1, position1 + rotation2 * mTargetAngularVelocity, Color::sRed, 0.1f);
  387. }
  388. if (mSwingMotorState == EMotorState::Position || mTwistMotorState == EMotorState::Position)
  389. {
  390. // Draw motor swing and twist
  391. Quat swing, twist;
  392. mTargetOrientation.GetSwingTwist(swing, twist);
  393. inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * twist).RotateAxisY(), Color::sYellow);
  394. inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * swing).RotateAxisX(), Color::sCyan);
  395. }
  396. }
  397. void SwingTwistConstraint::DrawConstraintLimits(DebugRenderer *inRenderer) const
  398. {
  399. // Get matrix that transforms from constraint space to world space
  400. RMat44 constraint_to_world = RMat44::sRotationTranslation(mBody1->GetRotation() * mConstraintToBody1, mBody1->GetCenterOfMassTransform() * mLocalSpacePosition1);
  401. // Draw limits
  402. if (mSwingTwistConstraintPart.GetSwingType() == ESwingType::Pyramid)
  403. inRenderer->DrawSwingPyramidLimits(constraint_to_world, -mPlaneHalfConeAngle, mPlaneHalfConeAngle, -mNormalHalfConeAngle, mNormalHalfConeAngle, mDrawConstraintSize, Color::sGreen, DebugRenderer::ECastShadow::Off);
  404. else
  405. inRenderer->DrawSwingConeLimits(constraint_to_world, mPlaneHalfConeAngle, mNormalHalfConeAngle, mDrawConstraintSize, Color::sGreen, DebugRenderer::ECastShadow::Off);
  406. inRenderer->DrawPie(constraint_to_world.GetTranslation(), mDrawConstraintSize, constraint_to_world.GetAxisX(), constraint_to_world.GetAxisY(), mTwistMinAngle, mTwistMaxAngle, Color::sPurple, DebugRenderer::ECastShadow::Off);
  407. }
  408. #endif // JPH_DEBUG_RENDERER
  409. void SwingTwistConstraint::SaveState(StateRecorder &inStream) const
  410. {
  411. TwoBodyConstraint::SaveState(inStream);
  412. mPointConstraintPart.SaveState(inStream);
  413. mSwingTwistConstraintPart.SaveState(inStream);
  414. for (const AngleConstraintPart &c : mMotorConstraintPart)
  415. c.SaveState(inStream);
  416. inStream.Write(mSwingMotorState);
  417. inStream.Write(mTwistMotorState);
  418. inStream.Write(mTargetAngularVelocity);
  419. inStream.Write(mTargetOrientation);
  420. }
  421. void SwingTwistConstraint::RestoreState(StateRecorder &inStream)
  422. {
  423. TwoBodyConstraint::RestoreState(inStream);
  424. mPointConstraintPart.RestoreState(inStream);
  425. mSwingTwistConstraintPart.RestoreState(inStream);
  426. for (AngleConstraintPart &c : mMotorConstraintPart)
  427. c.RestoreState(inStream);
  428. inStream.Read(mSwingMotorState);
  429. inStream.Read(mTwistMotorState);
  430. inStream.Read(mTargetAngularVelocity);
  431. inStream.Read(mTargetOrientation);
  432. }
  433. Ref<ConstraintSettings> SwingTwistConstraint::GetConstraintSettings() const
  434. {
  435. SwingTwistConstraintSettings *settings = new SwingTwistConstraintSettings;
  436. ToConstraintSettings(*settings);
  437. settings->mSpace = EConstraintSpace::LocalToBodyCOM;
  438. settings->mPosition1 = RVec3(mLocalSpacePosition1);
  439. settings->mTwistAxis1 = mConstraintToBody1.RotateAxisX();
  440. settings->mPlaneAxis1 = mConstraintToBody1.RotateAxisZ();
  441. settings->mPosition2 = RVec3(mLocalSpacePosition2);
  442. settings->mTwistAxis2 = mConstraintToBody2.RotateAxisX();
  443. settings->mPlaneAxis2 = mConstraintToBody2.RotateAxisZ();
  444. settings->mSwingType = mSwingTwistConstraintPart.GetSwingType();
  445. settings->mNormalHalfConeAngle = mNormalHalfConeAngle;
  446. settings->mPlaneHalfConeAngle = mPlaneHalfConeAngle;
  447. settings->mTwistMinAngle = mTwistMinAngle;
  448. settings->mTwistMaxAngle = mTwistMaxAngle;
  449. settings->mMaxFrictionTorque = mMaxFrictionTorque;
  450. settings->mSwingMotorSettings = mSwingMotorSettings;
  451. settings->mTwistMotorSettings = mTwistMotorSettings;
  452. return settings;
  453. }
  454. JPH_NAMESPACE_END