assets and etc.

This commit is contained in:
Valera 2025-11-08 17:05:04 +07:00
parent ae5cd2c8b6
commit ab8cc70785
12 changed files with 63 additions and 104 deletions

View File

@ -102,20 +102,25 @@ public partial class Clutch : PowertrainComponent
// Clutch engagement calculation for automatic clutch // Clutch engagement calculation for automatic clutch
else else
{ {
// Calculate engagement // Calculate engagement
// Engage the clutch if the input spinning faster than the output, but also if vice versa. // Engage the clutch if the input spinning faster than the output, but also if vice versa.
float throttleInput = Controller.SwappedThrottle; float throttleInput = Controller.SwappedThrottle;
float finalEngagementRPM = EngagementRPM + ThrottleEngagementOffsetRPM * (throttleInput * throttleInput); float finalEngagementRPM = 500 + ThrottleEngagementOffsetRPM * (throttleInput * throttleInput);
float referenceRPM = MathF.Max( InputRPM, OutputRPM ); float referenceRPM = MathF.Max( InputRPM, OutputRPM );
ClutchInput = (referenceRPM - finalEngagementRPM) / EngagementRange; ClutchInput = (referenceRPM - finalEngagementRPM) / EngagementRange;
ClutchInput = Math.Clamp( ClutchInput, 0f, 1f ); ClutchInput = Math.Clamp( ClutchInput, 0f, 1f );
// Avoid disconnecting clutch at high speed // Avoid disconnecting clutch at high speed
if ( engine.OutputRPM > engine.IdleRPM * 1.1f && Controller.TotalSpeed > 3f ) if ( engine.OutputRPM > engine.IdleRPM * 1.1f && Controller.TotalSpeed > 3f )
{ {
ClutchInput = 1f; ClutchInput = 1f;
} }
if ( Controller.SwappedBrakes > 0 )
{
ClutchInput = 0;
}
} }
if ( Controller.IsClutching > 0 ) if ( Controller.IsClutching > 0 )
{ {
@ -175,13 +180,14 @@ public partial class Clutch : PowertrainComponent
float slipOverflowTorque = -Math.Min( outputTorqueClamp - OutputTorque, 0 ); float slipOverflowTorque = -Math.Min( outputTorqueClamp - OutputTorque, 0 );
// Apply the creep torque commonly caused by torque converter drag in automatic transmissions // Apply the creep torque commonly caused by torque converter drag in automatic transmissions
ApplyCreepTorque( ref OutputTorque, CreepTorque ); //ApplyCreepTorque( ref OutputTorque, CreepTorque );
// Send the torque downstream // Send the torque downstream
float returnTorque = _output.ForwardStep( OutputTorque, OutputInertia, dt ) * _clutchEngagement; float returnTorque = _output.ForwardStep( OutputTorque, OutputInertia, dt ) * _clutchEngagement;
// Clamp the return torque to the slip torque of the clutch once again // Clamp the return torque to the slip torque of the clutch once again
returnTorque = Math.Clamp( returnTorque, -SlipTorque, SlipTorque ); //returnTorque = Math.Clamp( returnTorque, -SlipTorque, SlipTorque );
// Torque returned to the input is a combination of torque returned by the powertrain and the torque that // Torque returned to the input is a combination of torque returned by the powertrain and the torque that
// was possibly never sent downstream // was possibly never sent downstream

View File

@ -250,7 +250,7 @@ public class Engine : PowertrainComponent, IScenePhysicsEvents
while ( startTimer <= StartDuration && StarterActive ) while ( startTimer <= StartDuration && StarterActive )
{ {
startTimer += 0.1f; startTimer += 0.1f;
await GameTask.DelaySeconds( 0.1f ); await Task.DelaySeconds( 0.1f );
} }
} }
finally finally
@ -341,7 +341,7 @@ public class Engine : PowertrainComponent, IScenePhysicsEvents
// Calculate/get torque returned from wheels // Calculate/get torque returned from wheels
OutputTorque = generatedTorque - reactionTorque; OutputTorque = generatedTorque - reactionTorque;
float returnTorque = -ForwardStep( OutputTorque, 0, dt ); float returnTorque = ForwardStep( OutputTorque, 0, dt );
float totalTorque = generatedTorque + returnTorque + reactionTorque; float totalTorque = generatedTorque + returnTorque + reactionTorque;
OutputAngularVelocity += totalTorque / inertiaSum * dt; OutputAngularVelocity += totalTorque / inertiaSum * dt;
@ -367,7 +367,7 @@ public class Engine : PowertrainComponent, IScenePhysicsEvents
RevLimiterActive = true; RevLimiterActive = true;
OnRevLimiter?.Invoke(); OnRevLimiter?.Invoke();
await GameTask.DelayRealtimeSeconds( RevLimiterCutoffDuration ); await Task.DelayRealtimeSeconds( RevLimiterCutoffDuration );
RevLimiterActive = false; RevLimiterActive = false;
} }

