PulleyConstraint.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2022 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #include <Jolt/Jolt.h>
  5. #include <Jolt/Physics/Constraints/PulleyConstraint.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. using namespace literals;
  15. JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(PulleyConstraintSettings)
  16. {
  17. JPH_ADD_BASE_CLASS(PulleyConstraintSettings, TwoBodyConstraintSettings)
  18. JPH_ADD_ENUM_ATTRIBUTE(PulleyConstraintSettings, mSpace)
  19. JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mBodyPoint1)
  20. JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mFixedPoint1)
  21. JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mBodyPoint2)
  22. JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mFixedPoint2)
  23. JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mRatio)
  24. JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mMinLength)
  25. JPH_ADD_ATTRIBUTE(PulleyConstraintSettings, mMaxLength)
  26. }
  27. void PulleyConstraintSettings::SaveBinaryState(StreamOut &inStream) const
  28. {
  29. ConstraintSettings::SaveBinaryState(inStream);
  30. inStream.Write(mSpace);
  31. inStream.Write(mBodyPoint1);
  32. inStream.Write(mFixedPoint1);
  33. inStream.Write(mBodyPoint2);
  34. inStream.Write(mFixedPoint2);
  35. inStream.Write(mRatio);
  36. inStream.Write(mMinLength);
  37. inStream.Write(mMaxLength);
  38. }
  39. void PulleyConstraintSettings::RestoreBinaryState(StreamIn &inStream)
  40. {
  41. ConstraintSettings::RestoreBinaryState(inStream);
  42. inStream.Read(mSpace);
  43. inStream.Read(mBodyPoint1);
  44. inStream.Read(mFixedPoint1);
  45. inStream.Read(mBodyPoint2);
  46. inStream.Read(mFixedPoint2);
  47. inStream.Read(mRatio);
  48. inStream.Read(mMinLength);
  49. inStream.Read(mMaxLength);
  50. }
  51. TwoBodyConstraint *PulleyConstraintSettings::Create(Body &inBody1, Body &inBody2) const
  52. {
  53. return new PulleyConstraint(inBody1, inBody2, *this);
  54. }
  55. PulleyConstraint::PulleyConstraint(Body &inBody1, Body &inBody2, const PulleyConstraintSettings &inSettings) :
  56. TwoBodyConstraint(inBody1, inBody2, inSettings),
  57. mFixedPosition1(inSettings.mFixedPoint1),
  58. mFixedPosition2(inSettings.mFixedPoint2),
  59. mRatio(inSettings.mRatio),
  60. mMinLength(inSettings.mMinLength),
  61. mMaxLength(inSettings.mMaxLength)
  62. {
  63. if (inSettings.mSpace == EConstraintSpace::WorldSpace)
  64. {
  65. // If all properties were specified in world space, take them to local space now
  66. mLocalSpacePosition1 = Vec3(inBody1.GetInverseCenterOfMassTransform() * inSettings.mBodyPoint1);
  67. mLocalSpacePosition2 = Vec3(inBody2.GetInverseCenterOfMassTransform() * inSettings.mBodyPoint2);
  68. mWorldSpacePosition1 = inSettings.mBodyPoint1;
  69. mWorldSpacePosition2 = inSettings.mBodyPoint2;
  70. }
  71. else
  72. {
  73. // If properties were specified in local space, we need to calculate world space positions
  74. mLocalSpacePosition1 = Vec3(inSettings.mBodyPoint1);
  75. mLocalSpacePosition2 = Vec3(inSettings.mBodyPoint2);
  76. mWorldSpacePosition1 = inBody1.GetCenterOfMassTransform() * inSettings.mBodyPoint1;
  77. mWorldSpacePosition2 = inBody2.GetCenterOfMassTransform() * inSettings.mBodyPoint2;
  78. }
  79. // Calculate min/max length if it was not provided
  80. float current_length = GetCurrentLength();
  81. if (mMinLength < 0.0f)
  82. mMinLength = current_length;
  83. if (mMaxLength < 0.0f)
  84. mMaxLength = current_length;
  85. // Initialize the normals to a likely valid axis in case the fixed points overlap with the attachment points (most likely the fixed points are above both bodies)
  86. mWorldSpaceNormal1 = mWorldSpaceNormal2 = -Vec3::sAxisY();
  87. }
  88. void PulleyConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM)
  89. {
  90. if (mBody1->GetID() == inBodyID)
  91. mLocalSpacePosition1 -= inDeltaCOM;
  92. else if (mBody2->GetID() == inBodyID)
  93. mLocalSpacePosition2 -= inDeltaCOM;
  94. }
  95. float PulleyConstraint::CalculatePositionsNormalsAndLength()
  96. {
  97. // Update world space positions (the bodies may have moved)
  98. mWorldSpacePosition1 = mBody1->GetCenterOfMassTransform() * mLocalSpacePosition1;
  99. mWorldSpacePosition2 = mBody2->GetCenterOfMassTransform() * mLocalSpacePosition2;
  100. // Calculate world space normals
  101. Vec3 delta1 = Vec3(mWorldSpacePosition1 - mFixedPosition1);
  102. float delta1_len = delta1.Length();
  103. if (delta1_len > 0.0f)
  104. mWorldSpaceNormal1 = delta1 / delta1_len;
  105. Vec3 delta2 = Vec3(mWorldSpacePosition2 - mFixedPosition2);
  106. float delta2_len = delta2.Length();
  107. if (delta2_len > 0.0f)
  108. mWorldSpaceNormal2 = delta2 / delta2_len;
  109. // Calculate length
  110. return delta1_len + mRatio * delta2_len;
  111. }
  112. void PulleyConstraint::CalculateConstraintProperties()
  113. {
  114. // Calculate attachment points relative to COM
  115. Vec3 r1 = Vec3(mWorldSpacePosition1 - mBody1->GetCenterOfMassPosition());
  116. Vec3 r2 = Vec3(mWorldSpacePosition2 - mBody2->GetCenterOfMassPosition());
  117. mIndependentAxisConstraintPart.CalculateConstraintProperties(*mBody1, *mBody2, r1, mWorldSpaceNormal1, r2, mWorldSpaceNormal2, mRatio);
  118. }
  119. void PulleyConstraint::SetupVelocityConstraint(float inDeltaTime)
  120. {
  121. // Determine if the constraint is active
  122. float current_length = CalculatePositionsNormalsAndLength();
  123. bool min_length_violation = current_length <= mMinLength;
  124. bool max_length_violation = current_length >= mMaxLength;
  125. if (min_length_violation || max_length_violation)
  126. {
  127. // Determine max lambda based on if the length is too big or small
  128. mMinLambda = max_length_violation? -FLT_MAX : 0.0f;
  129. mMaxLambda = min_length_violation? FLT_MAX : 0.0f;
  130. CalculateConstraintProperties();
  131. }
  132. else
  133. mIndependentAxisConstraintPart.Deactivate();
  134. }
  135. void PulleyConstraint::ResetWarmStart()
  136. {
  137. mIndependentAxisConstraintPart.Deactivate();
  138. }
  139. void PulleyConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio)
  140. {
  141. mIndependentAxisConstraintPart.WarmStart(*mBody1, *mBody2, mWorldSpaceNormal1, mWorldSpaceNormal2, mRatio, inWarmStartImpulseRatio);
  142. }
  143. bool PulleyConstraint::SolveVelocityConstraint(float inDeltaTime)
  144. {
  145. if (mIndependentAxisConstraintPart.IsActive())
  146. return mIndependentAxisConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceNormal1, mWorldSpaceNormal2, mRatio, mMinLambda, mMaxLambda);
  147. else
  148. return false;
  149. }
  150. bool PulleyConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte)
  151. {
  152. // Calculate new length (bodies may have changed)
  153. float current_length = CalculatePositionsNormalsAndLength();
  154. float position_error = 0.0f;
  155. if (current_length < mMinLength)
  156. position_error = current_length - mMinLength;
  157. else if (current_length > mMaxLength)
  158. position_error = current_length - mMaxLength;
  159. if (position_error != 0.0f)
  160. {
  161. // Update constraint properties (bodies may have moved)
  162. CalculateConstraintProperties();
  163. return mIndependentAxisConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mWorldSpaceNormal1, mWorldSpaceNormal2, mRatio, position_error, inBaumgarte);
  164. }
  165. return false;
  166. }
  167. #ifdef JPH_DEBUG_RENDERER
  168. void PulleyConstraint::DrawConstraint(DebugRenderer *inRenderer) const
  169. {
  170. // Color according to length vs min/max length
  171. float current_length = GetCurrentLength();
  172. Color color = Color::sGreen;
  173. if (current_length < mMinLength)
  174. color = Color::sYellow;
  175. else if (current_length > mMaxLength)
  176. color = Color::sRed;
  177. // Draw constraint
  178. inRenderer->DrawLine(mWorldSpacePosition1, mFixedPosition1, color);
  179. inRenderer->DrawLine(mFixedPosition1, mFixedPosition2, color);
  180. inRenderer->DrawLine(mFixedPosition2, mWorldSpacePosition2, color);
  181. // Draw current length
  182. inRenderer->DrawText3D(0.5_r * (mFixedPosition1 + mFixedPosition2), StringFormat("%.2f", (double)current_length));
  183. }
  184. #endif // JPH_DEBUG_RENDERER
  185. void PulleyConstraint::SaveState(StateRecorder &inStream) const
  186. {
  187. TwoBodyConstraint::SaveState(inStream);
  188. mIndependentAxisConstraintPart.SaveState(inStream);
  189. inStream.Write(mWorldSpaceNormal1); // When distance to fixed point = 0, the normal is used from last frame so we need to store it
  190. inStream.Write(mWorldSpaceNormal2);
  191. }
  192. void PulleyConstraint::RestoreState(StateRecorder &inStream)
  193. {
  194. TwoBodyConstraint::RestoreState(inStream);
  195. mIndependentAxisConstraintPart.RestoreState(inStream);
  196. inStream.Read(mWorldSpaceNormal1);
  197. inStream.Read(mWorldSpaceNormal2);
  198. }
  199. Ref<ConstraintSettings> PulleyConstraint::GetConstraintSettings() const
  200. {
  201. PulleyConstraintSettings *settings = new PulleyConstraintSettings;
  202. ToConstraintSettings(*settings);
  203. settings->mSpace = EConstraintSpace::LocalToBodyCOM;
  204. settings->mBodyPoint1 = RVec3(mLocalSpacePosition1);
  205. settings->mFixedPoint1 = mFixedPosition1;
  206. settings->mBodyPoint2 = RVec3(mLocalSpacePosition2);
  207. settings->mFixedPoint2 = mFixedPosition2;
  208. settings->mRatio = mRatio;
  209. settings->mMinLength = mMinLength;
  210. settings->mMaxLength = mMaxLength;
  211. return settings;
  212. }
  213. JPH_NAMESPACE_END