VehicleSixDOFTest.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #include <TestFramework.h>
  4. #include <Tests/Vehicle/VehicleSixDOFTest.h>
  5. #include <Physics/Collision/Shape/BoxShape.h>
  6. #include <Physics/Collision/Shape/CylinderShape.h>
  7. #include <Physics/Collision/GroupFilterTable.h>
  8. #include <Physics/Body/BodyCreationSettings.h>
  9. #include <Application/DebugUI.h>
  10. #include <Layers.h>
  11. JPH_IMPLEMENT_RTTI_VIRTUAL(VehicleSixDOFTest)
  12. {
  13. JPH_ADD_BASE_CLASS(VehicleSixDOFTest, VehicleTest)
  14. }
  15. void VehicleSixDOFTest::Initialize()
  16. {
  17. VehicleTest::Initialize();
  18. const float half_vehicle_length = 2.0f;
  19. const float half_vehicle_width = 0.9f;
  20. const float half_vehicle_height = 0.2f;
  21. const float half_wheel_height = 0.3f;
  22. const float half_wheel_width = 0.05f;
  23. const float half_wheel_travel = 0.5f;
  24. Vec3 wheel_position[] =
  25. {
  26. Vec3(-half_vehicle_width, -half_vehicle_height, half_vehicle_length - 2.0f * half_wheel_height),
  27. Vec3(half_vehicle_width, -half_vehicle_height, half_vehicle_length - 2.0f * half_wheel_height),
  28. Vec3(-half_vehicle_width, -half_vehicle_height, -half_vehicle_length + 2.0f * half_wheel_height),
  29. Vec3(half_vehicle_width, -half_vehicle_height, -half_vehicle_length + 2.0f * half_wheel_height),
  30. };
  31. #ifdef _DEBUG
  32. const char *wheel_names[] = { "LeftFront", "RightFront", "LeftRear", "RightRear" };
  33. #endif
  34. Vec3 position(0, 2, 0);
  35. RefConst<Shape> body_shape = new BoxShape(Vec3(half_vehicle_width, half_vehicle_height, half_vehicle_length));
  36. Ref<CylinderShape> wheel_shape = new CylinderShape(half_wheel_width, half_wheel_height);
  37. wheel_shape->SetDensity(1.0e4f);
  38. // Create group filter
  39. Ref<GroupFilterTable> group_filter = new GroupFilterTable;
  40. // Create vehicle body
  41. mCarBody = mBodyInterface->CreateBody(BodyCreationSettings(body_shape, position, Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING));
  42. #ifdef _DEBUG
  43. mCarBody->SetDebugName("CarBody");
  44. #endif
  45. mCarBody->SetCollisionGroup(CollisionGroup(group_filter, 0, 0));
  46. mBodyInterface->AddBody(mCarBody->GetID(), EActivation::Activate);
  47. // Create wheels
  48. for (int i = 0; i < (int)EWheel::Num; ++i)
  49. {
  50. bool is_front = sIsFrontWheel((EWheel)i);
  51. bool is_left = sIsLeftWheel((EWheel)i);
  52. Vec3 wheel_pos1 = position + wheel_position[i];
  53. Vec3 wheel_pos2 = wheel_pos1 - Vec3(0, half_wheel_travel, 0);
  54. // Create body
  55. Body &wheel = *mBodyInterface->CreateBody(BodyCreationSettings(wheel_shape, wheel_pos2, Quat::sRotation(Vec3::sAxisZ(), 0.5f * JPH_PI), EMotionType::Dynamic, Layers::MOVING));
  56. wheel.SetFriction(1.0f);
  57. #ifdef _DEBUG
  58. wheel.SetDebugName(wheel_names[i]);
  59. #endif
  60. wheel.SetCollisionGroup(CollisionGroup(group_filter, 0, 0));
  61. mBodyInterface->AddBody(wheel.GetID(), EActivation::Activate);
  62. // Create constraint
  63. SixDOFConstraintSettings settings;
  64. settings.mPosition1 = wheel_pos1;
  65. settings.mPosition2 = wheel_pos2;
  66. settings.mAxisX1 = settings.mAxisX2 = is_left? -Vec3::sAxisX() : Vec3::sAxisX();
  67. settings.mAxisY1 = settings.mAxisY2 = Vec3::sAxisY();
  68. // The suspension works in the Y translation axis only
  69. settings.MakeFixedAxis(EAxis::TranslationX);
  70. settings.SetLimitedAxis(EAxis::TranslationY, -half_wheel_travel, half_wheel_travel);
  71. settings.MakeFixedAxis(EAxis::TranslationZ);
  72. settings.mMotorSettings[EAxis::TranslationY] = MotorSettings(2.0f, 1.0f, 1.0e5f, 0.0f);
  73. // Front wheel can rotate around the Y axis
  74. if (is_front)
  75. settings.SetLimitedAxis(EAxis::RotationY, -cMaxSteeringAngle, cMaxSteeringAngle);
  76. else
  77. settings.MakeFixedAxis(EAxis::RotationY);
  78. // The Z axis is static
  79. settings.MakeFixedAxis(EAxis::RotationZ);
  80. // The main engine drives the X axis
  81. settings.MakeFreeAxis(EAxis::RotationX);
  82. settings.mMotorSettings[EAxis::RotationX] = MotorSettings(2.0f, 1.0f, 0.0f, 0.5e4f);
  83. // The front wheel needs to be able to steer around the Y axis
  84. // However the motors work in the constraint space of the wheel, and since this rotates around the
  85. // X axis we need to drive both the Y and Z to steer
  86. if (is_front)
  87. settings.mMotorSettings[EAxis::RotationY] = settings.mMotorSettings[EAxis::RotationZ] = MotorSettings(10.0f, 1.0f, 0.0f, 1.0e6f);
  88. SixDOFConstraint *wheel_constraint = static_cast<SixDOFConstraint *>(settings.Create(*mCarBody, wheel));
  89. mPhysicsSystem->AddConstraint(wheel_constraint);
  90. mWheels[i] = wheel_constraint;
  91. // Drive the suspension
  92. wheel_constraint->SetTargetPositionCS(Vec3(0, -half_wheel_travel, 0));
  93. wheel_constraint->SetMotorState(EAxis::TranslationY, EMotorState::Position);
  94. // The front wheels steer around the Y axis, but in constraint space of the wheel this means we need to drive
  95. // both Y and Z (see comment above)
  96. if (is_front)
  97. {
  98. wheel_constraint->SetTargetOrientationCS(Quat::sIdentity());
  99. wheel_constraint->SetMotorState(EAxis::RotationY, EMotorState::Position);
  100. wheel_constraint->SetMotorState(EAxis::RotationZ, EMotorState::Position);
  101. }
  102. }
  103. }
  104. void VehicleSixDOFTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
  105. {
  106. const float max_rotation_speed = 10.0f * JPH_PI;
  107. // Determine steering and speed
  108. float steering_angle = 0.0f, speed = 0.0f;
  109. if (inParams.mKeyboard->IsKeyPressed(DIK_LEFT)) steering_angle = cMaxSteeringAngle;
  110. if (inParams.mKeyboard->IsKeyPressed(DIK_RIGHT)) steering_angle = -cMaxSteeringAngle;
  111. if (inParams.mKeyboard->IsKeyPressed(DIK_UP)) speed = max_rotation_speed;
  112. if (inParams.mKeyboard->IsKeyPressed(DIK_DOWN)) speed = -max_rotation_speed;
  113. // On user input, assure that the car is active
  114. if (steering_angle != 0.0f || speed != 0.0f)
  115. mBodyInterface->ActivateBody(mCarBody->GetID());
  116. // Brake if current velocity is in the opposite direction of the desired velocity
  117. float car_speed = mCarBody->GetLinearVelocity().Dot(mCarBody->GetRotation().RotateAxisZ());
  118. bool brake = speed != 0.0f && car_speed != 0.0f && Sign(speed) != Sign(car_speed);
  119. // Front wheels
  120. const EWheel front_wheels[] = { EWheel::LeftFront, EWheel::RightFront };
  121. for (EWheel w : front_wheels)
  122. {
  123. SixDOFConstraint *wheel_constraint = mWheels[(int)w];
  124. if (wheel_constraint == nullptr)
  125. continue;
  126. // Steer front wheels
  127. Quat steering_rotation = Quat::sRotation(Vec3::sAxisY(), steering_angle);
  128. wheel_constraint->SetTargetOrientationCS(steering_rotation);
  129. if (brake)
  130. {
  131. // Brake on front wheels
  132. wheel_constraint->SetTargetAngularVelocityCS(Vec3::sZero());
  133. wheel_constraint->SetMotorState(EAxis::RotationX, EMotorState::Velocity);
  134. }
  135. else if (speed != 0.0f)
  136. {
  137. // Front wheel drive, since the motors are applied in the constraint space of the wheel
  138. // it is always applied on the X axis
  139. wheel_constraint->SetTargetAngularVelocityCS(Vec3(sIsLeftWheel(w)? -speed : speed, 0, 0));
  140. wheel_constraint->SetMotorState(EAxis::RotationX, EMotorState::Velocity);
  141. }
  142. else
  143. {
  144. // Free spin
  145. wheel_constraint->SetMotorState(EAxis::RotationX, EMotorState::Off);
  146. }
  147. }
  148. // Rear wheels
  149. const EWheel rear_wheels[] = { EWheel::LeftRear, EWheel::RightRear };
  150. for (EWheel w : rear_wheels)
  151. {
  152. SixDOFConstraint *wheel_constraint = mWheels[(int)w];
  153. if (wheel_constraint == nullptr)
  154. continue;
  155. if (brake)
  156. {
  157. // Brake on rear wheels
  158. wheel_constraint->SetTargetAngularVelocityCS(Vec3::sZero());
  159. wheel_constraint->SetMotorState(EAxis::RotationX, EMotorState::Velocity);
  160. }
  161. else
  162. {
  163. // Free spin
  164. wheel_constraint->SetMotorState(EAxis::RotationX, EMotorState::Off);
  165. }
  166. }
  167. }
  168. void VehicleSixDOFTest::GetInitialCamera(CameraState &ioState) const
  169. {
  170. // Position camera behind car
  171. Vec3 cam_tgt = Vec3(0, 0, 5);
  172. ioState.mPos = Vec3(0, 2.5f, -5);
  173. ioState.mForward = (cam_tgt - ioState.mPos).Normalized();
  174. }
  175. Mat44 VehicleSixDOFTest::GetCameraPivot(float inCameraHeading, float inCameraPitch) const
  176. {
  177. // Pivot is center of car and rotates with car around Y axis only
  178. Vec3 fwd = mCarBody->GetRotation().RotateAxisZ();
  179. fwd.SetY(0.0f);
  180. float len = fwd.Length();
  181. if (len != 0.0f)
  182. fwd /= len;
  183. else
  184. fwd = Vec3::sAxisZ();
  185. Vec3 up = Vec3::sAxisY();
  186. Vec3 right = up.Cross(fwd);
  187. return Mat44(Vec4(right, 0), Vec4(up, 0), Vec4(fwd, 0), Vec4(mCarBody->GetPosition(), 1.0f));
  188. }