HingeConstraintTests.cpp 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2023 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #include "UnitTestFramework.h"
  5. #include "PhysicsTestContext.h"
  6. #include <Jolt/Physics/Constraints/HingeConstraint.h>
  7. #include <Jolt/Physics/Collision/Shape/SphereShape.h>
  8. #include "Layers.h"
  9. TEST_SUITE("HingeConstraintTests")
  10. {
  11. // Test if the hinge constraint can be used to create a spring
  12. TEST_CASE("TestHingeSpring")
  13. {
  14. // Configuration of the spring
  15. const float cInitialAngle = DegreesToRadians(100.0f);
  16. const float cFrequency = 2.0f;
  17. const float cDamping = 0.1f;
  18. for (int mode = 0; mode < 2; ++mode)
  19. {
  20. // Create a sphere
  21. PhysicsTestContext context;
  22. Body &body = context.CreateBody(new SphereShapeSettings(0.5f), RVec3::sZero(), Quat::sIdentity(), EMotionType::Dynamic, EMotionQuality::Discrete, Layers::MOVING, EActivation::Activate);
  23. body.GetMotionProperties()->SetAngularDamping(0.0f);
  24. body.SetAllowSleeping(false);
  25. // Calculate stiffness and damping of spring
  26. float inertia = body.GetMotionProperties()->GetInverseInertiaForRotation(Mat44::sIdentity()).Inversed3x3().GetAxisY().Length();
  27. float omega = 2.0f * JPH_PI * cFrequency;
  28. float k = inertia * Square(omega);
  29. float c = 2.0f * inertia * cDamping * omega;
  30. // Create spring
  31. HingeConstraintSettings constraint;
  32. if (mode == 0)
  33. {
  34. // First iteration use stiffness and damping
  35. constraint.mLimitsSpringSettings.mMode = ESpringMode::StiffnessAndDamping;
  36. constraint.mLimitsSpringSettings.mStiffness = k;
  37. constraint.mLimitsSpringSettings.mDamping = c;
  38. }
  39. else
  40. {
  41. // Second iteration use frequency and damping
  42. constraint.mLimitsSpringSettings.mMode = ESpringMode::FrequencyAndDamping;
  43. constraint.mLimitsSpringSettings.mFrequency = cFrequency;
  44. constraint.mLimitsSpringSettings.mDamping = cDamping;
  45. }
  46. constraint.mLimitsMin = constraint.mLimitsMax = 0.0f;
  47. context.CreateConstraint<HingeConstraint>(Body::sFixedToWorld, body, constraint);
  48. // Rotate the body to the initial angle
  49. context.GetBodyInterface().SetRotation(body.GetID(), Quat::sRotation(Vec3::sAxisY(), cInitialAngle), EActivation::Activate);
  50. // Simulate angular spring
  51. float angle = cInitialAngle;
  52. float angular_v = 0.0f;
  53. float dt = context.GetDeltaTime();
  54. for (int i = 0; i < 120; ++i)
  55. {
  56. // Using the equations from page 32 of Soft Constraints: Reinventing The Spring - Erin Catto - GDC 2011 for an implicit euler spring damper
  57. angular_v = (angular_v - dt * k / inertia * angle) / (1.0f + dt * c / inertia + Square(dt) * k / inertia);
  58. angle += angular_v * dt;
  59. // Run physics simulation
  60. context.SimulateSingleStep();
  61. // Decompose body rotation
  62. Vec3 actual_axis;
  63. float actual_angle;
  64. body.GetRotation().GetAxisAngle(actual_axis, actual_angle);
  65. if (actual_axis.GetY() < 0.0f)
  66. actual_angle = -actual_angle;
  67. // Test if simulation matches prediction
  68. CHECK_APPROX_EQUAL(angle, actual_angle, DegreesToRadians(0.1f));
  69. CHECK_APPROX_EQUAL(actual_axis.GetX(), 0);
  70. CHECK_APPROX_EQUAL(actual_axis.GetZ(), 0);
  71. }
  72. }
  73. }
  74. }