SixDOFConstraintTests.cpp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #include "UnitTestFramework.h"
  5. #include "PhysicsTestContext.h"
  6. #include <Jolt/Physics/Constraints/SixDOFConstraint.h>
  7. #include <Jolt/Physics/Collision/Shape/BoxShape.h>
  8. #include "Layers.h"
  9. TEST_SUITE("SixDOFConstraintTests")
  10. {
  11. // Test if the 6DOF constraint can be used to create a spring
  12. TEST_CASE("TestSixDOFSpring")
  13. {
  14. // Configuration of the spring
  15. const float cFrequency = 2.0f;
  16. const float cDamping = 0.1f;
  17. // Test all permutations of axis
  18. for (uint spring_axis = 0b001; spring_axis <= 0b111; ++spring_axis)
  19. {
  20. // Test all spring modes
  21. for (int mode = 0; mode < 2; ++mode)
  22. {
  23. const RVec3 cInitialPosition(10.0f * (spring_axis & 1), 8.0f * (spring_axis & 2), 6.0f * (spring_axis & 4));
  24. // Create a sphere
  25. PhysicsTestContext context;
  26. context.ZeroGravity();
  27. Body& body = context.CreateSphere(cInitialPosition, 0.5f, EMotionType::Dynamic, EMotionQuality::Discrete, Layers::MOVING);
  28. body.GetMotionProperties()->SetLinearDamping(0.0f);
  29. // Calculate stiffness and damping of spring
  30. float m = 1.0f / body.GetMotionProperties()->GetInverseMass();
  31. float omega = 2.0f * JPH_PI * cFrequency;
  32. float k = m * Square(omega);
  33. float c = 2.0f * m * cDamping * omega;
  34. // Create spring
  35. SixDOFConstraintSettings constraint;
  36. constraint.mPosition2 = cInitialPosition;
  37. for (int axis = 0; axis < 3; ++axis)
  38. {
  39. // Check if this axis is supposed to be a spring
  40. if (((1 << axis) & spring_axis) != 0)
  41. {
  42. if (mode == 0)
  43. {
  44. // First iteration use stiffness and damping
  45. constraint.mLimitsSpringSettings[axis].mMode = ESpringMode::StiffnessAndDamping;
  46. constraint.mLimitsSpringSettings[axis].mStiffness = k;
  47. constraint.mLimitsSpringSettings[axis].mDamping = c;
  48. }
  49. else
  50. {
  51. // Second iteration use frequency and damping
  52. constraint.mLimitsSpringSettings[axis].mMode = ESpringMode::FrequencyAndDamping;
  53. constraint.mLimitsSpringSettings[axis].mFrequency = cFrequency;
  54. constraint.mLimitsSpringSettings[axis].mDamping = cDamping;
  55. }
  56. constraint.mLimitMin[axis] = constraint.mLimitMax[axis] = 0.0f;
  57. }
  58. }
  59. context.CreateConstraint<SixDOFConstraint>(Body::sFixedToWorld, body, constraint);
  60. // Simulate spring
  61. RVec3 x = cInitialPosition;
  62. Vec3 v = Vec3::sZero();
  63. float dt = context.GetDeltaTime();
  64. for (int i = 0; i < 120; ++i)
  65. {
  66. // Using the equations from page 32 of Soft Constraints: Reinventing The Spring - Erin Catto - GDC 2011 for an implicit euler spring damper
  67. for (int axis = 0; axis < 3; ++axis)
  68. if (((1 << axis) & spring_axis) != 0) // Only update velocity for axis where there is a spring
  69. v.SetComponent(axis, (v[axis] - dt * k / m * float(x[axis])) / (1.0f + dt * c / m + Square(dt) * k / m));
  70. x += v * dt;
  71. // Run physics simulation
  72. context.SimulateSingleStep();
  73. // Test if simulation matches prediction
  74. CHECK_APPROX_EQUAL(x, body.GetPosition(), 1.0e-5_r);
  75. }
  76. }
  77. }
  78. }
  79. // Test combination of locked rotation axis with a 6DOF constraint
  80. TEST_CASE("TestSixDOFLockedRotation")
  81. {
  82. PhysicsTestContext context;
  83. BodyInterface &bi = context.GetBodyInterface();
  84. PhysicsSystem *system = context.GetSystem();
  85. RefConst<Shape> box_shape = new BoxShape(Vec3::sReplicate(1.0f));
  86. // Static 'anchor' body
  87. BodyCreationSettings settings1(box_shape, RVec3::sZero(), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING);
  88. Body &body1 = *bi.CreateBody(settings1);
  89. bi.AddBody(body1.GetID(), EActivation::Activate);
  90. // Dynamic body that cannot rotate around X and Y
  91. const RVec3 position2(3, 0, 0);
  92. const Quat rotation2 = Quat::sIdentity();
  93. BodyCreationSettings settings2(box_shape, position2, rotation2, EMotionType::Dynamic, Layers::MOVING);
  94. settings2.mAllowedDOFs = EAllowedDOFs::RotationZ | EAllowedDOFs::TranslationX | EAllowedDOFs::TranslationY | EAllowedDOFs::TranslationZ;
  95. Body &body2 = *bi.CreateBody(settings2);
  96. bi.AddBody(body2.GetID(), EActivation::Activate);
  97. // Lock all 6 axis with a 6DOF constraint
  98. SixDOFConstraintSettings six_dof;
  99. six_dof.MakeFixedAxis(SixDOFConstraintSettings::EAxis::TranslationX);
  100. six_dof.MakeFixedAxis(SixDOFConstraintSettings::EAxis::TranslationY);
  101. six_dof.MakeFixedAxis(SixDOFConstraintSettings::EAxis::TranslationZ);
  102. six_dof.MakeFixedAxis(SixDOFConstraintSettings::EAxis::RotationX);
  103. six_dof.MakeFixedAxis(SixDOFConstraintSettings::EAxis::RotationY);
  104. six_dof.MakeFixedAxis(SixDOFConstraintSettings::EAxis::RotationZ);
  105. system->AddConstraint(six_dof.Create(body1, body2));
  106. context.Simulate(1.0f);
  107. // Check that body didn't rotate
  108. CHECK_APPROX_EQUAL(body2.GetPosition(), position2, 5.0e-3f);
  109. CHECK_APPROX_EQUAL(body2.GetRotation(), rotation2, 5.0e-3f);
  110. }
  111. }