View File

@ -548,7 +548,7 @@ public class Transmission : PowertrainComponent
{ {
ShiftProgress = shiftTimer / ShiftDuration; ShiftProgress = shiftTimer / ShiftDuration;
shiftTimer += dt; shiftTimer += dt;
await GameTask.DelayRealtimeSeconds( dt ); await Task.DelayRealtimeSeconds( dt );
} }
// Do the shift at the half point of shift duration // Do the shift at the half point of shift duration
@ -566,7 +566,7 @@ public class Transmission : PowertrainComponent
{ {
ShiftProgress = shiftTimer / ShiftDuration; ShiftProgress = shiftTimer / ShiftDuration;
shiftTimer += dt; shiftTimer += dt;
await GameTask.DelayRealtimeSeconds( dt ); await Task.DelayRealtimeSeconds( dt );
} }
@ -583,7 +583,7 @@ public class Transmission : PowertrainComponent
while ( postShiftBanTimer < PostShiftBan ) while ( postShiftBanTimer < PostShiftBan )
{ {
postShiftBanTimer += dt; postShiftBanTimer += dt;
await GameTask.DelayRealtimeSeconds( dt ); await Task.DelayRealtimeSeconds( dt );
} }
// Post shift ban has finished // Post shift ban has finished

View File

@ -15,10 +15,8 @@ public partial class WheelPowertrain : PowertrainComponent
protected override void OnStart() protected override void OnStart()
{ {
_initialRollingResistance = Wheel.RollingResistanceTorque; _initialRollingResistance = Wheel.RollingResistanceTorque;
_initialWheelInertia = Wheel.BaseInertia;
} }
private float _initialRollingResistance; private float _initialRollingResistance;
private float _initialWheelInertia;
public override float QueryAngularVelocity( float angularVelocity, float dt ) public override float QueryAngularVelocity( float angularVelocity, float dt )
{ {
@ -48,7 +46,7 @@ public partial class WheelPowertrain : PowertrainComponent
OutputTorque = InputTorque; OutputTorque = InputTorque;
OutputInertia = Wheel.BaseInertia + inertiaSum; OutputInertia = Wheel.BaseInertia + inertiaSum;
Wheel.Torque = OutputTorque; Wheel.DriveTorque = OutputTorque;
Wheel.Inertia = OutputInertia; Wheel.Inertia = OutputInertia;
Wheel.AutoSimulate = false; Wheel.AutoSimulate = false;
@ -56,4 +54,14 @@ public partial class WheelPowertrain : PowertrainComponent
return Math.Abs( Wheel.CounterTorque ); return Math.Abs( Wheel.CounterTorque );
} }
protected override void DrawGizmos()
{
if ( !Gizmo.IsSelected )
return;
Gizmo.Transform = Wheel.WorldTransform;
Wheel?.GizmoDraw();
}
} }

View File

