PathConstraintTest.cpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #include <TestFramework.h>
  4. #include <Tests/Constraints/PathConstraintTest.h>
  5. #include <Jolt/Physics/Collision/Shape/CapsuleShape.h>
  6. #include <Jolt/Physics/Body/BodyCreationSettings.h>
  7. #include <Jolt/Physics/Collision/Shape/BoxShape.h>
  8. #include <Jolt/Physics/Constraints/PathConstraintPathHermite.h>
  9. #include <Application/DebugUI.h>
  10. #include <Layers.h>
  11. JPH_IMPLEMENT_RTTI_VIRTUAL(PathConstraintTest)
  12. {
  13. JPH_ADD_BASE_CLASS(PathConstraintTest, Test)
  14. }
  15. EPathRotationConstraintType PathConstraintTest::sOrientationType = EPathRotationConstraintType::Free;
  16. void PathConstraintTest::Initialize()
  17. {
  18. // Floor
  19. CreateFloor();
  20. {
  21. // Create spiral path
  22. Ref<PathConstraintPathHermite> path = new PathConstraintPathHermite;
  23. Vec3 normal(0, 1, 0);
  24. Array<Vec3> positions;
  25. for (float a = -0.1f * JPH_PI; a < 4.0f * JPH_PI; a += 0.1f * JPH_PI)
  26. positions.push_back(Vec3(5.0f * Cos(a), -a, 5.0f * Sin(a)));
  27. for (int i = 1; i < int(positions.size() - 1); ++i)
  28. {
  29. Vec3 tangent = 0.5f * (positions[i + 1] - positions[i - 1]);
  30. path->AddPoint(positions[i], tangent, normal);
  31. }
  32. mPaths[0] = path;
  33. // Dynamic base plate to which the path attaches
  34. Body &body1 = *mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3(5, 0.5f, 5)), Vec3(-10, 1, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
  35. mBodyInterface->AddBody(body1.GetID(), EActivation::Activate);
  36. // Dynamic body attached to the path
  37. Body &body2 = *mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3(0.5f, 1.0f, 2.0f)), Vec3(-5, 15, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
  38. body2.SetAllowSleeping(false);
  39. mBodyInterface->AddBody(body2.GetID(), EActivation::Activate);
  40. // Create path constraint
  41. PathConstraintSettings settings;
  42. settings.mPath = path;
  43. settings.mPathPosition = Vec3(0, 15, 0);
  44. settings.mRotationConstraintType = sOrientationType;
  45. mConstraints[0] = static_cast<PathConstraint *>(settings.Create(body1, body2));
  46. mPhysicsSystem->AddConstraint(mConstraints[0]);
  47. }
  48. {
  49. // Create circular path
  50. Ref<PathConstraintPathHermite> path = new PathConstraintPathHermite;
  51. path->SetIsLooping(true);
  52. Vec3 normal(0, 1, 0);
  53. Array<Vec3> positions;
  54. for (int i = -1; i < 11; ++i)
  55. {
  56. float a = 2.0f * JPH_PI * i / 10.0f;
  57. positions.push_back(Vec3(5.0f * Cos(a), 0.0f, 5.0f * Sin(a)));
  58. }
  59. for (int i = 1; i < int(positions.size() - 1); ++i)
  60. {
  61. Vec3 tangent = 0.5f * (positions[i + 1] - positions[i - 1]);
  62. path->AddPoint(positions[i], tangent, normal);
  63. }
  64. mPaths[1] = path;
  65. // Dynamic base plate to which the path attaches
  66. Body &body1 = *mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3(5, 0.5f, 5)), Vec3(10, 1, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
  67. mBodyInterface->AddBody(body1.GetID(), EActivation::Activate);
  68. // Dynamic body attached to the path
  69. Body &body2 = *mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3(0.5f, 1.0f, 2.0f)), Vec3(15, 5, 0), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
  70. body2.SetAllowSleeping(false);
  71. mBodyInterface->AddBody(body2.GetID(), EActivation::Activate);
  72. // Create path constraint
  73. PathConstraintSettings settings;
  74. settings.mPath = path;
  75. settings.mPathPosition = Vec3(0, 5, 0);
  76. settings.mPathRotation = Quat::sRotation(Vec3::sAxisX(), -0.1f * JPH_PI);
  77. settings.mRotationConstraintType = sOrientationType;
  78. mConstraints[1] = static_cast<PathConstraint *>(settings.Create(body1, body2));
  79. mPhysicsSystem->AddConstraint(mConstraints[1]);
  80. }
  81. }
  82. void PathConstraintTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
  83. {
  84. for (PathConstraint *c : mConstraints)
  85. {
  86. MotorSettings &motor_settings = c->GetPositionMotorSettings();
  87. motor_settings.SetForceLimit(sMaxMotorAcceleration / c->GetBody2()->GetMotionProperties()->GetInverseMass()); // F = m * a
  88. motor_settings.mFrequency = sFrequency;
  89. motor_settings.mDamping = sDamping;
  90. c->SetMaxFrictionForce(sMaxFrictionAcceleration / c->GetBody2()->GetMotionProperties()->GetInverseMass());
  91. }
  92. }
  93. void PathConstraintTest::CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu)
  94. {
  95. static Array<String> constraint_types = { "Free", "Tangent", "Normal", "Binormal", "Path", "Full" };
  96. inUI->CreateTextButton(inSubMenu, "Configuration Settings", [=]() {
  97. UIElement *configuration_settings = inUI->CreateMenu();
  98. inUI->CreateComboBox(configuration_settings, "Rotation Constraint", constraint_types, (int)sOrientationType, [=](int inItem) { sOrientationType = (EPathRotationConstraintType)inItem; });
  99. inUI->CreateTextButton(configuration_settings, "Accept Changes", [=]() { RestartTest(); });
  100. inUI->ShowMenu(configuration_settings);
  101. });
  102. inUI->CreateTextButton(inSubMenu, "Runtime Settings", [=]() {
  103. UIElement *runtime_settings = inUI->CreateMenu();
  104. inUI->CreateComboBox(runtime_settings, "Motor", { "Off", "Velocity", "Position" }, (int)mConstraints[0]->GetPositionMotorState(), [this](int inItem) { for (PathConstraint *c : mConstraints) c->SetPositionMotorState((EMotorState)inItem); });
  105. inUI->CreateSlider(runtime_settings, "Target Velocity (m/s)", mConstraints[0]->GetTargetVelocity(), -10.0f, 10.0f, 0.1f, [this](float inValue) { for (PathConstraint *c : mConstraints) c->SetTargetVelocity(inValue); });
  106. inUI->CreateSlider(runtime_settings, "Target Path Fraction", mConstraints[0]->GetTargetPathFraction(), 0, mPaths[0]->GetPathMaxFraction(), 0.1f, [this](float inValue) { for (PathConstraint *c : mConstraints) c->SetTargetPathFraction(inValue); });
  107. inUI->CreateSlider(runtime_settings, "Max Acceleration (m/s^2)", sMaxMotorAcceleration, 0.0f, 100.0f, 1.0f, [](float inValue) { sMaxMotorAcceleration = inValue; });
  108. inUI->CreateSlider(runtime_settings, "Frequency (Hz)", sFrequency, 0.0f, 20.0f, 0.1f, [](float inValue) { sFrequency = inValue; });
  109. inUI->CreateSlider(runtime_settings, "Damping", sDamping, 0.0f, 2.0f, 0.01f, [](float inValue) { sDamping = inValue; });
  110. inUI->CreateSlider(runtime_settings, "Max Friction Acceleration (m/s^2)", sMaxFrictionAcceleration, 0.0f, 10.0f, 0.1f, [](float inValue) { sMaxFrictionAcceleration = inValue; });
  111. inUI->ShowMenu(runtime_settings);
  112. });
  113. inUI->ShowMenu(inSubMenu);
  114. }