SwingTwistConstraint.cpp 19 KB

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