VehicleConstraint.h 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #pragma once
  4. #include <Jolt/Physics/Constraints/Constraint.h>
  5. #include <Jolt/Physics/PhysicsStepListener.h>
  6. #include <Jolt/Physics/Constraints/ConstraintPart/AngleConstraintPart.h>
  7. #include <Jolt/Physics/Vehicle/VehicleCollisionTester.h>
  8. #include <Jolt/Physics/Vehicle/VehicleAntiRollBar.h>
  9. #include <Jolt/Physics/Vehicle/Wheel.h>
  10. JPH_NAMESPACE_BEGIN
  11. class PhysicsSystem;
  12. class VehicleController;
  13. class VehicleControllerSettings;
  14. /// Configuration for constraint that simulates a wheeled vehicle.
  15. ///
  16. /// The properties in this constraint are largely based on "Car Physics for Games" by Marco Monster.
  17. /// See: https://www.asawicki.info/Mirror/Car%20Physics%20for%20Games/Car%20Physics%20for%20Games.html
  18. class VehicleConstraintSettings : public ConstraintSettings
  19. {
  20. public:
  21. JPH_DECLARE_SERIALIZABLE_VIRTUAL(VehicleConstraintSettings)
  22. /// Saves the contents of the constraint settings in binary form to inStream.
  23. virtual void SaveBinaryState(StreamOut &inStream) const override;
  24. Vec3 mUp { 0, 1, 0 }; ///< Vector indicating the up direction of the vehicle (in local space to the body)
  25. Vec3 mForward { 0, 0, 1 }; ///< Vector indicating forward direction of the vehicle (in local space to the body)
  26. float mMaxPitchRollAngle = JPH_PI; ///< Defines the maximum pitch/roll angle (rad), can be used to avoid the car from getting upside down. The vehicle up direction will stay within a cone centered around the up axis with half top angle mMaxPitchRollAngle, set to pi to turn off.
  27. Array<Ref<WheelSettings>> mWheels; ///< List of wheels and their properties
  28. Array<VehicleAntiRollBar> mAntiRollBars; ///< List of anti rollbars and their properties
  29. Ref<VehicleControllerSettings> mController; ///< Defines how the vehicle can accelerate / decellerate
  30. protected:
  31. /// This function should not be called directly, it is used by sRestoreFromBinaryState.
  32. virtual void RestoreBinaryState(StreamIn &inStream) override;
  33. };
  34. /// Constraint that simulates a vehicle
  35. /// Note: Don't forget to register the constraint as a StepListener with the PhysicsSystem!
  36. ///
  37. /// When the vehicle drives over very light objects (rubble) you may see the car body dip down. This is a known issue and is an artifact of the iterative solver that Jolt is using.
  38. /// Basically if a light object is sandwiched between two heavy objects (the static floor and the car body), the light object is not able to transfer enough force from the ground to
  39. /// the car body to keep the car body up. You can see this effect in the HeavyOnLightTest sample, the boxes on the right have a lot of penetration because they're on top of light objects.
  40. ///
  41. /// There are a couple of ways to improve this:
  42. ///
  43. /// 1. You can increase the number of velocity steps (global settings PhysicsSettings::mNumVelocitySteps or if you only want to increase it on
  44. /// the vehicle you can use VehicleConstraintSettings::mNumVelocityStepsOverride). E.g. going from 10 to 30 steps in the HeavyOnLightTest sample makes the penetration a lot less.
  45. /// The number of position steps can also be increased (the first prevents the body from going down, the second corrects it if the problem did
  46. /// occur which inevitably happens due to numerical drift). This solution costs CPU cycles.
  47. ///
  48. /// 2. You can reduce the mass difference between the vehicle body and the rubble on the floor (by making the rubble heavier or the car lighter).
  49. ///
  50. /// 3. You could filter out collisions between the vehicle collision test and the rubble completely. This would make the wheels ignore the rubble but would cause the vehicle to drive
  51. /// through it as if nothing happened. You could create fake wheels (keyframed bodies) that move along with the vehicle and that only collide with rubble (and not the vehicle or the ground).
  52. /// This would cause the vehicle to push away the rubble without the rubble being able to affect the vehicle (unless it hits the main body of course).
  53. ///
  54. /// Note that when driving over rubble, you may see the wheel jump up and down quite quickly because one frame a collision is found and the next frame not.
  55. /// To alleviate this, it may be needed to smooth the motion of the visual mesh for the wheel.
  56. class VehicleConstraint : public Constraint, public PhysicsStepListener
  57. {
  58. public:
  59. /// Constructor / destructor
  60. VehicleConstraint(Body &inVehicleBody, const VehicleConstraintSettings &inSettings);
  61. virtual ~VehicleConstraint() override;
  62. /// Get the type of a constraint
  63. virtual EConstraintSubType GetSubType() const override { return EConstraintSubType::Vehicle; }
  64. /// Defines the maximum pitch/roll angle (rad), can be used to avoid the car from getting upside down. The vehicle up direction will stay within a cone centered around the up axis with half top angle mMaxPitchRollAngle, set to pi to turn off.
  65. void SetMaxPitchRollAngle(float inMaxPitchRollAngle) { mCosMaxPitchRollAngle = Cos(inMaxPitchRollAngle); }
  66. /// Set the interface that tests collision between wheel and ground
  67. void SetVehicleCollisionTester(const VehicleCollisionTester *inTester) { mVehicleCollisionTester = inTester; }
  68. /// Get the local space forward vector of the vehicle
  69. Vec3 GetLocalForward() const { return mForward; }
  70. /// Get the local space up vector of the vehicle
  71. Vec3 GetLocalUp() const { return mUp; }
  72. /// Access to the vehicle body
  73. Body * GetVehicleBody() const { return mBody; }
  74. /// Access to the vehicle controller interface (determines acceleration / decelleration)
  75. const VehicleController * GetController() const { return mController; }
  76. /// Access to the vehicle controller interface (determines acceleration / decelleration)
  77. VehicleController * GetController() { return mController; }
  78. /// Get the state of the wheels
  79. const Wheels & GetWheels() const { return mWheels; }
  80. /// Get the state of a wheels (writable interface, allows you to make changes to the configuration which will take effect the next time step)
  81. Wheels & GetWheels() { return mWheels; }
  82. /// Get the state of a wheel
  83. Wheel * GetWheel(uint inIdx) { return mWheels[inIdx]; }
  84. /// Get the transform of a wheel in local space to the vehicle body, returns a matrix that transforms a cylinder aligned with the Y axis in body space (not COM space)
  85. /// @param inWheelIndex Index of the wheel to fetch
  86. /// @param inWheelRight Unit vector that indicates right in model space of the wheel (so if you only have 1 wheel model, you probably want to specify the opposite direction for the left and right wheels)
  87. /// @param inWheelUp Unit vector that indicates up in model space of the wheel
  88. Mat44 GetWheelLocalTransform(uint inWheelIndex, Vec3Arg inWheelRight, Vec3Arg inWheelUp) const;
  89. /// Get the transform of a wheel in world space, returns a matrix that transforms a cylinder aligned with the Y axis in world space
  90. /// @param inWheelIndex Index of the wheel to fetch
  91. /// @param inWheelRight Unit vector that indicates right in model space of the wheel (so if you only have 1 wheel model, you probably want to specify the opposite direction for the left and right wheels)
  92. /// @param inWheelUp Unit vector that indicates up in model space of the wheel
  93. Mat44 GetWheelWorldTransform(uint inWheelIndex, Vec3Arg inWheelRight, Vec3Arg inWheelUp) const;
  94. // Generic interface of a constraint
  95. virtual bool IsActive() const override { return mIsActive && Constraint::IsActive(); }
  96. virtual void SetupVelocityConstraint(float inDeltaTime) override;
  97. virtual void WarmStartVelocityConstraint(float inWarmStartImpulseRatio) override;
  98. virtual bool SolveVelocityConstraint(float inDeltaTime) override;
  99. virtual bool SolvePositionConstraint(float inDeltaTime, float inBaumgarte) override;
  100. virtual void BuildIslands(uint32 inConstraintIndex, IslandBuilder &ioBuilder, BodyManager &inBodyManager) override;
  101. #ifdef JPH_DEBUG_RENDERER
  102. virtual void DrawConstraint(DebugRenderer *inRenderer) const override;
  103. virtual void DrawConstraintLimits(DebugRenderer *inRenderer) const override;
  104. #endif // JPH_DEBUG_RENDERER
  105. virtual void SaveState(StateRecorder &inStream) const override;
  106. virtual void RestoreState(StateRecorder &inStream) override;
  107. virtual Ref<ConstraintSettings> GetConstraintSettings() const override;
  108. private:
  109. // See: PhysicsStepListener
  110. virtual void OnStep(float inDeltaTime, PhysicsSystem &inPhysicsSystem) override;
  111. // Calculate the contact positions of the wheel in world space, relative to the center of mass of both bodies
  112. void CalculateWheelContactPoint(Mat44Arg inBodyTransform, const Wheel &inWheel, Vec3 &outR1PlusU, Vec3 &outR2) const;
  113. // Calculate the constraint properties for mPitchRollPart
  114. void CalculatePitchRollConstraintProperties(float inDeltaTime, Mat44Arg inBodyTransform);
  115. // Simluation information
  116. Body * mBody; ///< Body of the vehicle
  117. Vec3 mForward; ///< Local space forward vector for the vehicle
  118. Vec3 mUp; ///< Local space up vector for the vehicle
  119. Wheels mWheels; ///< Wheel states of the vehicle
  120. Array<VehicleAntiRollBar> mAntiRollBars; ///< Anti rollbars of the vehicle
  121. VehicleController * mController; ///< Controls the acceleration / declerration of the vehicle
  122. bool mIsActive = false; ///< If this constraint is active
  123. // Prevent vehicle from toppling over
  124. float mCosMaxPitchRollAngle; ///< Cos of the max pitch/roll angle
  125. float mCosPitchRollAngle; ///< Cos of the current pitch/roll angle
  126. Vec3 mPitchRollRotationAxis { 0, 1, 0 }; ///< Current axis along which to apply torque to prevent the car from toppling over
  127. AngleConstraintPart mPitchRollPart; ///< Constraint part that prevents the car from toppling over
  128. // Interfaces
  129. RefConst<VehicleCollisionTester> mVehicleCollisionTester; ///< Class that performs testing of collision for the wheels
  130. };
  131. JPH_NAMESPACE_END