@ -4,8 +4,8 @@ namespace VeloX;
public abstract partial class VeloXBase public abstract partial class VeloXBase
{ {
[Property, Feature( "Input" )] internal InputResolver Input { get; set; } = new(); [Feature( "Input" )] internal InputResolver Input { get; set; } = new();
[Property, Feature( "Input" )] public Connection Driver { get => Input.Driver; set => Input.Driver = value; } [Feature( "Input" )] public Connection Driver { get => Input.Driver; set => Input.Driver = value; }
private bool IsDriverActive => Driver is not null; private bool IsDriverActive => Driver is not null;

View File

@ -7,7 +7,8 @@ public abstract partial class VeloXBase
private Vector3 linForce; private Vector3 linForce;
private Vector3 angForce; private Vector3 angForce;
[Property] float BrakeForce { get; set; } = 1500f;
[Property] float HandbrakeForce { get; set; } = 3500f;
private void PhysicsSimulate() private void PhysicsSimulate()
{ {
if ( Body.Sleeping && Input.AnalogMove.x == 0 ) if ( Body.Sleeping && Input.AnalogMove.x == 0 )
@ -35,9 +36,9 @@ public abstract partial class VeloXBase
CombinedLoad += v.Fz; CombinedLoad += v.Fz;
foreach ( var v in Wheels ) foreach ( var v in Wheels )
{ {
v.Brake = SwappedBrakes; v.BrakeTorque = SwappedBrakes * BrakeForce;
if ( !v.IsFront ) if ( !v.IsFront )
v.Brake += Handbrake; v.BrakeTorque += Handbrake * HandbrakeForce;
v.Update( this, in dt ); v.Update( this, in dt );
v.DoPhysics( in dt ); v.DoPhysics( in dt );

View File

@ -34,12 +34,9 @@ public partial class VeloXWheel
/// </summary> /// </summary>
public float CounterTorque { get; private set; } public float CounterTorque { get; private set; }
[Property, Sync] public bool AutoSetFriction { get; set; } = true;
[Property, Sync] public bool UseGroundVelocity { get; set; } = true;
[Property, Range( 0, 2 )] public float BrakeMult { get; set; } = 1f; [Property, Range( 0, 2 )] public float BrakeMult { get; set; } = 1f;
[Property] public Friction ForwardFriction = new(); public Friction ForwardFriction = new();
[Property] public Friction SidewayFriction = new(); public Friction SidewayFriction = new();
public Vector3 FrictionForce; public Vector3 FrictionForce;
@ -102,8 +99,6 @@ public partial class VeloXWheel
private Vector3 correctiveForce; private Vector3 correctiveForce;
private void UpdateFriction( float dt ) private void UpdateFriction( float dt )
{ {
var motorTorque = DriveTorque; var motorTorque = DriveTorque;
var brakeTorque = BrakeTorque * BrakeMult; var brakeTorque = BrakeTorque * BrakeMult;
@ -151,7 +146,7 @@ public partial class VeloXWheel
float absMotorTorque = Math.Abs( motorTorque ); float absMotorTorque = Math.Abs( motorTorque );
float absBrakeTorque = Math.Abs( brakeTorque ); float absBrakeTorque = Math.Abs( brakeTorque );
float maxForwardForce = Tire.Evaluate( Math.Abs( ForwardFriction.Slip ) ) * Math.Min( peakForwardFrictionForce, forwardForceClamp ); float maxForwardForce = Math.Min( peakForwardFrictionForce, forwardForceClamp );
maxForwardForce = absMotorTorque < absBrakeTorque ? maxForwardForce : peakForwardFrictionForce; maxForwardForce = absMotorTorque < absBrakeTorque ? maxForwardForce : peakForwardFrictionForce;
ForwardFriction.Force = forwardInputForce > maxForwardForce ? maxForwardForce ForwardFriction.Force = forwardInputForce > maxForwardForce ? maxForwardForce
@ -204,7 +199,7 @@ public partial class VeloXWheel
ForwardFriction.Slip = (ForwardFriction.Speed - AngularVelocity * mRadius) / clampedAbsForwardSpeed; ForwardFriction.Slip = (ForwardFriction.Speed - AngularVelocity * mRadius) / clampedAbsForwardSpeed;
ForwardFriction.Slip *= slipLoadModifier; ForwardFriction.Slip *= slipLoadModifier;
SidewayFriction.Slip = MathF.Atan2( SidewayFriction.Speed, clampedAbsForwardSpeed ).RadianToDegree() * 0.01111f; SidewayFriction.Slip = MathF.Atan2( SidewayFriction.Speed, clampedAbsForwardSpeed );
SidewayFriction.Slip *= slipLoadModifier; SidewayFriction.Slip *= slipLoadModifier;
float sideSlipSign = SidewayFriction.Slip > 0 ? 1 : -1; float sideSlipSign = SidewayFriction.Slip > 0 ? 1 : -1;
@ -288,7 +283,8 @@ public partial class VeloXWheel
//DebugOverlay.Normal( WorldPosition, hitSidewaysDirection * 10, overlay: true ); //DebugOverlay.Normal( WorldPosition, hitSidewaysDirection * 10, overlay: true );
//DebugOverlay.Normal( WorldPosition, hitForwardDirection * 10, overlay: true ); //DebugOverlay.Normal( WorldPosition, hitForwardDirection * 10, overlay: true );
//DebugOverlay.Normal( WorldPosition, FrictionForce / 100, overlay: true ); //DebugOverlay.Normal( WorldPosition, FrictionForce / 100, overlay: true );
//DebugOverlay.Normal( ContactPosition, ContactNormal * 10, overlay: true ); //DebugOverlay.Normal( ContactPosition, Vector3.Up * AngularVelocity, overlay: true );
//DebugOverlay.Sphere( new( ContactPosition, 4 ), overlay: true ); //DebugOverlay.Sphere( new( ContactPosition, 4 ), overlay: true );
//Vehicle.Body.ApplyForceAt( ContactPosition, FrictionForce ); //Vehicle.Body.ApplyForceAt( ContactPosition, FrictionForce );

View File

@ -5,7 +5,9 @@ namespace VeloX;
public partial class VeloXWheel : Component public partial class VeloXWheel : Component
{ {
protected override void DrawGizmos() protected override void DrawGizmos() => GizmoDraw();
public void GizmoDraw()
{ {
if ( !Gizmo.IsSelected ) if ( !Gizmo.IsSelected )
@ -60,15 +62,5 @@ public partial class VeloXWheel : Component
} }
} }
////
//// Forward direction
////
//{
// var arrowStart = Vector3.Forward * Radius.MeterToInch();
// var arrowEnd = arrowStart + Vector3.Forward * 8f;
// Gizmo.Draw.Color = Color.Red;
// Gizmo.Draw.Arrow( arrowStart, arrowEnd, 4, 1 );
//}
} }
} }

View File

@ -47,6 +47,7 @@ public partial class VeloXWheel
_skidMark.AutoCalculateNormals = false; _skidMark.AutoCalculateNormals = false;
_skidMark.SplineInterpolation = 4; _skidMark.SplineInterpolation = 4;
go.Flags = go.Flags.WithFlag( GameObjectFlags.Hidden, true ); go.Flags = go.Flags.WithFlag( GameObjectFlags.Hidden, true );
go.Flags = go.Flags.WithFlag( GameObjectFlags.NotNetworked, true );
SkidMarks.Enqueue( _skidMark ); SkidMarks.Enqueue( _skidMark );
} }
@ -84,6 +85,7 @@ public partial class VeloXWheel
WorldRotation = Rotation.LookAt( ContactNormal.RotateAround( Vector3.Zero, Rotation.FromRoll( 90 ) ) ) WorldRotation = Rotation.LookAt( ContactNormal.RotateAround( Vector3.Zero, Rotation.FromRoll( 90 ) ) )
}; };
go.Flags = go.Flags.WithFlag( GameObjectFlags.Hidden, true ); go.Flags = go.Flags.WithFlag( GameObjectFlags.Hidden, true );
go.Flags = go.Flags.WithFlag( GameObjectFlags.NotNetworked, true );
_skidMark.Points.Add( go ); _skidMark.Points.Add( go );
} }
} }

