ConeConstraint.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #include <Jolt/Jolt.h>
  4. #include <Jolt/Physics/Constraints/ConeConstraint.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(ConeConstraintSettings)
  14. {
  15. JPH_ADD_BASE_CLASS(ConeConstraintSettings, TwoBodyConstraintSettings)
  16. JPH_ADD_ENUM_ATTRIBUTE(ConeConstraintSettings, mSpace)
  17. JPH_ADD_ATTRIBUTE(ConeConstraintSettings, mPoint1)
  18. JPH_ADD_ATTRIBUTE(ConeConstraintSettings, mTwistAxis1)
  19. JPH_ADD_ATTRIBUTE(ConeConstraintSettings, mPoint2)
  20. JPH_ADD_ATTRIBUTE(ConeConstraintSettings, mTwistAxis2)
  21. JPH_ADD_ATTRIBUTE(ConeConstraintSettings, mHalfConeAngle)
  22. }
  23. void ConeConstraintSettings::SaveBinaryState(StreamOut &inStream) const
  24. {
  25. ConstraintSettings::SaveBinaryState(inStream);
  26. inStream.Write(mSpace);
  27. inStream.Write(mPoint1);
  28. inStream.Write(mTwistAxis1);
  29. inStream.Write(mPoint2);
  30. inStream.Write(mTwistAxis2);
  31. inStream.Write(mHalfConeAngle);
  32. }
  33. void ConeConstraintSettings::RestoreBinaryState(StreamIn &inStream)
  34. {
  35. ConstraintSettings::RestoreBinaryState(inStream);
  36. inStream.Read(mSpace);
  37. inStream.Read(mPoint1);
  38. inStream.Read(mTwistAxis1);
  39. inStream.Read(mPoint2);
  40. inStream.Read(mTwistAxis2);
  41. inStream.Read(mHalfConeAngle);
  42. }
  43. TwoBodyConstraint *ConeConstraintSettings::Create(Body &inBody1, Body &inBody2) const
  44. {
  45. return new ConeConstraint(inBody1, inBody2, *this);
  46. }
  47. ConeConstraint::ConeConstraint(Body &inBody1, Body &inBody2, const ConeConstraintSettings &inSettings) :
  48. TwoBodyConstraint(inBody1, inBody2, inSettings),
  49. mLocalSpacePosition1(inSettings.mPoint1),
  50. mLocalSpacePosition2(inSettings.mPoint2),
  51. mLocalSpaceTwistAxis1(inSettings.mTwistAxis1),
  52. mLocalSpaceTwistAxis2(inSettings.mTwistAxis2)
  53. {
  54. // Store limits
  55. SetHalfConeAngle(inSettings.mHalfConeAngle);
  56. // Initialize rotation axis to perpendicular of twist axis in case the angle between the twist axis is 0 in the first frame
  57. mWorldSpaceRotationAxis = inSettings.mTwistAxis1.GetNormalizedPerpendicular();
  58. if (inSettings.mSpace == EConstraintSpace::WorldSpace)
  59. {
  60. // If all properties were specified in world space, take them to local space now
  61. Mat44 inv_transform1 = inBody1.GetInverseCenterOfMassTransform();
  62. mLocalSpacePosition1 = inv_transform1 * mLocalSpacePosition1;
  63. mLocalSpaceTwistAxis1 = inv_transform1.Multiply3x3(mLocalSpaceTwistAxis1);
  64. Mat44 inv_transform2 = inBody2.GetInverseCenterOfMassTransform();
  65. mLocalSpacePosition2 = inv_transform2 * mLocalSpacePosition2;
  66. mLocalSpaceTwistAxis2 = inv_transform2.Multiply3x3(mLocalSpaceTwistAxis2);
  67. }
  68. else
  69. {
  70. // If they were in local space, we need to take the initial rotation axis to world space
  71. mWorldSpaceRotationAxis = inBody1.GetRotation() * mWorldSpaceRotationAxis;
  72. }
  73. }
  74. void ConeConstraint::CalculateRotationConstraintProperties(float inDeltaTime, Mat44Arg inRotation1, Mat44Arg inRotation2)
  75. {
  76. // Rotation is along the cross product of both twist axis
  77. Vec3 twist1 = inRotation1.Multiply3x3(mLocalSpaceTwistAxis1);
  78. Vec3 twist2 = inRotation2.Multiply3x3(mLocalSpaceTwistAxis2);
  79. // Calculate dot product between twist axis, if it's smaller than the cone angle we need to correct
  80. mCosTheta = twist1.Dot(twist2);
  81. if (mCosTheta < mCosHalfConeAngle)
  82. {
  83. // Rotation axis is defined by the two twist axis
  84. Vec3 rot_axis = twist2.Cross(twist1);
  85. // If we can't find a rotation axis because the twist is too small, we'll use last frame's rotation axis
  86. float len = rot_axis.Length();
  87. if (len > 0.0f)
  88. mWorldSpaceRotationAxis = rot_axis / len;
  89. mAngleConstraintPart.CalculateConstraintProperties(inDeltaTime, *mBody1, *mBody2, mWorldSpaceRotationAxis);
  90. }
  91. else
  92. mAngleConstraintPart.Deactivate();
  93. }
  94. void ConeConstraint::SetupVelocityConstraint(float inDeltaTime)
  95. {
  96. Mat44 rotation1 = Mat44::sRotation(mBody1->GetRotation());
  97. Mat44 rotation2 = Mat44::sRotation(mBody2->GetRotation());
  98. mPointConstraintPart.CalculateConstraintProperties(*mBody1, rotation1, mLocalSpacePosition1, *mBody2, rotation2, mLocalSpacePosition2);
  99. CalculateRotationConstraintProperties(inDeltaTime, rotation1, rotation2);
  100. }
  101. void ConeConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio)
  102. {
  103. // Warm starting: Apply previous frame impulse
  104. mPointConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio);
  105. mAngleConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio);
  106. }
  107. bool ConeConstraint::SolveVelocityConstraint(float inDeltaTime)
  108. {
  109. bool pos = mPointConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2);
  110. bool rot = false;
  111. if (mAngleConstraintPart.IsActive())
  112. rot = mAngleConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceRotationAxis, 0, FLT_MAX);
  113. return pos || rot;
  114. }
  115. bool ConeConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte)
  116. {
  117. mPointConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(mBody1->GetRotation()), mLocalSpacePosition1, *mBody2, Mat44::sRotation(mBody2->GetRotation()), mLocalSpacePosition2);
  118. bool pos = mPointConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, inBaumgarte);
  119. bool rot = false;
  120. CalculateRotationConstraintProperties(inDeltaTime, Mat44::sRotation(mBody1->GetRotation()), Mat44::sRotation(mBody2->GetRotation()));
  121. if (mAngleConstraintPart.IsActive())
  122. rot = mAngleConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mCosTheta - mCosHalfConeAngle, inBaumgarte);
  123. return pos || rot;
  124. }
  125. #ifdef JPH_DEBUG_RENDERER
  126. void ConeConstraint::DrawConstraint(DebugRenderer *inRenderer) const
  127. {
  128. Mat44 transform1 = mBody1->GetCenterOfMassTransform();
  129. Mat44 transform2 = mBody2->GetCenterOfMassTransform();
  130. Vec3 p1 = transform1 * mLocalSpacePosition1;
  131. Vec3 p2 = transform2 * mLocalSpacePosition2;
  132. // Draw constraint
  133. inRenderer->DrawMarker(p1, Color::sRed, 0.1f);
  134. inRenderer->DrawMarker(p2, Color::sGreen, 0.1f);
  135. // Draw twist axis
  136. inRenderer->DrawLine(p1, p1 + mDrawConstraintSize * transform1.Multiply3x3(mLocalSpaceTwistAxis1), Color::sRed);
  137. inRenderer->DrawLine(p2, p2 + mDrawConstraintSize * transform2.Multiply3x3(mLocalSpaceTwistAxis2), Color::sGreen);
  138. }
  139. void ConeConstraint::DrawConstraintLimits(DebugRenderer *inRenderer) const
  140. {
  141. // Get constraint properties in world space
  142. Mat44 transform1 = mBody1->GetCenterOfMassTransform();
  143. Vec3 position1 = transform1 * mLocalSpacePosition1;
  144. Vec3 twist_axis1 = transform1.Multiply3x3(mLocalSpaceTwistAxis1);
  145. Vec3 normal_axis1 = transform1.Multiply3x3(mLocalSpaceTwistAxis1.GetNormalizedPerpendicular());
  146. inRenderer->DrawOpenCone(position1, twist_axis1, normal_axis1, ACos(mCosHalfConeAngle), mDrawConstraintSize * mCosHalfConeAngle, Color::sPurple, DebugRenderer::ECastShadow::Off);
  147. }
  148. #endif // JPH_DEBUG_RENDERER
  149. void ConeConstraint::SaveState(StateRecorder &inStream) const
  150. {
  151. TwoBodyConstraint::SaveState(inStream);
  152. mPointConstraintPart.SaveState(inStream);
  153. mAngleConstraintPart.SaveState(inStream);
  154. inStream.Write(mWorldSpaceRotationAxis); // When twist is too small, the rotation is used from last frame so we need to store it
  155. }
  156. void ConeConstraint::RestoreState(StateRecorder &inStream)
  157. {
  158. TwoBodyConstraint::RestoreState(inStream);
  159. mPointConstraintPart.RestoreState(inStream);
  160. mAngleConstraintPart.RestoreState(inStream);
  161. inStream.Read(mWorldSpaceRotationAxis);
  162. }
  163. Ref<ConstraintSettings> ConeConstraint::GetConstraintSettings() const
  164. {
  165. ConeConstraintSettings *settings = new ConeConstraintSettings;
  166. ToConstraintSettings(*settings);
  167. settings->mSpace = EConstraintSpace::LocalToBodyCOM;
  168. settings->mPoint1 = mLocalSpacePosition1;
  169. settings->mTwistAxis1 = mLocalSpaceTwistAxis1;
  170. settings->mPoint2 = mLocalSpacePosition2;
  171. settings->mTwistAxis2 = mLocalSpaceTwistAxis2;
  172. settings->mHalfConeAngle = ACos(mCosHalfConeAngle);
  173. return settings;
  174. }
  175. Mat44 ConeConstraint::GetConstraintToBody1Matrix() const
  176. {
  177. Vec3 perp = mLocalSpaceTwistAxis1.GetNormalizedPerpendicular();
  178. Vec3 perp2 = mLocalSpaceTwistAxis1.Cross(perp);
  179. return Mat44(Vec4(mLocalSpaceTwistAxis1, 0), Vec4(perp, 0), Vec4(perp2, 0), Vec4(mLocalSpacePosition1, 1));
  180. }
  181. Mat44 ConeConstraint::GetConstraintToBody2Matrix() const
  182. {
  183. // Note: Incorrect in rotation around the twist axis (the perpendicular does not match that of body 1),
  184. // this should not matter as we're not limiting rotation around the twist axis.
  185. Vec3 perp = mLocalSpaceTwistAxis2.GetNormalizedPerpendicular();
  186. Vec3 perp2 = mLocalSpaceTwistAxis2.Cross(perp);
  187. return Mat44(Vec4(mLocalSpaceTwistAxis2, 0), Vec4(perp, 0), Vec4(perp2, 0), Vec4(mLocalSpacePosition2, 1));
  188. }
  189. JPH_NAMESPACE_END