Improve engine friction, add a rev limiter

This commit is contained in:
sergeypdev 2025-05-02 21:11:48 +04:00
parent 08b6748d76
commit 1ea89fb2c9
5 changed files with 78 additions and 52 deletions

View File

@ -1,6 +1,6 @@
b b
1.6 1.6
-80 -120
1100 1100
0 0
300 300

1 b
2 1.6
3 -80 -120
4 1100
5 0
6 300

View File

@ -434,13 +434,15 @@ update_runtime_world :: proc(runtime_world: ^Runtime_World, dt: f32) {
"assets/ae86_rpm_torque.csv", "assets/ae86_rpm_torque.csv",
), ),
lowest_rpm = 1100, lowest_rpm = 1100,
inertia = 0.264, rev_limit_rpm = 7800,
internal_friction = 0.00, rev_limit_interval = 0.025,
inertia = 0.264 * 0.5,
internal_friction = 0.01,
gear_ratios = []f32{3.48, 3.587, 2.022, 1.384, 1, 0.861}, gear_ratios = []f32{3.48, 3.587, 2.022, 1.384, 1, 0.861},
axle = physics.Drive_Axle_Config { axle = physics.Drive_Axle_Config {
wheels = {wheel_rl, wheel_rr}, wheels = {wheel_rl, wheel_rr},
wheel_count = 2, wheel_count = 2,
diff_type = .Open, diff_type = .Fixed,
final_drive_ratio = 4.1, final_drive_ratio = 4.1,
}, },
}, },

View File

