WheeledVehicleTests.cpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. // SPDX-FileCopyrightText: 2022 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #include "UnitTestFramework.h"
  4. #include "PhysicsTestContext.h"
  5. #include <Jolt/Physics/Collision/Shape/BoxShape.h>
  6. #include <Jolt/Physics/Collision/Shape/OffsetCenterOfMassShape.h>
  7. #include <Jolt/Physics/Vehicle/WheeledVehicleController.h>
  8. #include <Jolt/Physics/Body/BodyCreationSettings.h>
  9. #include "Layers.h"
  10. TEST_SUITE("WheeledVehicleTests")
  11. {
  12. // Simplified vehicle settings
  13. struct VehicleSettings
  14. {
  15. Vec3 mPosition { 0, 2, 0 };
  16. bool mUseCastSphere = true;
  17. float mWheelRadius = 0.3f;
  18. float mWheelWidth = 0.1f;
  19. float mHalfVehicleLength = 2.0f;
  20. float mHalfVehicleWidth = 0.9f;
  21. float mHalfVehicleHeight = 0.2f;
  22. float mWheelOffsetHorizontal = 1.4f;
  23. float mWheelOffsetVertical = 0.18f;
  24. float mSuspensionMinLength = 0.3f;
  25. float mSuspensionMaxLength = 0.5f;
  26. float mMaxSteeringAngle = DegreesToRadians(30);
  27. bool mFourWheelDrive = false;
  28. bool mAntiRollbar = true;
  29. };
  30. // Helper function to create a vehicle
  31. static VehicleConstraint *AddVehicle(PhysicsTestContext &inContext, VehicleSettings &inSettings)
  32. {
  33. // Create vehicle body
  34. RefConst<Shape> car_shape = OffsetCenterOfMassShapeSettings(Vec3(0, -inSettings.mHalfVehicleHeight, 0), new BoxShape(Vec3(inSettings.mHalfVehicleWidth, inSettings.mHalfVehicleHeight, inSettings.mHalfVehicleLength))).Create().Get();
  35. BodyCreationSettings car_body_settings(car_shape, inSettings.mPosition, Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
  36. car_body_settings.mOverrideMassProperties = EOverrideMassProperties::CalculateInertia;
  37. car_body_settings.mMassPropertiesOverride.mMass = 1500.0f;
  38. Body *car_body = inContext.GetBodyInterface().CreateBody(car_body_settings);
  39. inContext.GetBodyInterface().AddBody(car_body->GetID(), EActivation::Activate);
  40. // Create vehicle constraint
  41. VehicleConstraintSettings vehicle;
  42. vehicle.mDrawConstraintSize = 0.1f;
  43. vehicle.mMaxPitchRollAngle = DegreesToRadians(60.0f);
  44. // Wheels
  45. WheelSettingsWV *w1 = new WheelSettingsWV;
  46. w1->mPosition = Vec3(inSettings.mHalfVehicleWidth, -inSettings.mWheelOffsetVertical, inSettings.mWheelOffsetHorizontal);
  47. w1->mMaxSteerAngle = inSettings.mMaxSteeringAngle;
  48. w1->mMaxHandBrakeTorque = 0.0f; // Front wheel doesn't have hand brake
  49. WheelSettingsWV *w2 = new WheelSettingsWV;
  50. w2->mPosition = Vec3(-inSettings.mHalfVehicleWidth, -inSettings.mWheelOffsetVertical, inSettings.mWheelOffsetHorizontal);
  51. w2->mMaxSteerAngle = inSettings.mMaxSteeringAngle;
  52. w2->mMaxHandBrakeTorque = 0.0f; // Front wheel doesn't have hand brake
  53. WheelSettingsWV *w3 = new WheelSettingsWV;
  54. w3->mPosition = Vec3(inSettings.mHalfVehicleWidth, -inSettings.mWheelOffsetVertical, -inSettings.mWheelOffsetHorizontal);
  55. w3->mMaxSteerAngle = 0.0f;
  56. WheelSettingsWV *w4 = new WheelSettingsWV;
  57. w4->mPosition = Vec3(-inSettings.mHalfVehicleWidth, -inSettings.mWheelOffsetVertical, -inSettings.mWheelOffsetHorizontal);
  58. w4->mMaxSteerAngle = 0.0f;
  59. vehicle.mWheels = { w1, w2, w3, w4 };
  60. for (WheelSettings *w : vehicle.mWheels)
  61. {
  62. w->mRadius = inSettings.mWheelRadius;
  63. w->mWidth = inSettings.mWheelWidth;
  64. w->mSuspensionMinLength = inSettings.mSuspensionMinLength;
  65. w->mSuspensionMaxLength = inSettings.mSuspensionMaxLength;
  66. }
  67. WheeledVehicleControllerSettings *controller = new WheeledVehicleControllerSettings;
  68. vehicle.mController = controller;
  69. // Differential
  70. controller->mDifferentials.resize(inSettings.mFourWheelDrive? 2 : 1);
  71. controller->mDifferentials[0].mLeftWheel = 0;
  72. controller->mDifferentials[0].mRightWheel = 1;
  73. if (inSettings.mFourWheelDrive)
  74. {
  75. controller->mDifferentials[1].mLeftWheel = 2;
  76. controller->mDifferentials[1].mRightWheel = 3;
  77. // Split engine torque
  78. controller->mDifferentials[0].mEngineTorqueRatio = controller->mDifferentials[1].mEngineTorqueRatio = 0.5f;
  79. }
  80. // Anti rollbars
  81. if (inSettings.mAntiRollbar)
  82. {
  83. vehicle.mAntiRollBars.resize(2);
  84. vehicle.mAntiRollBars[0].mLeftWheel = 0;
  85. vehicle.mAntiRollBars[0].mRightWheel = 1;
  86. vehicle.mAntiRollBars[1].mLeftWheel = 2;
  87. vehicle.mAntiRollBars[1].mRightWheel = 3;
  88. }
  89. // Create the constraint
  90. VehicleConstraint *constraint = new VehicleConstraint(*car_body, vehicle);
  91. // Create collision tester
  92. RefConst<VehicleCollisionTester> tester;
  93. if (inSettings.mUseCastSphere)
  94. tester = new VehicleCollisionTesterCastSphere(Layers::MOVING, 0.5f * inSettings.mWheelWidth);
  95. else
  96. tester = new VehicleCollisionTesterRay(Layers::MOVING);
  97. constraint->SetVehicleCollisionTester(tester);
  98. // Add to the world
  99. inContext.GetSystem()->AddConstraint(constraint);
  100. inContext.GetSystem()->AddStepListener(constraint);
  101. return constraint;
  102. }
  103. static void CheckOnGround(VehicleConstraint *inConstraint, const VehicleSettings &inSettings, const BodyID &inGroundID)
  104. {
  105. // Between min and max suspension length
  106. Vec3 pos = inConstraint->GetVehicleBody()->GetPosition();
  107. CHECK(pos.GetY() > inSettings.mSuspensionMinLength + inSettings.mWheelOffsetVertical + inSettings.mHalfVehicleHeight);
  108. CHECK(pos.GetY() < inSettings.mSuspensionMaxLength + inSettings.mWheelOffsetVertical + inSettings.mHalfVehicleHeight);
  109. // Wheels touching ground
  110. for (const Wheel *w : inConstraint->GetWheels())
  111. CHECK(w->GetContactBodyID() == inGroundID);
  112. }
  113. TEST_CASE("TestBasicWheeledVehicle")
  114. {
  115. PhysicsTestContext c;
  116. BodyID floor_id = c.CreateFloor().GetID();
  117. VehicleSettings settings;
  118. VehicleConstraint *constraint = AddVehicle(c, settings);
  119. Body *body = constraint->GetVehicleBody();
  120. WheeledVehicleController *controller = static_cast<WheeledVehicleController *>(constraint->GetController());
  121. // Should start at specified position
  122. CHECK_APPROX_EQUAL(body->GetPosition(), settings.mPosition);
  123. // After 1 step we should not be at ground yet
  124. c.SimulateSingleStep();
  125. for (const Wheel *w : constraint->GetWheels())
  126. CHECK(w->GetContactBodyID().IsInvalid());
  127. CHECK(controller->GetTransmission().GetCurrentGear() == 0);
  128. // After 1 second we should be on ground but not moving horizontally
  129. c.Simulate(1.0f);
  130. CheckOnGround(constraint, settings, floor_id);
  131. Vec3 pos1 = body->GetPosition();
  132. CHECK_APPROX_EQUAL(pos1.GetX(), 0); // Not moving horizontally
  133. CHECK_APPROX_EQUAL(pos1.GetZ(), 0);
  134. CHECK(controller->GetTransmission().GetCurrentGear() == 0);
  135. // Start driving forward
  136. controller->SetDriverInput(1.0f, 0.0f, 0.0f, 0.0f);
  137. c.GetBodyInterface().ActivateBody(body->GetID());
  138. c.Simulate(1.0f);
  139. CheckOnGround(constraint, settings, floor_id);
  140. Vec3 pos2 = body->GetPosition();
  141. CHECK_APPROX_EQUAL(pos2.GetX(), 0, 1.0e-3f); // Not moving left/right
  142. CHECK(pos2.GetZ() > pos1.GetZ() + 1.0f); // Moving in Z direction
  143. Vec3 vel = body->GetLinearVelocity();
  144. CHECK_APPROX_EQUAL(vel.GetX(), 0, 1.0e-2f); // Not moving left/right
  145. CHECK(vel.GetZ() > 1.0f); // Moving in Z direction
  146. CHECK(controller->GetTransmission().GetCurrentGear() > 0);
  147. // Brake
  148. controller->SetDriverInput(0.0f, 0.0f, 1.0f, 0.0f);
  149. c.GetBodyInterface().ActivateBody(body->GetID());
  150. c.Simulate(2.0f);
  151. CheckOnGround(constraint, settings, floor_id);
  152. CHECK(!body->IsActive()); // Car should have gone sleeping
  153. Vec3 pos3 = body->GetPosition();
  154. CHECK_APPROX_EQUAL(pos3.GetX(), 0, 1.0e-3f); // Not moving left/right
  155. CHECK(pos3.GetZ() > pos2.GetZ() + 1.0f); // Moving in Z direction while braking
  156. vel = body->GetLinearVelocity();
  157. CHECK_APPROX_EQUAL(vel, Vec3::sZero(), 1.0e-3f); // Not moving
  158. // Start driving backwards
  159. controller->SetDriverInput(-1.0f, 0.0f, 0.0f, 0.0f);
  160. c.GetBodyInterface().ActivateBody(body->GetID());
  161. c.Simulate(2.0f);
  162. CheckOnGround(constraint, settings, floor_id);
  163. Vec3 pos4 = body->GetPosition();
  164. CHECK_APPROX_EQUAL(pos4.GetX(), 0, 1.0e-2f); // Not moving left/right
  165. CHECK(pos4.GetZ() < pos3.GetZ() - 1.0f); // Moving in -Z direction
  166. vel = body->GetLinearVelocity();
  167. CHECK_APPROX_EQUAL(vel.GetX(), 0, 1.0e-2f); // Not moving left/right
  168. CHECK(vel.GetZ() < -1.0f); // Moving in -Z direction
  169. CHECK(controller->GetTransmission().GetCurrentGear() < 0);
  170. // Brake
  171. controller->SetDriverInput(0.0f, 0.0f, 1.0f, 0.0f);
  172. c.GetBodyInterface().ActivateBody(body->GetID());
  173. c.Simulate(3.0f);
  174. CheckOnGround(constraint, settings, floor_id);
  175. CHECK(!body->IsActive()); // Car should have gone sleeping
  176. Vec3 pos5 = body->GetPosition();
  177. CHECK_APPROX_EQUAL(pos5.GetX(), 0, 1.0e-2f); // Not moving left/right
  178. CHECK(pos5.GetZ() < pos4.GetZ() - 1.0f); // Moving in -Z direction while braking
  179. vel = body->GetLinearVelocity();
  180. CHECK_APPROX_EQUAL(vel, Vec3::sZero(), 1.0e-3f); // Not moving
  181. // Turn right
  182. controller->SetDriverInput(1.0f, 1.0f, 0.0f, 0.0f);
  183. c.GetBodyInterface().ActivateBody(body->GetID());
  184. c.Simulate(1.0f);
  185. CheckOnGround(constraint, settings, floor_id);
  186. Vec3 omega = body->GetAngularVelocity();
  187. CHECK(omega.GetY() < -0.4f); // Rotating right
  188. CHECK(controller->GetTransmission().GetCurrentGear() > 0);
  189. // Hand brake
  190. controller->SetDriverInput(0.0f, 0.0f, 0.0f, 1.0f);
  191. c.GetBodyInterface().ActivateBody(body->GetID());
  192. c.Simulate(4.0f);
  193. CheckOnGround(constraint, settings, floor_id);
  194. CHECK(!body->IsActive()); // Car should have gone sleeping
  195. vel = body->GetLinearVelocity();
  196. CHECK_APPROX_EQUAL(vel, Vec3::sZero(), 1.0e-3f); // Not moving
  197. // Turn left
  198. controller->SetDriverInput(1.0f, -1.0f, 0.0f, 0.0f);
  199. c.GetBodyInterface().ActivateBody(body->GetID());
  200. c.Simulate(1.0f);
  201. CheckOnGround(constraint, settings, floor_id);
  202. omega = body->GetAngularVelocity();
  203. CHECK(omega.GetY() > 0.4f); // Rotating left
  204. CHECK(controller->GetTransmission().GetCurrentGear() > 0);
  205. }
  206. }