SixDOFConstraintTests.cpp 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  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 "Layers.h"
  8. TEST_SUITE("SixDOFConstraintTests")
  9. {
  10. // Test if the 6DOF constraint can be used to create a spring
  11. TEST_CASE("TestSixDOFSpring")
  12. {
  13. // Configuration of the spring
  14. const float cFrequency = 2.0f;
  15. const float cDamping = 0.1f;
  16. // Test all permutations of axis
  17. for (uint spring_axis = 0b001; spring_axis <= 0b111; ++spring_axis)
  18. {
  19. // Test all spring modes
  20. for (int mode = 0; mode < 2; ++mode)
  21. {
  22. const RVec3 cInitialPosition(10.0f * (spring_axis & 1), 8.0f * (spring_axis & 2), 6.0f * (spring_axis & 4));
  23. // Create a sphere
  24. PhysicsTestContext context;
  25. context.ZeroGravity();
  26. Body& body = context.CreateSphere(cInitialPosition, 0.5f, EMotionType::Dynamic, EMotionQuality::Discrete, Layers::MOVING);
  27. body.GetMotionProperties()->SetLinearDamping(0.0f);
  28. // Calculate stiffness and damping of spring
  29. float m = 1.0f / body.GetMotionProperties()->GetInverseMass();
  30. float omega = 2.0f * JPH_PI * cFrequency;
  31. float k = m * Square(omega);
  32. float c = 2.0f * m * cDamping * omega;
  33. // Create spring
  34. SixDOFConstraintSettings constraint;
  35. constraint.mPosition2 = cInitialPosition;
  36. for (int axis = 0; axis < 3; ++axis)
  37. {
  38. // Check if this axis is supposed to be a spring
  39. if (((1 << axis) & spring_axis) != 0)
  40. {
  41. if (mode == 0)
  42. {
  43. // First iteration use stiffness and damping
  44. constraint.mLimitsSpringSettings[axis].mMode = ESpringMode::StiffnessAndDamping;
  45. constraint.mLimitsSpringSettings[axis].mStiffness = k;
  46. constraint.mLimitsSpringSettings[axis].mDamping = c;
  47. }
  48. else
  49. {
  50. // Second iteration use frequency and damping
  51. constraint.mLimitsSpringSettings[axis].mMode = ESpringMode::FrequencyAndDamping;
  52. constraint.mLimitsSpringSettings[axis].mFrequency = cFrequency;
  53. constraint.mLimitsSpringSettings[axis].mDamping = cDamping;
  54. }
  55. constraint.mLimitMin[axis] = constraint.mLimitMax[axis] = 0.0f;
  56. }
  57. }
  58. context.CreateConstraint<SixDOFConstraint>(Body::sFixedToWorld, body, constraint);
  59. // Simulate spring
  60. RVec3 x = cInitialPosition;
  61. Vec3 v = Vec3::sZero();
  62. float dt = context.GetDeltaTime();
  63. for (int i = 0; i < 120; ++i)
  64. {
  65. // Using the equations from page 32 of Soft Constraints: Reinventing The Spring - Erin Catto - GDC 2011 for an implicit euler spring damper
  66. for (int axis = 0; axis < 3; ++axis)
  67. if (((1 << axis) & spring_axis) != 0) // Only update velocity for axis where there is a spring
  68. v.SetComponent(axis, (v[axis] - dt * k / m * float(x[axis])) / (1.0f + dt * c / m + Square(dt) * k / m));
  69. x += v * dt;
  70. // Run physics simulation
  71. context.SimulateSingleStep();
  72. // Test if simulation matches prediction
  73. CHECK_APPROX_EQUAL(x, body.GetPosition(), 1.0e-5_r);
  74. }
  75. }
  76. }
  77. }
  78. }