VehicleConstraintTest.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #include <TestFramework.h>
  5. #include <Tests/Vehicle/VehicleConstraintTest.h>
  6. #include <Jolt/Physics/Collision/Shape/BoxShape.h>
  7. #include <Jolt/Physics/Collision/Shape/OffsetCenterOfMassShape.h>
  8. #include <Jolt/Physics/Vehicle/WheeledVehicleController.h>
  9. #include <Jolt/Physics/Body/BodyCreationSettings.h>
  10. #include <Application/DebugUI.h>
  11. #include <Layers.h>
  12. #include <Renderer/DebugRendererImp.h>
  13. JPH_IMPLEMENT_RTTI_VIRTUAL(VehicleConstraintTest)
  14. {
  15. JPH_ADD_BASE_CLASS(VehicleConstraintTest, VehicleTest)
  16. }
  17. VehicleConstraintTest::~VehicleConstraintTest()
  18. {
  19. mPhysicsSystem->RemoveStepListener(mVehicleConstraint);
  20. }
  21. void VehicleConstraintTest::Initialize()
  22. {
  23. VehicleTest::Initialize();
  24. const float wheel_radius = 0.3f;
  25. const float wheel_width = 0.1f;
  26. const float half_vehicle_length = 2.0f;
  27. const float half_vehicle_width = 0.9f;
  28. const float half_vehicle_height = 0.2f;
  29. // Create collision testers
  30. mTesters[0] = new VehicleCollisionTesterRay(Layers::MOVING);
  31. mTesters[1] = new VehicleCollisionTesterCastSphere(Layers::MOVING, 0.5f * wheel_width);
  32. mTesters[2] = new VehicleCollisionTesterCastCylinder(Layers::MOVING);
  33. // Create vehicle body
  34. RVec3 position(0, 2, 0);
  35. RefConst<Shape> car_shape = OffsetCenterOfMassShapeSettings(Vec3(0, -half_vehicle_height, 0), new BoxShape(Vec3(half_vehicle_width, half_vehicle_height, half_vehicle_length))).Create().Get();
  36. BodyCreationSettings car_body_settings(car_shape, position, Quat::sRotation(Vec3::sAxisZ(), sInitialRollAngle), EMotionType::Dynamic, Layers::MOVING);
  37. car_body_settings.mOverrideMassProperties = EOverrideMassProperties::CalculateInertia;
  38. car_body_settings.mMassPropertiesOverride.mMass = 1500.0f;
  39. mCarBody = mBodyInterface->CreateBody(car_body_settings);
  40. mBodyInterface->AddBody(mCarBody->GetID(), EActivation::Activate);
  41. // Create vehicle constraint
  42. VehicleConstraintSettings vehicle;
  43. vehicle.mDrawConstraintSize = 0.1f;
  44. vehicle.mMaxPitchRollAngle = sMaxRollAngle;
  45. // Suspension direction
  46. Vec3 front_suspension_dir = Vec3(Tan(sFrontSuspensionSidewaysAngle), -1, Tan(sFrontSuspensionForwardAngle)).Normalized();
  47. Vec3 front_steering_axis = Vec3(-Tan(sFrontKingPinAngle), 1, -Tan(sFrontCasterAngle)).Normalized();
  48. Vec3 front_wheel_up = Vec3(Sin(sFrontCamber), Cos(sFrontCamber), 0);
  49. Vec3 front_wheel_forward = Vec3(-Sin(sFrontToe), 0, Cos(sFrontToe));
  50. Vec3 rear_suspension_dir = Vec3(Tan(sRearSuspensionSidewaysAngle), -1, Tan(sRearSuspensionForwardAngle)).Normalized();
  51. Vec3 rear_steering_axis = Vec3(-Tan(sRearKingPinAngle), 1, -Tan(sRearCasterAngle)).Normalized();
  52. Vec3 rear_wheel_up = Vec3(Sin(sRearCamber), Cos(sRearCamber), 0);
  53. Vec3 rear_wheel_forward = Vec3(-Sin(sRearToe), 0, Cos(sRearToe));
  54. Vec3 flip_x(-1, 1, 1);
  55. // Wheels, left front
  56. WheelSettingsWV *w1 = new WheelSettingsWV;
  57. w1->mPosition = Vec3(half_vehicle_width, -0.9f * half_vehicle_height, half_vehicle_length - 2.0f * wheel_radius);
  58. w1->mSuspensionDirection = front_suspension_dir;
  59. w1->mSteeringAxis = front_steering_axis;
  60. w1->mWheelUp = front_wheel_up;
  61. w1->mWheelForward = front_wheel_forward;
  62. w1->mSuspensionMinLength = sFrontSuspensionMinLength;
  63. w1->mSuspensionMaxLength = sFrontSuspensionMaxLength;
  64. w1->mSuspensionSpring.mFrequency = sFrontSuspensionFrequency;
  65. w1->mSuspensionSpring.mDamping = sFrontSuspensionDamping;
  66. w1->mMaxSteerAngle = sMaxSteeringAngle;
  67. w1->mMaxHandBrakeTorque = 0.0f; // Front wheel doesn't have hand brake
  68. // Right front
  69. WheelSettingsWV *w2 = new WheelSettingsWV;
  70. w2->mPosition = Vec3(-half_vehicle_width, -0.9f * half_vehicle_height, half_vehicle_length - 2.0f * wheel_radius);
  71. w2->mSuspensionDirection = flip_x * front_suspension_dir;
  72. w2->mSteeringAxis = flip_x * front_steering_axis;
  73. w2->mWheelUp = flip_x * front_wheel_up;
  74. w2->mWheelForward = flip_x * front_wheel_forward;
  75. w2->mSuspensionMinLength = sFrontSuspensionMinLength;
  76. w2->mSuspensionMaxLength = sFrontSuspensionMaxLength;
  77. w2->mSuspensionSpring.mFrequency = sFrontSuspensionFrequency;
  78. w2->mSuspensionSpring.mDamping = sFrontSuspensionDamping;
  79. w2->mMaxSteerAngle = sMaxSteeringAngle;
  80. w2->mMaxHandBrakeTorque = 0.0f; // Front wheel doesn't have hand brake
  81. // Left rear
  82. WheelSettingsWV *w3 = new WheelSettingsWV;
  83. w3->mPosition = Vec3(half_vehicle_width, -0.9f * half_vehicle_height, -half_vehicle_length + 2.0f * wheel_radius);
  84. w3->mSuspensionDirection = rear_suspension_dir;
  85. w3->mSteeringAxis = rear_steering_axis;
  86. w3->mWheelUp = rear_wheel_up;
  87. w3->mWheelForward = rear_wheel_forward;
  88. w3->mSuspensionMinLength = sRearSuspensionMinLength;
  89. w3->mSuspensionMaxLength = sRearSuspensionMaxLength;
  90. w3->mSuspensionSpring.mFrequency = sRearSuspensionFrequency;
  91. w3->mSuspensionSpring.mDamping = sRearSuspensionDamping;
  92. w3->mMaxSteerAngle = 0.0f;
  93. // Right rear
  94. WheelSettingsWV *w4 = new WheelSettingsWV;
  95. w4->mPosition = Vec3(-half_vehicle_width, -0.9f * half_vehicle_height, -half_vehicle_length + 2.0f * wheel_radius);
  96. w4->mSuspensionDirection = flip_x * rear_suspension_dir;
  97. w4->mSteeringAxis = flip_x * rear_steering_axis;
  98. w4->mWheelUp = flip_x * rear_wheel_up;
  99. w4->mWheelForward = flip_x * rear_wheel_forward;
  100. w4->mSuspensionMinLength = sRearSuspensionMinLength;
  101. w4->mSuspensionMaxLength = sRearSuspensionMaxLength;
  102. w4->mSuspensionSpring.mFrequency = sRearSuspensionFrequency;
  103. w4->mSuspensionSpring.mDamping = sRearSuspensionDamping;
  104. w4->mMaxSteerAngle = 0.0f;
  105. vehicle.mWheels = { w1, w2, w3, w4 };
  106. for (WheelSettings *w : vehicle.mWheels)
  107. {
  108. w->mRadius = wheel_radius;
  109. w->mWidth = wheel_width;
  110. }
  111. WheeledVehicleControllerSettings *controller = new WheeledVehicleControllerSettings;
  112. vehicle.mController = controller;
  113. // Differential
  114. controller->mDifferentials.resize(sFourWheelDrive? 2 : 1);
  115. controller->mDifferentials[0].mLeftWheel = 0;
  116. controller->mDifferentials[0].mRightWheel = 1;
  117. if (sFourWheelDrive)
  118. {
  119. controller->mDifferentials[1].mLeftWheel = 2;
  120. controller->mDifferentials[1].mRightWheel = 3;
  121. // Split engine torque
  122. controller->mDifferentials[0].mEngineTorqueRatio = controller->mDifferentials[1].mEngineTorqueRatio = 0.5f;
  123. }
  124. // Anti rollbars
  125. if (sAntiRollbar)
  126. {
  127. vehicle.mAntiRollBars.resize(2);
  128. vehicle.mAntiRollBars[0].mLeftWheel = 0;
  129. vehicle.mAntiRollBars[0].mRightWheel = 1;
  130. vehicle.mAntiRollBars[1].mLeftWheel = 2;
  131. vehicle.mAntiRollBars[1].mRightWheel = 3;
  132. }
  133. mVehicleConstraint = new VehicleConstraint(*mCarBody, vehicle);
  134. mPhysicsSystem->AddConstraint(mVehicleConstraint);
  135. mPhysicsSystem->AddStepListener(mVehicleConstraint);
  136. }
  137. void VehicleConstraintTest::ProcessInput(const ProcessInputParams &inParams)
  138. {
  139. // Determine acceleration and brake
  140. mForward = 0.0f;
  141. if (inParams.mKeyboard->IsKeyPressed(DIK_UP))
  142. mForward = 1.0f;
  143. else if (inParams.mKeyboard->IsKeyPressed(DIK_DOWN))
  144. mForward = -1.0f;
  145. // Check if we're reversing direction
  146. mBrake = 0.0f;
  147. if (mPreviousForward * mForward < 0.0f)
  148. {
  149. // Get vehicle velocity in local space to the body of the vehicle
  150. float velocity = (mCarBody->GetRotation().Conjugated() * mCarBody->GetLinearVelocity()).GetZ();
  151. if ((mForward > 0.0f && velocity < -0.1f) || (mForward < 0.0f && velocity > 0.1f))
  152. {
  153. // Brake while we've not stopped yet
  154. mForward = 0.0f;
  155. mBrake = 1.0f;
  156. }
  157. else
  158. {
  159. // When we've come to a stop, accept the new direction
  160. mPreviousForward = mForward;
  161. }
  162. }
  163. // Hand brake will cancel gas pedal
  164. mHandBrake = 0.0f;
  165. if (inParams.mKeyboard->IsKeyPressed(DIK_Z))
  166. {
  167. mForward = 0.0f;
  168. mHandBrake = 1.0f;
  169. }
  170. // Steering
  171. mRight = 0.0f;
  172. if (inParams.mKeyboard->IsKeyPressed(DIK_LEFT))
  173. mRight = -1.0f;
  174. else if (inParams.mKeyboard->IsKeyPressed(DIK_RIGHT))
  175. mRight = 1.0f;
  176. }
  177. void VehicleConstraintTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
  178. {
  179. VehicleTest::PrePhysicsUpdate(inParams);
  180. // On user input, assure that the car is active
  181. if (mRight != 0.0f || mForward != 0.0f || mBrake != 0.0f || mHandBrake != 0.0f)
  182. mBodyInterface->ActivateBody(mCarBody->GetID());
  183. WheeledVehicleController *controller = static_cast<WheeledVehicleController *>(mVehicleConstraint->GetController());
  184. // Update vehicle statistics
  185. controller->GetEngine().mMaxTorque = sMaxEngineTorque;
  186. controller->GetTransmission().mClutchStrength = sClutchStrength;
  187. // Set slip ratios to the same for everything
  188. float limited_slip_ratio = sLimitedSlipDifferentials? 1.4f : FLT_MAX;
  189. controller->SetDifferentialLimitedSlipRatio(limited_slip_ratio);
  190. for (VehicleDifferentialSettings &d : controller->GetDifferentials())
  191. d.mLimitedSlipRatio = limited_slip_ratio;
  192. // Pass the input on to the constraint
  193. controller->SetDriverInput(mForward, mRight, mBrake, mHandBrake);
  194. // Set the collision tester
  195. mVehicleConstraint->SetVehicleCollisionTester(mTesters[sCollisionMode]);
  196. // Draw our wheels (this needs to be done in the pre update since we draw the bodies too in the state before the step)
  197. for (uint w = 0; w < 4; ++w)
  198. {
  199. const WheelSettings *settings = mVehicleConstraint->GetWheels()[w]->GetSettings();
  200. RMat44 wheel_transform = mVehicleConstraint->GetWheelWorldTransform(w, Vec3::sAxisY(), Vec3::sAxisX()); // The cyclinder we draw is aligned with Y so we specify that as rotational axis
  201. mDebugRenderer->DrawCylinder(wheel_transform, 0.5f * settings->mWidth, settings->mRadius, Color::sGreen);
  202. }
  203. }
  204. void VehicleConstraintTest::SaveInputState(StateRecorder &inStream) const
  205. {
  206. inStream.Write(mForward);
  207. inStream.Write(mPreviousForward);
  208. inStream.Write(mRight);
  209. inStream.Write(mBrake);
  210. inStream.Write(mHandBrake);
  211. }
  212. void VehicleConstraintTest::RestoreInputState(StateRecorder &inStream)
  213. {
  214. inStream.Read(mForward);
  215. inStream.Read(mPreviousForward);
  216. inStream.Read(mRight);
  217. inStream.Read(mBrake);
  218. inStream.Read(mHandBrake);
  219. }
  220. void VehicleConstraintTest::GetInitialCamera(CameraState &ioState) const
  221. {
  222. // Position camera behind car
  223. RVec3 cam_tgt = RVec3(0, 0, 5);
  224. ioState.mPos = RVec3(0, 2.5f, -5);
  225. ioState.mForward = Vec3(cam_tgt - ioState.mPos).Normalized();
  226. }
  227. RMat44 VehicleConstraintTest::GetCameraPivot(float inCameraHeading, float inCameraPitch) const
  228. {
  229. // Pivot is center of car and rotates with car around Y axis only
  230. Vec3 fwd = mCarBody->GetRotation().RotateAxisZ();
  231. fwd.SetY(0.0f);
  232. float len = fwd.Length();
  233. if (len != 0.0f)
  234. fwd /= len;
  235. else
  236. fwd = Vec3::sAxisZ();
  237. Vec3 up = Vec3::sAxisY();
  238. Vec3 right = up.Cross(fwd);
  239. return RMat44(Vec4(right, 0), Vec4(up, 0), Vec4(fwd, 0), mCarBody->GetPosition());
  240. }
  241. void VehicleConstraintTest::CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu)
  242. {
  243. VehicleTest::CreateSettingsMenu(inUI, inSubMenu);
  244. inUI->CreateSlider(inSubMenu, "Initial Roll Angle", RadiansToDegrees(sInitialRollAngle), 0.0f, 90.0f, 1.0f, [](float inValue) { sInitialRollAngle = DegreesToRadians(inValue); });
  245. inUI->CreateSlider(inSubMenu, "Max Roll Angle", RadiansToDegrees(sMaxRollAngle), 0.0f, 90.0f, 1.0f, [](float inValue) { sMaxRollAngle = DegreesToRadians(inValue); });
  246. inUI->CreateSlider(inSubMenu, "Max Steering Angle", RadiansToDegrees(sMaxSteeringAngle), 0.0f, 90.0f, 1.0f, [](float inValue) { sMaxSteeringAngle = DegreesToRadians(inValue); });
  247. inUI->CreateComboBox(inSubMenu, "Collision Mode", { "Ray", "Cast Sphere", "Cast Cylinder" }, sCollisionMode, [](int inItem) { sCollisionMode = inItem; });
  248. inUI->CreateCheckBox(inSubMenu, "4 Wheel Drive", sFourWheelDrive, [](UICheckBox::EState inState) { sFourWheelDrive = inState == UICheckBox::STATE_CHECKED; });
  249. inUI->CreateCheckBox(inSubMenu, "Anti Rollbars", sAntiRollbar, [](UICheckBox::EState inState) { sAntiRollbar = inState == UICheckBox::STATE_CHECKED; });
  250. inUI->CreateCheckBox(inSubMenu, "Limited Slip Differentials", sLimitedSlipDifferentials, [](UICheckBox::EState inState) { sLimitedSlipDifferentials = inState == UICheckBox::STATE_CHECKED; });
  251. inUI->CreateSlider(inSubMenu, "Max Engine Torque", float(sMaxEngineTorque), 100.0f, 2000.0f, 10.0f, [](float inValue) { sMaxEngineTorque = inValue; });
  252. inUI->CreateSlider(inSubMenu, "Clutch Strength", float(sClutchStrength), 1.0f, 40.0f, 1.0f, [](float inValue) { sClutchStrength = inValue; });
  253. inUI->CreateSlider(inSubMenu, "Front Caster Angle", RadiansToDegrees(sFrontCasterAngle), -89.0f, 89.0f, 1.0f, [](float inValue) { sFrontCasterAngle = DegreesToRadians(inValue); });
  254. inUI->CreateSlider(inSubMenu, "Front King Pin Angle", RadiansToDegrees(sFrontKingPinAngle), -89.0f, 89.0f, 1.0f, [](float inValue) { sFrontKingPinAngle = DegreesToRadians(inValue); });
  255. inUI->CreateSlider(inSubMenu, "Front Camber", RadiansToDegrees(sFrontCamber), -89.0f, 89.0f, 1.0f, [](float inValue) { sFrontCamber = DegreesToRadians(inValue); });
  256. inUI->CreateSlider(inSubMenu, "Front Toe", RadiansToDegrees(sFrontToe), -89.0f, 89.0f, 1.0f, [](float inValue) { sFrontToe = DegreesToRadians(inValue); });
  257. inUI->CreateSlider(inSubMenu, "Front Suspension Forward Angle", RadiansToDegrees(sFrontSuspensionForwardAngle), -89.0f, 89.0f, 1.0f, [](float inValue) { sFrontSuspensionForwardAngle = DegreesToRadians(inValue); });
  258. inUI->CreateSlider(inSubMenu, "Front Suspension Sideways Angle", RadiansToDegrees(sFrontSuspensionSidewaysAngle), -89.0f, 89.0f, 1.0f, [](float inValue) { sFrontSuspensionSidewaysAngle = DegreesToRadians(inValue); });
  259. inUI->CreateSlider(inSubMenu, "Front Suspension Min Length", sFrontSuspensionMinLength, 0.0f, 3.0f, 0.01f, [](float inValue) { sFrontSuspensionMinLength = inValue; });
  260. inUI->CreateSlider(inSubMenu, "Front Suspension Max Length", sFrontSuspensionMaxLength, 0.0f, 3.0f, 0.01f, [](float inValue) { sFrontSuspensionMaxLength = inValue; });
  261. inUI->CreateSlider(inSubMenu, "Front Suspension Frequency", sFrontSuspensionFrequency, 0.1f, 5.0f, 0.01f, [](float inValue) { sFrontSuspensionFrequency = inValue; });
  262. inUI->CreateSlider(inSubMenu, "Front Suspension Damping", sFrontSuspensionDamping, 0.0f, 2.0f, 0.01f, [](float inValue) { sFrontSuspensionDamping = inValue; });
  263. inUI->CreateSlider(inSubMenu, "Rear Caster Angle", RadiansToDegrees(sRearCasterAngle), -89.0f, 89.0f, 1.0f, [](float inValue) { sRearCasterAngle = DegreesToRadians(inValue); });
  264. inUI->CreateSlider(inSubMenu, "Rear King Pin Angle", RadiansToDegrees(sRearKingPinAngle), -89.0f, 89.0f, 1.0f, [](float inValue) { sRearKingPinAngle = DegreesToRadians(inValue); });
  265. inUI->CreateSlider(inSubMenu, "Rear Camber", RadiansToDegrees(sRearCamber), -89.0f, 89.0f, 1.0f, [](float inValue) { sRearCamber = DegreesToRadians(inValue); });
  266. inUI->CreateSlider(inSubMenu, "Rear Toe", RadiansToDegrees(sRearToe), -89.0f, 89.0f, 1.0f, [](float inValue) { sRearToe = DegreesToRadians(inValue); });
  267. inUI->CreateSlider(inSubMenu, "Rear Suspension Forward Angle", RadiansToDegrees(sRearSuspensionForwardAngle), -89.0f, 89.0f, 1.0f, [](float inValue) { sRearSuspensionForwardAngle = DegreesToRadians(inValue); });
  268. inUI->CreateSlider(inSubMenu, "Rear Suspension Sideways Angle", RadiansToDegrees(sRearSuspensionSidewaysAngle), -89.0f, 89.0f, 1.0f, [](float inValue) { sRearSuspensionSidewaysAngle = DegreesToRadians(inValue); });
  269. inUI->CreateSlider(inSubMenu, "Rear Suspension Min Length", sRearSuspensionMinLength, 0.0f, 3.0f, 0.01f, [](float inValue) { sRearSuspensionMinLength = inValue; });
  270. inUI->CreateSlider(inSubMenu, "Rear Suspension Max Length", sRearSuspensionMaxLength, 0.0f, 3.0f, 0.01f, [](float inValue) { sRearSuspensionMaxLength = inValue; });
  271. inUI->CreateSlider(inSubMenu, "Rear Suspension Frequency", sRearSuspensionFrequency, 0.1f, 5.0f, 0.01f, [](float inValue) { sRearSuspensionFrequency = inValue; });
  272. inUI->CreateSlider(inSubMenu, "Rear Suspension Damping", sRearSuspensionDamping, 0.0f, 2.0f, 0.01f, [](float inValue) { sRearSuspensionDamping = inValue; });
  273. inUI->CreateTextButton(inSubMenu, "Accept", [this]() { RestartTest(); });
  274. }