OffsetCenterOfMassShapeTests.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  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 "Layers.h"
  7. #include <Jolt/Physics/Collision/Shape/BoxShape.h>
  8. #include <Jolt/Physics/Collision/Shape/OffsetCenterOfMassShape.h>
  9. TEST_SUITE("OffsetCenterOfMassShapeTests")
  10. {
  11. TEST_CASE("TestAddAngularImpulseCOMZero")
  12. {
  13. PhysicsTestContext c;
  14. c.ZeroGravity();
  15. // Create box
  16. const Vec3 cHalfExtent = Vec3(0.5f, 1.0f, 1.5f);
  17. BoxShapeSettings box(cHalfExtent);
  18. box.SetEmbedded();
  19. // Create body with COM offset 0
  20. OffsetCenterOfMassShapeSettings com(Vec3::sZero(), &box);
  21. com.SetEmbedded();
  22. Body &body = c.CreateBody(&com, RVec3::sZero(), Quat::sIdentity(), EMotionType::Dynamic, EMotionQuality::Discrete, Layers::MOVING, EActivation::DontActivate);
  23. // Check mass and inertia calculated correctly
  24. float mass = (8.0f * cHalfExtent.GetX() * cHalfExtent.GetY() * cHalfExtent.GetZ()) * box.mDensity;
  25. CHECK_APPROX_EQUAL(body.GetMotionProperties()->GetInverseMass(), 1.0f / mass);
  26. float inertia_y = mass / 12.0f * (Square(2.0f * cHalfExtent.GetX()) + Square(2.0f * cHalfExtent.GetZ())); // See: https://en.wikipedia.org/wiki/List_of_moments_of_inertia
  27. CHECK_APPROX_EQUAL(body.GetMotionProperties()->GetInverseInertiaForRotation(Mat44::sIdentity())(1, 1), 1.0f / inertia_y);
  28. // Add impulse
  29. Vec3 cImpulse(0, 10000, 0);
  30. CHECK(!body.IsActive());
  31. c.GetBodyInterface().AddAngularImpulse(body.GetID(), cImpulse);
  32. CHECK(body.IsActive());
  33. // Check resulting velocity change
  34. // dv = I^-1 * L
  35. float delta_v = (1.0f / inertia_y) * cImpulse.GetY();
  36. CHECK_APPROX_EQUAL(body.GetLinearVelocity(), Vec3::sZero());
  37. CHECK_APPROX_EQUAL(body.GetAngularVelocity(), Vec3(0, delta_v, 0));
  38. }
  39. TEST_CASE("TestAddAngularImpulseCOMOffset")
  40. {
  41. PhysicsTestContext c;
  42. c.ZeroGravity();
  43. // Create box
  44. const Vec3 cHalfExtent = Vec3(0.5f, 1.0f, 1.5f);
  45. BoxShapeSettings box(cHalfExtent);
  46. box.SetEmbedded();
  47. // Create body with COM offset
  48. const Vec3 cCOMOffset(5.0f, 0, 0);
  49. OffsetCenterOfMassShapeSettings com(cCOMOffset, &box);
  50. com.SetEmbedded();
  51. Body &body = c.CreateBody(&com, RVec3::sZero(), Quat::sIdentity(), EMotionType::Dynamic, EMotionQuality::Discrete, Layers::MOVING, EActivation::DontActivate);
  52. // Check mass and inertia calculated correctly
  53. float mass = (8.0f * cHalfExtent.GetX() * cHalfExtent.GetY() * cHalfExtent.GetZ()) * box.mDensity;
  54. CHECK_APPROX_EQUAL(body.GetMotionProperties()->GetInverseMass(), 1.0f / mass);
  55. float inertia_y = mass / 12.0f * (Square(2.0f * cHalfExtent.GetX()) + Square(2.0f * cHalfExtent.GetZ())) + mass * Square(cCOMOffset.GetX()); // See: https://en.wikipedia.org/wiki/List_of_moments_of_inertia & https://en.wikipedia.org/wiki/Parallel_axis_theorem
  56. CHECK_APPROX_EQUAL(body.GetMotionProperties()->GetInverseInertiaForRotation(Mat44::sIdentity())(1, 1), 1.0f / inertia_y);
  57. // Add impulse
  58. Vec3 cImpulse(0, 10000, 0);
  59. CHECK(!body.IsActive());
  60. c.GetBodyInterface().AddAngularImpulse(body.GetID(), cImpulse);
  61. CHECK(body.IsActive());
  62. // Check resulting velocity change
  63. // dv = I^-1 * L
  64. float delta_v = (1.0f / inertia_y) * cImpulse.GetY();
  65. CHECK_APPROX_EQUAL(body.GetLinearVelocity(), Vec3::sZero());
  66. CHECK_APPROX_EQUAL(body.GetAngularVelocity(), Vec3(0, delta_v, 0));
  67. }
  68. TEST_CASE("TestAddTorqueCOMZero")
  69. {
  70. PhysicsTestContext c;
  71. c.ZeroGravity();
  72. // Create box
  73. const Vec3 cHalfExtent = Vec3(0.5f, 1.0f, 1.5f);
  74. BoxShapeSettings box(cHalfExtent);
  75. box.SetEmbedded();
  76. // Create body with COM offset 0
  77. OffsetCenterOfMassShapeSettings com(Vec3::sZero(), &box);
  78. com.SetEmbedded();
  79. Body &body = c.CreateBody(&com, RVec3::sZero(), Quat::sIdentity(), EMotionType::Dynamic, EMotionQuality::Discrete, Layers::MOVING, EActivation::DontActivate);
  80. // Check mass and inertia calculated correctly
  81. float mass = (8.0f * cHalfExtent.GetX() * cHalfExtent.GetY() * cHalfExtent.GetZ()) * box.mDensity;
  82. CHECK_APPROX_EQUAL(body.GetMotionProperties()->GetInverseMass(), 1.0f / mass);
  83. float inertia_y = mass / 12.0f * (Square(2.0f * cHalfExtent.GetX()) + Square(2.0f * cHalfExtent.GetZ())); // See: https://en.wikipedia.org/wiki/List_of_moments_of_inertia
  84. CHECK_APPROX_EQUAL(body.GetMotionProperties()->GetInverseInertiaForRotation(Mat44::sIdentity())(1, 1), 1.0f / inertia_y);
  85. // Add torque
  86. Vec3 cTorque(0, 100000, 0);
  87. CHECK(!body.IsActive());
  88. c.GetBodyInterface().AddTorque(body.GetID(), cTorque);
  89. CHECK(body.IsActive());
  90. CHECK(body.GetAngularVelocity() == Vec3::sZero()); // Angular velocity change should come after the next time step
  91. c.SimulateSingleStep();
  92. // Check resulting velocity change
  93. // dv = I^-1 * T * dt
  94. float delta_v = (1.0f / inertia_y) * cTorque.GetY() * c.GetDeltaTime();
  95. CHECK_APPROX_EQUAL(body.GetLinearVelocity(), Vec3::sZero());
  96. CHECK_APPROX_EQUAL(body.GetAngularVelocity(), Vec3(0, delta_v, 0));
  97. }
  98. TEST_CASE("TestAddTorqueCOMOffset")
  99. {
  100. PhysicsTestContext c;
  101. c.ZeroGravity();
  102. // Create box
  103. const Vec3 cHalfExtent = Vec3(0.5f, 1.0f, 1.5f);
  104. BoxShapeSettings box(cHalfExtent);
  105. box.SetEmbedded();
  106. // Create body with COM offset
  107. const Vec3 cCOMOffset(5.0f, 0, 0);
  108. OffsetCenterOfMassShapeSettings com(cCOMOffset, &box);
  109. com.SetEmbedded();
  110. Body &body = c.CreateBody(&com, RVec3::sZero(), Quat::sIdentity(), EMotionType::Dynamic, EMotionQuality::Discrete, Layers::MOVING, EActivation::DontActivate);
  111. // Check mass and inertia calculated correctly
  112. float mass = (8.0f * cHalfExtent.GetX() * cHalfExtent.GetY() * cHalfExtent.GetZ()) * box.mDensity;
  113. CHECK_APPROX_EQUAL(body.GetMotionProperties()->GetInverseMass(), 1.0f / mass);
  114. float inertia_y = mass / 12.0f * (Square(2.0f * cHalfExtent.GetX()) + Square(2.0f * cHalfExtent.GetZ())) + mass * Square(cCOMOffset.GetX()); // See: https://en.wikipedia.org/wiki/List_of_moments_of_inertia & https://en.wikipedia.org/wiki/Parallel_axis_theorem
  115. CHECK_APPROX_EQUAL(body.GetMotionProperties()->GetInverseInertiaForRotation(Mat44::sIdentity())(1, 1), 1.0f / inertia_y);
  116. // Add torque
  117. Vec3 cTorque(0, 100000, 0);
  118. CHECK(!body.IsActive());
  119. c.GetBodyInterface().AddTorque(body.GetID(), cTorque);
  120. CHECK(body.IsActive());
  121. CHECK(body.GetAngularVelocity() == Vec3::sZero()); // Angular velocity change should come after the next time step
  122. c.SimulateSingleStep();
  123. // Check resulting velocity change
  124. // dv = I^-1 * T * dt
  125. float delta_v = (1.0f / inertia_y) * cTorque.GetY() * c.GetDeltaTime();
  126. CHECK_APPROX_EQUAL(body.GetLinearVelocity(), Vec3::sZero());
  127. CHECK_APPROX_EQUAL(body.GetAngularVelocity(), Vec3(0, delta_v, 0));
  128. }
  129. }