@ -151,7 +151,7 @@ draw_debug_ui :: proc(ctx: ^ui.Context, scene: ^Scene, config: Solver_Config) {
sim_state := get_sim_state(scene) sim_state := get_sim_state(scene)
active_wheels := []int{2, 3} active_wheels := []int{}
w, h: i32 = 200, 200 w, h: i32 = 200, 200

View File

@ -217,39 +217,46 @@ Gear_Ratios_Handle :: distinct spanpool.Handle
// This actually handles everything, engine, transmission, differential, etc. It's easier to keep it in one place // This actually handles everything, engine, transmission, differential, etc. It's easier to keep it in one place
Engine :: struct { Engine :: struct {
alive: bool, alive: bool,
// Engine Params // Engine Params
rpm_torque_curve: Engine_Curve_Handle, rpm_torque_curve: Engine_Curve_Handle,
lowest_rpm: f32, lowest_rpm: f32,
inertia: f32, // Rpm when rev limiter activates
internal_friction: f32, rev_limit_rpm: f32,
// Time in seconds for how long rev limiter cuts the throttle
rev_limit_interval: f32,
inertia: f32,
internal_friction: f32,
// Transmission Params // Transmission Params
// 0 - reverse, 1 - first, etc. // 0 - reverse, 1 - first, etc.
gear_ratios: Gear_Ratios_Handle, gear_ratios: Gear_Ratios_Handle,
axle: Drive_Axle, axle: Drive_Axle,
// Engine State // Engine State
// Angular velocity, omega // Angular velocity, omega
q: f32, q: f32,
w: f32, w: f32,
// Reset to -rev_limit_interval when rpm exceeds rev_limit_rpm, has to be >= 0 for throttle to work
rev_limit_time: f32,
// Impulse applied when engine is stalling (rpm < lowest_rpm) // Impulse applied when engine is stalling (rpm < lowest_rpm)
unstall_impulse: f32, unstall_impulse: f32,
// Friction that makes rpm go down when you're not accelerating // Friction that makes rpm go down when you're not accelerating
friction_impulse: f32, friction_impulse: f32,
// Impulse applied from releasing throttle // Impulse applied from releasing throttle
throttle_impulse: f32, throttle_impulse: f32,
// Transmission State // Transmission State
// -1 - reeverse, 0 - neutral, 1 - first, etc. // -1 - reeverse, 0 - neutral, 1 - first, etc.
gear: i32, gear: i32,
// Controls // Controls
throttle: f32, throttle: f32,
// Free list // Free list
next_plus_one: i32, next_plus_one: i32,
} }
// Index plus one, so handle 0 maps to invalid body // Index plus one, so handle 0 maps to invalid body
@ -378,15 +385,17 @@ Drive_Axle_Config :: struct {
} }
Engine_Config :: struct { Engine_Config :: struct {
rpm_torque_curve: [][2]f32, rpm_torque_curve: [][2]f32,
lowest_rpm: f32, lowest_rpm: f32,
inertia: f32, rev_limit_rpm: f32,
internal_friction: f32, rev_limit_interval: f32,
inertia: f32,
internal_friction: f32,
// Transmission Params // Transmission Params
// 0 - reverse, 1 - first, etc. // 0 - reverse, 1 - first, etc.
gear_ratios: []f32, gear_ratios: []f32,
axle: Drive_Axle_Config, axle: Drive_Axle_Config,
} }
calculate_body_params_from_config :: proc( calculate_body_params_from_config :: proc(
@ -500,6 +509,8 @@ update_engine_from_config :: proc(
engine.gear_ratios = add_gear_ratios(sim_state, config.gear_ratios) engine.gear_ratios = add_gear_ratios(sim_state, config.gear_ratios)
engine.lowest_rpm = config.lowest_rpm engine.lowest_rpm = config.lowest_rpm
engine.rev_limit_rpm = config.rev_limit_rpm
engine.rev_limit_interval = config.rev_limit_interval
engine.inertia = config.inertia engine.inertia = config.inertia
engine.internal_friction = config.internal_friction engine.internal_friction = config.internal_friction

View File

@ -571,36 +571,48 @@ pgs_solve_engines :: proc(sim_state: ^Sim_State, config: Solver_Config, dt: f32,
engine.w += applied_impulse / engine.inertia engine.w += applied_impulse / engine.inertia
} }
rpm := angular_velocity_to_rpm(engine.w)
throttle := engine.throttle
if engine.rev_limit_time < 0.0 {
engine.rev_limit_time += dt
throttle = 0
} else if (rpm >= engine.rev_limit_rpm) {
engine.rev_limit_time = -engine.rev_limit_interval
throttle = 0
}
// Throttle // Throttle
{ {
rpm := angular_velocity_to_rpm(engine.w)
torque: f32 {
torque: f32
idx, _ := slice.binary_search_by( idx, _ := slice.binary_search_by(
rpm_torque_curve, rpm_torque_curve,
rpm, rpm,
proc(a: [2]f32, k: f32) -> slice.Ordering { proc(a: [2]f32, k: f32) -> slice.Ordering {
return slice.cmp(a[0], k) return slice.cmp(a[0], k)
}, },
) )
if idx > 0 && idx < len(rpm_torque_curve) - 1 { if idx > 0 && idx < len(rpm_torque_curve) - 1 {
cur_point := rpm_torque_curve[idx] cur_point := rpm_torque_curve[idx]
next_point := rpm_torque_curve[idx + 1] next_point := rpm_torque_curve[idx + 1]
rpm_diff := next_point[0] - cur_point[0] rpm_diff := next_point[0] - cur_point[0]
alpha := (rpm - cur_point[0]) / rpm_diff alpha := (rpm - cur_point[0]) / rpm_diff
torque = math.lerp(cur_point[1], next_point[1], alpha) torque = math.lerp(cur_point[1], next_point[1], alpha)
} else { } else {
torque = rpm_torque_curve[math.clamp(idx, 0, len(rpm_torque_curve) - 1)][1] torque = rpm_torque_curve[math.clamp(idx, 0, len(rpm_torque_curve) - 1)][1]
}
// log.debugf("torque: %v Nm", torque)
torque *= throttle
engine.w += (torque / engine.inertia) * dt
} }
// log.debugf("torque: %v Nm", torque)
torque *= engine.throttle
engine.w += (torque / engine.inertia) * dt
} }
// Internal Friction // Internal Friction
@ -610,13 +622,14 @@ pgs_solve_engines :: proc(sim_state: ^Sim_State, config: Solver_Config, dt: f32,
inv_w := engine.inertia inv_w := engine.inertia
friction := friction :=
math.pow( math.pow(max(engine.w - rpm_to_angular_velocity(engine.lowest_rpm), 0), 2) *
max(engine.w - rpm_to_angular_velocity(engine.lowest_rpm), 0) * 0.002, 0.0001 *
4,
) *
engine.internal_friction + engine.internal_friction +
engine.internal_friction engine.internal_friction
// Not physically based, but we assume when throttle is open there is no friction
friction *= (1.0 - throttle)
incremental_impulse := inv_w * delta_omega incremental_impulse := inv_w * delta_omega
new_total_impulse := math.clamp( new_total_impulse := math.clamp(
engine.friction_impulse + incremental_impulse, engine.friction_impulse + incremental_impulse,