Gears and engine->wheel power transfer. Not working well yet, it's losing a lot of power somewhere. Maybe it needs to be a position constraint

This commit is contained in:
sergeypdev 2025-04-27 00:02:23 +04:00
parent c3945a80e5
commit cbe0dd20e5
3 changed files with 104 additions and 69 deletions

View File

@ -53,6 +53,7 @@ Runtime_World :: struct {
solver_state: physics.Solver_State,
car_com: rl.Vector3,
car_handle: physics.Body_Handle,
engine_handle: physics.Engine_Handle,
camera_yaw_pitch: rl.Vector2,
camera_speed: f32,
camera: rl.Camera3D,
@ -331,7 +332,7 @@ update_runtime_world :: proc(runtime_world: ^Runtime_World, dt: f32) {
radius := f32(0.2888)
wheel_front_z := f32(1.3)
wheel_back_z := f32(-1.1)
wheel_mass := f32(30)
wheel_mass := f32(14)
wheel_fl := physics.immediate_suspension_constraint(
&world.physics_scene,
@ -394,21 +395,21 @@ update_runtime_world :: proc(runtime_world: ^Runtime_World, dt: f32) {
},
)
engine := physics.immediate_engine(
runtime_world.engine_handle = physics.immediate_engine(
&world.physics_scene,
&runtime_world.solver_state,
#hash("engine", "fnv32a"),
physics.Engine_Config {
rpm_torque_curve = assets.get_curve_2d(&g_mem.assetman, "assets/ae86_rpm_torque.csv").points,
lowest_rpm = 1000,
inertia = 10,
internal_friction = 8,
inertia = 0.0264,
internal_friction = 0.00,
gear_ratios = []f32{3.48, 3.587, 2.022, 1.384, 1, 0.861},
axle = physics.Drive_Axle_Config {
wheels = {wheel_rl, wheel_rr},
wheel_count = 2,
diff_type = .Fixed,
final_drive_ratio = 4.3,
final_drive_ratio = 4.1,
},
},
)
@ -445,7 +446,15 @@ update_runtime_world :: proc(runtime_world: ^Runtime_World, dt: f32) {
wheel.brake_impulse = brake_input * (1.0 - BRAKE_BIAS) * BRAKE_IMPULSE
}
physics.get_engine(sim_state, engine).throttle = gas_input
engine := physics.get_engine(sim_state, runtime_world.engine_handle)
engine.throttle = gas_input
if rl.IsKeyPressed(.LEFT_SHIFT) || rl.IsGamepadButtonPressed(0, .RIGHT_FACE_DOWN) {
engine.gear += 1
}
if rl.IsKeyPressed(.LEFT_CONTROL) || rl.IsGamepadButtonPressed(0, .RIGHT_FACE_LEFT) {
engine.gear -= 1
}
car_body := physics.get_body(sim_state, runtime_world.car_handle)
turn_vel_correction := clamp(30.0 / linalg.length(car_body.v), 0, 1)
@ -867,13 +876,17 @@ draw :: proc() {
}
} else {
car := physics.get_body(sim_state, runtime_world.car_handle)
engine := physics.get_engine(sim_state, runtime_world.engine_handle)
gear_ratios := physics.get_gear_ratios(sim_state, engine.gear_ratios)
rl.DrawText(
fmt.ctprintf(
"p: %v\nv: %v\nw: %v\ng: %v",
"p: %v\nv: %v\ngear: %v\nratio: %v\nrpm: %v\nspeed: %v km/h",
car.x,
car.v,
car.w,
SOLVER_CONFIG.gravity,
engine.gear,
physics.lookup_gear_ratio(gear_ratios, engine.gear),
physics.angular_velocity_to_rpm(engine.w),
linalg.length(car.v) * 3.6,
),
5,
32,

View File

@ -12,6 +12,7 @@ import "core:slice"
import "game:container/spanpool"
import "libs:tracy"
_ :: log
_ :: rand
_ :: math
_ :: fmt
@ -484,6 +485,23 @@ pgs_solve_contacts :: proc(
}
}
// Returns index into the gear ratios array
// -1 (revers) mapped to 0
// 1..N mapped to 0..N-1
lookup_gear_ratio :: #force_inline proc(gear_ratios: []f32, gear: i32) -> (ratio: f32) {
assert(len(gear_ratios) > 1)
if gear == 0 {
return 0
} else {
index := int(gear + 1)
if index > 0 {
index -= 1
}
index = clamp(index, 0, len(gear_ratios) - 1)
return gear_ratios[index]
}
}
pgs_solve_engines :: proc(sim_state: ^Sim_State, config: Solver_Config, dt: f32, inv_dt: f32) {
for &engine in sim_state.engines {
if engine.alive {
@ -506,6 +524,38 @@ pgs_solve_engines :: proc(sim_state: ^Sim_State, config: Solver_Config, dt: f32,
engine.w += applied_impulse / engine.inertia
}
// Throttle
{
rpm := angular_velocity_to_rpm(engine.w)
torque: f32
idx, _ := slice.binary_search_by(
rpm_torque_curve,
rpm,
proc(a: [2]f32, k: f32) -> slice.Ordering {
return slice.cmp(a[0], k)
},
)
if idx > 0 && idx < len(rpm_torque_curve) - 1 {
cur_point := rpm_torque_curve[idx]
next_point := rpm_torque_curve[idx + 1]
rpm_diff := next_point[0] - cur_point[0]
alpha := (rpm - cur_point[0]) / rpm_diff
torque = math.lerp(cur_point[1], next_point[1], alpha)
} else {
torque = rpm_torque_curve[math.clamp(idx, 0, len(rpm_torque_curve) - 1)][1]
}
log.debugf("torque: %v Nm", torque)
torque *= engine.throttle
engine.w += (torque / engine.inertia) * dt
}
// Internal Friction
{
delta_omega := -engine.w
@ -532,67 +582,34 @@ pgs_solve_engines :: proc(sim_state: ^Sim_State, config: Solver_Config, dt: f32,
engine.w += applied_impulse / engine.inertia
}
// Throttle
{
rpm := angular_velocity_to_rpm(engine.w)
torque: f32
idx, _ := slice.binary_search_by(
rpm_torque_curve,
rpm,
proc(a: [2]f32, k: f32) -> slice.Ordering {
return slice.cmp(a[0], k)
},
)
if idx > 0 && idx < len(rpm_torque_curve) - 1 {
cur_point := rpm_torque_curve[idx]
next_point := rpm_torque_curve[idx + 1]
rpm_diff := next_point[0] - cur_point[0]
alpha := (rpm - cur_point[0]) / rpm_diff
torque = math.lerp(cur_point[1], next_point[1], alpha)
} else {
torque = rpm_torque_curve[math.clamp(idx, 0, len(rpm_torque_curve) - 1)][1]
}
torque *= engine.throttle
engine.w += torque / engine.inertia
}
// Transmission
{
// TODO: update from game
engine.gear = 1
gear_ratio := lookup_gear_ratio(gear_ratios, engine.gear)
power_split := 1.0 / f32(engine.axle.wheel_count)
for i in 0 ..< engine.axle.wheel_count {
drive_wheel := &engine.axle.wheels[i]
wheel := get_suspension_constraint(sim_state, drive_wheel.wheel)
if engine.gear != 0 {
for i in 0 ..< engine.axle.wheel_count {
drive_wheel := &engine.axle.wheels[i]
wheel := get_suspension_constraint(sim_state, drive_wheel.wheel)
ratio := gear_ratios[1] * engine.axle.final_drive_ratio
inv_ratio := f32(1.0 / ratio)
ratio := gear_ratio * engine.axle.final_drive_ratio
inv_ratio := f32(1.0 / (ratio))
w1 := wheel.inv_inertia
w2 := f32(1.0 / (engine.inertia * ratio))
w1 := wheel.inv_inertia
w2 := f32(1.0 / (engine.inertia * ratio * ratio))
w := w1 + w2
inv_w := f32(1.0 / w)
w := w1 + w2
inv_w := f32(1.0 / w)
delta_omega := -engine.w - wheel.w * ratio
delta_omega := wheel.w * ratio - (-engine.w)
incremental_impulse := -inv_w * delta_omega * power_split
drive_wheel.impulse += incremental_impulse
incremental_impulse := -inv_w * delta_omega
// drive_wheel.impulse += incremental_impulse
wheel.w += -incremental_impulse * wheel.inv_inertia * inv_ratio
engine.w += -(incremental_impulse / engine.inertia)
wheel.w += incremental_impulse * w1 * inv_ratio
engine.w += incremental_impulse / (engine.inertia * ratio * ratio)
}
}
}
// tracy.Plot("rpm", f64(angular_velocity_to_rpm(engine.w)))
log.debugf("rpm: %v", angular_velocity_to_rpm(engine.w))
}
}
}
@ -844,15 +861,20 @@ pgs_substep :: proc(sim_state: ^Sim_State, config: Solver_Config, dt: f32, inv_d
e.w += e.friction_impulse / e.inertia
for i in 0 ..< e.axle.wheel_count {
drive_wheel := &e.axle.wheels[i]
wheel := get_suspension_constraint(sim_state, drive_wheel.wheel)
if true {
if e.gear != 0 {
for i in 0 ..< e.axle.wheel_count {
drive_wheel := &e.axle.wheels[i]
wheel := get_suspension_constraint(sim_state, drive_wheel.wheel)
ratio := gear_ratios[1] * e.axle.final_drive_ratio
inv_ratio := f32(1.0 / ratio)
ratio :=
lookup_gear_ratio(gear_ratios, e.gear) * e.axle.final_drive_ratio
inv_ratio := f32(1.0 / ratio)
wheel.w += -drive_wheel.impulse * wheel.inv_inertia * inv_ratio
e.w += -(drive_wheel.impulse / e.inertia)
wheel.w += drive_wheel.impulse * wheel.inv_inertia * inv_ratio
e.w += (drive_wheel.impulse / (e.inertia * ratio * ratio))
}
}
}
}
}

View File

@ -23,14 +23,14 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 29,
"id": "3074424c",
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "dfc8c93ea1774a788835f524f65b6843",
"model_id": "c2fcd32bbbd1482ebe2be970bfe3ed33",
"version_major": 2,
"version_minor": 0
},
@ -58,7 +58,7 @@
"10"
]
},
"execution_count": 28,
"execution_count": 29,
"metadata": {},
"output_type": "execute_result"
}