DistanceConstraint.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  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/DistanceConstraint.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(DistanceConstraintSettings)
  16. {
  17. JPH_ADD_BASE_CLASS(DistanceConstraintSettings, TwoBodyConstraintSettings)
  18. JPH_ADD_ENUM_ATTRIBUTE(DistanceConstraintSettings, mSpace)
  19. JPH_ADD_ATTRIBUTE(DistanceConstraintSettings, mPoint1)
  20. JPH_ADD_ATTRIBUTE(DistanceConstraintSettings, mPoint2)
  21. JPH_ADD_ATTRIBUTE(DistanceConstraintSettings, mMinDistance)
  22. JPH_ADD_ATTRIBUTE(DistanceConstraintSettings, mMaxDistance)
  23. JPH_ADD_ENUM_ATTRIBUTE_WITH_ALIAS(DistanceConstraintSettings, mLimitsSpringSettings.mMode, "mSpringMode")
  24. JPH_ADD_ATTRIBUTE_WITH_ALIAS(DistanceConstraintSettings, mLimitsSpringSettings.mFrequency, "mFrequency") // Renaming attributes to stay compatible with old versions of the library
  25. JPH_ADD_ATTRIBUTE_WITH_ALIAS(DistanceConstraintSettings, mLimitsSpringSettings.mDamping, "mDamping")
  26. }
  27. void DistanceConstraintSettings::SaveBinaryState(StreamOut &inStream) const
  28. {
  29. ConstraintSettings::SaveBinaryState(inStream);
  30. inStream.Write(mSpace);
  31. inStream.Write(mPoint1);
  32. inStream.Write(mPoint2);
  33. inStream.Write(mMinDistance);
  34. inStream.Write(mMaxDistance);
  35. mLimitsSpringSettings.SaveBinaryState(inStream);
  36. }
  37. void DistanceConstraintSettings::RestoreBinaryState(StreamIn &inStream)
  38. {
  39. ConstraintSettings::RestoreBinaryState(inStream);
  40. inStream.Read(mSpace);
  41. inStream.Read(mPoint1);
  42. inStream.Read(mPoint2);
  43. inStream.Read(mMinDistance);
  44. inStream.Read(mMaxDistance);
  45. mLimitsSpringSettings.RestoreBinaryState(inStream);
  46. }
  47. TwoBodyConstraint *DistanceConstraintSettings::Create(Body &inBody1, Body &inBody2) const
  48. {
  49. return new DistanceConstraint(inBody1, inBody2, *this);
  50. }
  51. DistanceConstraint::DistanceConstraint(Body &inBody1, Body &inBody2, const DistanceConstraintSettings &inSettings) :
  52. TwoBodyConstraint(inBody1, inBody2, inSettings),
  53. mMinDistance(inSettings.mMinDistance),
  54. mMaxDistance(inSettings.mMaxDistance)
  55. {
  56. if (inSettings.mSpace == EConstraintSpace::WorldSpace)
  57. {
  58. // If all properties were specified in world space, take them to local space now
  59. mLocalSpacePosition1 = Vec3(inBody1.GetInverseCenterOfMassTransform() * inSettings.mPoint1);
  60. mLocalSpacePosition2 = Vec3(inBody2.GetInverseCenterOfMassTransform() * inSettings.mPoint2);
  61. mWorldSpacePosition1 = inSettings.mPoint1;
  62. mWorldSpacePosition2 = inSettings.mPoint2;
  63. }
  64. else
  65. {
  66. // If properties were specified in local space, we need to calculate world space positions
  67. mLocalSpacePosition1 = Vec3(inSettings.mPoint1);
  68. mLocalSpacePosition2 = Vec3(inSettings.mPoint2);
  69. mWorldSpacePosition1 = inBody1.GetCenterOfMassTransform() * inSettings.mPoint1;
  70. mWorldSpacePosition2 = inBody2.GetCenterOfMassTransform() * inSettings.mPoint2;
  71. }
  72. // Store distance we want to keep between the world space points
  73. float distance = Vec3(mWorldSpacePosition2 - mWorldSpacePosition1).Length();
  74. float min_distance, max_distance;
  75. if (mMinDistance < 0.0f && mMaxDistance < 0.0f)
  76. {
  77. min_distance = max_distance = distance;
  78. }
  79. else
  80. {
  81. min_distance = mMinDistance < 0.0f? min(distance, mMaxDistance) : mMinDistance;
  82. max_distance = mMaxDistance < 0.0f? max(distance, mMinDistance) : mMaxDistance;
  83. }
  84. SetDistance(min_distance, max_distance);
  85. // Most likely gravity is going to tear us apart (this is only used when the distance between the points = 0)
  86. mWorldSpaceNormal = Vec3::sAxisY();
  87. // Store spring settings
  88. SetLimitsSpringSettings(inSettings.mLimitsSpringSettings);
  89. }
  90. void DistanceConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM)
  91. {
  92. if (mBody1->GetID() == inBodyID)
  93. mLocalSpacePosition1 -= inDeltaCOM;
  94. else if (mBody2->GetID() == inBodyID)
  95. mLocalSpacePosition2 -= inDeltaCOM;
  96. }
  97. void DistanceConstraint::CalculateConstraintProperties(float inDeltaTime)
  98. {
  99. // Update world space positions (the bodies may have moved)
  100. mWorldSpacePosition1 = mBody1->GetCenterOfMassTransform() * mLocalSpacePosition1;
  101. mWorldSpacePosition2 = mBody2->GetCenterOfMassTransform() * mLocalSpacePosition2;
  102. // Calculate world space normal
  103. Vec3 delta = Vec3(mWorldSpacePosition2 - mWorldSpacePosition1);
  104. float delta_len = delta.Length();
  105. if (delta_len > 0.0f)
  106. mWorldSpaceNormal = delta / delta_len;
  107. // Calculate points relative to body
  108. // r1 + u = (p1 - x1) + (p2 - p1) = p2 - x1
  109. Vec3 r1_plus_u = Vec3(mWorldSpacePosition2 - mBody1->GetCenterOfMassPosition());
  110. Vec3 r2 = Vec3(mWorldSpacePosition2 - mBody2->GetCenterOfMassPosition());
  111. if (mMinDistance == mMaxDistance)
  112. {
  113. mAxisConstraint.CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, r1_plus_u, *mBody2, r2, mWorldSpaceNormal, 0.0f, delta_len - mMinDistance, mLimitsSpringSettings);
  114. // Single distance, allow constraint forces in both directions
  115. mMinLambda = -FLT_MAX;
  116. mMaxLambda = FLT_MAX;
  117. }
  118. else if (delta_len <= mMinDistance)
  119. {
  120. mAxisConstraint.CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, r1_plus_u, *mBody2, r2, mWorldSpaceNormal, 0.0f, delta_len - mMinDistance, mLimitsSpringSettings);
  121. // Allow constraint forces to make distance bigger only
  122. mMinLambda = 0;
  123. mMaxLambda = FLT_MAX;
  124. }
  125. else if (delta_len >= mMaxDistance)
  126. {
  127. mAxisConstraint.CalculateConstraintPropertiesWithSettings(inDeltaTime, *mBody1, r1_plus_u, *mBody2, r2, mWorldSpaceNormal, 0.0f, delta_len - mMaxDistance, mLimitsSpringSettings);
  128. // Allow constraint forces to make distance smaller only
  129. mMinLambda = -FLT_MAX;
  130. mMaxLambda = 0;
  131. }
  132. else
  133. mAxisConstraint.Deactivate();
  134. }
  135. void DistanceConstraint::SetupVelocityConstraint(float inDeltaTime)
  136. {
  137. CalculateConstraintProperties(inDeltaTime);
  138. }
  139. void DistanceConstraint::ResetWarmStart()
  140. {
  141. mAxisConstraint.Deactivate();
  142. }
  143. void DistanceConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio)
  144. {
  145. mAxisConstraint.WarmStart(*mBody1, *mBody2, mWorldSpaceNormal, inWarmStartImpulseRatio);
  146. }
  147. bool DistanceConstraint::SolveVelocityConstraint(float inDeltaTime)
  148. {
  149. if (mAxisConstraint.IsActive())
  150. return mAxisConstraint.SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceNormal, mMinLambda, mMaxLambda);
  151. else
  152. return false;
  153. }
  154. bool DistanceConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte)
  155. {
  156. if (mLimitsSpringSettings.mFrequency <= 0.0f) // When the spring is active, we don't need to solve the position constraint
  157. {
  158. float distance = Vec3(mWorldSpacePosition2 - mWorldSpacePosition1).Dot(mWorldSpaceNormal);
  159. // Calculate position error
  160. float position_error = 0.0f;
  161. if (distance < mMinDistance)
  162. position_error = distance - mMinDistance;
  163. else if (distance > mMaxDistance)
  164. position_error = distance - mMaxDistance;
  165. if (position_error != 0.0f)
  166. {
  167. // Update constraint properties (bodies may have moved)
  168. CalculateConstraintProperties(inDeltaTime);
  169. return mAxisConstraint.SolvePositionConstraint(*mBody1, *mBody2, mWorldSpaceNormal, position_error, inBaumgarte);
  170. }
  171. }
  172. return false;
  173. }
  174. #ifdef JPH_DEBUG_RENDERER
  175. void DistanceConstraint::DrawConstraint(DebugRenderer *inRenderer) const
  176. {
  177. // Draw constraint
  178. Vec3 delta = Vec3(mWorldSpacePosition2 - mWorldSpacePosition1);
  179. float len = delta.Length();
  180. if (len < mMinDistance)
  181. {
  182. RVec3 real_end_pos = mWorldSpacePosition1 + (len > 0.0f? delta * mMinDistance / len : Vec3(0, len, 0));
  183. inRenderer->DrawLine(mWorldSpacePosition1, mWorldSpacePosition2, Color::sGreen);
  184. inRenderer->DrawLine(mWorldSpacePosition2, real_end_pos, Color::sYellow);
  185. }
  186. else if (len > mMaxDistance)
  187. {
  188. RVec3 real_end_pos = mWorldSpacePosition1 + (len > 0.0f? delta * mMaxDistance / len : Vec3(0, len, 0));
  189. inRenderer->DrawLine(mWorldSpacePosition1, real_end_pos, Color::sGreen);
  190. inRenderer->DrawLine(real_end_pos, mWorldSpacePosition2, Color::sRed);
  191. }
  192. else
  193. inRenderer->DrawLine(mWorldSpacePosition1, mWorldSpacePosition2, Color::sGreen);
  194. // Draw constraint end points
  195. inRenderer->DrawMarker(mWorldSpacePosition1, Color::sWhite, 0.1f);
  196. inRenderer->DrawMarker(mWorldSpacePosition2, Color::sWhite, 0.1f);
  197. // Draw current length
  198. inRenderer->DrawText3D(0.5_r * (mWorldSpacePosition1 + mWorldSpacePosition2), StringFormat("%.2f", (double)len));
  199. }
  200. #endif // JPH_DEBUG_RENDERER
  201. void DistanceConstraint::SaveState(StateRecorder &inStream) const
  202. {
  203. TwoBodyConstraint::SaveState(inStream);
  204. mAxisConstraint.SaveState(inStream);
  205. inStream.Write(mWorldSpaceNormal); // When distance = 0, the normal is used from last frame so we need to store it
  206. }
  207. void DistanceConstraint::RestoreState(StateRecorder &inStream)
  208. {
  209. TwoBodyConstraint::RestoreState(inStream);
  210. mAxisConstraint.RestoreState(inStream);
  211. inStream.Read(mWorldSpaceNormal);
  212. }
  213. Ref<ConstraintSettings> DistanceConstraint::GetConstraintSettings() const
  214. {
  215. DistanceConstraintSettings *settings = new DistanceConstraintSettings;
  216. ToConstraintSettings(*settings);
  217. settings->mSpace = EConstraintSpace::LocalToBodyCOM;
  218. settings->mPoint1 = RVec3(mLocalSpacePosition1);
  219. settings->mPoint2 = RVec3(mLocalSpacePosition2);
  220. settings->mMinDistance = mMinDistance;
  221. settings->mMaxDistance = mMaxDistance;
  222. settings->mLimitsSpringSettings = mLimitsSpringSettings;
  223. return settings;
  224. }
  225. JPH_NAMESPACE_END