diff --git a/assets/tyre_lateral.csv b/assets/tyre_lateral.csv index db306b4..a6cb641 100644 --- a/assets/tyre_lateral.csv +++ b/assets/tyre_lateral.csv @@ -1,6 +1,6 @@ a 1.5 -0 +-80 1100 1100 10 diff --git a/assets/tyre_longitudinal.csv b/assets/tyre_longitudinal.csv index 3f7a49c..30a19e7 100644 --- a/assets/tyre_longitudinal.csv +++ b/assets/tyre_longitudinal.csv @@ -1,6 +1,6 @@ b -1.5 --2.0 +1.6 +-80 1100 0 300 diff --git a/game/game.odin b/game/game.odin index 440652a..54bde23 100644 --- a/game/game.odin +++ b/game/game.odin @@ -440,7 +440,7 @@ update_runtime_world :: proc(runtime_world: ^Runtime_World, dt: f32) { axle = physics.Drive_Axle_Config { wheels = {wheel_rl, wheel_rr}, wheel_count = 2, - diff_type = .Fixed, + diff_type = .Open, final_drive_ratio = 4.1, }, }, @@ -586,6 +586,8 @@ orbit_camera_to_rl :: proc(camera: Orbit_Camera) -> rl.Camera3D { update :: proc() { tracy.Zone() + ui.begin(&g_mem.ui_context) + if rl.IsKeyPressed(.TAB) { g_mem.editor = !g_mem.editor } @@ -670,28 +672,6 @@ update :: proc() { } } - { - ui.begin(&g_mem.ui_context) - defer ui.end(&g_mem.ui_context) - - if ui.window( - &g_mem.ui_context, - "Hello, world", - ui.Rect{x = 0, y = 0, w = 100, h = 100}, - ui.Options{.AUTO_SIZE}, - ) { - ui.layout_column(&g_mem.ui_context) - - ui.text(&g_mem.ui_context, "It Works!") - - ui.begin_line(&g_mem.ui_context, ui.Color{255, 0, 0, 255}) - defer ui.end_line(&g_mem.ui_context) - - ui.push_line_point(&g_mem.ui_context, {0, 0}) - ui.push_line_point(&g_mem.ui_context, {1, 1}) - } - } - update_runtime_world(get_runtime_world(), dt) } } @@ -768,6 +748,7 @@ draw :: proc() { // rl.DrawGrid(100, 1) physics.draw_debug_scene(&world.physics_scene) + physics.draw_debug_ui(&g_mem.ui_context, &world.physics_scene, SOLVER_CONFIG) box1_mat := linalg.Matrix4f32(1) box1_mat = linalg.matrix4_rotate(45 * math.RAD_PER_DEG, rl.Vector3{0, 1, 0}) * box1_mat @@ -887,7 +868,10 @@ draw :: proc() { } } + { + ui.end(&g_mem.ui_context) + rl.BeginMode2D(ui_camera()) defer rl.EndMode2D() diff --git a/game/physics/debug.odin b/game/physics/debug.odin index b4ff3a6..9569933 100644 --- a/game/physics/debug.odin +++ b/game/physics/debug.odin @@ -1,10 +1,12 @@ package physics +import "core:fmt" import "core:log" import "core:math" import lg "core:math/linalg" import "game:debug" import "game:halfedge" +import "game:ui" import "libs:tracy" import rl "vendor:raylib" import "vendor:raylib/rlgl" @@ -144,6 +146,141 @@ draw_debug_scene :: proc(scene: ^Scene) { } } +draw_debug_ui :: proc(ctx: ^ui.Context, scene: ^Scene, config: Solver_Config) { + tracy.Zone() + + sim_state := get_sim_state(scene) + + active_wheels := []int{2, 3} + + w, h: i32 = 200, 200 + + window_x: i32 = 0 + + for i in 0 ..< len(sim_state.suspension_constraints_slice) { + s := &sim_state.suspension_constraints_slice[i] + + if s.alive { + for idx in active_wheels { + if i == idx { + if ui.window( + ctx, + fmt.tprintf("Wheel %v", i), + ui.Rect{x = window_x, y = 0, w = w, h = h}, + ui.Options{.AUTO_SIZE}, + ) { + NUM_SAMPLES :: 100 + + dt := f32(config.timestep) / f32(config.substreps_minus_one + 1) + inv_dt := 1.0 / dt + + { + // ui.layout_row(ctx, {0}, 200) + { + ui.begin_line(ctx, ui.Color{255, 0, 0, 255}) + defer ui.end_line(ctx) + + for j in 0 ..< NUM_SAMPLES { + alpha := f32(j) / f32(NUM_SAMPLES - 1) + x := alpha * 200.0 - 100.0 + + long_friction := abs( + pacejka_94_longitudinal( + s.pacejka_long, + x, + max(abs(s.spring_impulse), 0.001) * inv_dt * 0.001, + ), + ) + + ui.push_line_point( + ctx, + ui.Vec2f{alpha, long_friction * -0.5 + 1}, + ) + } + + long_friction := abs( + pacejka_94_longitudinal( + s.pacejka_long, + s.slip_ratio, + max(abs(s.spring_impulse), 0.001) * inv_dt * 0.001, + ), + ) + + rect := ui.get_line(ctx).rect + + cur_point := + Vec2 { + (s.slip_ratio + 100.0) / 200.0, + long_friction * -0.5 + 1, + } * + Vec2{f32(rect.w), f32(rect.h)} + + Vec2{f32(rect.x), f32(rect.y)} + ui.draw_rect( + ctx, + ui.rect_from_point_extent( + ui.Vec2{i32(cur_point.x), i32(cur_point.y)}, + 2, + ), + ui.Color{255, 255, 0, 255}, + ) + } + } + + { + // ui.layout_row(ctx, {0}, 200) + + ui.begin_line(ctx, ui.Color{0, 255, 0, 255}) + defer ui.end_line(ctx) + + for j in 0 ..< NUM_SAMPLES { + alpha := f32(j) / f32(NUM_SAMPLES - 1) + x := alpha * 180.0 - 90.0 + + lat_friction := abs( + pacejka_94_lateral( + s.pacejka_lat, + x, + max(abs(s.spring_impulse), 0.001) * inv_dt * 0.001, + 0.0, + ), + ) + + ui.push_line_point(ctx, ui.Vec2f{alpha, lat_friction * -0.5 + 1}) + } + + lat_friction := abs( + pacejka_94_lateral( + s.pacejka_lat, + s.slip_angle, + max(abs(s.spring_impulse), 0.001) * inv_dt * 0.001, + 0.0, + ), + ) + + rect := ui.get_line(ctx).rect + + cur_point := + Vec2{(s.slip_angle + 100.0) / 200.0, lat_friction * -0.5 + 1} * + Vec2{f32(rect.w), f32(rect.h)} + + Vec2{f32(rect.x), f32(rect.y)} + ui.draw_rect( + ctx, + ui.rect_from_point_extent( + ui.Vec2{i32(cur_point.x), i32(cur_point.y)}, + 2, + ), + ui.Color{255, 255, 0, 255}, + ) + } + + window_x += w + } + } + } + } + } +} + debug_transform_points_local_to_world :: proc(body: Body_Ptr, points: []Vec3) { for i in 0 ..< len(points) { points[i] = body_local_to_world(body, points[i]) diff --git a/game/physics/scene.odin b/game/physics/scene.odin index fe3392f..98088bc 100644 --- a/game/physics/scene.odin +++ b/game/physics/scene.odin @@ -175,6 +175,12 @@ Suspension_Constraint :: struct { brake_impulse: f32, applied_impulse: Vec3, + // Convenience for debug visualization to avoid recomputing + slip_angle: f32, + slip_ratio: f32, + // Multipliers for combined friction + slip_vec: Vec2, + // Free list next_plus_one: i32, } diff --git a/game/physics/simulation.odin b/game/physics/simulation.odin index 966ddca..7359627 100644 --- a/game/physics/simulation.odin +++ b/game/physics/simulation.odin @@ -720,6 +720,7 @@ pgs_solve_suspension :: proc( v.hit_point = wheel_world_pos + dir * v.hit_t forward := wheel_get_forward_vec(body, v) + right := wheel_get_right_vec(body, v) body_vel_at_contact_patch := body_velocity_at_point(body, v.hit_point) @@ -780,20 +781,24 @@ pgs_solve_suspension :: proc( apply_velocity_correction(body, incremental_impulse * dir, wheel_world_pos) } - right := wheel_get_right_vec(body, v) - // Positive means spinning forward wheel_spin_vel := -v.radius * v.w - ground_vel := lg.dot(body_vel_at_contact_patch, forward) + ground_vel_x := lg.dot(body_vel_at_contact_patch, right) + ground_vel_y := lg.dot(body_vel_at_contact_patch, forward) // contact_patch_linear_vel := // body_vel_at_contact_patch + (v.radius * v.w * forward) slip_ratio := - ground_vel == 0 ? 0 : clamp(wheel_spin_vel / ground_vel - 1, -1, 1) * 100.0 - slip_angle := - -lg.angle_between(forward, body_vel_at_contact_patch) * math.DEG_PER_RAD + ground_vel_y == 0 ? 0 : clamp(wheel_spin_vel / ground_vel_y - 1, -1, 1) * 100.0 - MAX_SLIP_LEN :: f32(1.0) + slip_angle := + -lg.angle_between(Vec2{0, 1}, Vec2{ground_vel_x, ground_vel_y}) * + math.DEG_PER_RAD + + v.slip_ratio = slip_ratio + v.slip_angle = slip_angle + + MAX_SLIP_LEN :: f32(2.0) slip_vec := Vec2 { slip_angle / PACEJKA94_LATERAL_PEAK_X / MAX_SLIP_LEN, @@ -804,6 +809,8 @@ pgs_solve_suspension :: proc( slip_len = slip_len == 0 ? 0 : min(slip_len, 1) / slip_len slip_vec *= slip_len + v.slip_vec = slip_vec + // log.debugf("slip_vec: %v", slip_vec) long_friction := @@ -829,7 +836,7 @@ pgs_solve_suspension :: proc( // Longitudinal friction if true { // Wheel linear velocity relative to ground - relative_vel := ground_vel - wheel_spin_vel + relative_vel := ground_vel_y - wheel_spin_vel friction_clamp := abs(v.spring_impulse) * long_friction diff --git a/game/ui/microui.odin b/game/ui/microui.odin index c876427..63a975e 100644 --- a/game/ui/microui.odin +++ b/game/ui/microui.odin @@ -345,6 +345,10 @@ rect_overlaps_vec2 :: proc(r: Rect, p: Vec2) -> bool { return p.x >= r.x && p.x < r.x + r.w && p.y >= r.y && p.y < r.y + r.h } +rect_from_point_extent :: proc(p, e: Vec2) -> Rect { + return Rect{x = p.x - e.x, y = p.y - e.y, w = e.x * 2, h = e.y * 2} +} + @(private) default_draw_frame :: proc(ctx: ^Context, rect: Rect, colorid: Color_Type) { draw_rect(ctx, rect, ctx.style.colors[colorid]) @@ -836,7 +840,7 @@ push_line_point :: proc(ctx: ^Context, p: Vec2f) { } get_line_segments :: proc(ctx: ^Context, first_segment: i32, num_segments: i32) -> []Vec2f { - return ctx.line_segments_pool[first_segment:num_segments] + return ctx.line_segments_pool[first_segment:first_segment + num_segments] } /*============================================================================