diff --git a/game/game.odin b/game/game.odin index bf0c8a5..916e5a0 100644 --- a/game/game.odin +++ b/game/game.odin @@ -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, diff --git a/game/physics/simulation.odin b/game/physics/simulation.odin index 7ba5341..6f07feb 100644 --- a/game/physics/simulation.odin +++ b/game/physics/simulation.odin @@ -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)) + } + } } } } diff --git a/research/engine_friction.ipynb b/research/engine_friction.ipynb index 1b0570b..680a469 100644 --- a/research/engine_friction.ipynb +++ b/research/engine_friction.ipynb @@ -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" }