diff --git a/Source/Engine/Physics/Actors/WheeledVehicle.cpp b/Source/Engine/Physics/Actors/WheeledVehicle.cpp index 243d9cc48..29d1a9e63 100644 --- a/Source/Engine/Physics/Actors/WheeledVehicle.cpp +++ b/Source/Engine/Physics/Actors/WheeledVehicle.cpp @@ -55,6 +55,48 @@ WheeledVehicle::DriveControlSettings WheeledVehicle::GetDriveControl() const void WheeledVehicle::SetDriveControl(DriveControlSettings &value) { + // Don't let have an invalid steer vs speed list. + if (&value.SteerVsSpeed == nullptr) + value.SteerVsSpeed = Array(); + + 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; } @@ -392,7 +434,6 @@ void WheeledVehicle::Serialize(SerializeStream &stream, const void *otherObj) SERIALIZE_MEMBER(Engine, _engine); SERIALIZE_MEMBER(Differential, _differential); SERIALIZE_MEMBER(Gearbox, _gearbox); - } void WheeledVehicle::Deserialize(DeserializeStream &stream, ISerializeModifier *modifier) diff --git a/Source/Engine/Physics/Actors/WheeledVehicle.h b/Source/Engine/Physics/Actors/WheeledVehicle.h index bfa3dc294..7d7c782df 100644 --- a/Source/Engine/Physics/Actors/WheeledVehicle.h +++ b/Source/Engine/Physics/Actors/WheeledVehicle.h @@ -40,6 +40,45 @@ API_CLASS(Attributes="ActorContextMenu(\"New/Physics/Wheeled Vehicle\"), ActorTo Special }; + /// + /// Storage the relationship between speed and steer. + /// + API_STRUCT() struct SteerControl : ISerializable + { + DECLARE_SCRIPTING_TYPE_MINIMAL(SteerControl); + API_AUTO_SERIALIZATION(); + + /// + /// The vehicle speed. + /// + API_FIELD(Attributes = "Limit(0)") float Speed; + + /// + /// The target max steer of the speed. + /// + API_FIELD(Attributes = "Limit(0, 1)") float Steer; + + /// + /// Create a Steer/Speed relationship structure. + /// + SteerControl() + { + Speed = 1000; + Steer = 1; + } + + /// + /// Create a Steer/Speed relationship structure. + /// The vehicle speed. + /// The target max steer of the speed. + /// + SteerControl(float speed, float steer) + { + Speed = speed; + Steer = steer; + } + }; + /// /// Vehicle engine settings. /// @@ -94,42 +133,54 @@ API_CLASS(Attributes="ActorContextMenu(\"New/Physics/Wheeled Vehicle\"), ActorTo /// /// Acceleration input sensitive. /// - API_FIELD(Attributes="Limit(0), EditorDisplay(\"Inputs\"), EditorOrder(10)") float RiseRateAcceleration = 6.0f; + API_FIELD(Attributes="Limit(0), EditorOrder(10)") float RiseRateAcceleration = 6.0f; /// /// Deceleration input sensitive. /// - API_FIELD(Attributes="Limit(0), EditorDisplay(\"Inputs\"), EditorOrder(11)") float FallRateAcceleration = 10.0f; + API_FIELD(Attributes="Limit(0), EditorOrder(11)") float FallRateAcceleration = 10.0f; /// /// Brake input sensitive. /// - API_FIELD(Attributes="Limit(0), EditorDisplay(\"Inputs\"), EditorOrder(12)") float RiseRateBrake = 6.0f; + API_FIELD(Attributes="Limit(0), EditorOrder(12)") float RiseRateBrake = 6.0f; /// /// Release brake sensitive. /// - API_FIELD(Attributes="Limit(0), EditorDisplay(\"Inputs\"), EditorOrder(13)") float FallRateBrake = 10.0f; + API_FIELD(Attributes="Limit(0), EditorOrder(13)") float FallRateBrake = 10.0f; /// /// Brake input sensitive. /// - API_FIELD(Attributes="Limit(0), EditorDisplay(\"Inputs\"), EditorOrder(14)") float RiseRateHandBrake = 12.0f; + API_FIELD(Attributes="Limit(0), EditorOrder(14)") float RiseRateHandBrake = 12.0f; /// /// Release handbrake sensitive. /// - API_FIELD(Attributes="Limit(0), EditorDisplay(\"Inputs\"), EditorOrder(15)") float FallRateHandBrake = 12.0f; + API_FIELD(Attributes="Limit(0), EditorOrder(15)") float FallRateHandBrake = 12.0f; /// /// Steer input sensitive. /// - API_FIELD(Attributes="Limit(0), EditorDisplay(\"Inputs\"), EditorOrder(16)") float RiseRateSteer = 2.5f; + API_FIELD(Attributes="Limit(0), EditorOrder(16)") float RiseRateSteer = 2.5f; /// /// Release steer input sensitive. /// - API_FIELD(Attributes="Limit(0), EditorDisplay(\"Inputs\"), EditorOrder(17)") float FallRateSteer = 5.0f; + API_FIELD(Attributes="Limit(0), EditorOrder(17)") float FallRateSteer = 5.0f; + + /// + /// Vehicle control relationship between speed and steer. The higher is the speed, + /// decrease steer to make vehicle more maneuverable (limited only 4 relationships). + /// + API_FIELD() Array SteerVsSpeed = Array + { + SteerControl(800, 1.0f), + SteerControl(1500, 0.7f), + SteerControl(2500, 0.5f), + SteerControl(5000, 0.2f), + }; }; /// diff --git a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp index 19743396c..98fbf7596 100644 --- a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp +++ b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp @@ -1554,9 +1554,7 @@ void PhysicsBackend::EndSimulateScene(void* scene) leftBrake = 1.0f; } - // @formatter:off - // Reference: PhysX SDK docs - // TODO: expose input control smoothing data + // Smooth input controls PxVehiclePadSmoothingData padSmoothing = { { @@ -1591,20 +1589,31 @@ void PhysicsBackend::EndSimulateScene(void* scene) 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) - static constexpr PxF32 steerVsForwardSpeedData[] = + + // Reduce steer by speed to make vehicle more easier to maneuver + + 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, - 20.0f, 0.9f, - 65.0f, 0.8f, - 120.0f, 0.7f, - PX_MAX_F32, PX_MAX_F32, - PX_MAX_F32, PX_MAX_F32, - PX_MAX_F32, PX_MAX_F32, - PX_MAX_F32, PX_MAX_F32, - }; - const PxFixedSizeLookupTable<8> steerVsForwardSpeed(steerVsForwardSpeedData, 4); + steerVsForwardSpeedData[i] = wheelVehicle->_driveControl.SteerVsSpeed[steerVsSpeedIndex].Speed; + steerVsForwardSpeedData[i + 1] = wheelVehicle->_driveControl.SteerVsSpeed[steerVsSpeedIndex].Steer; + steerVsSpeedIndex = Math::Min(steerVsSpeedIndex + 1, lastSteerVsSpeedIndex); + } + const PxFixedSizeLookupTable steerVsForwardSpeed(steerVsForwardSpeedData, 4); + // @formatter:on if (wheelVehicle->UseAnalogSteering) {