View File

@ -32,10 +32,9 @@ public partial class VeloXWheel
var val = Math.Abs( LateralSlip ) + Math.Abs( LongitudinalSlip ); var val = Math.Abs( LateralSlip ) + Math.Abs( LongitudinalSlip );
timeMul = timeMul.LerpTo( val, 0.1f ); timeMul = timeMul.LerpTo( val, 0.1f );
if ( timeMul > 2 ) //if ( timeMul > 2 )
return val; // return val;
return val * 5;
return 0;
} }
protected override void OnUpdate() protected override void OnUpdate()
{ {
@ -78,7 +77,7 @@ public partial class VeloXWheel
{ {
Type = ParticleFloat.ValueType.Curve, Type = ParticleFloat.ValueType.Curve,
Evaluation = ParticleFloat.EvaluationType.Life, Evaluation = ParticleFloat.EvaluationType.Life,
CurveA = new( new List<Curve.Frame>() { new( 0, 10f ), new( 0.8f, 50f ), new( 1f, 160f * sizeMul ) } ), CurveA = new( new List<Curve.Frame>() { new( 0, 10f ), new( 0.8f, 50f ), new( 1f, 160f ) } ),
}; };
effect.StartDelay = 0.025f + (1 - smokeMul) * 0.03f; effect.StartDelay = 0.025f + (1 - smokeMul) * 0.03f;
@ -98,7 +97,7 @@ public partial class VeloXWheel
ConstantB = 70, ConstantB = 70,
}; };
effect.Force = true; effect.Force = true;
effect.InitialVelocity = hitForwardDirection * LongitudinalSlip * 10f; effect.InitialVelocity = Vehicle.Body.Velocity / 3 + hitForwardDirection * LongitudinalSlip * 10f;
effect.ForceDirection = 0; effect.ForceDirection = 0;
effect.SheetSequence = true; effect.SheetSequence = true;
effect.SequenceSpeed = 0.5f; effect.SequenceSpeed = 0.5f;

