PoweredSwingTwistConstraintTest.cpp 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #include <TestFramework.h>
  4. #include <Tests/Constraints/PoweredSwingTwistConstraintTest.h>
  5. #include <Jolt/Physics/Collision/Shape/BoxShape.h>
  6. #include <Jolt/Physics/Collision/GroupFilterTable.h>
  7. #include <Jolt/Physics/Body/BodyCreationSettings.h>
  8. #include <Application/DebugUI.h>
  9. #include <Layers.h>
  10. JPH_IMPLEMENT_RTTI_VIRTUAL(PoweredSwingTwistConstraintTest)
  11. {
  12. JPH_ADD_BASE_CLASS(PoweredSwingTwistConstraintTest, Test)
  13. }
  14. Vec3 PoweredSwingTwistConstraintTest::sBodyRotation[] = { Vec3::sZero(), Vec3::sZero() };
  15. void PoweredSwingTwistConstraintTest::Initialize()
  16. {
  17. // Floor
  18. CreateFloor();
  19. // Create group filter
  20. Ref<GroupFilterTable> group_filter = new GroupFilterTable;
  21. float half_box_height = 1.5f;
  22. RefConst<Shape> box = new BoxShape(Vec3(0.25f, half_box_height, 0.5f));
  23. Quat body1_rotation = Quat::sEulerAngles(sBodyRotation[0]);
  24. Quat body2_rotation = Quat::sEulerAngles(sBodyRotation[1]) * body1_rotation;
  25. Vec3 body1_position(0, 20, 0);
  26. Body &body1 = *mBodyInterface->CreateBody(BodyCreationSettings(box, body1_position, body1_rotation, EMotionType::Static, Layers::NON_MOVING));
  27. body1.SetCollisionGroup(CollisionGroup(group_filter, 0, 0));
  28. mBodyInterface->AddBody(body1.GetID(), EActivation::DontActivate);
  29. Vec3 constraint_position = body1_position + body1_rotation * Vec3(0, -half_box_height, 0);
  30. Vec3 body2_position = constraint_position + body2_rotation * Vec3(0, -half_box_height, 0);
  31. Body &body2 = *mBodyInterface->CreateBody(BodyCreationSettings(box, body2_position, body2_rotation, EMotionType::Dynamic, Layers::MOVING));
  32. body2.SetCollisionGroup(CollisionGroup(group_filter, 0, 0));
  33. body2.GetMotionProperties()->SetLinearDamping(0.0f);
  34. body2.GetMotionProperties()->SetAngularDamping(0.0f);
  35. body2.SetAllowSleeping(false);
  36. mBodyInterface->AddBody(body2.GetID(), EActivation::Activate);
  37. Ref<SwingTwistConstraintSettings> settings = new SwingTwistConstraintSettings;
  38. settings->mNormalHalfConeAngle = sNormalHalfConeAngle;
  39. settings->mPlaneHalfConeAngle = sPlaneHalfConeAngle;
  40. settings->mTwistMinAngle = sTwistMinAngle;
  41. settings->mTwistMaxAngle = sTwistMaxAngle;
  42. settings->mPosition1 = settings->mPosition2 = constraint_position;
  43. settings->mTwistAxis1 = settings->mTwistAxis2 = -body1_rotation.RotateAxisY();
  44. settings->mPlaneAxis1 = settings->mPlaneAxis2 = body1_rotation.RotateAxisX();
  45. mConstraint = static_cast<SwingTwistConstraint *>(settings->Create(body1, body2));
  46. mPhysicsSystem->AddConstraint(mConstraint);
  47. // Calculate inertia along the axis of the box, so that the acceleration of the motor / friction are correct for twist
  48. Mat44 body2_inertia = body2.GetMotionProperties()->GetLocalSpaceInverseInertia().Inversed3x3();
  49. mInertiaBody2AsSeenFromConstraint = (body2_inertia * Vec3::sAxisY()).Length();
  50. }
  51. void PoweredSwingTwistConstraintTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
  52. {
  53. // Torque = Inertia * Angular Acceleration (alpha)
  54. mConstraint->SetMaxFrictionTorque(mInertiaBody2AsSeenFromConstraint * sMaxFrictionAngularAcceleration);
  55. mConstraint->SetNormalHalfConeAngle(sNormalHalfConeAngle);
  56. mConstraint->SetPlaneHalfConeAngle(sPlaneHalfConeAngle);
  57. mConstraint->SetTwistMinAngle(sTwistMinAngle);
  58. mConstraint->SetTwistMaxAngle(sTwistMaxAngle);
  59. mConstraint->SetSwingMotorState(sSwingMotorState);
  60. mConstraint->SetTwistMotorState(sTwistMotorState);
  61. mConstraint->SetTargetAngularVelocityCS(sTargetVelocityCS);
  62. mConstraint->SetTargetOrientationCS(Quat::sEulerAngles(sTargetOrientationCS));
  63. MotorSettings &swing = mConstraint->GetSwingMotorSettings();
  64. swing.SetTorqueLimit(mInertiaBody2AsSeenFromConstraint * sMaxAngularAcceleration);
  65. swing.mFrequency = sFrequency;
  66. swing.mDamping = sDamping;
  67. MotorSettings &twist = mConstraint->GetTwistMotorSettings();
  68. twist.SetTorqueLimit(mInertiaBody2AsSeenFromConstraint * sMaxAngularAcceleration);
  69. twist.mFrequency = sFrequency;
  70. twist.mDamping = sDamping;
  71. }
  72. void PoweredSwingTwistConstraintTest::GetInitialCamera(CameraState &ioState) const
  73. {
  74. ioState.mPos = Vec3(4, 25, 4);
  75. ioState.mForward = Vec3(-1, -1, -1).Normalized();
  76. }
  77. void PoweredSwingTwistConstraintTest::CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu)
  78. {
  79. Array<String> axis_label = { "X", "Y", "Z" };
  80. Array<String> constraint_label = { "Twist", "Plane", "Normal" };
  81. inUI->CreateTextButton(inSubMenu, "Configuration Settings", [=]() {
  82. UIElement *configuration_settings = inUI->CreateMenu();
  83. for (int body = 0; body < 2; ++body)
  84. for (int axis = 0; axis < 3; ++axis)
  85. inUI->CreateSlider(configuration_settings, "Body " + ConvertToString(body + 1) + " Rotation " + axis_label[axis] + " (deg)", RadiansToDegrees(sBodyRotation[body][axis]), -180.0f, 180.0f, 1.0f, [=](float inValue) { sBodyRotation[body].SetComponent(axis, DegreesToRadians(inValue)); });
  86. inUI->CreateTextButton(configuration_settings, "Accept Changes", [=]() { RestartTest(); });
  87. inUI->ShowMenu(configuration_settings);
  88. });
  89. inUI->CreateTextButton(inSubMenu, "Runtime Settings", [=]() {
  90. UIElement *runtime_settings = inUI->CreateMenu();
  91. inUI->CreateSlider(runtime_settings, "Min Twist Angle (deg)", RadiansToDegrees(sTwistMinAngle), -180.0f, 0.0f, 1.0f, [=](float inValue) { sTwistMinAngle = DegreesToRadians(inValue); });
  92. inUI->CreateSlider(runtime_settings, "Max Twist Angle (deg)", RadiansToDegrees(sTwistMaxAngle), 0.0f, 180.0f, 1.0f, [=](float inValue) { sTwistMaxAngle = DegreesToRadians(inValue); });
  93. inUI->CreateSlider(runtime_settings, "Normal Half Cone Angle (deg)", RadiansToDegrees(sNormalHalfConeAngle), 0.0f, 180.0f, 1.0f, [=](float inValue) { sNormalHalfConeAngle = DegreesToRadians(inValue); });
  94. inUI->CreateSlider(runtime_settings, "Plane Half Cone Angle (deg)", RadiansToDegrees(sPlaneHalfConeAngle), 0.0f, 180.0f, 1.0f, [=](float inValue) { sPlaneHalfConeAngle = DegreesToRadians(inValue); });
  95. inUI->CreateComboBox(runtime_settings, "Swing Motor", { "Off", "Velocity", "Position" }, (int)sSwingMotorState, [=](int inItem) { sSwingMotorState = (EMotorState)inItem; });
  96. inUI->CreateComboBox(runtime_settings, "Twist Motor", { "Off", "Velocity", "Position" }, (int)sTwistMotorState, [=](int inItem) { sTwistMotorState = (EMotorState)inItem; });
  97. for (int i = 0; i < 3; ++i)
  98. inUI->CreateSlider(runtime_settings, "Target Angular Velocity " + constraint_label[i] + " (deg/s)", RadiansToDegrees(sTargetVelocityCS[i]), -90.0f, 90.0f, 1.0f, [i](float inValue) { sTargetVelocityCS.SetComponent(i, DegreesToRadians(inValue)); });
  99. for (int i = 0; i < 3; ++i)
  100. inUI->CreateSlider(runtime_settings, "Target Angle " + constraint_label[i] + " (deg)", RadiansToDegrees(sTargetOrientationCS[i]), -180, 180.0f, 1.0f, [i](float inValue) {
  101. sTargetOrientationCS.SetComponent(i, DegreesToRadians(Clamp(inValue, -179.99f, 179.99f))); // +/- 180 degrees is ambiguous, so add a little bit of a margin
  102. });
  103. inUI->CreateSlider(runtime_settings, "Max Angular Acceleration (deg/s^2)", RadiansToDegrees(sMaxAngularAcceleration), 0.0f, 36000.0f, 100.0f, [=](float inValue) { sMaxAngularAcceleration = DegreesToRadians(inValue); });
  104. inUI->CreateSlider(runtime_settings, "Frequency (Hz)", sFrequency, 0.0f, 20.0f, 0.1f, [=](float inValue) { sFrequency = inValue; });
  105. inUI->CreateSlider(runtime_settings, "Damping", sDamping, 0.0f, 2.0f, 0.01f, [=](float inValue) { sDamping = inValue; });
  106. inUI->CreateSlider(runtime_settings, "Max Friction Angular Acceleration (deg/s^2)", RadiansToDegrees(sMaxFrictionAngularAcceleration), 0.0f, 900.0f, 1.0f, [=](float inValue) { sMaxFrictionAngularAcceleration = DegreesToRadians(inValue); });
  107. inUI->ShowMenu(runtime_settings);
  108. });
  109. }