DistanceConstraint.cpp 7.1 KB

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