2
0

SixDOFConstraintTest.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #include <TestFramework.h>
  5. #include <Tests/Constraints/SixDOFConstraintTest.h>
  6. #include <Jolt/Physics/Collision/Shape/BoxShape.h>
  7. #include <Jolt/Physics/Collision/GroupFilterTable.h>
  8. #include <Jolt/Physics/Body/BodyCreationSettings.h>
  9. #include <Application/DebugUI.h>
  10. #include <Layers.h>
  11. JPH_IMPLEMENT_RTTI_VIRTUAL(SixDOFConstraintTest)
  12. {
  13. JPH_ADD_BASE_CLASS(SixDOFConstraintTest, Test)
  14. }
  15. float SixDOFConstraintTest::sLimitMin[EAxis::Num] = { 0, 0, 0, 0, 0, 0 };
  16. float SixDOFConstraintTest::sLimitMax[EAxis::Num] = { 0, 0, 0, 0, 0, 0 };
  17. bool SixDOFConstraintTest::sEnableLimits[EAxis::Num] = { true, true, true, true, true, true };
  18. SixDOFConstraintTest::SettingsRef SixDOFConstraintTest::sSettings = []() {
  19. static SixDOFConstraintSettings settings;
  20. settings.SetEmbedded();
  21. settings.mAxisX1 = settings.mAxisX2 = -Vec3::sAxisY();
  22. settings.mAxisY1 = settings.mAxisY2 = Vec3::sAxisZ();
  23. for (int i = 0; i < 6; ++i)
  24. settings.mMotorSettings[i] = MotorSettings(10.0f, 2.0f);
  25. return &settings;
  26. }();
  27. void SixDOFConstraintTest::Initialize()
  28. {
  29. // Floor
  30. CreateFloor();
  31. // Convert limits to settings class
  32. for (int i = 0; i < EAxis::Num; ++i)
  33. if (sEnableLimits[i])
  34. {
  35. if (sLimitMax[i] - sLimitMin[i] < 1.0e-3f)
  36. sSettings->MakeFixedAxis((EAxis)i);
  37. else
  38. sSettings->SetLimitedAxis((EAxis)i, sLimitMin[i], sLimitMax[i]);
  39. }
  40. else
  41. {
  42. sSettings->MakeFreeAxis((EAxis)i);
  43. }
  44. // Create group filter
  45. Ref<GroupFilterTable> group_filter = new GroupFilterTable;
  46. // Create box
  47. float half_box_height = 1.5f;
  48. RVec3 position(0, 25, 0);
  49. RefConst<BoxShape> box = new BoxShape(Vec3(0.5f, half_box_height, 0.25f));
  50. // Create static body
  51. Body &body1 = *mBodyInterface->CreateBody(BodyCreationSettings(box, position, Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING));
  52. body1.SetCollisionGroup(CollisionGroup(group_filter, 0, 0));
  53. mBodyInterface->AddBody(body1.GetID(), EActivation::DontActivate);
  54. // Create dynamic body
  55. Body &body2 = *mBodyInterface->CreateBody(BodyCreationSettings(box, position - Vec3(0, 2.0f * half_box_height, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
  56. body2.SetCollisionGroup(CollisionGroup(group_filter, 0, 0));
  57. body2.SetAllowSleeping(false);
  58. mBodyInterface->AddBody(body2.GetID(), EActivation::Activate);
  59. // Set constraint position
  60. sSettings->mPosition1 = sSettings->mPosition2 = position - Vec3(0, half_box_height, 0);
  61. // Set force limits
  62. const float max_acceleration = 1.0f;
  63. for (int i = 0; i < 3; ++i)
  64. sSettings->mMotorSettings[i].SetForceLimit(max_acceleration / body2.GetMotionProperties()->GetInverseMass());
  65. // Create constraint
  66. mConstraint = static_cast<SixDOFConstraint *>(sSettings->Create(body1, body2));
  67. mPhysicsSystem->AddConstraint(mConstraint);
  68. }
  69. void SixDOFConstraintTest::GetInitialCamera(CameraState &ioState) const
  70. {
  71. ioState.mPos = RVec3(4, 30, 4);
  72. ioState.mForward = Vec3(-1, -1, -1).Normalized();
  73. }
  74. void SixDOFConstraintTest::CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu)
  75. {
  76. Array<String> labels = { "Translation X", "Translation Y", "Translation Z", "Rotation X", "Rotation Y", "Rotation Z" };
  77. Array<String> motor_states = { "Off", "Velocity", "Position" };
  78. inUI->CreateTextButton(inSubMenu, "Configuration Settings", [=]() {
  79. UIElement *configuration_settings = inUI->CreateMenu();
  80. for (int i = 0; i < 3; ++i)
  81. {
  82. inUI->CreateCheckBox(configuration_settings, "Enable Limits " + labels[i], sEnableLimits[i], [=](UICheckBox::EState inState) { sEnableLimits[i] = inState == UICheckBox::STATE_CHECKED; });
  83. inUI->CreateSlider(configuration_settings, "Limit Min", sLimitMin[i], -10.0f, 0.0f, 0.1f, [=](float inValue) { sLimitMin[i] = inValue; });
  84. inUI->CreateSlider(configuration_settings, "Limit Max", sLimitMax[i], 0.0f, 10.0f, 0.1f, [=](float inValue) { sLimitMax[i] = inValue; });
  85. inUI->CreateSlider(configuration_settings, "Limit Frequency (Hz)", sSettings->mLimitsSpringSettings[i].mFrequency, 0.0f, 20.0f, 0.1f, [=](float inValue) { sSettings->mLimitsSpringSettings[i].mFrequency = inValue; });
  86. inUI->CreateSlider(configuration_settings, "Limit Damping", sSettings->mLimitsSpringSettings[i].mDamping, 0.0f, 2.0f, 0.01f, [=](float inValue) { sSettings->mLimitsSpringSettings[i].mDamping = inValue; });
  87. }
  88. for (int i = 3; i < 6; ++i)
  89. {
  90. inUI->CreateCheckBox(configuration_settings, "Enable Limits " + labels[i], sEnableLimits[i], [=](UICheckBox::EState inState) { sEnableLimits[i] = inState == UICheckBox::STATE_CHECKED; });
  91. if (i == 3)
  92. {
  93. inUI->CreateSlider(configuration_settings, "Limit Min", RadiansToDegrees(sLimitMin[i]), -180.0f, 0.0f, 1.0f, [=](float inValue) { sLimitMin[i] = DegreesToRadians(inValue); });
  94. inUI->CreateSlider(configuration_settings, "Limit Max", RadiansToDegrees(sLimitMax[i]), 0.0f, 180.0f, 1.0f, [=](float inValue) { sLimitMax[i] = DegreesToRadians(inValue); });
  95. }
  96. else
  97. {
  98. inUI->CreateSlider(configuration_settings, "Limit Max", RadiansToDegrees(sLimitMax[i]), 0.0f, 180.0f, 1.0f, [=](float inValue) { sLimitMin[i] = -DegreesToRadians(inValue); sLimitMax[i] = DegreesToRadians(inValue); });
  99. }
  100. }
  101. for (int i = 0; i < 6; ++i)
  102. inUI->CreateSlider(configuration_settings, "Max Friction " + labels[i], sSettings->mMaxFriction[i], 0.0f, 500.0f, 1.0f, [=](float inValue) { sSettings->mMaxFriction[i] = inValue; });
  103. for (int i = 0; i < 6; ++i)
  104. inUI->CreateSlider(configuration_settings, "Motor Frequency " + labels[i] + " (Hz)", sSettings->mMotorSettings[i].mSpringSettings.mFrequency, 0.0f, 20.0f, 0.1f, [i](float inValue) { sSettings->mMotorSettings[i].mSpringSettings.mFrequency = inValue; });
  105. for (int i = 0; i < 6; ++i)
  106. inUI->CreateSlider(configuration_settings, "Motor Damping " + labels[i], sSettings->mMotorSettings[i].mSpringSettings.mDamping, 0.0f, 2.0f, 0.01f, [i](float inValue) { sSettings->mMotorSettings[i].mSpringSettings.mDamping = inValue; });
  107. inUI->CreateTextButton(configuration_settings, "Accept Changes", [=]() { RestartTest(); });
  108. inUI->ShowMenu(configuration_settings);
  109. });
  110. inUI->CreateTextButton(inSubMenu, "Runtime Settings", [=]() {
  111. UIElement *runtime_settings = inUI->CreateMenu();
  112. for (int i = 0; i < 3; ++i)
  113. {
  114. EAxis axis = EAxis(EAxis::TranslationX + i);
  115. UIComboBox *combo = inUI->CreateComboBox(runtime_settings, "Motor " + labels[i], motor_states, (int)mConstraint->GetMotorState(axis), [=](int inItem) { mConstraint->SetMotorState(axis, (EMotorState)inItem); });
  116. combo->SetDisabled(sSettings->IsFixedAxis(axis));
  117. UISlider *velocity = inUI->CreateSlider(runtime_settings, "Target Velocity", mConstraint->GetTargetVelocityCS()[i], -10.0f, 10.0f, 0.1f, [=](float inValue) {
  118. Vec3 v = mConstraint->GetTargetVelocityCS();
  119. v.SetComponent(i, inValue);
  120. mConstraint->SetTargetVelocityCS(v); });
  121. velocity->SetDisabled(sSettings->IsFixedAxis(axis));
  122. UISlider *position = inUI->CreateSlider(runtime_settings, "Target Position", mConstraint->GetTargetPositionCS()[i], -10.0f, 10.0f, 0.1f, [=](float inValue) {
  123. Vec3 v = mConstraint->GetTargetPositionCS();
  124. v.SetComponent(i, inValue);
  125. mConstraint->SetTargetPositionCS(v); });
  126. position->SetDisabled(sSettings->IsFixedAxis(axis));
  127. }
  128. for (int i = 0; i < 3; ++i)
  129. {
  130. EAxis axis = EAxis(EAxis::RotationX + i);
  131. inUI->CreateComboBox(runtime_settings, "Motor " + labels[axis], motor_states, (int)mConstraint->GetMotorState(axis), [=](int inItem) { mConstraint->SetMotorState(axis, (EMotorState)inItem); });
  132. inUI->CreateSlider(runtime_settings, "Target Velocity", RadiansToDegrees(mConstraint->GetTargetAngularVelocityCS()[i]), -90.0f, 90.0f, 1.0f, [this, i](float inValue) {
  133. Vec3 v = mConstraint->GetTargetAngularVelocityCS();
  134. v.SetComponent(i, DegreesToRadians(inValue));
  135. mConstraint->SetTargetAngularVelocityCS(v); });
  136. inUI->CreateSlider(runtime_settings, "Target Position", RadiansToDegrees(mTargetOrientationCS[i]), -180.0f, 180.0f, 1.0f, [this, i](float inValue) {
  137. mTargetOrientationCS.SetComponent(i, DegreesToRadians(Clamp(inValue, -179.99f, 179.99f))); // +/- 180 degrees is ambiguous, so add a little bit of a margin
  138. mConstraint->SetTargetOrientationCS(Quat::sEulerAngles(mTargetOrientationCS)); });
  139. }
  140. inUI->ShowMenu(runtime_settings);
  141. });
  142. }