SwingTwistConstraint.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  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(inDeltaTime, *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(inDeltaTime, *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(inDeltaTime, *mBody1, *mBody2, mWorldSpaceMotorAxis[i], -mTargetAngularVelocity[i]);
  235. break;
  236. case EMotorState::Position:
  237. // Use motor to drive rotation error to zero
  238. for (int i = 1; i < 3; ++i)
  239. mMotorConstraintPart[i].CalculateConstraintProperties(inDeltaTime, *mBody1, *mBody2, mWorldSpaceMotorAxis[i], 0.0f, rotation_error[i], mSwingMotorSettings.mFrequency, mSwingMotorSettings.mDamping);
  240. break;
  241. }
  242. // Twist motor
  243. switch (mTwistMotorState)
  244. {
  245. case EMotorState::Off:
  246. if (mMaxFrictionTorque > 0.0f)
  247. {
  248. // Enable friction
  249. mMotorConstraintPart[0].CalculateConstraintProperties(inDeltaTime, *mBody1, *mBody2, mWorldSpaceMotorAxis[0], 0.0f);
  250. }
  251. else
  252. {
  253. // Disable friction
  254. mMotorConstraintPart[0].Deactivate();
  255. }
  256. break;
  257. case EMotorState::Velocity:
  258. // Use motor to create angular velocity around desired axis
  259. mMotorConstraintPart[0].CalculateConstraintProperties(inDeltaTime, *mBody1, *mBody2, mWorldSpaceMotorAxis[0], -mTargetAngularVelocity[0]);
  260. break;
  261. case EMotorState::Position:
  262. // Use motor to drive rotation error to zero
  263. mMotorConstraintPart[0].CalculateConstraintProperties(inDeltaTime, *mBody1, *mBody2, mWorldSpaceMotorAxis[0], 0.0f, rotation_error[0], mTwistMotorSettings.mFrequency, mTwistMotorSettings.mDamping);
  264. break;
  265. }
  266. }
  267. else
  268. {
  269. // Disable rotation motor
  270. for (AngleConstraintPart &c : mMotorConstraintPart)
  271. c.Deactivate();
  272. }
  273. }
  274. void SwingTwistConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio)
  275. {
  276. // Warm starting: Apply previous frame impulse
  277. for (AngleConstraintPart &c : mMotorConstraintPart)
  278. c.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio);
  279. mSwingTwistConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio);
  280. mPointConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio);
  281. }
  282. bool SwingTwistConstraint::SolveVelocityConstraint(float inDeltaTime)
  283. {
  284. bool impulse = false;
  285. // Solve twist rotation motor
  286. if (mMotorConstraintPart[0].IsActive())
  287. {
  288. // Twist limits
  289. float min_twist_limit, max_twist_limit;
  290. if (mTwistMotorState == EMotorState::Off)
  291. {
  292. max_twist_limit = inDeltaTime * mMaxFrictionTorque;
  293. min_twist_limit = -max_twist_limit;
  294. }
  295. else
  296. {
  297. min_twist_limit = inDeltaTime * mTwistMotorSettings.mMinTorqueLimit;
  298. max_twist_limit = inDeltaTime * mTwistMotorSettings.mMaxTorqueLimit;
  299. }
  300. impulse |= mMotorConstraintPart[0].SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceMotorAxis[0], min_twist_limit, max_twist_limit);
  301. }
  302. // Solve swing rotation motor
  303. if (mMotorConstraintPart[1].IsActive())
  304. {
  305. // Swing parts should turn on / off together
  306. JPH_ASSERT(mMotorConstraintPart[2].IsActive());
  307. // Swing limits
  308. float min_swing_limit, max_swing_limit;
  309. if (mSwingMotorState == EMotorState::Off)
  310. {
  311. max_swing_limit = inDeltaTime * mMaxFrictionTorque;
  312. min_swing_limit = -max_swing_limit;
  313. }
  314. else
  315. {
  316. min_swing_limit = inDeltaTime * mSwingMotorSettings.mMinTorqueLimit;
  317. max_swing_limit = inDeltaTime * mSwingMotorSettings.mMaxTorqueLimit;
  318. }
  319. for (int i = 1; i < 3; ++i)
  320. impulse |= mMotorConstraintPart[i].SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceMotorAxis[i], min_swing_limit, max_swing_limit);
  321. }
  322. else
  323. {
  324. // Swing parts should turn on / off together
  325. JPH_ASSERT(!mMotorConstraintPart[2].IsActive());
  326. }
  327. // Solve rotation limits
  328. impulse |= mSwingTwistConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2);
  329. // Solve position constraint
  330. impulse |= mPointConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2);
  331. return impulse;
  332. }
  333. bool SwingTwistConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte)
  334. {
  335. bool impulse = false;
  336. // Solve rotation violations
  337. Quat q = GetRotationInConstraintSpace();
  338. impulse |= mSwingTwistConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, q, mConstraintToBody1, mConstraintToBody2, inBaumgarte);
  339. // Solve position violations
  340. mPointConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(mBody1->GetRotation()), mLocalSpacePosition1, *mBody2, Mat44::sRotation(mBody2->GetRotation()), mLocalSpacePosition2);
  341. impulse |= mPointConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, inBaumgarte);
  342. return impulse;
  343. }
  344. #ifdef JPH_DEBUG_RENDERER
  345. void SwingTwistConstraint::DrawConstraint(DebugRenderer *inRenderer) const
  346. {
  347. // Get constraint properties in world space
  348. RMat44 transform1 = mBody1->GetCenterOfMassTransform();
  349. RVec3 position1 = transform1 * mLocalSpacePosition1;
  350. Quat rotation1 = mBody1->GetRotation() * mConstraintToBody1;
  351. Quat rotation2 = mBody2->GetRotation() * mConstraintToBody2;
  352. // Draw constraint orientation
  353. inRenderer->DrawCoordinateSystem(RMat44::sRotationTranslation(rotation1, position1), mDrawConstraintSize);
  354. // Draw current swing and twist
  355. Quat q = GetRotationInConstraintSpace();
  356. Quat q_swing, q_twist;
  357. q.GetSwingTwist(q_swing, q_twist);
  358. inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * q_twist).RotateAxisY(), Color::sWhite);
  359. inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * q_swing).RotateAxisX(), Color::sWhite);
  360. if (mSwingMotorState == EMotorState::Velocity || mTwistMotorState == EMotorState::Velocity)
  361. {
  362. // Draw target angular velocity
  363. inRenderer->DrawArrow(position1, position1 + rotation2 * mTargetAngularVelocity, Color::sRed, 0.1f);
  364. }
  365. if (mSwingMotorState == EMotorState::Position || mTwistMotorState == EMotorState::Position)
  366. {
  367. // Draw motor swing and twist
  368. Quat swing, twist;
  369. mTargetOrientation.GetSwingTwist(swing, twist);
  370. inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * twist).RotateAxisY(), Color::sYellow);
  371. inRenderer->DrawLine(position1, position1 + mDrawConstraintSize * (rotation1 * swing).RotateAxisX(), Color::sCyan);
  372. }
  373. }
  374. void SwingTwistConstraint::DrawConstraintLimits(DebugRenderer *inRenderer) const
  375. {
  376. // Get matrix that transforms from constraint space to world space
  377. RMat44 constraint_to_world = RMat44::sRotationTranslation(mBody1->GetRotation() * mConstraintToBody1, mBody1->GetCenterOfMassTransform() * mLocalSpacePosition1);
  378. // Draw limits
  379. inRenderer->DrawSwingLimits(constraint_to_world, mPlaneHalfConeAngle, mNormalHalfConeAngle, mDrawConstraintSize, Color::sGreen, DebugRenderer::ECastShadow::Off);
  380. inRenderer->DrawPie(constraint_to_world.GetTranslation(), mDrawConstraintSize, constraint_to_world.GetAxisX(), constraint_to_world.GetAxisY(), mTwistMinAngle, mTwistMaxAngle, Color::sPurple, DebugRenderer::ECastShadow::Off);
  381. }
  382. #endif // JPH_DEBUG_RENDERER
  383. void SwingTwistConstraint::SaveState(StateRecorder &inStream) const
  384. {
  385. TwoBodyConstraint::SaveState(inStream);
  386. mPointConstraintPart.SaveState(inStream);
  387. mSwingTwistConstraintPart.SaveState(inStream);
  388. for (const AngleConstraintPart &c : mMotorConstraintPart)
  389. c.SaveState(inStream);
  390. inStream.Write(mSwingMotorState);
  391. inStream.Write(mTwistMotorState);
  392. inStream.Write(mTargetAngularVelocity);
  393. inStream.Write(mTargetOrientation);
  394. }
  395. void SwingTwistConstraint::RestoreState(StateRecorder &inStream)
  396. {
  397. TwoBodyConstraint::RestoreState(inStream);
  398. mPointConstraintPart.RestoreState(inStream);
  399. mSwingTwistConstraintPart.RestoreState(inStream);
  400. for (AngleConstraintPart &c : mMotorConstraintPart)
  401. c.RestoreState(inStream);
  402. inStream.Read(mSwingMotorState);
  403. inStream.Read(mTwistMotorState);
  404. inStream.Read(mTargetAngularVelocity);
  405. inStream.Read(mTargetOrientation);
  406. }
  407. Ref<ConstraintSettings> SwingTwistConstraint::GetConstraintSettings() const
  408. {
  409. SwingTwistConstraintSettings *settings = new SwingTwistConstraintSettings;
  410. ToConstraintSettings(*settings);
  411. settings->mSpace = EConstraintSpace::LocalToBodyCOM;
  412. settings->mPosition1 = RVec3(mLocalSpacePosition1);
  413. settings->mTwistAxis1 = mConstraintToBody1.RotateAxisX();
  414. settings->mPlaneAxis1 = mConstraintToBody1.RotateAxisZ();
  415. settings->mPosition2 = RVec3(mLocalSpacePosition2);
  416. settings->mTwistAxis2 = mConstraintToBody2.RotateAxisX();
  417. settings->mPlaneAxis2 = mConstraintToBody2.RotateAxisZ();
  418. settings->mNormalHalfConeAngle = mNormalHalfConeAngle;
  419. settings->mPlaneHalfConeAngle = mPlaneHalfConeAngle;
  420. settings->mTwistMinAngle = mTwistMinAngle;
  421. settings->mTwistMaxAngle = mTwistMaxAngle;
  422. settings->mMaxFrictionTorque = mMaxFrictionTorque;
  423. settings->mSwingMotorSettings = mSwingMotorSettings;
  424. settings->mTwistMotorSettings = mTwistMotorSettings;
  425. return settings;
  426. }
  427. JPH_NAMESPACE_END