ConeConstraint.cpp 8.9 KB

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