DistanceConstraint.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #include <Jolt/Jolt.h>
  4. #include <Jolt/Physics/Constraints/DistanceConstraint.h>
  5. #include <Jolt/Physics/Body/Body.h>
  6. #include <Jolt/ObjectStream/TypeDeclarations.h>
  7. #include <Jolt/Core/StreamIn.h>
  8. #include <Jolt/Core/StreamOut.h>
  9. #ifdef JPH_DEBUG_RENDERER
  10. #include <Jolt/Renderer/DebugRenderer.h>
  11. #endif // JPH_DEBUG_RENDERER
  12. JPH_NAMESPACE_BEGIN
  13. JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(DistanceConstraintSettings)
  14. {
  15. JPH_ADD_BASE_CLASS(DistanceConstraintSettings, TwoBodyConstraintSettings)
  16. JPH_ADD_ENUM_ATTRIBUTE(DistanceConstraintSettings, mSpace)
  17. JPH_ADD_ATTRIBUTE(DistanceConstraintSettings, mPoint1)
  18. JPH_ADD_ATTRIBUTE(DistanceConstraintSettings, mPoint2)
  19. JPH_ADD_ATTRIBUTE(DistanceConstraintSettings, mMinDistance)
  20. JPH_ADD_ATTRIBUTE(DistanceConstraintSettings, mMaxDistance)
  21. JPH_ADD_ATTRIBUTE(DistanceConstraintSettings, mFrequency)
  22. JPH_ADD_ATTRIBUTE(DistanceConstraintSettings, mDamping)
  23. }
  24. void DistanceConstraintSettings::SaveBinaryState(StreamOut &inStream) const
  25. {
  26. ConstraintSettings::SaveBinaryState(inStream);
  27. inStream.Write(mSpace);
  28. inStream.Write(mPoint1);
  29. inStream.Write(mPoint2);
  30. inStream.Write(mMinDistance);
  31. inStream.Write(mMaxDistance);
  32. inStream.Write(mFrequency);
  33. inStream.Write(mDamping);
  34. }
  35. void DistanceConstraintSettings::RestoreBinaryState(StreamIn &inStream)
  36. {
  37. ConstraintSettings::RestoreBinaryState(inStream);
  38. inStream.Read(mSpace);
  39. inStream.Read(mPoint1);
  40. inStream.Read(mPoint2);
  41. inStream.Read(mMinDistance);
  42. inStream.Read(mMaxDistance);
  43. inStream.Read(mFrequency);
  44. inStream.Read(mDamping);
  45. }
  46. TwoBodyConstraint *DistanceConstraintSettings::Create(Body &inBody1, Body &inBody2) const
  47. {
  48. return new DistanceConstraint(inBody1, inBody2, *this);
  49. }
  50. DistanceConstraint::DistanceConstraint(Body &inBody1, Body &inBody2, const DistanceConstraintSettings &inSettings) :
  51. TwoBodyConstraint(inBody1, inBody2, inSettings),
  52. mLocalSpacePosition1(inSettings.mPoint1),
  53. mLocalSpacePosition2(inSettings.mPoint2),
  54. mMinDistance(inSettings.mMinDistance),
  55. mMaxDistance(inSettings.mMaxDistance),
  56. mWorldSpacePosition1(inSettings.mPoint1),
  57. mWorldSpacePosition2(inSettings.mPoint2)
  58. {
  59. if (inSettings.mSpace == EConstraintSpace::WorldSpace)
  60. {
  61. // If all properties were specified in world space, take them to local space now
  62. mLocalSpacePosition1 = inBody1.GetInverseCenterOfMassTransform() * mLocalSpacePosition1;
  63. mLocalSpacePosition2 = inBody2.GetInverseCenterOfMassTransform() * mLocalSpacePosition2;
  64. }
  65. else
  66. {
  67. // If properties were specified in local space, we need to calculate world space positions
  68. mWorldSpacePosition1 = inBody1.GetCenterOfMassTransform() * mWorldSpacePosition1;
  69. mWorldSpacePosition2 = inBody2.GetCenterOfMassTransform() * mWorldSpacePosition2;
  70. }
  71. // Store distance we want to keep between the world space points
  72. float distance = (mWorldSpacePosition2 - mWorldSpacePosition1).Length();
  73. SetDistance(mMinDistance < 0.0f? distance : mMinDistance, mMaxDistance < 0.0f? distance : mMaxDistance);
  74. // Most likely gravity is going to tear us apart (this is only used when the distance between the points = 0)
  75. mWorldSpaceNormal = Vec3::sAxisY();
  76. // Store frequency and damping
  77. SetFrequency(inSettings.mFrequency);
  78. SetDamping(inSettings.mDamping);
  79. }
  80. void DistanceConstraint::CalculateConstraintProperties(float inDeltaTime)
  81. {
  82. // Update world space positions (the bodies may have moved)
  83. mWorldSpacePosition1 = mBody1->GetCenterOfMassTransform() * mLocalSpacePosition1;
  84. mWorldSpacePosition2 = mBody2->GetCenterOfMassTransform() * mLocalSpacePosition2;
  85. // Calculate world space normal
  86. Vec3 delta = mWorldSpacePosition2 - mWorldSpacePosition1;
  87. float delta_len = delta.Length();
  88. if (delta_len > 0.0f)
  89. mWorldSpaceNormal = delta / delta_len;
  90. // Calculate points relative to body
  91. // r1 + u = (p1 - x1) + (p2 - p1) = p2 - x1
  92. Vec3 r1_plus_u = mWorldSpacePosition2 - mBody1->GetCenterOfMassPosition();
  93. Vec3 r2 = mWorldSpacePosition2 - mBody2->GetCenterOfMassPosition();
  94. if (mMinDistance == mMaxDistance)
  95. {
  96. mAxisConstraint.CalculateConstraintProperties(inDeltaTime, *mBody1, r1_plus_u, *mBody2, r2, mWorldSpaceNormal, 0.0f, delta_len - mMinDistance, mFrequency, mDamping);
  97. // Single distance, allow constraint forces in both directions
  98. mMinLambda = -FLT_MAX;
  99. mMaxLambda = FLT_MAX;
  100. }
  101. if (delta_len <= mMinDistance)
  102. {
  103. mAxisConstraint.CalculateConstraintProperties(inDeltaTime, *mBody1, r1_plus_u, *mBody2, r2, mWorldSpaceNormal, 0.0f, delta_len - mMinDistance, mFrequency, mDamping);
  104. // Allow constraint forces to make distance bigger only
  105. mMinLambda = 0;
  106. mMaxLambda = FLT_MAX;
  107. }
  108. else if (delta_len >= mMaxDistance)
  109. {
  110. mAxisConstraint.CalculateConstraintProperties(inDeltaTime, *mBody1, r1_plus_u, *mBody2, r2, mWorldSpaceNormal, 0.0f, delta_len - mMaxDistance, mFrequency, mDamping);
  111. // Allow constraint forces to make distance smaller only
  112. mMinLambda = -FLT_MAX;
  113. mMaxLambda = 0;
  114. }
  115. else
  116. mAxisConstraint.Deactivate();
  117. }
  118. void DistanceConstraint::SetupVelocityConstraint(float inDeltaTime)
  119. {
  120. CalculateConstraintProperties(inDeltaTime);
  121. }
  122. void DistanceConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio)
  123. {
  124. mAxisConstraint.WarmStart(*mBody1, *mBody2, mWorldSpaceNormal, inWarmStartImpulseRatio);
  125. }
  126. bool DistanceConstraint::SolveVelocityConstraint(float inDeltaTime)
  127. {
  128. if (mAxisConstraint.IsActive())
  129. return mAxisConstraint.SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceNormal, mMinLambda, mMaxLambda);
  130. else
  131. return false;
  132. }
  133. bool DistanceConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte)
  134. {
  135. float distance = (mWorldSpacePosition2 - mWorldSpacePosition1).Dot(mWorldSpaceNormal);
  136. // Calculate position error
  137. float position_error = 0.0f;
  138. if (distance < mMinDistance)
  139. position_error = distance - mMinDistance;
  140. else if (distance > mMaxDistance)
  141. position_error = distance - mMaxDistance;
  142. if (position_error != 0.0f)
  143. {
  144. // Update constraint properties (bodies may have moved)
  145. CalculateConstraintProperties(inDeltaTime);
  146. return mAxisConstraint.SolvePositionConstraint(*mBody1, *mBody2, mWorldSpaceNormal, position_error, inBaumgarte);
  147. }
  148. return false;
  149. }
  150. #ifdef JPH_DEBUG_RENDERER
  151. void DistanceConstraint::DrawConstraint(DebugRenderer *inRenderer) const
  152. {
  153. // Draw constraint
  154. Vec3 delta = mWorldSpacePosition2 - mWorldSpacePosition1;
  155. float len = delta.Length();
  156. if (len < mMinDistance)
  157. {
  158. Vec3 real_end_pos = mWorldSpacePosition1 + (len > 0.0f? delta * mMinDistance / len : Vec3(0, len, 0));
  159. inRenderer->DrawLine(mWorldSpacePosition1, mWorldSpacePosition2, Color::sGreen);
  160. inRenderer->DrawLine(mWorldSpacePosition2, real_end_pos, Color::sYellow);
  161. }
  162. else if (len > mMaxDistance)
  163. {
  164. Vec3 real_end_pos = mWorldSpacePosition1 + (len > 0.0f? delta * mMaxDistance / len : Vec3(0, len, 0));
  165. inRenderer->DrawLine(mWorldSpacePosition1, real_end_pos, Color::sGreen);
  166. inRenderer->DrawLine(real_end_pos, mWorldSpacePosition2, Color::sRed);
  167. }
  168. else
  169. inRenderer->DrawLine(mWorldSpacePosition1, mWorldSpacePosition2, Color::sGreen);
  170. // Draw constraint end points
  171. inRenderer->DrawMarker(mWorldSpacePosition1, Color::sWhite, 0.1f);
  172. inRenderer->DrawMarker(mWorldSpacePosition2, Color::sWhite, 0.1f);
  173. // Draw current length
  174. inRenderer->DrawText3D(0.5f * (mWorldSpacePosition1 + mWorldSpacePosition2), StringFormat("%.2f", (double)len));
  175. }
  176. #endif // JPH_DEBUG_RENDERER
  177. void DistanceConstraint::SaveState(StateRecorder &inStream) const
  178. {
  179. TwoBodyConstraint::SaveState(inStream);
  180. mAxisConstraint.SaveState(inStream);
  181. inStream.Write(mWorldSpaceNormal); // When distance = 0, the normal is used from last frame so we need to store it
  182. }
  183. void DistanceConstraint::RestoreState(StateRecorder &inStream)
  184. {
  185. TwoBodyConstraint::RestoreState(inStream);
  186. mAxisConstraint.RestoreState(inStream);
  187. inStream.Read(mWorldSpaceNormal);
  188. }
  189. Ref<ConstraintSettings> DistanceConstraint::GetConstraintSettings() const
  190. {
  191. DistanceConstraintSettings *settings = new DistanceConstraintSettings;
  192. ToConstraintSettings(*settings);
  193. settings->mSpace = EConstraintSpace::LocalToBodyCOM;
  194. settings->mPoint1 = mLocalSpacePosition1;
  195. settings->mPoint2 = mLocalSpacePosition2;
  196. settings->mMinDistance = mMinDistance;
  197. settings->mMaxDistance = mMaxDistance;
  198. settings->mFrequency = mFrequency;
  199. settings->mDamping = mDamping;
  200. return settings;
  201. }
  202. JPH_NAMESPACE_END