View File

@ -24,15 +24,10 @@ public partial class VeloXWheel : Component
public float BaseInertia => Mass * (Radius * Radius); // kg·m² public float BaseInertia => Mass * (Radius * Radius); // kg·m²
[Property] public float Inertia { get; set; } = 1.5f; // kg·m² [Property] public float Inertia { get; set; } = 1.5f; // kg·m²
public float SideSlip => SlipAngle;
public float ForwardSlip => DynamicSlipRatio;
[Sync] public float Torque { get; set; }
[Sync, Range( 0, 1 )] public float Brake { get; set; }
[Property] public bool IsFront { get; protected set; } [Property] public bool IsFront { get; protected set; }
[Property] public float SteerMultiplier { get; set; } [Property] public float SteerMultiplier { get; set; }
public float RPM { get => AngularVelocity * 60f / MathF.Tau; set => AngularVelocity = value / (60 / MathF.Tau); } public float RPM { get => AngularVelocity * 60f / MathF.Tau; set => AngularVelocity = value / (60 / MathF.Tau); }
[Sync] internal float DistributionFactor { get; set; }
private Vector3 StartPos { get; set; } private Vector3 StartPos { get; set; }
private static Rotation CylinderOffset = Rotation.FromRoll( 90 ); private static Rotation CylinderOffset = Rotation.FromRoll( 90 );
@ -40,25 +35,19 @@ public partial class VeloXWheel : Component
[Sync] public bool IsOnGround { get; private set; } [Sync] public bool IsOnGround { get; private set; }
[Property] public float DriveTorque => Torque; [Property] public float DriveTorque { get; set; }
[Property] public float BrakeTorque => Brake * 5000f; [Property] public float BrakeTorque { get; set; }
public float Compression { get; protected set; } // meters public float Compression { get; protected set; } // meters
public float LastLength { get; protected set; } // meters public float LastLength { get; protected set; } // meters
public float Fz { get; protected set; } // N public float Fz { get; protected set; } // N
public float AngularVelocity { get; protected set; } // rad/s public float AngularVelocity { get; protected set; } // rad/s
public float Fx { get; protected set; } // N
public float Fy { get; protected set; } // N
public float RollAngle { get; protected set; } // degrees public float RollAngle { get; protected set; } // degrees
private VeloXBase Vehicle; private VeloXBase Vehicle;
[Sync] public Vector3 ContactNormal { get; protected set; } [Sync] public Vector3 ContactNormal { get; protected set; }
[Sync] public Vector3 ContactPosition { get; protected set; } [Sync] public Vector3 ContactPosition { get; protected set; }
public float SlipRatio { get; protected set; }
public float SlipAngle { get; protected set; }
public float DynamicSlipRatio { get; protected set; }
public float DynamicSlipAngle { get; protected set; }
Rotation TransformRotationSteer => Vehicle.WorldTransform.RotationToWorld( Vehicle.SteerAngle * SteerMultiplier ); Rotation TransformRotationSteer => Vehicle.WorldTransform.RotationToWorld( Vehicle.SteerAngle * SteerMultiplier );
protected override void OnAwake() protected override void OnAwake()
@ -96,15 +85,9 @@ public partial class VeloXWheel : Component
internal void StepPhys( VeloXBase vehicle, in float dt ) internal void StepPhys( VeloXBase vehicle, in float dt )
{ {
var _rigidbody = vehicle.Body;
const int numSamples = 3; const int numSamples = 3;
float halfWidth = Width.MeterToInch() * 0.5f; float halfWidth = Width.MeterToInch() * 0.5f;
var ang = vehicle.WorldTransform.RotationToWorld( vehicle.SteerAngle * SteerMultiplier );
Vector3 right = Vector3.VectorPlaneProject( ang.Right, Vector3.Up ).Normal;
int hitCount = 0; int hitCount = 0;
WheelTraceData wheelTraceData = new(); WheelTraceData wheelTraceData = new();
for ( int i = 0; i < numSamples; i++ ) for ( int i = 0; i < numSamples; i++ )
@ -123,7 +106,6 @@ public partial class VeloXWheel : Component
IsOnGround = true; IsOnGround = true;
//// Average all contacts
Fz = Math.Max( wheelTraceData.Force / hitCount, 0 ); Fz = Math.Max( wheelTraceData.Force / hitCount, 0 );
Compression = wheelTraceData.Compression / hitCount; Compression = wheelTraceData.Compression / hitCount;
ContactNormal = (wheelTraceData.ContactNormal / hitCount).Normal; ContactNormal = (wheelTraceData.ContactNormal / hitCount).Normal;
@ -131,32 +113,7 @@ public partial class VeloXWheel : Component
//DoSuspensionSounds( vehicle, (RestLength - Compression) * 0.8f); //DoSuspensionSounds( vehicle, (RestLength - Compression) * 0.8f);
LastLength = RestLength - Compression; LastLength = RestLength - Compression;
//DebugOverlay.Normal( ContactPosition, ContactNormal * Fz / 1000 );
// Apply suspension force
//_rigidbody.ApplyForceAt( ContactPosition, ContactNormal * Fz.MeterToInch() );
UpdateHitVariables(); UpdateHitVariables();
// Friction
Vector3 forward = ContactNormal.Cross( right ).Normal;
right = Rotation.FromAxis( ContactNormal, 90f ) * forward;
var velAtContact = _rigidbody.GetVelocityAtPoint( ContactPosition + vehicle.Body.MassCenter ) * 0.0254f;
float vx = Vector3.Dot( velAtContact, forward );
float vy = Vector3.Dot( velAtContact, right );
float wheelLinearVel = AngularVelocity * Radius;
float creepVel = 0.5f;
SlipRatio = (wheelLinearVel - vx) / (MathF.Abs( vx ) + creepVel);
SlipAngle = MathX.RadianToDegree( MathF.Atan2( vy, MathF.Abs( vx ) + creepVel ) );
float latCoeff = 1.0f - MathF.Exp( -MathF.Max( MathF.Abs( vx ), 1f ) * dt / 0.01f );
float longCoeff = 1.0f - MathF.Exp( -MathF.Max( MathF.Abs( vx ), 1f ) * dt / 0.01f );
DynamicSlipAngle += (SlipAngle - DynamicSlipAngle) * latCoeff;
DynamicSlipRatio += (SlipRatio - DynamicSlipRatio) * longCoeff;
UpdateFriction( dt ); UpdateFriction( dt );
} }
else else
@ -165,8 +122,6 @@ public partial class VeloXWheel : Component
// Wheel is off the ground // Wheel is off the ground
Compression = 0f; Compression = 0f;
Fz = 0f; Fz = 0f;
Fx = 0f;
Fy = 0f;
ContactNormal = Vector3.Up; ContactNormal = Vector3.Up;
ContactPosition = WorldPosition; ContactPosition = WorldPosition;
} }
@ -225,22 +180,20 @@ public partial class VeloXWheel : Component
{ {
if ( IsProxy ) if ( IsProxy )
return; return;
StepRotation( Vehicle, dt );
if ( AutoSimulate ) if ( AutoSimulate )
StepPhys( Vehicle, dt ); StepPhys( Vehicle, dt );
StepRotation( Vehicle, dt );
} }
const float HubCoulombNm = 20f;
const float HubViscous = 0.1f;
private void StepRotation( VeloXBase vehicle, in float dt ) private void StepRotation( VeloXBase vehicle, in float dt )
{ {
float inertia = MathF.Max( 1f, Inertia ); float inertia = MathF.Max( 1f, Inertia );
float roadTorque = ForwardFriction.Speed * Radius;
float roadTorque = Fx * Radius;
float externalTorque = DriveTorque - roadTorque; float externalTorque = DriveTorque - roadTorque;
float rollingResistanceTorque = Fz * Radius * SurfaceResistance; float rollingResistanceTorque = Fz * Radius * SurfaceResistance;
const float HubCoulombNm = 20f;
const float HubViscous = 0.1f;
float coulombTorque = BrakeTorque + rollingResistanceTorque + HubCoulombNm; float coulombTorque = BrakeTorque + rollingResistanceTorque + HubCoulombNm;
float omega = AngularVelocity; float omega = AngularVelocity;
@ -251,11 +204,8 @@ public partial class VeloXWheel : Component
} }
else else
{ {
// viscous decay
if ( HubViscous > 0f ) omega *= MathF.Exp( -(HubViscous / inertia) * dt ); if ( HubViscous > 0f ) omega *= MathF.Exp( -(HubViscous / inertia) * dt );
// Coulomb drag
if ( coulombTorque > 0f && omega != 0f ) if ( coulombTorque > 0f && omega != 0f )
{ {
float dir = MathF.Sign( omega ); float dir = MathF.Sign( omega );
@ -266,7 +216,7 @@ public partial class VeloXWheel : Component
if ( MathF.Abs( omega ) < 0.01f ) omega = 0f; if ( MathF.Abs( omega ) < 0.01f ) omega = 0f;
} }
AngularVelocity = omega; // wider sanity range AngularVelocity = omega;
RollAngle += MathX.RadianToDegree( AngularVelocity ) * dt; RollAngle += MathX.RadianToDegree( AngularVelocity ) * dt;
RollAngle = (RollAngle % 360f + 360f) % 360f; RollAngle = (RollAngle % 360f + 360f) % 360f;

View File

@ -12,7 +12,7 @@ public partial class VeloXCar
[KeyProperty] public float MaxForce { get; set; } [KeyProperty] public float MaxForce { get; set; }
[KeyProperty] public Vector3 Position { get; set; } [KeyProperty] public Vector3 Position { get; set; }
} }
public const float RHO = 10.225f; public const float RHO = 1.225f;
[Property, Feature( "Aerodinamics" )] public Vector3 Dimensions = new( 2f, 4.5f, 1.5f ); [Property, Feature( "Aerodinamics" )] public Vector3 Dimensions = new( 2f, 4.5f, 1.5f );
[Property, Feature( "Aerodinamics" )] public float FrontalCd { get; set; } = 0.35f; [Property, Feature( "Aerodinamics" )] public float FrontalCd { get; set; } = 0.35f;
[Property, Feature( "Aerodinamics" )] public float SideCd { get; set; } = 1.05f; [Property, Feature( "Aerodinamics" )] public float SideCd { get; set; } = 1.05f;
@ -52,4 +52,9 @@ public partial class VeloXCar
foreach ( DownforcePoint dp in DownforcePoints ) foreach ( DownforcePoint dp in DownforcePoints )
Body.ApplyForceAt( Transform.World.PointToWorld( dp.Position ), forceCoeff.MeterToInch() * dp.MaxForce.MeterToInch() * -WorldRotation.Up ); Body.ApplyForceAt( Transform.World.PointToWorld( dp.Position ), forceCoeff.MeterToInch() * dp.MaxForce.MeterToInch() * -WorldRotation.Up );
} }
//protected override void DrawGizmos()
//{
// Gizmo.Draw.LineBBox( new BBox( -Dimensions / 2 * 39.37f, Dimensions / 2 * 39.37f ) );
//}
} }