diff --git a/game/game.odin b/game/game.odin index 95e43ac..6c2f232 100644 --- a/game/game.odin +++ b/game/game.odin @@ -390,24 +390,41 @@ update_runtime_world :: proc(runtime_world: ^Runtime_World, dt: f32) { drive_wheels := []physics.Suspension_Constraint_Handle{wheel_rl, wheel_rr} turn_wheels := []physics.Suspension_Constraint_Handle{wheel_fl, wheel_fr} + front_wheels := turn_wheels + back_wheels := drive_wheels - DRIVE_IMPULSE :: 10000 - BRAKE_IMPULSE :: 10000 + DRIVE_IMPULSE :: 2000 + BRAKE_IMPULSE :: 500 TURN_ANGLE :: -f32(20) * math.RAD_PER_DEG + // 68% front, 32% rear + BRAKE_BIAS :: f32(0.68) + + for wheel_handle in front_wheels { + wheel := physics.get_suspension_constraint(sim_state, wheel_handle) + + wheel.brake_impulse = 0 + if (rl.IsKeyDown(.S)) { + wheel.brake_impulse = BRAKE_BIAS * BRAKE_IMPULSE + } + } + + for wheel_handle in back_wheels { + wheel := physics.get_suspension_constraint(sim_state, wheel_handle) + + wheel.brake_impulse = 0 + if (rl.IsKeyDown(.S)) { + wheel.brake_impulse = (1.0 - BRAKE_BIAS) * BRAKE_IMPULSE + } + } for wheel_handle in drive_wheels { wheel := physics.get_suspension_constraint(sim_state, wheel_handle) wheel.drive_impulse = 0 - wheel.brake_impulse = 0 if rl.IsKeyDown(.W) && !g_mem.free_cam { wheel.drive_impulse = DRIVE_IMPULSE } - - if rl.IsKeyDown(.S) && !g_mem.free_cam { - wheel.brake_impulse = BRAKE_IMPULSE - } } car_body := physics.get_body(sim_state, runtime_world.car_handle) diff --git a/game/physics/helpers.odin b/game/physics/helpers.odin index 5b6ea3a..92cd7e2 100644 --- a/game/physics/helpers.odin +++ b/game/physics/helpers.odin @@ -75,6 +75,17 @@ wheel_get_right_vec :: #force_inline proc( return body_local_to_world_vec(body, local_right) } +wheel_get_forward_vec :: #force_inline proc( + body: Body_Ptr, + wheel: Suspension_Constraint_Ptr, +) -> Vec3 { + local_right := lg.quaternion_mul_vector3( + lg.quaternion_angle_axis(wheel.turn_angle, Vec3{0, 1, 0}), + Vec3{0, 0, 1}, + ) + return body_local_to_world_vec(body, local_right) +} + body_get_shape_offset_local :: proc(body: Body_Ptr) -> (offset: Vec3) { #partial switch s in body.shape { case Internal_Shape_Convex: diff --git a/game/physics/scene.odin b/game/physics/scene.odin index bf1fba0..097c9a5 100644 --- a/game/physics/scene.odin +++ b/game/physics/scene.odin @@ -120,49 +120,51 @@ Collision_Shape :: union { } Suspension_Constraint :: struct { - alive: bool, + alive: bool, // Pos relative to the body - rel_pos: Vec3, + rel_pos: Vec3, // Dir relative to the body - rel_dir: Vec3, + rel_dir: Vec3, // Handle of the rigid body - body: Body_Handle, + body: Body_Handle, // Wheel radius - radius: f32, + radius: f32, // Wheel mass - mass: f32, + mass: f32, // Rest distance - rest: f32, + rest: f32, // Frequency of the spring, affects stiffness - natural_frequency: f32, + natural_frequency: f32, // How much to damp velocity of the spring - damping: f32, + damping: f32, // Runtime state // Accumulated impulse - spring_impulse: f32, - lateral_impulse: f32, - longitudinal_impulse: f32, + spring_impulse: f32, + lateral_impulse: f32, + longitudinal_impulse: f32, + brake_friction_impulse: f32, // Inverse inertia along wheel rotation axis - inv_inertia: f32, + inv_inertia: f32, // Rotation - q: f32, + q: f32, // Angular velocity - w: f32, - hit: bool, - hit_point: Vec3, + w: f32, + hit: bool, + hit_point: Vec3, // rel_hit_point = rel_pos + rel_dir * hit_t - hit_t: f32, - turn_angle: f32, - drive_impulse: f32, - brake_impulse: f32, - applied_impulse: Vec3, + hit_t: f32, + turn_angle: f32, + drive_impulse: f32, + // Impulse of brake pad on brake disc, set outsie + brake_impulse: f32, + applied_impulse: Vec3, // Free list - next_plus_one: i32, + next_plus_one: i32, } // Index plus one, so handle 0 maps to invalid body diff --git a/game/physics/simulation.odin b/game/physics/simulation.odin index e7577bb..1f08fe4 100644 --- a/game/physics/simulation.odin +++ b/game/physics/simulation.odin @@ -488,14 +488,48 @@ pgs_solve_suspension :: proc(sim_state: ^Sim_State, config: Solver_Config, dt: f {wheel_world_pos, pos2}, collision.plane_from_point_normal({}, collision.Vec3{0, 1, 0}), ) - forward := body_local_to_world_vec(body, Vec3{0, 0, 1}) + forward := wheel_get_forward_vec(body, v) - contact_patch_linear_vel := - body_velocity_at_point(body, v.hit_point) + (v.radius * v.w * forward) + body_vel_at_contact_patch := body_velocity_at_point(body, v.hit_point) + + w_normal := get_body_angular_inverse_mass(body, dir) + inv_w_normal := 1.0 / w_normal + + // Drive force + if true { + total_impulse := -v.drive_impulse + + v.w += v.inv_inertia * total_impulse * dt + } + + // Brake force + if true { + // How strong is brake pad pushing against brake disc + brake_pad_impulse := v.brake_impulse + + if brake_pad_impulse != 0 { + // TODO: figure out what's the realistic value + brake_friction := f32(1.0) + friction_clamp := brake_pad_impulse * brake_friction + + incremental_impulse := (1.0 / v.inv_inertia) * -v.w + new_total_impulse := clamp( + v.brake_friction_impulse + incremental_impulse, + -friction_clamp, + friction_clamp, + ) + applied_impulse := new_total_impulse - v.brake_friction_impulse + v.brake_friction_impulse = new_total_impulse + + v.w += v.inv_inertia * applied_impulse + } else { + v.brake_friction_impulse = 0 + } + } - w := get_body_angular_inverse_mass(body, dir) - inv_w := 1.0 / w if v.hit { + + // Spring force { bias_coef, mass_coef, impulse_coef := calculate_soft_constraint_params( v.natural_frequency, @@ -508,7 +542,7 @@ pgs_solve_suspension :: proc(sim_state: ^Sim_State, config: Solver_Config, dt: f separation := v.rest - x incremental_impulse := - -inv_w * mass_coef * (vel + separation * bias_coef) - + -inv_w_normal * mass_coef * (vel + separation * bias_coef) - impulse_coef * v.spring_impulse v.spring_impulse += incremental_impulse @@ -517,6 +551,9 @@ pgs_solve_suspension :: proc(sim_state: ^Sim_State, config: Solver_Config, dt: f right := wheel_get_right_vec(body, v) + contact_patch_linear_vel := + body_vel_at_contact_patch + (v.radius * v.w * forward) + // Longitudinal friction if true { vel_long := lg.dot(contact_patch_linear_vel, forward) @@ -524,7 +561,12 @@ pgs_solve_suspension :: proc(sim_state: ^Sim_State, config: Solver_Config, dt: f friction := f32(1) friction_clamp := abs(v.spring_impulse) * friction - incremental_impulse := -v.inv_inertia * vel_long + w_body := get_body_inverse_mass(body, forward, v.hit_point) + + w_long := w_body + v.inv_inertia + inv_w_long := 1.0 / w_long + + incremental_impulse := -inv_w_long * vel_long new_total_impulse := clamp( v.longitudinal_impulse + incremental_impulse, -friction_clamp, @@ -533,30 +575,19 @@ pgs_solve_suspension :: proc(sim_state: ^Sim_State, config: Solver_Config, dt: f applied_impulse := new_total_impulse - v.longitudinal_impulse v.longitudinal_impulse = new_total_impulse + apply_velocity_correction(body, applied_impulse * forward, v.hit_point) v.w += v.inv_inertia * applied_impulse } - - // Drive forces - if true { - total_impulse := v.drive_impulse - v.brake_impulse - - apply_velocity_correction( - body, - total_impulse * forward * dt, - wheel_world_pos, - ) - } - // Lateral friction if true { - vel_contact := body_velocity_at_point(body, wheel_world_pos) + vel_contact := body_vel_at_contact_patch lateral_vel := lg.dot(right, vel_contact) friction := f32(1) friction_clamp := -v.spring_impulse * friction - incremental_impulse := -inv_w * lateral_vel + incremental_impulse := -inv_w_normal * lateral_vel new_total_impulse := clamp( v.lateral_impulse + incremental_impulse, -friction_clamp, @@ -565,7 +596,7 @@ pgs_solve_suspension :: proc(sim_state: ^Sim_State, config: Solver_Config, dt: f applied_impulse := new_total_impulse - v.lateral_impulse v.lateral_impulse = new_total_impulse - apply_velocity_correction(body, applied_impulse * right, wheel_world_pos) + apply_velocity_correction(body, applied_impulse * right, v.hit_point) } } else { v.lateral_impulse = 0 @@ -618,6 +649,8 @@ pgs_substep :: proc(sim_state: ^Sim_State, config: Solver_Config, dt: f32, inv_d if s.hit { body := get_body(sim_state, s.body) p := body_local_to_world(body, s.rel_pos) + hit_p := body_local_to_world(body, s.rel_pos + s.rel_dir * s.hit_t) + forward := wheel_get_forward_vec(body, s) right := wheel_get_right_vec(body, s) apply_velocity_correction( @@ -627,8 +660,11 @@ pgs_substep :: proc(sim_state: ^Sim_State, config: Solver_Config, dt: f32, inv_d ) apply_velocity_correction(body, s.lateral_impulse * right, p) + apply_velocity_correction(body, s.longitudinal_impulse * forward, hit_p) s.w += s.inv_inertia * s.longitudinal_impulse } + + s.w += s.inv_inertia * s.brake_friction_impulse } }