using Sandbox;
using System;
namespace VeloX;
[GameResource( "Wheel Friction", "tire", "Wheel Friction", Category = "VeloX", Icon = "radio_button_checked" )]
public class TirePreset : GameResource
{
[Property] public Pacejka Pacejka { get; set; }
public float RollResistanceLin { get; set; } = 1E-3f;
public float RollResistanceQuad { get; set; } = 1E-6f;
public float GetRollingResistance( float velocity, float resistance_factor )
{ // surface influence on rolling resistance
float resistance = resistance_factor * RollResistanceLin;
// heat due to tire deformation increases rolling resistance
// approximate by quadratic function
resistance += velocity * velocity * RollResistanceQuad;
return resistance;
}
public void ComputeSlip( float vlon, float vlat, float vrot, out float slip_ratio, out float slip_angle )
{
float rvlon = 1 / MathF.Max( MathF.Abs( vlon ), 1E-3f );
float vslip = vrot - vlon;
slip_ratio = vslip * rvlon;
slip_angle = -MathF.Atan( vlat * rvlon );
}
/// approximate asin(x) = x + x^3/6 for +-18 deg range
public static float ComputeCamberAngle( float sin_camber )
{
float sc = Math.Clamp( sin_camber, -0.3f, 0.3f );
return ((1 / 6.0f) * (sc * sc) + 1) * sc;
}
public struct TireState()
{
///
/// surface friction coefficient
///
public float friction = 0;
///
/// tire camber angle relative to track surface
///
public float camber = 0;
///
/// camber thrust induced lateral slip velocity
///
public float vcam = 0;
///
/// ratio of tire contact patch speed to road speed
///
public float slip = 0;
///
/// the angle between the wheel heading and the wheel velocity
///
public float slip_angle = 0;
///
/// peak force slip ratio
///
public float ideal_slip = 0;
///
/// peak force slip angle
///
public float ideal_slip_angle = 0;
///
/// positive during traction
///
public float fx = 0;
///
/// positive during traction in a right turn
///
public float fy = 0;
///
/// positive during traction in a left turn
///
public float mz = 0;
};
public void ComputeState(
float normal_force,
float rot_velocity,
float lon_velocity,
float lat_velocity,
float camber_angle,
out TireState s
)
{
s = new TireState
{
camber = camber_angle,
friction = 1.0f
};
if ( normal_force * s.friction < 1E-6f )
{
s.slip = s.slip_angle = 0;
s.fx = s.fy = s.mz = 0;
return;
}
float Fz = MathF.Min( normal_force * 0.001f, 30f );
ComputeSlip( lon_velocity, lat_velocity, rot_velocity, out float slip, out float slip_angle );
float sigma = slip;
float alpha = slip_angle.RadianToDegree();
float gamma = s.camber.RadianToDegree();
float Fx0 = Pacejka.PacejkaFx( sigma, Fz, s.friction );
float Fy0 = Pacejka.PacejkaFy( alpha, Fz, gamma, s.friction, out float camber_alpha );
// combined slip
float Gx = Pacejka.PacejkaGx( slip, slip_angle );
float Gy = Pacejka.PacejkaGy( slip, slip_angle );
float Fx = Gx * Fx0;
float Fy = Gy * Fy0;
s.vcam = ComputeCamberVelocity( camber_alpha.DegreeToRadian(), lon_velocity );
s.slip = slip;
s.slip_angle = slip_angle;
s.fx = Fx;
s.fy = Fy;
}
public static float ComputeCamberVelocity( float sa, float vx )
{
float tansa = (1 / 3.0f * (sa * sa) + 1) * sa;
return tansa * vx;
}
}