ConeConstraint.cpp 7.5 KB

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