Merge branch 'vehicle-physics' of https://github.com/RuanLucasGD/FlaxEngine into RuanLucasGD-vehicle-physics

This commit is contained in:
Wojtek Figat
2024-02-22 23:22:09 +01:00
4 changed files with 860 additions and 297 deletions

View File

@@ -38,6 +38,65 @@ const Array<WheeledVehicle::Wheel>& WheeledVehicle::GetWheels() const
return _wheels; return _wheels;
} }
WheeledVehicle::DriveControlSettings WheeledVehicle::GetDriveControl() const
{
return _driveControl;
}
void WheeledVehicle::SetDriveControl(DriveControlSettings& value)
{
value.RiseRateAcceleration = Math::Max(value.RiseRateAcceleration, 0.01f);
value.RiseRateBrake = Math::Max(value.RiseRateBrake, 0.01f);
value.RiseRateHandBrake = Math::Max(value.RiseRateHandBrake, 0.01f);
value.RiseRateSteer = Math::Max(value.RiseRateSteer, 0.01f);
value.FallRateAcceleration = Math::Max(value.FallRateAcceleration, 0.01f);
value.FallRateBrake = Math::Max(value.FallRateBrake, 0.01f);
value.FallRateHandBrake = Math::Max(value.FallRateHandBrake, 0.01f);
value.FallRateSteer = Math::Max(value.FallRateSteer, 0.01f);
// Don't let have an invalid steer vs speed list.
if (value.SteerVsSpeed.Count() < 1)
value.SteerVsSpeed.Add(WheeledVehicle::SteerControl());
else // physx backend requires the max of 4 values only
while (value.SteerVsSpeed.Count() > 4)
value.SteerVsSpeed.RemoveLast();
// Maintain all values clamped to have a ordened speed list
int steerVsSpeedCount = value.SteerVsSpeed.Count();
for (int i = 0; i < steerVsSpeedCount; i++)
{
// Apply only on changed value
if (_driveControl.SteerVsSpeed[i].Speed != value.SteerVsSpeed[i].Speed || _driveControl.SteerVsSpeed[i].Steer != value.SteerVsSpeed[i].Steer)
{
WheeledVehicle::SteerControl& steerVsSpeed = value.SteerVsSpeed[i];
steerVsSpeed.Steer = Math::Saturate(steerVsSpeed.Steer);
steerVsSpeed.Speed = Math::Max(steerVsSpeed.Speed, 10.0f);
// Clamp speeds to have an ordened list.
if (i >= 1)
{
WheeledVehicle::SteerControl& lastSteerVsSpeed = value.SteerVsSpeed[i - 1];
WheeledVehicle::SteerControl& nextSteerVsSpeed = value.SteerVsSpeed[Math::Clamp(i + 1, 0, steerVsSpeedCount - 1)];
float minSpeed = lastSteerVsSpeed.Speed;
float maxSpeed = nextSteerVsSpeed.Speed;
if (i + 1 < steerVsSpeedCount - 1)
steerVsSpeed.Speed = Math::Clamp(steerVsSpeed.Speed, minSpeed, maxSpeed);
else
steerVsSpeed.Speed = Math::Max(steerVsSpeed.Speed, minSpeed);
}
else if (steerVsSpeedCount > 1)
{
WheeledVehicle::SteerControl& nextSteerVsSpeed = value.SteerVsSpeed[i + 1];
steerVsSpeed.Speed = Math::Min(steerVsSpeed.Speed, nextSteerVsSpeed.Speed);
}
}
}
_driveControl = value;
}
void WheeledVehicle::SetWheels(const Array<Wheel>& value) void WheeledVehicle::SetWheels(const Array<Wheel>& value)
{ {
#if WITH_VEHICLE #if WITH_VEHICLE
@@ -49,9 +108,7 @@ void WheeledVehicle::SetWheels(const Array<Wheel>& value)
{ {
auto& oldWheel = _wheels.Get()[wheelIndex]; auto& oldWheel = _wheels.Get()[wheelIndex];
auto& newWheel = value.Get()[wheelIndex]; auto& newWheel = value.Get()[wheelIndex];
if (oldWheel.Type != newWheel.Type || if (Math::NotNearEqual(oldWheel.SuspensionForceOffset, newWheel.SuspensionForceOffset) || oldWheel.Collider != newWheel.Collider)
Math::NotNearEqual(oldWheel.SuspensionForceOffset, newWheel.SuspensionForceOffset) ||
oldWheel.Collider != newWheel.Collider)
{ {
softUpdate = false; softUpdate = false;
break; break;
@@ -111,6 +168,20 @@ void WheeledVehicle::SetGearbox(const GearboxSettings& value)
_gearbox = value; _gearbox = value;
} }
void WheeledVehicle::SetAntiRollBars(const Array<AntiRollBar>& value)
{
_antiRollBars = value;
#if WITH_VEHICLE
if (_vehicle)
PhysicsBackend::UpdateVehicleAntiRollBars(this);
#endif
}
const Array<WheeledVehicle::AntiRollBar>& WheeledVehicle::GetAntiRollBars() const
{
return _antiRollBars;
}
void WheeledVehicle::SetThrottle(float value) void WheeledVehicle::SetThrottle(float value)
{ {
_throttle = Math::Clamp(value, -1.0f, 1.0f); _throttle = Math::Clamp(value, -1.0f, 1.0f);
@@ -123,7 +194,10 @@ void WheeledVehicle::SetSteering(float value)
void WheeledVehicle::SetBrake(float value) void WheeledVehicle::SetBrake(float value)
{ {
_brake = Math::Saturate(value); value = Math::Saturate(value);
_brake = value;
_tankLeftBrake = value;
_tankRightBrake = value;
} }
void WheeledVehicle::SetHandbrake(float value) void WheeledVehicle::SetHandbrake(float value)
@@ -131,12 +205,36 @@ void WheeledVehicle::SetHandbrake(float value)
_handBrake = Math::Saturate(value); _handBrake = Math::Saturate(value);
} }
void WheeledVehicle::SetTankLeftThrottle(float value)
{
_tankLeftThrottle = Math::Clamp(value, -1.0f, 1.0f);
}
void WheeledVehicle::SetTankRightThrottle(float value)
{
_tankRightThrottle = Math::Clamp(value, -1.0f, 1.0f);
}
void WheeledVehicle::SetTankLeftBrake(float value)
{
_tankLeftBrake = Math::Saturate(value);
}
void WheeledVehicle::SetTankRightBrake(float value)
{
_tankRightBrake = Math::Saturate(value);
}
void WheeledVehicle::ClearInput() void WheeledVehicle::ClearInput()
{ {
_throttle = 0; _throttle = 0;
_steering = 0; _steering = 0;
_brake = 0; _brake = 0;
_handBrake = 0; _handBrake = 0;
_tankLeftThrottle = 0;
_tankRightThrottle = 0;
_tankLeftBrake = 0;
_tankRightBrake = 0;
} }
float WheeledVehicle::GetForwardSpeed() const float WheeledVehicle::GetForwardSpeed() const
@@ -223,7 +321,7 @@ void WheeledVehicle::Setup()
return; return;
// Release previous // Release previous
void* scene = GetPhysicsScene()->GetPhysicsScene(); void *scene = GetPhysicsScene()->GetPhysicsScene();
if (_vehicle) if (_vehicle)
{ {
PhysicsBackend::RemoveVehicle(scene, this); PhysicsBackend::RemoveVehicle(scene, this);
@@ -316,6 +414,21 @@ void WheeledVehicle::OnDebugDrawSelected()
} }
} }
// Draw anti roll bars axes
int wheelsCount = _wheels.Count();
for (int i = 0; i < GetAntiRollBars().Count(); i++)
{
int axleIndex = GetAntiRollBars()[i].Axle;
int leftWheelIndex = axleIndex * 2;
int rightWheelIndex = leftWheelIndex + 1;
if (leftWheelIndex >= wheelsCount || rightWheelIndex >= wheelsCount)
continue;
if (!_wheels[leftWheelIndex].Collider || !_wheels[rightWheelIndex].Collider)
continue;
DEBUG_DRAW_LINE(_wheels[leftWheelIndex].Collider->GetPosition(), _wheels[rightWheelIndex].Collider->GetPosition(), Color::Yellow, 0, false);
}
// Center of mass // Center of mass
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(_transform.LocalToWorld(_centerOfMassOffset), 10.0f), Color::Blue, 0, false); DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(_transform.LocalToWorld(_centerOfMassOffset), 10.0f), Color::Blue, 0, false);
@@ -324,7 +437,7 @@ void WheeledVehicle::OnDebugDrawSelected()
#endif #endif
void WheeledVehicle::Serialize(SerializeStream& stream, const void* otherObj) void WheeledVehicle::Serialize(SerializeStream& stream, const void *otherObj)
{ {
RigidBody::Serialize(stream, otherObj); RigidBody::Serialize(stream, otherObj);
@@ -332,30 +445,34 @@ void WheeledVehicle::Serialize(SerializeStream& stream, const void* otherObj)
SERIALIZE_MEMBER(DriveType, _driveType); SERIALIZE_MEMBER(DriveType, _driveType);
SERIALIZE_MEMBER(Wheels, _wheels); SERIALIZE_MEMBER(Wheels, _wheels);
SERIALIZE_MEMBER(DriveControl, _driveControl);
SERIALIZE(UseReverseAsBrake); SERIALIZE(UseReverseAsBrake);
SERIALIZE(UseAnalogSteering); SERIALIZE(UseAnalogSteering);
SERIALIZE_MEMBER(Engine, _engine); SERIALIZE_MEMBER(Engine, _engine);
SERIALIZE_MEMBER(Differential, _differential); SERIALIZE_MEMBER(Differential, _differential);
SERIALIZE_MEMBER(Gearbox, _gearbox); SERIALIZE_MEMBER(Gearbox, _gearbox);
SERIALIZE_MEMBER(AntiRollBars, _antiRollBars);
} }
void WheeledVehicle::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) void WheeledVehicle::Deserialize(DeserializeStream& stream, ISerializeModifier *modifier)
{ {
RigidBody::Deserialize(stream, modifier); RigidBody::Deserialize(stream, modifier);
DESERIALIZE_MEMBER(DriveType, _driveType); DESERIALIZE_MEMBER(DriveType, _driveType);
DESERIALIZE_MEMBER(Wheels, _wheels); DESERIALIZE_MEMBER(Wheels, _wheels);
DESERIALIZE_MEMBER(DriveControl, _driveControl);
DESERIALIZE(UseReverseAsBrake); DESERIALIZE(UseReverseAsBrake);
DESERIALIZE(UseAnalogSteering); DESERIALIZE(UseAnalogSteering);
DESERIALIZE_MEMBER(Engine, _engine); DESERIALIZE_MEMBER(Engine, _engine);
DESERIALIZE_MEMBER(Differential, _differential); DESERIALIZE_MEMBER(Differential, _differential);
DESERIALIZE_MEMBER(Gearbox, _gearbox); DESERIALIZE_MEMBER(Gearbox, _gearbox);
DESERIALIZE_MEMBER(AntiRollBars, _antiRollBars);
// [Deprecated on 13.06.2023, expires on 13.06.2025] // [Deprecated on 13.06.2023, expires on 13.06.2025]
_fixInvalidForwardDir |= modifier->EngineBuild < 6341; _fixInvalidForwardDir |= modifier->EngineBuild < 6341;
} }
void WheeledVehicle::OnColliderChanged(Collider* c) void WheeledVehicle::OnColliderChanged(Collider *c)
{ {
RigidBody::OnColliderChanged(c); RigidBody::OnColliderChanged(c);
@@ -378,7 +495,7 @@ void WheeledVehicle::OnActiveInTreeChanged()
Setup(); Setup();
} }
void WheeledVehicle::OnPhysicsSceneChanged(PhysicsScene* previous) void WheeledVehicle::OnPhysicsSceneChanged(PhysicsScene *previous)
{ {
RigidBody::OnPhysicsSceneChanged(previous); RigidBody::OnPhysicsSceneChanged(previous);
@@ -401,9 +518,10 @@ void WheeledVehicle::OnTransformChanged()
// Transform all vehicle children around the vehicle origin to fix the vehicle facing direction // Transform all vehicle children around the vehicle origin to fix the vehicle facing direction
const Quaternion rotationDelta(0.0f, -0.7071068f, 0.0f, 0.7071068f); const Quaternion rotationDelta(0.0f, -0.7071068f, 0.0f, 0.7071068f);
const Vector3 origin = GetPosition(); const Vector3 origin = GetPosition();
for (Actor* child : Children) for (Actor *child : Children)
{ {
Transform trans = child->GetTransform();; Transform trans = child->GetTransform();
;
const Vector3 pivotOffset = trans.Translation - origin; const Vector3 pivotOffset = trans.Translation - origin;
if (pivotOffset.IsZero()) if (pivotOffset.IsZero())
{ {
@@ -423,7 +541,7 @@ void WheeledVehicle::OnTransformChanged()
} }
} }
void WheeledVehicle::BeginPlay(SceneBeginData* data) void WheeledVehicle::BeginPlay(SceneBeginData *data)
{ {
RigidBody::BeginPlay(data); RigidBody::BeginPlay(data);
@@ -432,14 +550,14 @@ void WheeledVehicle::BeginPlay(SceneBeginData* data)
#endif #endif
#if USE_EDITOR #if USE_EDITOR
GetSceneRendering()->AddPhysicsDebug<WheeledVehicle, &WheeledVehicle::DrawPhysicsDebug>(this); GetSceneRendering()->AddPhysicsDebug<WheeledVehicle,& WheeledVehicle::DrawPhysicsDebug>(this);
#endif #endif
} }
void WheeledVehicle::EndPlay() void WheeledVehicle::EndPlay()
{ {
#if USE_EDITOR #if USE_EDITOR
GetSceneRendering()->RemovePhysicsDebug<WheeledVehicle, &WheeledVehicle::DrawPhysicsDebug>(this); GetSceneRendering()->RemovePhysicsDebug<WheeledVehicle,& WheeledVehicle::DrawPhysicsDebug>(this);
#endif #endif
#if WITH_VEHICLE #if WITH_VEHICLE

View File

@@ -26,6 +26,58 @@ API_CLASS(Attributes="ActorContextMenu(\"New/Physics/Wheeled Vehicle\"), ActorTo
DriveNW, DriveNW,
// Non-drivable vehicle. // Non-drivable vehicle.
NoDrive, NoDrive,
// Tank Drive. Can have more than 4 wheel. Not use steer, control acceleration for each tank track.
Tank,
};
/// <summary>
/// Vehicle driving types.
/// Used only on tanks to specify the drive mode.
/// </summary>
API_ENUM() enum class DriveModes
{
// Drive turning the vehicle using only one track
Standard,
// Drive turning the vehicle using all tracks inverse direction.
Special
};
/// <summary>
/// Storage the relationship between speed and steer.
/// </summary>
API_STRUCT() struct SteerControl : ISerializable
{
DECLARE_SCRIPTING_TYPE_MINIMAL(SteerControl);
API_AUTO_SERIALIZATION();
/// <summary>
/// The vehicle speed.
/// </summary>
API_FIELD(Attributes = "Limit(0)") float Speed;
/// <summary>
/// The target max steer of the speed.
/// </summary>
API_FIELD(Attributes = "Limit(0, 1)") float Steer;
/// <summary>
/// Create a Steer/Speed relationship structure.
/// </summary>
SteerControl()
{
Speed = 1000;
Steer = 1;
}
/// <summary>
/// Create a Steer/Speed relationship structure.
/// <param name="speed">The vehicle speed.</param>
/// <param name="steer">The target max steer of the speed.</param>
/// </summary>
SteerControl(float speed, float steer)
{
Speed = speed;
Steer = steer;
}
}; };
/// <summary> /// <summary>
@@ -71,6 +123,72 @@ API_CLASS(Attributes="ActorContextMenu(\"New/Physics/Wheeled Vehicle\"), ActorTo
OpenRearDrive, OpenRearDrive,
}; };
/// <summary>
/// Vehicle drive control settings.
/// </summary>
API_STRUCT() struct DriveControlSettings : ISerializable
{
DECLARE_SCRIPTING_TYPE_MINIMAL(DriveControlSettings);
API_AUTO_SERIALIZATION();
/// <summary>
/// Gets or sets the drive mode, used by vehicles specify the way of the tracks control.
/// </summary>
API_FIELD(Attributes="EditorOrder(0), EditorDisplay(\"Tanks\")") WheeledVehicle::DriveModes DriveMode = WheeledVehicle::DriveModes::Standard;
/// <summary>
/// Acceleration input sensitive.
/// </summary>
API_FIELD(Attributes="EditorOrder(10)") float RiseRateAcceleration = 6.0f;
/// <summary>
/// Deceleration input sensitive.
/// </summary>
API_FIELD(Attributes="EditorOrder(11)") float FallRateAcceleration = 10.0f;
/// <summary>
/// Brake input sensitive.
/// </summary>
API_FIELD(Attributes="EditorOrder(12)") float RiseRateBrake = 6.0f;
/// <summary>
/// Release brake sensitive.
/// </summary>
API_FIELD(Attributes="EditorOrder(13)") float FallRateBrake = 10.0f;
/// <summary>
/// Brake input sensitive.
/// </summary>
API_FIELD(Attributes="EditorOrder(14)") float RiseRateHandBrake = 12.0f;
/// <summary>
/// Release handbrake sensitive.
/// </summary>
API_FIELD(Attributes="EditorOrder(15)") float FallRateHandBrake = 12.0f;
/// <summary>
/// Steer input sensitive.
/// </summary>
API_FIELD(Attributes="EditorOrder(16)") float RiseRateSteer = 2.5f;
/// <summary>
/// Release steer input sensitive.
/// </summary>
API_FIELD(Attributes="EditorOrder(17)") float FallRateSteer = 5.0f;
/// <summary>
/// Vehicle control relationship between speed and steer. The higher is the speed,
/// decrease steer to make vehicle more maneuverable (limited only 4 relationships).
/// </summary>
API_FIELD() Array<WheeledVehicle::SteerControl> SteerVsSpeed = Array<WheeledVehicle::SteerControl>
{
SteerControl(800, 1.0f),
SteerControl(1500, 0.7f),
SteerControl(2500, 0.5f),
SteerControl(5000, 0.2f),
};
};
/// <summary> /// <summary>
/// Vehicle differential settings. /// Vehicle differential settings.
/// </summary> /// </summary>
@@ -128,6 +246,11 @@ API_CLASS(Attributes="ActorContextMenu(\"New/Physics/Wheeled Vehicle\"), ActorTo
/// </summary> /// </summary>
API_FIELD() bool AutoGear = true; API_FIELD() bool AutoGear = true;
/// <summary>
/// Number of gears to move to forward
/// </summary>
API_FIELD(Attributes = "Limit(1, 30)") int ForwardGearsRatios = 5;
/// <summary> /// <summary>
/// Time it takes to switch gear. Specified in seconds (s). /// Time it takes to switch gear. Specified in seconds (s).
/// </summary> /// </summary>
@@ -141,23 +264,6 @@ API_CLASS(Attributes="ActorContextMenu(\"New/Physics/Wheeled Vehicle\"), ActorTo
API_FIELD(Attributes="Limit(0)") float ClutchStrength = 10.0f; API_FIELD(Attributes="Limit(0)") float ClutchStrength = 10.0f;
}; };
/// <summary>
/// Vehicle wheel types.
/// </summary>
API_ENUM() enum class WheelTypes
{
// Left wheel of the front axle.
FrontLeft,
// Right wheel of the front axle.
FrontRight,
// Left wheel of the rear axle.
RearLeft,
// Right wheel of the rear axle.
RearRight,
// Non-drivable wheel.
NoDrive,
};
/// <summary> /// <summary>
/// Vehicle wheel settings. /// Vehicle wheel settings.
/// </summary> /// </summary>
@@ -166,11 +272,6 @@ API_CLASS(Attributes="ActorContextMenu(\"New/Physics/Wheeled Vehicle\"), ActorTo
DECLARE_SCRIPTING_TYPE_MINIMAL(Wheel); DECLARE_SCRIPTING_TYPE_MINIMAL(Wheel);
API_AUTO_SERIALIZATION(); API_AUTO_SERIALIZATION();
/// <summary>
/// Wheel placement type.
/// </summary>
API_FIELD(Attributes="EditorOrder(0)") WheelTypes Type = WheelTypes::FrontLeft;
/// <summary> /// <summary>
/// Combined mass of the wheel and the tire in kg. Typically, a wheel has mass between 20Kg and 80Kg but can be lower and higher depending on the vehicle. /// Combined mass of the wheel and the tire in kg. Typically, a wheel has mass between 20Kg and 80Kg but can be lower and higher depending on the vehicle.
/// </summary> /// </summary>
@@ -187,9 +288,9 @@ API_CLASS(Attributes="ActorContextMenu(\"New/Physics/Wheeled Vehicle\"), ActorTo
API_FIELD(Attributes="EditorOrder(3)") float Width = 20.0f; API_FIELD(Attributes="EditorOrder(3)") float Width = 20.0f;
/// <summary> /// <summary>
/// Max steer angle that can be achieved by the wheel (in degrees). /// Max steer angle that can be achieved by the wheel (in degrees, -180 to 180).
/// </summary> /// </summary>
API_FIELD(Attributes="Limit(0), EditorDisplay(\"Steering\"), EditorOrder(10)") float MaxSteerAngle = 0.0f; API_FIELD(Attributes="Limit(-180, 180), EditorDisplay(\"Steering\"), EditorOrder(10)") float MaxSteerAngle = 0.0f;
/// <summary> /// <summary>
/// Damping rate applied to wheel. Specified in kilograms metres-squared per second (kg m^2 s^-1). /// Damping rate applied to wheel. Specified in kilograms metres-squared per second (kg m^2 s^-1).
@@ -211,6 +312,11 @@ API_CLASS(Attributes="ActorContextMenu(\"New/Physics/Wheeled Vehicle\"), ActorTo
/// </summary> /// </summary>
API_FIELD(Attributes="EditorOrder(4)") ScriptingObjectReference<Collider> Collider; API_FIELD(Attributes="EditorOrder(4)") ScriptingObjectReference<Collider> Collider;
/// <summary>
/// Spring sprung mass force multiplier.
/// </summary>
API_FIELD(Attributes="Limit(0.01f), EditorDisplay(\"Suspension\"), EditorOrder(19)") float SprungMassMultiplier = 1.0f;
/// <summary> /// <summary>
/// Spring damper rate of suspension unit. /// Spring damper rate of suspension unit.
/// </summary> /// </summary>
@@ -312,6 +418,25 @@ API_CLASS(Attributes="ActorContextMenu(\"New/Physics/Wheeled Vehicle\"), ActorTo
#endif #endif
}; };
/// <summary>
/// Vehicle axle anti roll bar.
/// </summary>
API_STRUCT() struct AntiRollBar : ISerializable
{
DECLARE_SCRIPTING_TYPE_MINIMAL(AntiRollBar);
API_AUTO_SERIALIZATION();
/// <summary>
/// The specific axle with wheels to apply anti roll.
/// </summary>
API_FIELD() int Axle;
/// <summary>
/// The anti roll stiffness.
/// </summary>
API_FIELD() float Stiffness;
};
private: private:
struct WheelData struct WheelData
{ {
@@ -323,8 +448,10 @@ private:
void* _vehicle = nullptr; void* _vehicle = nullptr;
DriveTypes _driveType = DriveTypes::Drive4W, _driveTypeCurrent; DriveTypes _driveType = DriveTypes::Drive4W, _driveTypeCurrent;
Array<WheelData, FixedAllocation<20>> _wheelsData; Array<WheelData, FixedAllocation<20>> _wheelsData;
float _throttle = 0.0f, _steering = 0.0f, _brake = 0.0f, _handBrake = 0.0f; float _throttle = 0.0f, _steering = 0.0f, _brake = 0.0f, _handBrake = 0.0f, _tankLeftThrottle = 0.0f, _tankRightThrottle = 0.0f, _tankLeftBrake = 0.0f, _tankRightBrake = 0.0f;
Array<Wheel> _wheels; Array<WheeledVehicle::Wheel> _wheels;
Array<WheeledVehicle::AntiRollBar> _antiRollBars;
DriveControlSettings _driveControl;
EngineSettings _engine; EngineSettings _engine;
DifferentialSettings _differential; DifferentialSettings _differential;
GearboxSettings _gearbox; GearboxSettings _gearbox;
@@ -347,7 +474,7 @@ public:
/// <summary> /// <summary>
/// Gets the vehicle driving model type. /// Gets the vehicle driving model type.
/// </summary> /// </summary>
API_PROPERTY(Attributes="EditorOrder(1), EditorDisplay(\"Vehicle\")") DriveTypes GetDriveType() const; API_PROPERTY(Attributes="EditorOrder(2), EditorDisplay(\"Vehicle\")") DriveTypes GetDriveType() const;
/// <summary> /// <summary>
/// Sets the vehicle driving model type. /// Sets the vehicle driving model type.
@@ -357,7 +484,17 @@ public:
/// <summary> /// <summary>
/// Gets the vehicle wheels settings. /// Gets the vehicle wheels settings.
/// </summary> /// </summary>
API_PROPERTY(Attributes="EditorOrder(2), EditorDisplay(\"Vehicle\")") const Array<Wheel>& GetWheels() const; API_PROPERTY(Attributes="EditorOrder(4), EditorDisplay(\"Vehicle\")") const Array<Wheel>& GetWheels() const;
/// <summary>
/// Gets the vehicle drive control settings.
/// </summary>
API_PROPERTY(Attributes = "EditorOrder(5), EditorDisplay(\"Vehicle\")") DriveControlSettings GetDriveControl() const;
/// <summary>
/// Sets the vehicle drive control settings.
/// </summary>
API_PROPERTY() void SetDriveControl(DriveControlSettings& value);
/// <summary> /// <summary>
/// Sets the vehicle wheels settings. /// Sets the vehicle wheels settings.
@@ -367,7 +504,7 @@ public:
/// <summary> /// <summary>
/// Gets the vehicle engine settings. /// Gets the vehicle engine settings.
/// </summary> /// </summary>
API_PROPERTY(Attributes="EditorOrder(3), EditorDisplay(\"Vehicle\")") EngineSettings GetEngine() const; API_PROPERTY(Attributes="EditorOrder(6), EditorDisplay(\"Vehicle\")") EngineSettings GetEngine() const;
/// <summary> /// <summary>
/// Sets the vehicle engine settings. /// Sets the vehicle engine settings.
@@ -377,7 +514,7 @@ public:
/// <summary> /// <summary>
/// Gets the vehicle differential settings. /// Gets the vehicle differential settings.
/// </summary> /// </summary>
API_PROPERTY(Attributes="EditorOrder(4), EditorDisplay(\"Vehicle\")") DifferentialSettings GetDifferential() const; API_PROPERTY(Attributes="EditorOrder(7), EditorDisplay(\"Vehicle\")") DifferentialSettings GetDifferential() const;
/// <summary> /// <summary>
/// Sets the vehicle differential settings. /// Sets the vehicle differential settings.
@@ -387,13 +524,23 @@ public:
/// <summary> /// <summary>
/// Gets the vehicle gearbox settings. /// Gets the vehicle gearbox settings.
/// </summary> /// </summary>
API_PROPERTY(Attributes="EditorOrder(5), EditorDisplay(\"Vehicle\")") GearboxSettings GetGearbox() const; API_PROPERTY(Attributes="EditorOrder(8), EditorDisplay(\"Vehicle\")") GearboxSettings GetGearbox() const;
/// <summary> /// <summary>
/// Sets the vehicle gearbox settings. /// Sets the vehicle gearbox settings.
/// </summary> /// </summary>
API_PROPERTY() void SetGearbox(const GearboxSettings& value); API_PROPERTY() void SetGearbox(const GearboxSettings& value);
// <summary>
/// Sets axles anti roll bars to increase vehicle estability.
/// </summary>
API_PROPERTY() void SetAntiRollBars(const Array<AntiRollBar>& value);
// <summary>
/// Gets axles anti roll bars.
/// </summary>
API_PROPERTY() const Array<AntiRollBar>& GetAntiRollBars() const;
public: public:
/// <summary> /// <summary>
/// Sets the input for vehicle throttle. It is the analog accelerator pedal value in range (0,1) where 1 represents the pedal fully pressed and 0 represents the pedal in its rest state. /// Sets the input for vehicle throttle. It is the analog accelerator pedal value in range (0,1) where 1 represents the pedal fully pressed and 0 represents the pedal in its rest state.
@@ -419,6 +566,32 @@ public:
/// <param name="value">The value (0,1 range).</param> /// <param name="value">The value (0,1 range).</param>
API_FUNCTION() void SetHandbrake(float value); API_FUNCTION() void SetHandbrake(float value);
/// <summary>
/// Sets the input for tank left track throttle. It is the analog accelerator pedal value in range (-1,1) where 1 represents the pedal fully pressed to move to forward, 0 to represents the
/// pedal in its rest state and -1 represents the pedal fully pressed to move to backward. The track direction will be inverted if the vehicle current gear is rear.
/// </summary>
/// <param name="value">The value (-1,1 range).</param>
API_FUNCTION() void SetTankLeftThrottle(float value);
/// <summary>
/// Sets the input for tank right track throttle. It is the analog accelerator pedal value in range (-1,1) where 1 represents the pedal fully pressed to move to forward, 0 to represents the
/// pedal in its rest state and -1 represents the pedal fully pressed to move to backward. The track direction will be inverted if the vehicle current gear is rear.
/// </summary>
/// <param name="value">The value (-1,1 range).</param>
API_FUNCTION() void SetTankRightThrottle(float value);
/// <summary>
/// Sets the input for tank brakes the left track. Brake is the analog brake pedal value in range (0,1) where 1 represents the pedal fully pressed and 0 represents the pedal in its rest state.
/// </summary>
/// <param name="value">The value (0,1 range).</param>
API_FUNCTION() void SetTankLeftBrake(float value);
/// <summary>
/// Sets the input for tank brakes the right track. Brake is the analog brake pedal value in range (0,1) where 1 represents the pedal fully pressed and 0 represents the pedal in its rest state.
/// </summary>
/// <param name="value">The value (0,1 range).</param>
API_FUNCTION() void SetTankRightBrake(float value);
/// <summary> /// <summary>
/// Clears all the vehicle control inputs to the default values (throttle, steering, breaks). /// Clears all the vehicle control inputs to the default values (throttle, steering, breaks).
/// </summary> /// </summary>

View File

@@ -37,7 +37,9 @@
#include <ThirdParty/PhysX/vehicle/PxVehicleNoDrive.h> #include <ThirdParty/PhysX/vehicle/PxVehicleNoDrive.h>
#include <ThirdParty/PhysX/vehicle/PxVehicleDrive4W.h> #include <ThirdParty/PhysX/vehicle/PxVehicleDrive4W.h>
#include <ThirdParty/PhysX/vehicle/PxVehicleDriveNW.h> #include <ThirdParty/PhysX/vehicle/PxVehicleDriveNW.h>
#include <ThirdParty/PhysX/vehicle/PxVehicleDriveTank.h>
#include <ThirdParty/PhysX/vehicle/PxVehicleUtilSetup.h> #include <ThirdParty/PhysX/vehicle/PxVehicleUtilSetup.h>
#include <ThirdParty/PhysX/vehicle/PxVehicleComponents.h>
#include <ThirdParty/PhysX/PxFiltering.h> #include <ThirdParty/PhysX/PxFiltering.h>
#endif #endif
#if WITH_CLOTH #if WITH_CLOTH
@@ -57,7 +59,6 @@
#if WITH_PVD #if WITH_PVD
#include <ThirdParty/PhysX/pvd/PxPvd.h> #include <ThirdParty/PhysX/pvd/PxPvd.h>
#endif #endif
// Temporary memory size used by the PhysX during the simulation. Must be multiply of 4kB and 16bit aligned. // Temporary memory size used by the PhysX during the simulation. Must be multiply of 4kB and 16bit aligned.
#define PHYSX_SCRATCH_BLOCK_SIZE (1024 * 128) #define PHYSX_SCRATCH_BLOCK_SIZE (1024 * 128)
@@ -974,7 +975,6 @@ void PhysicalMaterial::UpdatePhysicsMaterial()
bool CollisionCooking::CookConvexMesh(CookingInput& input, BytesContainer& output) bool CollisionCooking::CookConvexMesh(CookingInput& input, BytesContainer& output)
{ {
PROFILE_CPU();
ENSURE_CAN_COOK; ENSURE_CAN_COOK;
if (input.VertexCount == 0) if (input.VertexCount == 0)
LOG(Warning, "Empty mesh data for collision cooking."); LOG(Warning, "Empty mesh data for collision cooking.");
@@ -1018,7 +1018,6 @@ bool CollisionCooking::CookConvexMesh(CookingInput& input, BytesContainer& outpu
bool CollisionCooking::CookTriangleMesh(CookingInput& input, BytesContainer& output) bool CollisionCooking::CookTriangleMesh(CookingInput& input, BytesContainer& output)
{ {
PROFILE_CPU();
ENSURE_CAN_COOK; ENSURE_CAN_COOK;
if (input.VertexCount == 0 || input.IndexCount == 0) if (input.VertexCount == 0 || input.IndexCount == 0)
LOG(Warning, "Empty mesh data for collision cooking."); LOG(Warning, "Empty mesh data for collision cooking.");
@@ -1053,7 +1052,6 @@ bool CollisionCooking::CookTriangleMesh(CookingInput& input, BytesContainer& out
bool CollisionCooking::CookHeightField(int32 cols, int32 rows, const PhysicsBackend::HeightFieldSample* data, WriteStream& stream) bool CollisionCooking::CookHeightField(int32 cols, int32 rows, const PhysicsBackend::HeightFieldSample* data, WriteStream& stream)
{ {
PROFILE_CPU();
ENSURE_CAN_COOK; ENSURE_CAN_COOK;
PxHeightFieldDesc heightFieldDesc; PxHeightFieldDesc heightFieldDesc;
@@ -1399,113 +1397,234 @@ void PhysicsBackend::EndSimulateScene(void* scene)
WheelVehiclesCache.Add(drive); WheelVehiclesCache.Add(drive);
wheelsCount += drive->mWheelsSimData.getNbWheels(); wheelsCount += drive->mWheelsSimData.getNbWheels();
const float deadZone = 0.1f;
bool isTank = wheelVehicle->_driveType == WheeledVehicle::DriveTypes::Tank;
float throttle = wheelVehicle->_throttle; float throttle = wheelVehicle->_throttle;
float steering = wheelVehicle->_steering;
float brake = wheelVehicle->_brake; float brake = wheelVehicle->_brake;
float leftThrottle = wheelVehicle->_tankLeftThrottle;
float rightThrottle = wheelVehicle->_tankRightThrottle;
float leftBrake = Math::Max(wheelVehicle->_tankLeftBrake, wheelVehicle->_handBrake);
float rightBrake = Math::Max(wheelVehicle->_tankRightBrake, wheelVehicle->_handBrake);
WheeledVehicle::DriveModes vehicleDriveMode = wheelVehicle->_driveControl.DriveMode;
if (isTank)
{
// Converting default vehicle controls to tank controls.
if (throttle != 0 || steering != 0)
{
leftThrottle = Math::Clamp(throttle + steering, -1.0f, 1.0f);
rightThrottle = Math::Clamp(throttle - steering, -1.0f, 1.0f);
}
}
// Converting special tank drive mode to standard tank mode when is turning.
if (isTank && vehicleDriveMode == WheeledVehicle::DriveModes::Standard)
{
// Special inputs when turning vehicle -1 1 to left or 1 -1 to turn right
// to:
// Standard inputs when turning vehicle 0 1 to left or 1 0 to turn right
if (leftThrottle < -deadZone && rightThrottle > deadZone)
{
leftThrottle = 0;
leftBrake = 1;
}
else if (leftThrottle > deadZone && rightThrottle < -deadZone)
{
rightThrottle = 0;
rightBrake = 1;
}
}
if (wheelVehicle->UseReverseAsBrake) if (wheelVehicle->UseReverseAsBrake)
{ {
const float invalidDirectionThreshold = 80.0f; const float invalidDirectionThreshold = 80.0f;
const float breakThreshold = 8.0f; const float breakThreshold = 8.0f;
const float forwardSpeed = wheelVehicle->GetForwardSpeed(); const float forwardSpeed = wheelVehicle->GetForwardSpeed();
int currentGear = wheelVehicle->GetCurrentGear();
// Tank tracks direction: 1 forward -1 backward 0 neutral
bool toForward = false;
toForward |= throttle > deadZone;
toForward |= (leftThrottle > deadZone) && (rightThrottle > deadZone); // 1 1
bool toBackward = false;
toBackward |= throttle < -deadZone;
toBackward |= (leftThrottle < -deadZone) && (rightThrottle < -deadZone); // -1 -1
toBackward |= (leftThrottle < -deadZone) && (rightThrottle < deadZone); // -1 0
toBackward |= (leftThrottle < deadZone) && (rightThrottle < -deadZone); // 0 -1
bool isTankTurning = false;
if (isTank)
{
isTankTurning |= leftThrottle > deadZone && rightThrottle < -deadZone; // 1 -1
isTankTurning |= leftThrottle < -deadZone && rightThrottle > deadZone; // -1 1
isTankTurning |= leftThrottle < deadZone && rightThrottle > deadZone; // 0 1
isTankTurning |= leftThrottle > deadZone && rightThrottle < deadZone; // 1 0
isTankTurning |= leftThrottle < -deadZone && rightThrottle < deadZone; // -1 0
isTankTurning |= leftThrottle < deadZone && rightThrottle < -deadZone; // 0 -1
if (toForward || toBackward)
{
isTankTurning = false;
}
}
// Automatic gear change when changing driving direction // Automatic gear change when changing driving direction
if (Math::Abs(forwardSpeed) < invalidDirectionThreshold) if (Math::Abs(forwardSpeed) < invalidDirectionThreshold)
{ {
if (throttle < -ZeroTolerance && wheelVehicle->GetCurrentGear() >= 0 && wheelVehicle->GetTargetGear() >= 0) int targetGear = wheelVehicle->GetTargetGear();
if (toBackward && currentGear > 0 && targetGear >= 0)
{ {
wheelVehicle->SetCurrentGear(-1); currentGear = -1;
} }
else if (throttle > ZeroTolerance && wheelVehicle->GetCurrentGear() <= 0 && wheelVehicle->GetTargetGear() <= 0) else if (!toBackward && currentGear <= 0 && targetGear <= 0)
{ {
wheelVehicle->SetCurrentGear(1); currentGear = 1;
}
else if (isTankTurning && currentGear <= 0)
{
currentGear = 1;
}
if (wheelVehicle->GetCurrentGear() != currentGear)
{
wheelVehicle->SetCurrentGear(currentGear);
} }
} }
// Automatic break when changing driving direction // Automatic break when changing driving direction
if (throttle > 0.0f) if (toForward)
{ {
if (forwardSpeed < -invalidDirectionThreshold) if (forwardSpeed < -invalidDirectionThreshold)
{ {
brake = 1.0f; brake = 1.0f;
leftBrake = 1.0f;
rightBrake = 1.0f;
} }
} }
else if (throttle < 0.0f) else if (toBackward)
{ {
if (forwardSpeed > invalidDirectionThreshold) if (forwardSpeed > invalidDirectionThreshold)
{ {
brake = 1.0f; brake = 1.0f;
leftBrake = 1.0f;
rightBrake = 1.0f;
} }
} }
else else
{ {
if (forwardSpeed < breakThreshold && forwardSpeed > -breakThreshold) if (forwardSpeed < breakThreshold && forwardSpeed > -breakThreshold && !isTankTurning) // not accelerating, very slow speed -> stop
{ {
brake = 1.0f; brake = 1.0f;
leftBrake = 1.0f;
rightBrake = 1.0f;
} }
} }
// Block throttle if user is changing driving direction // Block throttle if user is changing driving direction
if ((throttle > 0.0f && wheelVehicle->GetTargetGear() < 0) || (throttle < 0.0f && wheelVehicle->GetTargetGear() > 0)) if ((toForward && currentGear < 0) || (toBackward && currentGear > 0))
{ {
throttle = 0.0f; throttle = 0.0f;
leftThrottle = 0;
rightThrottle = 0;
} }
throttle = Math::Abs(throttle); throttle = Math::Abs(throttle);
if (isTank)
{
// invert acceleration when moving to backward because tank inputs can be < 0
if (currentGear < 0)
{
float lt = -leftThrottle;
float rt = -rightThrottle;
float lb = leftBrake;
float rb = rightBrake;
leftThrottle = rt;
rightThrottle = lt;
leftBrake = rb;
rightBrake = lb;
}
}
} }
else else
{ {
throttle = Math::Max(throttle, 0.0f); throttle = Math::Max(throttle, 0.0f);
} }
// @formatter:off
// Reference: PhysX SDK docs // Force brake the another side track to turn more faster.
// TODO: expose input control smoothing data if (Math::Abs(leftThrottle) > deadZone && Math::Abs(rightThrottle) < deadZone)
static constexpr PxVehiclePadSmoothingData padSmoothing =
{ {
{ rightBrake = 1.0f;
6.0f, // rise rate eANALOG_INPUT_ACCEL }
6.0f, // rise rate eANALOG_INPUT_BRAKE if (Math::Abs(rightThrottle) > deadZone && Math::Abs(leftThrottle) < deadZone)
12.0f, // rise rate eANALOG_INPUT_HANDBRAKE {
2.5f, // rise rate eANALOG_INPUT_STEER_LEFT leftBrake = 1.0f;
2.5f, // rise rate eANALOG_INPUT_STEER_RIGHT }
},
{ // Smooth input controls
10.0f, // fall rate eANALOG_INPUT_ACCEL PxVehiclePadSmoothingData padSmoothing =
10.0f, // fall rate eANALOG_INPUT_BRAKE {
12.0f, // fall rate eANALOG_INPUT_HANDBRAKE {
5.0f, // fall rate eANALOG_INPUT_STEER_LEFT wheelVehicle->_driveControl.RiseRateAcceleration, // rise rate eANALOG_INPUT_ACCEL
5.0f, // fall rate eANALOG_INPUT_STEER_RIGHT wheelVehicle->_driveControl.RiseRateBrake, // rise rate eANALOG_INPUT_BRAKE
} wheelVehicle->_driveControl.RiseRateHandBrake, // rise rate eANALOG_INPUT_HANDBRAKE
}; wheelVehicle->_driveControl.RiseRateSteer, // rise rate eANALOG_INPUT_STEER_LEFT
static constexpr PxVehicleKeySmoothingData keySmoothing = wheelVehicle->_driveControl.RiseRateSteer, // rise rate eANALOG_INPUT_STEER_RIGHT
},
{
wheelVehicle->_driveControl.FallRateAcceleration, // fall rate eANALOG_INPUT_ACCEL
wheelVehicle->_driveControl.FallRateBrake, // fall rate eANALOG_INPUT_BRAKE
wheelVehicle->_driveControl.FallRateHandBrake, // fall rate eANALOG_INPUT_HANDBRAKE
wheelVehicle->_driveControl.FallRateSteer, // fall rate eANALOG_INPUT_STEER_LEFT
wheelVehicle->_driveControl.FallRateSteer, // fall rate eANALOG_INPUT_STEER_RIGHT
}
};
PxVehicleKeySmoothingData keySmoothing =
{ {
{ {
3.0f, // rise rate eANALOG_INPUT_ACCEL wheelVehicle->_driveControl.RiseRateAcceleration, // rise rate eANALOG_INPUT_ACCEL
3.0f, // rise rate eANALOG_INPUT_BRAKE wheelVehicle->_driveControl.RiseRateBrake, // rise rate eANALOG_INPUT_BRAKE
10.0f, // rise rate eANALOG_INPUT_HANDBRAKE wheelVehicle->_driveControl.RiseRateHandBrake, // rise rate eANALOG_INPUT_HANDBRAKE
2.5f, // rise rate eANALOG_INPUT_STEER_LEFT wheelVehicle->_driveControl.RiseRateSteer, // rise rate eANALOG_INPUT_STEER_LEFT
2.5f, // rise rate eANALOG_INPUT_STEER_RIGHT wheelVehicle->_driveControl.RiseRateSteer, // rise rate eANALOG_INPUT_STEER_RIGHT
}, },
{ {
5.0f, // fall rate eANALOG_INPUT__ACCEL wheelVehicle->_driveControl.FallRateAcceleration, // fall rate eANALOG_INPUT_ACCEL
5.0f, // fall rate eANALOG_INPUT__BRAKE wheelVehicle->_driveControl.FallRateBrake, // fall rate eANALOG_INPUT_BRAKE
10.0f, // fall rate eANALOG_INPUT__HANDBRAKE wheelVehicle->_driveControl.FallRateHandBrake, // fall rate eANALOG_INPUT_HANDBRAKE
5.0f, // fall rate eANALOG_INPUT_STEER_LEFT wheelVehicle->_driveControl.FallRateSteer, // fall rate eANALOG_INPUT_STEER_LEFT
5.0f // fall rate eANALOG_INPUT_STEER_RIGHT wheelVehicle->_driveControl.FallRateSteer, // fall rate eANALOG_INPUT_STEER_RIGHT
} }
}; };
// Reference: PhysX SDK docs
// TODO: expose steer vs forward curve into per-vehicle (up to 8 points, values clamped into 0/1 range) // Reduce steer by speed to make vehicle more easier to maneuver
static constexpr PxF32 steerVsForwardSpeedData[] =
constexpr int steerVsSpeedN = 8;
PxF32 steerVsForwardSpeedData[steerVsSpeedN];
const int lastSteerVsSpeedIndex = wheelVehicle->_driveControl.SteerVsSpeed.Count() - 1;
int steerVsSpeedIndex = 0;
// Steer vs speed data structure example:
// array:
// speed, steer
// 1000, 1.0,
// 2000, 0.7,
// 5000, 0.5,
// ..
// fill the steerVsForwardSpeedData with the speed and steer
for (int i = 0; i < 8; i += 2)
{ {
0.0f, 1.0f, steerVsForwardSpeedData[i] = wheelVehicle->_driveControl.SteerVsSpeed[steerVsSpeedIndex].Speed;
20.0f, 0.9f, steerVsForwardSpeedData[i + 1] = wheelVehicle->_driveControl.SteerVsSpeed[steerVsSpeedIndex].Steer;
65.0f, 0.8f, steerVsSpeedIndex = Math::Min(steerVsSpeedIndex + 1, lastSteerVsSpeedIndex);
120.0f, 0.7f, }
PX_MAX_F32, PX_MAX_F32, const PxFixedSizeLookupTable<steerVsSpeedN> steerVsForwardSpeed(steerVsForwardSpeedData, 4);
PX_MAX_F32, PX_MAX_F32,
PX_MAX_F32, PX_MAX_F32,
PX_MAX_F32, PX_MAX_F32,
};
const PxFixedSizeLookupTable<8> steerVsForwardSpeed(steerVsForwardSpeedData, 4);
// @formatter:on // @formatter:on
if (wheelVehicle->UseAnalogSteering) if (wheelVehicle->UseAnalogSteering)
{ {
@@ -1531,11 +1650,23 @@ void PhysicsBackend::EndSimulateScene(void* scene)
PxVehicleDriveNWSmoothAnalogRawInputsAndSetAnalogInputs(padSmoothing, steerVsForwardSpeed, rawInputData, scenePhysX->LastDeltaTime, false, *(PxVehicleDriveNW*)drive); PxVehicleDriveNWSmoothAnalogRawInputsAndSetAnalogInputs(padSmoothing, steerVsForwardSpeed, rawInputData, scenePhysX->LastDeltaTime, false, *(PxVehicleDriveNW*)drive);
break; break;
} }
case WheeledVehicle::DriveTypes::Tank:
{
PxVehicleDriveTankRawInputData driveMode = vehicleDriveMode == WheeledVehicle::DriveModes::Standard ? PxVehicleDriveTankControlModel::eSTANDARD : PxVehicleDriveTankControlModel::eSPECIAL;
PxVehicleDriveTankRawInputData rawInputData = PxVehicleDriveTankRawInputData(driveMode);
rawInputData.setAnalogAccel(Math::Max(Math::Abs(leftThrottle), Math::Abs(rightThrottle)));
rawInputData.setAnalogLeftBrake(leftBrake);
rawInputData.setAnalogRightBrake(rightBrake);
rawInputData.setAnalogLeftThrust(leftThrottle);
rawInputData.setAnalogRightThrust(rightThrottle);
PxVehicleDriveTankSmoothAnalogRawInputsAndSetAnalogInputs(padSmoothing, rawInputData, scenePhysX->LastDeltaTime, *(PxVehicleDriveTank*)drive);
break;
}
} }
} }
else else
{ {
const float deadZone = 0.1f;
switch (wheelVehicle->_driveTypeCurrent) switch (wheelVehicle->_driveTypeCurrent)
{ {
case WheeledVehicle::DriveTypes::Drive4W: case WheeledVehicle::DriveTypes::Drive4W:
@@ -1560,6 +1691,26 @@ void PhysicsBackend::EndSimulateScene(void* scene)
PxVehicleDriveNWSmoothDigitalRawInputsAndSetAnalogInputs(keySmoothing, steerVsForwardSpeed, rawInputData, scenePhysX->LastDeltaTime, false, *(PxVehicleDriveNW*)drive); PxVehicleDriveNWSmoothDigitalRawInputsAndSetAnalogInputs(keySmoothing, steerVsForwardSpeed, rawInputData, scenePhysX->LastDeltaTime, false, *(PxVehicleDriveNW*)drive);
break; break;
} }
case WheeledVehicle::DriveTypes::Tank:
{
// Convert analogic inputs to digital inputs.
leftThrottle = Math::Round(leftThrottle);
rightThrottle = Math::Round(rightThrottle);
leftBrake = Math::Round(leftBrake);
rightBrake = Math::Round(rightBrake);
PxVehicleDriveTankRawInputData driveMode = vehicleDriveMode == WheeledVehicle::DriveModes::Standard ? PxVehicleDriveTankControlModel::eSTANDARD : PxVehicleDriveTankControlModel::eSPECIAL;
PxVehicleDriveTankRawInputData rawInputData = PxVehicleDriveTankRawInputData(driveMode);
rawInputData.setAnalogAccel(Math::Max(Math::Abs(leftThrottle), Math::Abs(rightThrottle)));
rawInputData.setAnalogLeftBrake(leftBrake);
rawInputData.setAnalogRightBrake(rightBrake);
rawInputData.setAnalogLeftThrust(leftThrottle);
rawInputData.setAnalogRightThrust(rightThrottle);
// Needs to pass analogic values to vehicle to maintein current moviment direction because digital inputs accept only true/false values to tracks thrust instead of -1 to 1
PxVehicleDriveTankSmoothAnalogRawInputsAndSetAnalogInputs(padSmoothing, rawInputData, scenePhysX->LastDeltaTime, *(PxVehicleDriveTank *)drive);
break;
}
} }
} }
} }
@@ -2813,7 +2964,7 @@ void PhysicsBackend::SetHingeJointLimit(void* joint, const LimitAngularRange& va
void PhysicsBackend::SetHingeJointDrive(void* joint, const HingeJointDrive& value) void PhysicsBackend::SetHingeJointDrive(void* joint, const HingeJointDrive& value)
{ {
auto jointPhysX = (PxRevoluteJoint*)joint; auto jointPhysX = (PxRevoluteJoint*)joint;
jointPhysX->setDriveVelocity(value.Velocity); jointPhysX->setDriveVelocity(Math::Max(value.Velocity, 0.0f));
jointPhysX->setDriveForceLimit(Math::Max(value.ForceLimit, 0.0f)); jointPhysX->setDriveForceLimit(Math::Max(value.ForceLimit, 0.0f));
jointPhysX->setDriveGearRatio(Math::Max(value.GearRatio, 0.0f)); jointPhysX->setDriveGearRatio(Math::Max(value.GearRatio, 0.0f));
jointPhysX->setRevoluteJointFlag(PxRevoluteJointFlag::eDRIVE_FREESPIN, value.FreeSpin); jointPhysX->setRevoluteJointFlag(PxRevoluteJointFlag::eDRIVE_FREESPIN, value.FreeSpin);
@@ -3096,18 +3247,189 @@ int32 PhysicsBackend::MoveController(void* controller, void* shape, const Vector
#if WITH_VEHICLE #if WITH_VEHICLE
bool SortWheels(WheeledVehicle::Wheel const& a, WheeledVehicle::Wheel const& b) PxVehicleDifferential4WData CreatePxVehicleDifferential4WData(const WheeledVehicle::DifferentialSettings& settings)
{ {
return (int32)a.Type < (int32)b.Type; PxVehicleDifferential4WData differential4WData;
differential4WData.mType = (PxVehicleDifferential4WData::Enum)settings.Type;
differential4WData.mFrontRearSplit = settings.FrontRearSplit;
differential4WData.mFrontLeftRightSplit = settings.FrontLeftRightSplit;
differential4WData.mRearLeftRightSplit = settings.RearLeftRightSplit;
differential4WData.mCentreBias = settings.CentreBias;
differential4WData.mFrontBias = settings.FrontBias;
differential4WData.mRearBias = settings.RearBias;
return differential4WData;
}
PxVehicleDifferentialNWData CreatePxVehicleDifferentialNWData(const WheeledVehicle::DifferentialSettings& settings, const Array<WheeledVehicle::Wheel*, FixedAllocation<PX_MAX_NB_WHEELS>>& wheels)
{
PxVehicleDifferentialNWData differentialNwData;
for (int32 i = 0; i < wheels.Count(); i++)
differentialNwData.setDrivenWheel(i, true);
return differentialNwData;
}
PxVehicleEngineData CreatePxVehicleEngineData(const WheeledVehicle::EngineSettings& settings)
{
PxVehicleEngineData engineData;
engineData.mMOI = M2ToCm2(settings.MOI);
engineData.mPeakTorque = M2ToCm2(settings.MaxTorque);
engineData.mMaxOmega = RpmToRadPerS(settings.MaxRotationSpeed);
engineData.mDampingRateFullThrottle = M2ToCm2(0.15f);
engineData.mDampingRateZeroThrottleClutchEngaged = M2ToCm2(2.0f);
engineData.mDampingRateZeroThrottleClutchDisengaged = M2ToCm2(0.35f);
return engineData;
}
PxVehicleGearsData CreatePxVehicleGearsData(const WheeledVehicle::GearboxSettings& settings)
{
PxVehicleGearsData gears;
// Total gears is forward gears + neutral/rear gears
int gearsAmount = settings.ForwardGearsRatios + 2;
// Setup gears torque/top speed relations
// Higher torque = less speed
// Low torque = high speed
// Example:
// ForwardGearsRatios = 4
// GearRev = -5
// Gear0 = 0
// Gear1 = 4.2
// Gear2 = 3.4
// Gear3 = 2.6
// Gear4 = 1.8
// Gear5 = 1
gears.mRatios[0] = -(gearsAmount - 2); // reverse
gears.mRatios[1] = 0; // neutral
// Setup all gears except neutral and reverse
for (int i = gearsAmount; i > 2; i--) {
float gearsRatios = settings.ForwardGearsRatios;
float currentGear = i - 2;
gears.mRatios[i] = Math::Lerp(gearsRatios, 1.0f, (currentGear / gearsRatios));
}
// reset unused gears
for (int i = gearsAmount; i < PxVehicleGearsData::eGEARSRATIO_COUNT; i++)
gears.mRatios[i] = 0;
gears.mSwitchTime = Math::Max(settings.SwitchTime, 0.0f);
gears.mNbRatios = Math::Clamp(gearsAmount, 2, (int)PxVehicleGearsData::eGEARSRATIO_COUNT);
return gears;
}
PxVehicleAutoBoxData CreatePxVehicleAutoBoxData()
{
return PxVehicleAutoBoxData();
}
PxVehicleClutchData CreatePxVehicleClutchData(const WheeledVehicle::GearboxSettings& settings)
{
PxVehicleClutchData clutch;
clutch.mStrength = M2ToCm2(settings.ClutchStrength);
return clutch;
}
PxVehicleSuspensionData CreatePxVehicleSuspensionData(const WheeledVehicle::Wheel& settings, const PxReal wheelSprungMass)
{
PxVehicleSuspensionData suspensionData;
const float suspensionFrequency = 7.0f;
suspensionData.mMaxCompression = settings.SuspensionMaxRaise;
suspensionData.mMaxDroop = settings.SuspensionMaxDrop;
suspensionData.mSprungMass = wheelSprungMass * settings.SprungMassMultiplier;
suspensionData.mSpringStrength = Math::Square(suspensionFrequency) * suspensionData.mSprungMass;
suspensionData.mSpringDamperRate = settings.SuspensionDampingRate * 2.0f * Math::Sqrt(suspensionData.mSpringStrength * suspensionData.mSprungMass);
return suspensionData;
}
PxVehicleTireData CreatePxVehicleTireData(const WheeledVehicle::Wheel& settings)
{
PxVehicleTireData tire;
int32 tireIndex = WheelTireTypes.Find(settings.TireFrictionScale);
if (tireIndex == -1)
{
// New tire type
tireIndex = WheelTireTypes.Count();
WheelTireTypes.Add(settings.TireFrictionScale);
WheelTireFrictionsDirty = true;
}
tire.mType = tireIndex;
tire.mLatStiffX = settings.TireLateralMax;
tire.mLatStiffY = settings.TireLateralStiffness;
tire.mLongitudinalStiffnessPerUnitGravity = settings.TireLongitudinalStiffness;
return tire;
}
PxVehicleWheelData CreatePxVehicleWheelData(const WheeledVehicle::Wheel& settings)
{
PxVehicleWheelData wheelData;
wheelData.mMass = settings.Mass;
wheelData.mRadius = settings.Radius;
wheelData.mWidth = settings.Width;
wheelData.mMOI = 0.5f * wheelData.mMass * Math::Square(wheelData.mRadius);
wheelData.mDampingRate = M2ToCm2(settings.DampingRate);
wheelData.mMaxSteer = settings.MaxSteerAngle * DegreesToRadians;
wheelData.mMaxBrakeTorque = M2ToCm2(settings.MaxBrakeTorque);
wheelData.mMaxHandBrakeTorque = M2ToCm2(settings.MaxHandBrakeTorque);
return wheelData;
}
PxVehicleAckermannGeometryData CreatePxVehicleAckermannGeometryData(PxVehicleWheelsSimData* wheelsSimData)
{
PxVehicleAckermannGeometryData ackermann;
ackermann.mAxleSeparation = Math::Abs(wheelsSimData->getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eFRONT_LEFT).z - wheelsSimData->getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eREAR_LEFT).z);
ackermann.mFrontWidth = Math::Abs(wheelsSimData->getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eFRONT_RIGHT).x - wheelsSimData->getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eFRONT_LEFT).x);
ackermann.mRearWidth = Math::Abs(wheelsSimData->getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eREAR_RIGHT).x - wheelsSimData->getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eREAR_LEFT).x);
return ackermann;
}
PxVehicleAntiRollBarData CreatePxPxVehicleAntiRollBarData(const WheeledVehicle::AntiRollBar& settings, int leftWheelIndex, int rightWheelIndex)
{
PxVehicleAntiRollBarData antiRollBar;
antiRollBar.mWheel0 = leftWheelIndex;
antiRollBar.mWheel1 = rightWheelIndex;
antiRollBar.mStiffness = settings.Stiffness;
return antiRollBar;
}
bool SortWheelsFrontToBack(WheeledVehicle::Wheel const &a, WheeledVehicle::Wheel const &b)
{
return a.Collider && b.Collider && a.Collider->GetLocalPosition().Z > b.Collider->GetLocalPosition().Z;
} }
void* PhysicsBackend::CreateVehicle(WheeledVehicle* actor) void* PhysicsBackend::CreateVehicle(WheeledVehicle* actor)
{ {
// TODO: handle PxVehicleDrive4WWheelOrder internally rather than sorting wheels directly on the vehicle // Physx vehicles needs to have all wheels sorted to apply controls correctly.
if (actor->_driveType == WheeledVehicle::DriveTypes::Drive4W) // Its needs to know what wheels are on front to turn wheel to correctly side
// and needs to know wheel side to apply throttle to correctly direction for each track when using tanks.
// Anti roll bars needs to have all wheels sorted to get correctly wheel index too.
if (actor->_driveType != WheeledVehicle::DriveTypes::NoDrive)
{ {
// Drive4W requires wheels to match order from PxVehicleDrive4WWheelOrder enum Sorting::QuickSort(actor->_wheels.Get(), actor->_wheels.Count(), SortWheelsFrontToBack);
Sorting::QuickSort(actor->_wheels.Get(), actor->_wheels.Count(), SortWheels);
// sort wheels by side
if (actor->_driveType == WheeledVehicle::DriveTypes::Tank)
{
for (int i = 0; i < actor->_wheels.Count() - 1; i += 2)
{
auto a = actor->_wheels[i];
auto b = actor->_wheels[i + 1];
if (!a.Collider || !b.Collider)
continue;
if (a.Collider->GetLocalPosition().X > b.Collider->GetLocalPosition().X)
{
actor->_wheels[i] = b;
actor->_wheels[i + 1] = a;
}
}
}
} }
// Get wheels // Get wheels
@@ -3160,7 +3482,8 @@ void* PhysicsBackend::CreateVehicle(WheeledVehicle* actor)
// TODO: get gravityDirection from scenePhysX->Scene->getGravity() // TODO: get gravityDirection from scenePhysX->Scene->getGravity()
PxVehicleComputeSprungMasses(wheels.Count(), offsets, centerOfMassOffset.p, mass, 1, sprungMasses); PxVehicleComputeSprungMasses(wheels.Count(), offsets, centerOfMassOffset.p, mass, 1, sprungMasses);
PxVehicleWheelsSimData* wheelsSimData = PxVehicleWheelsSimData::allocate(wheels.Count()); PxVehicleWheelsSimData* wheelsSimData = PxVehicleWheelsSimData::allocate(wheels.Count());
for (int32 i = 0; i < wheels.Count(); i++) int wheelsCount = wheels.Count();
for (int32 i = 0; i < wheelsCount; i++)
{ {
auto& wheel = *wheels[i]; auto& wheel = *wheels[i];
@@ -3168,42 +3491,14 @@ void* PhysicsBackend::CreateVehicle(WheeledVehicle* actor)
data.Collider = wheel.Collider; data.Collider = wheel.Collider;
data.LocalOrientation = wheel.Collider->GetLocalOrientation(); data.LocalOrientation = wheel.Collider->GetLocalOrientation();
PxVehicleSuspensionData suspensionData;
const float suspensionFrequency = 7.0f;
suspensionData.mMaxCompression = wheel.SuspensionMaxRaise;
suspensionData.mMaxDroop = wheel.SuspensionMaxDrop;
suspensionData.mSprungMass = sprungMasses[i];
suspensionData.mSpringStrength = Math::Square(suspensionFrequency) * suspensionData.mSprungMass;
suspensionData.mSpringDamperRate = wheel.SuspensionDampingRate * 2.0f * Math::Sqrt(suspensionData.mSpringStrength * suspensionData.mSprungMass);
PxVehicleTireData tire;
int32 tireIndex = WheelTireTypes.Find(wheel.TireFrictionScale);
if (tireIndex == -1)
{
// New tire type
tireIndex = WheelTireTypes.Count();
WheelTireTypes.Add(wheel.TireFrictionScale);
WheelTireFrictionsDirty = true;
}
tire.mType = tireIndex;
tire.mLatStiffX = wheel.TireLateralMax;
tire.mLatStiffY = wheel.TireLateralStiffness;
tire.mLongitudinalStiffnessPerUnitGravity = wheel.TireLongitudinalStiffness;
PxVehicleWheelData wheelData;
wheelData.mMass = wheel.Mass;
wheelData.mRadius = wheel.Radius;
wheelData.mWidth = wheel.Width;
wheelData.mMOI = 0.5f * wheelData.mMass * Math::Square(wheelData.mRadius);
wheelData.mDampingRate = M2ToCm2(wheel.DampingRate);
wheelData.mMaxSteer = wheel.MaxSteerAngle * DegreesToRadians;
wheelData.mMaxBrakeTorque = M2ToCm2(wheel.MaxBrakeTorque);
wheelData.mMaxHandBrakeTorque = M2ToCm2(wheel.MaxHandBrakeTorque);
PxVec3 centreOffset = centerOfMassOffset.transformInv(offsets[i]); PxVec3 centreOffset = centerOfMassOffset.transformInv(offsets[i]);
PxVec3 forceAppPointOffset(centreOffset.x, wheel.SuspensionForceOffset, centreOffset.z); PxVec3 forceAppPointOffset(centreOffset.x, wheel.SuspensionForceOffset, centreOffset.z);
wheelsSimData->setTireData(i, tire); const PxVehicleTireData& tireData = CreatePxVehicleTireData(wheel);
const PxVehicleWheelData& wheelData = CreatePxVehicleWheelData(wheel);
const PxVehicleSuspensionData& suspensionData = CreatePxVehicleSuspensionData(wheel, sprungMasses[i]);
wheelsSimData->setTireData(i,tireData);
wheelsSimData->setWheelData(i, wheelData); wheelsSimData->setWheelData(i, wheelData);
wheelsSimData->setSuspensionData(i, suspensionData); wheelsSimData->setSuspensionData(i, suspensionData);
wheelsSimData->setSuspTravelDirection(i, centerOfMassOffset.rotate(PxVec3(0.0f, -1.0f, 0.0f))); wheelsSimData->setSuspTravelDirection(i, centerOfMassOffset.rotate(PxVec3(0.0f, -1.0f, 0.0f)));
@@ -3234,6 +3529,19 @@ void* PhysicsBackend::CreateVehicle(WheeledVehicle* actor)
wheelsSimData->disableWheel(i); wheelsSimData->disableWheel(i);
} }
} }
// Add Anti roll bars for wheels axles
for (int i = 0; i < actor->GetAntiRollBars().Count(); i++)
{
int axleIndex = actor->GetAntiRollBars()[i].Axle;
int leftWheelIndex = axleIndex * 2;
int rightWheelIndex = leftWheelIndex + 1;
if (leftWheelIndex >= wheelsCount || rightWheelIndex >= wheelsCount)
continue;
const PxVehicleAntiRollBarData &antiRollBar = CreatePxPxVehicleAntiRollBarData(actor->GetAntiRollBars()[i], leftWheelIndex, rightWheelIndex);
wheelsSimData->addAntiRollBarData(antiRollBar);
}
for (auto child : actor->Children) for (auto child : actor->Children)
{ {
auto collider = ScriptingObject::Cast<Collider>(child); auto collider = ScriptingObject::Cast<Collider>(child);
@@ -3270,48 +3578,19 @@ void* PhysicsBackend::CreateVehicle(WheeledVehicle* actor)
case WheeledVehicle::DriveTypes::Drive4W: case WheeledVehicle::DriveTypes::Drive4W:
{ {
PxVehicleDriveSimData4W driveSimData; PxVehicleDriveSimData4W driveSimData;
const PxVehicleDifferential4WData& differentialData = CreatePxVehicleDifferential4WData(differential);
const PxVehicleEngineData& engineData = CreatePxVehicleEngineData(engine);
const PxVehicleGearsData& gearsData = CreatePxVehicleGearsData(gearbox);
const PxVehicleAutoBoxData& autoBoxData = CreatePxVehicleAutoBoxData();
const PxVehicleClutchData& cluchData = CreatePxVehicleClutchData(gearbox);
const PxVehicleAckermannGeometryData& geometryData = CreatePxVehicleAckermannGeometryData(wheelsSimData);
// Differential driveSimData.setDiffData(differentialData);
PxVehicleDifferential4WData differential4WData;
differential4WData.mType = (PxVehicleDifferential4WData::Enum)differential.Type;
differential4WData.mFrontRearSplit = differential.FrontRearSplit;
differential4WData.mFrontLeftRightSplit = differential.FrontLeftRightSplit;
differential4WData.mRearLeftRightSplit = differential.RearLeftRightSplit;
differential4WData.mCentreBias = differential.CentreBias;
differential4WData.mFrontBias = differential.FrontBias;
differential4WData.mRearBias = differential.RearBias;
driveSimData.setDiffData(differential4WData);
// Engine
PxVehicleEngineData engineData;
engineData.mMOI = M2ToCm2(engine.MOI);
engineData.mPeakTorque = M2ToCm2(engine.MaxTorque);
engineData.mMaxOmega = RpmToRadPerS(engine.MaxRotationSpeed);
engineData.mDampingRateFullThrottle = M2ToCm2(0.15f);
engineData.mDampingRateZeroThrottleClutchEngaged = M2ToCm2(2.0f);
engineData.mDampingRateZeroThrottleClutchDisengaged = M2ToCm2(0.35f);
driveSimData.setEngineData(engineData); driveSimData.setEngineData(engineData);
driveSimData.setGearsData(gearsData);
// Gears driveSimData.setAutoBoxData(autoBoxData);
PxVehicleGearsData gears; driveSimData.setClutchData(cluchData);
gears.mSwitchTime = Math::Max(gearbox.SwitchTime, 0.0f); driveSimData.setAckermannGeometryData(geometryData);
driveSimData.setGearsData(gears);
// Auto Box
PxVehicleAutoBoxData autoBox;
driveSimData.setAutoBoxData(autoBox);
// Clutch
PxVehicleClutchData clutch;
clutch.mStrength = M2ToCm2(gearbox.ClutchStrength);
driveSimData.setClutchData(clutch);
// Ackermann steer accuracy
PxVehicleAckermannGeometryData ackermann;
ackermann.mAxleSeparation = Math::Abs(wheelsSimData->getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eFRONT_LEFT).z - wheelsSimData->getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eREAR_LEFT).z);
ackermann.mFrontWidth = Math::Abs(wheelsSimData->getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eFRONT_RIGHT).x - wheelsSimData->getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eFRONT_LEFT).x);
ackermann.mRearWidth = Math::Abs(wheelsSimData->getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eREAR_RIGHT).x - wheelsSimData->getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eREAR_LEFT).x);
driveSimData.setAckermannGeometryData(ackermann);
// Create vehicle drive // Create vehicle drive
auto drive4W = PxVehicleDrive4W::allocate(wheels.Count()); auto drive4W = PxVehicleDrive4W::allocate(wheels.Count());
@@ -3324,36 +3603,18 @@ void* PhysicsBackend::CreateVehicle(WheeledVehicle* actor)
case WheeledVehicle::DriveTypes::DriveNW: case WheeledVehicle::DriveTypes::DriveNW:
{ {
PxVehicleDriveSimDataNW driveSimData; PxVehicleDriveSimDataNW driveSimData;
const PxVehicleDifferentialNWData& differentialData = CreatePxVehicleDifferentialNWData(differential, wheels);
const PxVehicleEngineData& engineData = CreatePxVehicleEngineData(engine);
const PxVehicleGearsData& gearsData = CreatePxVehicleGearsData(gearbox);
const PxVehicleAutoBoxData& autoBoxData = CreatePxVehicleAutoBoxData();
const PxVehicleClutchData& cluchData = CreatePxVehicleClutchData(gearbox);
const PxVehicleAckermannGeometryData& geometryData = CreatePxVehicleAckermannGeometryData(wheelsSimData);
// Differential driveSimData.setDiffData(differentialData);
PxVehicleDifferentialNWData differentialNwData;
for (int32 i = 0; i < wheels.Count(); i++)
differentialNwData.setDrivenWheel(i, true);
driveSimData.setDiffData(differentialNwData);
// Engine
PxVehicleEngineData engineData;
engineData.mMOI = M2ToCm2(engine.MOI);
engineData.mPeakTorque = M2ToCm2(engine.MaxTorque);
engineData.mMaxOmega = RpmToRadPerS(engine.MaxRotationSpeed);
engineData.mDampingRateFullThrottle = M2ToCm2(0.15f);
engineData.mDampingRateZeroThrottleClutchEngaged = M2ToCm2(2.0f);
engineData.mDampingRateZeroThrottleClutchDisengaged = M2ToCm2(0.35f);
driveSimData.setEngineData(engineData); driveSimData.setEngineData(engineData);
driveSimData.setGearsData(gearsData);
// Gears driveSimData.setAutoBoxData(autoBoxData);
PxVehicleGearsData gears; driveSimData.setClutchData(cluchData);
gears.mSwitchTime = Math::Max(gearbox.SwitchTime, 0.0f);
driveSimData.setGearsData(gears);
// Auto Box
PxVehicleAutoBoxData autoBox;
driveSimData.setAutoBoxData(autoBox);
// Clutch
PxVehicleClutchData clutch;
clutch.mStrength = M2ToCm2(gearbox.ClutchStrength);
driveSimData.setClutchData(clutch);
// Create vehicle drive // Create vehicle drive
auto driveNW = PxVehicleDriveNW::allocate(wheels.Count()); auto driveNW = PxVehicleDriveNW::allocate(wheels.Count());
@@ -3371,6 +3632,31 @@ void* PhysicsBackend::CreateVehicle(WheeledVehicle* actor)
vehicle = driveNo; vehicle = driveNo;
break; break;
} }
case WheeledVehicle::DriveTypes::Tank:
{
PxVehicleDriveSimData4W driveSimData;
const PxVehicleDifferential4WData &differentialData = CreatePxVehicleDifferential4WData(differential);
const PxVehicleEngineData &engineData = CreatePxVehicleEngineData(engine);
const PxVehicleGearsData &gearsData = CreatePxVehicleGearsData(gearbox);
const PxVehicleAutoBoxData &autoBoxData = CreatePxVehicleAutoBoxData();
const PxVehicleClutchData &cluchData = CreatePxVehicleClutchData(gearbox);
const PxVehicleAckermannGeometryData &geometryData = CreatePxVehicleAckermannGeometryData(wheelsSimData);
driveSimData.setDiffData(differentialData);
driveSimData.setEngineData(engineData);
driveSimData.setGearsData(gearsData);
driveSimData.setAutoBoxData(autoBoxData);
driveSimData.setClutchData(cluchData);
driveSimData.setAckermannGeometryData(geometryData);
// Create vehicle drive
auto driveTank = PxVehicleDriveTank::allocate(wheels.Count());
driveTank->setup(PhysX, actorPhysX, *wheelsSimData, driveSimData, wheels.Count());
driveTank->mDriveDynData.forceGearChange(PxVehicleGearsData::eFIRST);
driveTank->mDriveDynData.setUseAutoGears(gearbox.AutoGear);
vehicle = driveTank;
break;
}
default: default:
CRASH; CRASH;
} }
@@ -3392,6 +3678,9 @@ void PhysicsBackend::DestroyVehicle(void* vehicle, int32 driveType)
case WheeledVehicle::DriveTypes::NoDrive: case WheeledVehicle::DriveTypes::NoDrive:
((PxVehicleNoDrive*)vehicle)->free(); ((PxVehicleNoDrive*)vehicle)->free();
break; break;
case WheeledVehicle::DriveTypes::Tank:
((PxVehicleDriveTank*)vehicle)->free();
break;
} }
} }
@@ -3402,46 +3691,43 @@ void PhysicsBackend::UpdateVehicleWheels(WheeledVehicle* actor)
for (uint32 i = 0; i < wheelsSimData->getNbWheels(); i++) for (uint32 i = 0; i < wheelsSimData->getNbWheels(); i++)
{ {
auto& wheel = actor->_wheels[i]; auto& wheel = actor->_wheels[i];
const PxVehicleSuspensionData& suspensionData = CreatePxVehicleSuspensionData(wheel, wheelsSimData->getSuspensionData(i).mSprungMass);
// Update suspension data const PxVehicleTireData& tireData = CreatePxVehicleTireData(wheel);
PxVehicleSuspensionData suspensionData = wheelsSimData->getSuspensionData(i); const PxVehicleWheelData& wheelData = CreatePxVehicleWheelData(wheel);
const float suspensionFrequency = 7.0f;
suspensionData.mMaxCompression = wheel.SuspensionMaxRaise;
suspensionData.mMaxDroop = wheel.SuspensionMaxDrop;
suspensionData.mSpringStrength = Math::Square(suspensionFrequency) * suspensionData.mSprungMass;
suspensionData.mSpringDamperRate = wheel.SuspensionDampingRate * 2.0f * Math::Sqrt(suspensionData.mSpringStrength * suspensionData.mSprungMass);
wheelsSimData->setSuspensionData(i, suspensionData); wheelsSimData->setSuspensionData(i, suspensionData);
wheelsSimData->setTireData(i, tireData);
// Update tire data
PxVehicleTireData tire;
int32 tireIndex = WheelTireTypes.Find(wheel.TireFrictionScale);
if (tireIndex == -1)
{
// New tire type
tireIndex = WheelTireTypes.Count();
WheelTireTypes.Add(wheel.TireFrictionScale);
WheelTireFrictionsDirty = true;
}
tire.mType = tireIndex;
tire.mLatStiffX = wheel.TireLateralMax;
tire.mLatStiffY = wheel.TireLateralStiffness;
tire.mLongitudinalStiffnessPerUnitGravity = wheel.TireLongitudinalStiffness;
wheelsSimData->setTireData(i, tire);
// Update wheel data
PxVehicleWheelData wheelData;
wheelData.mMass = wheel.Mass;
wheelData.mRadius = wheel.Radius;
wheelData.mWidth = wheel.Width;
wheelData.mMOI = 0.5f * wheelData.mMass * Math::Square(wheelData.mRadius);
wheelData.mDampingRate = M2ToCm2(wheel.DampingRate);
wheelData.mMaxSteer = wheel.MaxSteerAngle * DegreesToRadians;
wheelData.mMaxBrakeTorque = M2ToCm2(wheel.MaxBrakeTorque);
wheelData.mMaxHandBrakeTorque = M2ToCm2(wheel.MaxHandBrakeTorque);
wheelsSimData->setWheelData(i, wheelData); wheelsSimData->setWheelData(i, wheelData);
} }
} }
void PhysicsBackend::UpdateVehicleAntiRollBars(WheeledVehicle *actor)
{
int wheelsCount = actor->_wheels.Count();
auto drive = (PxVehicleWheels *)actor->_vehicle;
PxVehicleWheelsSimData *wheelsSimData = &drive->mWheelsSimData;
// Update Anti roll bars for wheels axles
for (int i = 0; i < actor->GetAntiRollBars().Count(); i++)
{
int axleIndex = actor->GetAntiRollBars()[i].Axle;
int leftWheelIndex = axleIndex * 2;
int rightWheelIndex = leftWheelIndex + 1;
if (leftWheelIndex >= wheelsCount || rightWheelIndex >= wheelsCount)
continue;
const PxVehicleAntiRollBarData &antiRollBar = CreatePxPxVehicleAntiRollBarData(actor->GetAntiRollBars()[i], leftWheelIndex, rightWheelIndex);
if (wheelsSimData->getNbAntiRollBarData() - 1 < i)
{
wheelsSimData->addAntiRollBarData(antiRollBar);
}
else
{
wheelsSimData->setAntiRollBarData(axleIndex, antiRollBar);
}
}
}
void PhysicsBackend::SetVehicleEngine(void* vehicle, const void* value) void PhysicsBackend::SetVehicleEngine(void* vehicle, const void* value)
{ {
auto drive = (PxVehicleDrive*)vehicle; auto drive = (PxVehicleDrive*)vehicle;
@@ -3451,28 +3737,24 @@ void PhysicsBackend::SetVehicleEngine(void* vehicle, const void* value)
case PxVehicleTypes::eDRIVE4W: case PxVehicleTypes::eDRIVE4W:
{ {
auto drive4W = (PxVehicleDrive4W*)drive; auto drive4W = (PxVehicleDrive4W*)drive;
const PxVehicleEngineData& engineData = CreatePxVehicleEngineData(engine);
PxVehicleDriveSimData4W& driveSimData = drive4W->mDriveSimData; PxVehicleDriveSimData4W& driveSimData = drive4W->mDriveSimData;
PxVehicleEngineData engineData;
engineData.mMOI = M2ToCm2(engine.MOI);
engineData.mPeakTorque = M2ToCm2(engine.MaxTorque);
engineData.mMaxOmega = RpmToRadPerS(engine.MaxRotationSpeed);
engineData.mDampingRateFullThrottle = M2ToCm2(0.15f);
engineData.mDampingRateZeroThrottleClutchEngaged = M2ToCm2(2.0f);
engineData.mDampingRateZeroThrottleClutchDisengaged = M2ToCm2(0.35f);
driveSimData.setEngineData(engineData); driveSimData.setEngineData(engineData);
break; break;
} }
case PxVehicleTypes::eDRIVENW: case PxVehicleTypes::eDRIVENW:
{ {
auto drive4W = (PxVehicleDriveNW*)drive; auto drive4W = (PxVehicleDriveNW*)drive;
const PxVehicleEngineData& engineData = CreatePxVehicleEngineData(engine);
PxVehicleDriveSimDataNW& driveSimData = drive4W->mDriveSimData; PxVehicleDriveSimDataNW& driveSimData = drive4W->mDriveSimData;
PxVehicleEngineData engineData; driveSimData.setEngineData(engineData);
engineData.mMOI = M2ToCm2(engine.MOI); break;
engineData.mPeakTorque = M2ToCm2(engine.MaxTorque); }
engineData.mMaxOmega = RpmToRadPerS(engine.MaxRotationSpeed); case PxVehicleTypes::eDRIVETANK:
engineData.mDampingRateFullThrottle = M2ToCm2(0.15f); {
engineData.mDampingRateZeroThrottleClutchEngaged = M2ToCm2(2.0f); auto driveTank = (PxVehicleDriveTank*)drive;
engineData.mDampingRateZeroThrottleClutchDisengaged = M2ToCm2(0.35f); const PxVehicleEngineData &engineData = CreatePxVehicleEngineData(engine);
PxVehicleDriveSimData &driveSimData = driveTank->mDriveSimData;
driveSimData.setEngineData(engineData); driveSimData.setEngineData(engineData);
break; break;
} }
@@ -3488,16 +3770,9 @@ void PhysicsBackend::SetVehicleDifferential(void* vehicle, const void* value)
case PxVehicleTypes::eDRIVE4W: case PxVehicleTypes::eDRIVE4W:
{ {
auto drive4W = (PxVehicleDrive4W*)drive; auto drive4W = (PxVehicleDrive4W*)drive;
const PxVehicleDifferential4WData& differentialData = CreatePxVehicleDifferential4WData(differential);
PxVehicleDriveSimData4W& driveSimData = drive4W->mDriveSimData; PxVehicleDriveSimData4W& driveSimData = drive4W->mDriveSimData;
PxVehicleDifferential4WData differential4WData; driveSimData.setDiffData(differentialData);
differential4WData.mType = (PxVehicleDifferential4WData::Enum)differential.Type;
differential4WData.mFrontRearSplit = differential.FrontRearSplit;
differential4WData.mFrontLeftRightSplit = differential.FrontLeftRightSplit;
differential4WData.mRearLeftRightSplit = differential.RearLeftRightSplit;
differential4WData.mCentreBias = differential.CentreBias;
differential4WData.mFrontBias = differential.FrontBias;
differential4WData.mRearBias = differential.RearBias;
driveSimData.setDiffData(differential4WData);
break; break;
} }
} }
@@ -3514,41 +3789,37 @@ void PhysicsBackend::SetVehicleGearbox(void* vehicle, const void* value)
case PxVehicleTypes::eDRIVE4W: case PxVehicleTypes::eDRIVE4W:
{ {
auto drive4W = (PxVehicleDrive4W*)drive; auto drive4W = (PxVehicleDrive4W*)drive;
const PxVehicleGearsData& gearData = CreatePxVehicleGearsData(gearbox);
const PxVehicleClutchData& clutchData = CreatePxVehicleClutchData(gearbox);
const PxVehicleAutoBoxData& autoBoxData = CreatePxVehicleAutoBoxData();
PxVehicleDriveSimData4W& driveSimData = drive4W->mDriveSimData; PxVehicleDriveSimData4W& driveSimData = drive4W->mDriveSimData;
driveSimData.setGearsData(gearData);
// Gears driveSimData.setAutoBoxData(autoBoxData);
PxVehicleGearsData gears; driveSimData.setClutchData(clutchData);
gears.mSwitchTime = Math::Max(gearbox.SwitchTime, 0.0f);
driveSimData.setGearsData(gears);
// Auto Box
PxVehicleAutoBoxData autoBox;
driveSimData.setAutoBoxData(autoBox);
// Clutch
PxVehicleClutchData clutch;
clutch.mStrength = M2ToCm2(gearbox.ClutchStrength);
driveSimData.setClutchData(clutch);
break; break;
} }
case PxVehicleTypes::eDRIVENW: case PxVehicleTypes::eDRIVENW:
{ {
auto drive4W = (PxVehicleDriveNW*)drive; auto drive4W = (PxVehicleDriveNW*)drive;
const PxVehicleGearsData& gearData = CreatePxVehicleGearsData(gearbox);
const PxVehicleClutchData& clutchData = CreatePxVehicleClutchData(gearbox);
const PxVehicleAutoBoxData& autoBoxData = CreatePxVehicleAutoBoxData();
PxVehicleDriveSimDataNW& driveSimData = drive4W->mDriveSimData; PxVehicleDriveSimDataNW& driveSimData = drive4W->mDriveSimData;
driveSimData.setGearsData(gearData);
// Gears driveSimData.setAutoBoxData(autoBoxData);
PxVehicleGearsData gears; driveSimData.setClutchData(clutchData);
gears.mSwitchTime = Math::Max(gearbox.SwitchTime, 0.0f); break;
driveSimData.setGearsData(gears); }
case PxVehicleTypes::eDRIVETANK:
// Auto Box {
PxVehicleAutoBoxData autoBox; auto driveTank = (PxVehicleDriveTank *)drive;
driveSimData.setAutoBoxData(autoBox); const PxVehicleGearsData &gearData = CreatePxVehicleGearsData(gearbox);
const PxVehicleClutchData &clutchData = CreatePxVehicleClutchData(gearbox);
// Clutch const PxVehicleAutoBoxData &autoBoxData = CreatePxVehicleAutoBoxData();
PxVehicleClutchData clutch; PxVehicleDriveSimData &driveSimData = driveTank->mDriveSimData;
clutch.mStrength = M2ToCm2(gearbox.ClutchStrength); driveSimData.setGearsData(gearData);
driveSimData.setClutchData(clutch); driveSimData.setAutoBoxData(autoBoxData);
driveSimData.setClutchData(clutchData);
break; break;
} }
} }

View File

@@ -263,6 +263,7 @@ public:
static void* CreateVehicle(class WheeledVehicle* actor); static void* CreateVehicle(class WheeledVehicle* actor);
static void DestroyVehicle(void* vehicle, int32 driveType); static void DestroyVehicle(void* vehicle, int32 driveType);
static void UpdateVehicleWheels(WheeledVehicle* actor); static void UpdateVehicleWheels(WheeledVehicle* actor);
static void UpdateVehicleAntiRollBars(WheeledVehicle* actor);
static void SetVehicleEngine(void* vehicle, const void* value); static void SetVehicleEngine(void* vehicle, const void* value);
static void SetVehicleDifferential(void* vehicle, const void* value); static void SetVehicleDifferential(void* vehicle, const void* value);
static void SetVehicleGearbox(void* vehicle, const void* value); static void SetVehicleGearbox(void* vehicle, const void* value);