From a4ed430efec723d38e6747dabcd027b1f12bc312 Mon Sep 17 00:00:00 2001 From: sergeypdev Date: Sat, 3 May 2025 23:24:45 +0400 Subject: [PATCH] Fix a bunch of collision bugs --- assets/car_convex.obj | 77 +++++----- assets/shaders/lit_ps.glsl | 5 +- build_hot_reload.sh | 2 +- game/assets/assets.odin | 6 +- game/game.odin | 21 ++- game/halfedge/debug.odin | 10 +- game/physics/collision/convex.odin | 32 +++-- game/physics/debug.odin | 9 +- game/physics/helpers.odin | 2 +- game/physics/scene.odin | 1 + game/physics/simulation.odin | 221 ++++++++++++++++++----------- src_assets/car_convex.blend | 4 +- src_assets/car_convex.blend1 | 4 +- 13 files changed, 232 insertions(+), 162 deletions(-) diff --git a/assets/car_convex.obj b/assets/car_convex.obj index 927953e..176ad33 100644 --- a/assets/car_convex.obj +++ b/assets/car_convex.obj @@ -1,19 +1,7 @@ # Blender 4.4.1 # www.blender.org o Object_5 -v -0.812577 0.172395 1.254792 -v -0.646673 -0.190612 1.848713 -v -0.666983 -0.006338 1.874366 -v -0.635056 0.256211 1.783894 -v -0.508939 0.803110 -0.009132 -v -0.769510 0.352081 -1.291787 -v -0.761933 0.013607 -1.722252 v 0.677186 -0.189706 1.840916 -v -0.590011 0.087673 1.933771 -v -0.669674 -0.180060 -1.336290 -v -0.512564 0.833925 -0.989792 -v -0.627160 0.507336 -2.050498 -v -0.682347 0.022719 -2.244187 v 0.678533 0.090349 1.926879 v 0.700321 -0.179143 -1.337300 v 0.792520 0.013658 -1.722244 @@ -24,32 +12,41 @@ v 0.543159 0.833925 -0.989791 v 0.657754 0.507336 -2.050497 v 0.712870 0.023363 -2.243935 v 0.800105 0.352079 -1.291788 +v -0.646592 -0.189706 1.840916 +v -0.647939 0.090349 1.926879 +v -0.669726 -0.179143 -1.337300 +v -0.761926 0.013658 -1.722244 +v -0.812613 0.172325 1.254793 +v -0.635077 0.258142 1.777555 +v -0.508940 0.803109 -0.009139 +v -0.512565 0.833925 -0.989791 +v -0.627160 0.507336 -2.050497 +v -0.682276 0.023363 -2.243935 +v -0.769511 0.352079 -1.291788 s 0 -f 1 2 3 -f 1 3 4 -f 1 4 5 -f 23 20 19 17 -f 1 6 7 -f 16 17 8 15 -f 2 9 3 -f 3 9 4 -f 9 14 18 4 -f 6 11 12 -f 21 23 16 22 -f 7 13 10 -f 8 17 14 -f 8 14 9 2 -f 12 21 22 13 -f 20 11 5 19 -f 15 10 13 22 -f 14 17 18 -f 15 22 16 -f 16 23 17 -f 7 10 2 1 -f 17 19 18 -f 20 23 21 -f 5 11 6 1 -f 5 4 18 19 -f 13 7 6 12 -f 12 11 20 21 -f 8 2 10 15 +f 11 8 7 5 +f 4 5 1 3 +f 12 1 2 13 +f 9 11 4 10 +f 1 5 2 +f 9 20 19 8 +f 2 5 6 +f 3 10 4 +f 4 11 5 +f 5 7 6 +f 8 11 9 +f 14 21 10 3 +f 19 18 7 8 +f 7 18 17 6 +f 6 17 13 2 +f 12 14 3 1 +f 22 16 18 19 +f 15 14 12 16 +f 20 21 15 22 +f 12 13 16 +f 13 17 16 +f 14 15 21 +f 15 16 22 +f 16 17 18 +f 19 20 22 +f 10 21 20 9 diff --git a/assets/shaders/lit_ps.glsl b/assets/shaders/lit_ps.glsl index e674637..5fb7e35 100644 --- a/assets/shaders/lit_ps.glsl +++ b/assets/shaders/lit_ps.glsl @@ -16,6 +16,7 @@ out vec4 finalColor; uniform vec3 ambient; uniform vec3 lightDir; +uniform vec3 lightColor; // NOTE: Add your custom variables here @@ -30,8 +31,8 @@ void main() // times the fragment color (interpolated vertex color) float NDotL = dot(lightDir, -worldNormal); - float toon = 0.5 * smoothstep(0.66, 0.67, NDotL) + 0.5; - vec3 light = mix(vec3(0), vec3(1), toon); + float toon = 0.5 * smoothstep(0.5, 0.51, NDotL) + 0.5; + vec3 light = mix(vec3(ambient), lightColor, toon); finalColor = texelColor*colDiffuse*fragColor * vec4(light, 1); } diff --git a/build_hot_reload.sh b/build_hot_reload.sh index 1a6965e..a990553 100755 --- a/build_hot_reload.sh +++ b/build_hot_reload.sh @@ -36,7 +36,7 @@ esac # Build the game. echo "Building game$DLL_EXT" -odin build game -extra-linker-flags:"$EXTRA_LINKER_FLAGS" -define:RAYLIB_SHARED=true -define:PHYSFS_SHARED=true -define:TRACY_ENABLE=true -collection:libs=./libs -collection:common=./common -collection:game=./game -build-mode:dll -out:game_tmp$DLL_EXT -strict-style -vet -debug -o:speed +odin build game -extra-linker-flags:"$EXTRA_LINKER_FLAGS" -define:RAYLIB_SHARED=true -define:PHYSFS_SHARED=true -define:TRACY_ENABLE=true -collection:libs=./libs -collection:common=./common -collection:game=./game -build-mode:dll -out:game_tmp$DLL_EXT -strict-style -vet -debug # Need to use a temp file on Linux because it first writes an empty `game.so`, which the game will load before it is actually fully written. mv game_tmp$DLL_EXT game$DLL_EXT diff --git a/game/assets/assets.odin b/game/assets/assets.odin index 5e6f4aa..8d5487d 100644 --- a/game/assets/assets.odin +++ b/game/assets/assets.odin @@ -78,13 +78,15 @@ Asset_Cache :: struct($E: typeid) { Shader_Location :: enum { Ambient, LightDir, + LightColor, } Shader_Location_Set :: bit_set[Shader_Location] Shader_Location_Array :: [Shader_Location]i32 SHADER_LOCATION_NAMES := [Shader_Location]cstring { - .Ambient = "ambient", - .LightDir = "lightDir", + .Ambient = "ambient", + .LightDir = "lightDir", + .LightColor = "lightColor", } Loaded_Shader :: struct { diff --git a/game/game.odin b/game/game.odin index 56fe0e7..761e03d 100644 --- a/game/game.odin +++ b/game/game.odin @@ -78,7 +78,7 @@ Car :: struct { SOLVER_CONFIG :: physics.Solver_Config { timestep = 1.0 / 60, gravity = rl.Vector3{0, -9.8, 0}, - substreps_minus_one = 8 - 1, + substreps_minus_one = 4 - 1, } Game_Memory :: struct { @@ -433,11 +433,11 @@ update_runtime_world :: proc(runtime_world: ^Runtime_World, dt: f32) { &g_mem.assetman, "assets/ae86_rpm_torque.csv", ), - lowest_rpm = 1100, + lowest_rpm = 1200, rev_limit_rpm = 7800, rev_limit_interval = 0.025, inertia = 0.264 * 0.5, - internal_friction = 0.01, + internal_friction = 0.005, gear_ratios = []f32{3.48, 3.587, 2.022, 1.384, 1, 0.861}, axle = physics.Drive_Axle_Config { wheels = {wheel_rl, wheel_rr}, @@ -733,6 +733,7 @@ draw :: proc() { car_body := physics.get_body(sim_state, runtime_world.car_handle) car_model := assets.get_model(&g_mem.assetman, "assets/toyota_corolla_ae86_trueno.glb") + _ = car_model mesh_col: bvh.Collision hit_mesh_idx := -1 @@ -803,10 +804,11 @@ draw :: proc() { &g_mem.assetman, "assets/shaders/lit_vs.glsl", "assets/shaders/lit_ps.glsl", - {.Ambient, .LightDir}, + {.Ambient, .LightDir, .LightColor}, ) light_dir := linalg.normalize(rl.Vector3{1, -1, 0}) - ambient := linalg.normalize(rl.Vector3{0.1, 0.1, 0.1}) + ambient := rl.Vector3{0.1, 0.1, 0.1} + light_color := rl.Vector3{0.816, 0.855, 0.89} rl.SetShaderValue( basic_shader.shader, basic_shader.locations[.LightDir], @@ -819,6 +821,12 @@ draw :: proc() { &ambient, .VEC3, ) + rl.SetShaderValue( + basic_shader.shader, + basic_shader.locations[.LightColor], + &light_color, + .VEC3, + ) render.draw_model(car_model, basic_shader.shader, car_matrix) @@ -952,12 +960,13 @@ draw :: proc() { gear_ratios := physics.get_gear_ratios(sim_state, engine.gear_ratios) rl.DrawText( fmt.ctprintf( - "p: %v\nv: %v\ngear: %v\nratio: %v\nrpm: %v\nspeed: %v km/h", + "p: %v\nv: %v\ngear: %v\nratio: %v\nrpm: %v\nclutch: %v\nspeed: %v km/h", car.x, car.v, engine.gear, physics.lookup_gear_ratio(gear_ratios, engine.gear), physics.angular_velocity_to_rpm(engine.w), + engine.clutch, linalg.length(car.v) * 3.6, ), 5, diff --git a/game/halfedge/debug.odin b/game/halfedge/debug.odin index ee3ba90..2f64ae9 100644 --- a/game/halfedge/debug.odin +++ b/game/halfedge/debug.odin @@ -3,13 +3,9 @@ package halfedge import rl "libs:raylib" debug_draw_mesh_wires :: proc(mesh: Half_Edge_Mesh, color: rl.Color) { - for _, f in mesh.faces { - it := iterator_face_edges(mesh, Face_Index(f)) + for edge in mesh.edges { + a, b := get_edge_points(mesh, edge) - for edge in iterate_next_edge(&it) { - a, b := get_edge_points(mesh, edge) - - rl.DrawLine3D(a, b, color) - } + rl.DrawLine3D(a, b, color) } } diff --git a/game/physics/collision/convex.odin b/game/physics/collision/convex.odin index 1af6d03..bbaa22d 100644 --- a/game/physics/collision/convex.odin +++ b/game/physics/collision/convex.odin @@ -5,9 +5,9 @@ import "core:log" import "core:math" import lg "core:math/linalg" import "game:halfedge" -import "libs:tracy" import rl "libs:raylib" import "libs:raylib/rlgl" +import "libs:tracy" _ :: math _ :: rl @@ -84,12 +84,9 @@ convex_vs_convex_sat :: proc(a, b: Convex) -> (manifold: Contact_Manifold, colli if edge_separation > 0 { return } - biased_face_a_separation := face_query_a.separation + 0.3 - biased_face_b_separation := face_query_b.separation + 0.2 - biased_edge_separation := edge_separation - is_face_a_contact := biased_face_a_separation >= biased_edge_separation - is_face_b_contact := biased_face_b_separation >= biased_edge_separation + is_face_a_contact := face_query_a.separation > edge_separation + is_face_b_contact := face_query_b.separation > edge_separation if is_face_a_contact || is_face_b_contact { manifold = create_face_contact_manifold(face_query_a, a, face_query_b, b) @@ -290,7 +287,7 @@ create_face_contact_manifold :: proc( ) { tracy.Zone() - is_ref_a := face_query_a.separation > face_query_b.separation + is_ref_a := (face_query_a.separation + 0.1) > face_query_b.separation ref_face_query := is_ref_a ? face_query_a : face_query_b ref_convex := is_ref_a ? a : b inc_convex := is_ref_a ? b : a @@ -303,13 +300,19 @@ create_face_contact_manifold :: proc( inc_face_idx: halfedge.Face_Index // Find the most anti parallel face { - min_dot := f32(1.0) - for face, face_idx in inc_convex.faces { + _, support_idx, _ := find_support_point(inc_convex, -ref_face.normal) + + it := halfedge.iterator_vertex_edges(inc_convex, support_idx) + + min_dot := max(f32) + + for edge in halfedge.iterate_next_vertex_edge(&it) { + face := inc_convex.faces[edge.face] dot := lg.dot(ref_face.normal, face.normal) if dot < min_dot { min_dot = dot inc_face = face - inc_face_idx = halfedge.Face_Index(face_idx) + inc_face_idx = halfedge.Face_Index(edge.face) } } } @@ -336,6 +339,7 @@ create_face_contact_manifold :: proc( } } + assert(len(inc_polygon) > 0) // Set up ping pong buffers @@ -352,6 +356,14 @@ create_face_contact_manifold :: proc( step := 0 vert_count := original_vert_count + + // { + // polygon := clip_bufs[get_other_clip_buf(clip_buf_idx)] + // for i in 2 ..< vert_count { + // rl.DrawTriangle3D(polygon[0], polygon[i - 1], polygon[i], rl.RED) + // } + // } + EPS :: 1e-6 { diff --git a/game/physics/debug.odin b/game/physics/debug.odin index 65eedac..1e6041c 100644 --- a/game/physics/debug.odin +++ b/game/physics/debug.odin @@ -5,15 +5,16 @@ import "core:log" import "core:math" import lg "core:math/linalg" import "game:debug" -import "game:halfedge" +import he "game:halfedge" import "game:ui" -import "libs:tracy" import rl "libs:raylib" import "libs:raylib/rlgl" +import "libs:tracy" _ :: log _ :: math _ :: debug +_ :: he draw_debug_shape :: proc( sim_state: ^Sim_State, @@ -35,7 +36,7 @@ draw_debug_shape :: proc( rl.DrawCubeV(0, s.size, color) case Internal_Shape_Convex: mesh := convex_container_get_mesh(&sim_state.convex_container, s.mesh) - halfedge.debug_draw_mesh_wires(mesh, color) + he.debug_draw_mesh_wires(mesh, color) } } @@ -316,6 +317,6 @@ debug_draw_manifold_points :: proc( contact.total_friction_impulse[point_idx].y * contact.manifold.bitangent rl.DrawLine3D(p, p + total_impulse * impulse_sign, color) - // rl.DrawSphereWires(p, 0.1, 4, 4, color) + rl.DrawSphereWires(p, 0.1, 4, 4, color) } } diff --git a/game/physics/helpers.odin b/game/physics/helpers.odin index ca21891..beb3bd3 100644 --- a/game/physics/helpers.odin +++ b/game/physics/helpers.odin @@ -119,7 +119,7 @@ body_get_convex_shape_world :: proc( } transform := - lg.matrix4_translate(body_get_shape_pos(body)) * lg.matrix4_from_quaternion(body.q) + lg.matrix4_translate_f32(body_get_shape_pos(body)) * lg.matrix4_from_quaternion_f32(body.q) halfedge.transform_mesh(&mesh, transform) return diff --git a/game/physics/scene.odin b/game/physics/scene.odin index 1f74e2b..9f9e511 100644 --- a/game/physics/scene.odin +++ b/game/physics/scene.odin @@ -254,6 +254,7 @@ Engine :: struct { // Controls throttle: f32, + clutch: f32, // Free list next_plus_one: i32, diff --git a/game/physics/simulation.odin b/game/physics/simulation.odin index 10c2e3c..0e28aa3 100644 --- a/game/physics/simulation.odin +++ b/game/physics/simulation.odin @@ -11,6 +11,9 @@ import lg "core:math/linalg" import "core:math/rand" import "core:slice" import "game:container/spanpool" +import "game:debug" +import he "game:halfedge" +import rl "libs:raylib" import "libs:tracy" _ :: log @@ -18,6 +21,8 @@ _ :: rand _ :: math _ :: fmt _ :: slice +_ :: he +_ :: debug Solver_Config :: struct { // Will automatically do fixed timestep @@ -198,7 +203,7 @@ find_new_contacts :: proc(sim_state: ^Sim_State, tlas: ^TLAS) { pair := make_contact_pair(i32(body_idx), i32(other_body_idx)) if body_idx != other_body_idx && - bvh.test_aabb_vs_aabb(body_aabb, prim_aabb) && + (true || bvh.test_aabb_vs_aabb(body_aabb, prim_aabb)) && !(pair in sim_state.contact_container.lookup) { new_contact_idx := len(sim_state.contact_container.contacts) @@ -341,6 +346,11 @@ update_contacts :: proc(sim_state: ^Sim_State) { body_get_convex_shape_world(sim_state, body), body_get_convex_shape_world(sim_state, body2) + if contact_idx == 2 { + he.debug_draw_mesh_wires(m1, rl.RED) + he.debug_draw_mesh_wires(m2, rl.BLUE) + } + // Raw manifold has contact points in world space raw_manifold, collision := collision.convex_vs_convex_sat(m1, m2) @@ -440,24 +450,24 @@ pgs_solve_contacts :: proc( body_local_to_world(body2, manifold.points_b[point_idx]) p_diff_normal := lg.dot(p2 - p1, manifold.normal) - separation := min(p_diff_normal, 0) + separation := p_diff_normal - if separation < 0 { + w1 := get_body_inverse_mass(body1, manifold.normal, p1) + w2 := get_body_inverse_mass(body2, manifold.normal, p2) + + w := w1 + w2 + + if w == 0 { + continue + } + + inv_w := 1.0 / w + + { // r1, r2 := p1 - body1.x, p2 - body2.x v1 := body_velocity_at_point(body1, p1) v2 := body_velocity_at_point(body2, p2) - w1 := get_body_inverse_mass(body1, manifold.normal, p1) - w2 := get_body_inverse_mass(body2, manifold.normal, p2) - - w := w1 + w2 - - if w == 0 { - continue - } - - inv_w := 1.0 / w - delta_v := v2 - v1 { delta_v_norm := lg.dot(delta_v, manifold.normal) @@ -488,45 +498,51 @@ pgs_solve_contacts :: proc( apply_velocity_correction(body1, -applied_impulse_vec, p1) apply_velocity_correction(body2, applied_impulse_vec, p2) } + } - { - DYNAMIC_FRICTION :: 0.6 - STATIC_FRICTION :: 0.8 - STATIC_FRICTION_VELOCITY_THRESHOLD :: 0.01 + { + // r1, r2 := p1 - body1.x, p2 - body2.x + v1 := body_velocity_at_point(body1, p1) + v2 := body_velocity_at_point(body2, p2) - delta_v_tang := Vec2 { - lg.dot(delta_v, manifold.tangent), - lg.dot(delta_v, manifold.bitangent), - } + delta_v := v2 - v1 - use_static_friction := - lg.dot(delta_v_tang, delta_v_tang) < - STATIC_FRICTION_VELOCITY_THRESHOLD * STATIC_FRICTION_VELOCITY_THRESHOLD - friction: f32 = use_static_friction ? STATIC_FRICTION : DYNAMIC_FRICTION - friction_clamp := contact.total_normal_impulse[point_idx] * friction + DYNAMIC_FRICTION :: 0.6 + STATIC_FRICTION :: 0.8 + STATIC_FRICTION_VELOCITY_THRESHOLD :: 0.01 - incremental_impulse := -inv_w * delta_v_tang - - new_total_impulse: Vec2 = lg.clamp( - contact.total_friction_impulse[point_idx] + incremental_impulse, - Vec2(-friction_clamp), - Vec2(friction_clamp), - ) - - applied_impulse := - new_total_impulse - contact.total_friction_impulse[point_idx] - contact.total_friction_impulse[point_idx] = new_total_impulse - - applied_impulse_vec := - applied_impulse.x * manifold.tangent + - applied_impulse.y * manifold.bitangent - - apply_velocity_correction(body1, -applied_impulse_vec, p1) - apply_velocity_correction(body2, applied_impulse_vec, p2) + delta_v_tang := Vec2 { + lg.dot(delta_v, manifold.tangent), + lg.dot(delta_v, manifold.bitangent), } - } else { - contact.total_normal_impulse[point_idx] = 0 - contact.total_friction_impulse[point_idx] = 0 + + use_static_friction := + lg.dot(delta_v_tang, delta_v_tang) < + STATIC_FRICTION_VELOCITY_THRESHOLD * STATIC_FRICTION_VELOCITY_THRESHOLD + friction: f32 = use_static_friction ? STATIC_FRICTION : DYNAMIC_FRICTION + friction_clamp := contact.total_normal_impulse[point_idx] * friction + + incremental_impulse := -inv_w * delta_v_tang + + new_total_impulse: Vec2 = lg.clamp( + contact.total_friction_impulse[point_idx] + incremental_impulse, + Vec2(-friction_clamp), + Vec2(friction_clamp), + ) + + applied_impulse := new_total_impulse - contact.total_friction_impulse[point_idx] + contact.total_friction_impulse[point_idx] = new_total_impulse + + applied_impulse_vec := + applied_impulse.x * manifold.tangent + applied_impulse.y * manifold.bitangent + + rl.DrawSphereWires(p1, 0.05, 8, 8, rl.RED) + rl.DrawLine3D(p1, p1 + v1, rl.RED) + rl.DrawSphereWires(p2, 0.05, 8, 8, rl.BLUE) + rl.DrawLine3D(p2, p2 + v2, rl.BLUE) + + apply_velocity_correction(body1, -applied_impulse_vec, p1) + apply_velocity_correction(body2, applied_impulse_vec, p2) } } } @@ -555,24 +571,29 @@ pgs_solve_engines :: proc(sim_state: ^Sim_State, config: Solver_Config, dt: f32, rpm_torque_curve := get_engine_curve(sim_state, engine.rpm_torque_curve) gear_ratios := get_gear_ratios(sim_state, engine.gear_ratios) - // Unstall impulse - { - engine_lowest_velocity := rpm_to_angular_velocity(engine.lowest_rpm) - - delta_omega := engine_lowest_velocity - engine.w - - inv_w := engine.inertia - - incremental_impulse := inv_w * delta_omega - new_total_impulse := max(engine.unstall_impulse + incremental_impulse, 0) - applied_impulse := new_total_impulse - engine.unstall_impulse - engine.unstall_impulse = new_total_impulse - - engine.w += applied_impulse / engine.inertia - } - rpm := angular_velocity_to_rpm(engine.w) throttle := engine.throttle + clutch := f32(0) + + // Blending range in rpm of auto clutch activation + AUTO_CLUTCH_RPM_RANGE :: f32(100) + AUTO_BLIP_RPM_RANGE :: f32(100) + + // Prevent stalling + { + if rpm < engine.lowest_rpm { + clutch = + min(engine.lowest_rpm - rpm, AUTO_CLUTCH_RPM_RANGE) / AUTO_CLUTCH_RPM_RANGE + + throttle = max( + throttle, + min((engine.lowest_rpm - AUTO_BLIP_RPM_RANGE) - rpm, AUTO_BLIP_RPM_RANGE) / + AUTO_BLIP_RPM_RANGE, + ) + } + } + + engine.clutch = clutch if engine.rev_limit_time < 0.0 { engine.rev_limit_time += dt @@ -582,7 +603,6 @@ pgs_solve_engines :: proc(sim_state: ^Sim_State, config: Solver_Config, dt: f32, throttle = 0 } - // Throttle { @@ -670,6 +690,8 @@ pgs_solve_engines :: proc(sim_state: ^Sim_State, config: Solver_Config, dt: f32, incremental_impulse := -inv_w * delta_omega engine.axle.engine_impulse += incremental_impulse + incremental_impulse *= (1.0 - clutch) + engine.w += incremental_impulse * w1 wheel1.w += incremental_impulse * w2 * inv_ratio wheel2.w += incremental_impulse * w3 * inv_ratio @@ -706,6 +728,21 @@ pgs_solve_engines :: proc(sim_state: ^Sim_State, config: Solver_Config, dt: f32, } } + +calculate_ground_vel :: proc( + body: Body_Ptr, + p: Vec3, + right, forward: Vec3, +) -> ( + body_vel_at_contact_patch: Vec3, + ground_vel: Vec2, +) { + body_vel_at_contact_patch = body_velocity_at_point(body, p) + ground_vel.x = lg.dot(body_vel_at_contact_patch, right) + ground_vel.y = lg.dot(body_vel_at_contact_patch, forward) + return +} + pgs_solve_suspension :: proc( sim_state: ^Sim_State, tlas: ^TLAS, @@ -735,8 +772,6 @@ pgs_solve_suspension :: proc( 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) - w_normal := get_body_angular_inverse_mass(body, dir) inv_w_normal := 1.0 / w_normal @@ -796,22 +831,25 @@ pgs_solve_suspension :: proc( // Positive means spinning forward wheel_spin_vel := -v.radius * v.w - 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) + + body_vel_at_contact_patch, ground_vel := calculate_ground_vel( + body, + v.hit_point, + right, + forward, + ) slip_ratio := - ground_vel_y == 0 ? 0 : clamp(wheel_spin_vel / ground_vel_y - 1, -1, 1) * 100.0 + ground_vel.y == 0 ? 0 : clamp(wheel_spin_vel / ground_vel.y - 1, -1, 1) * 100.0 slip_angle := - -lg.angle_between(Vec2{0, 1}, Vec2{ground_vel_x, ground_vel_y}) * + -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) + MAX_SLIP_LEN :: f32(1.5) slip_vec := Vec2 { slip_angle / PACEJKA94_LATERAL_PEAK_X / MAX_SLIP_LEN, @@ -848,8 +886,15 @@ pgs_solve_suspension :: proc( // Longitudinal friction if true { + body_vel_at_contact_patch, ground_vel = calculate_ground_vel( + body, + v.hit_point, + right, + forward, + ) + // Wheel linear velocity relative to ground - relative_vel := ground_vel_y - wheel_spin_vel + relative_vel := ground_vel.y - wheel_spin_vel friction_clamp := abs(v.spring_impulse) * long_friction @@ -872,9 +917,13 @@ pgs_solve_suspension :: proc( } // Lateral friction if true { - vel_contact := body_vel_at_contact_patch - - lateral_vel := lg.dot(right, vel_contact) + body_vel_at_contact_patch, ground_vel = calculate_ground_vel( + body, + v.hit_point, + right, + forward, + ) + lateral_vel := ground_vel.x friction_clamp := abs(v.spring_impulse) * lat_friction incremental_impulse := -inv_w_normal * lateral_vel @@ -944,7 +993,7 @@ pgs_substep :: proc( if e.alive { gear_ratios := get_gear_ratios(sim_state, e.gear_ratios) - e.w += e.unstall_impulse / e.inertia + // e.w += e.unstall_impulse / e.inertia e.w += e.friction_impulse / e.inertia @@ -966,9 +1015,9 @@ pgs_substep :: proc( w2 := wheel1.inv_inertia w3 := wheel2.inv_inertia - e.w += e.axle.engine_impulse * w1 - wheel1.w += e.axle.engine_impulse * w2 * inv_ratio - wheel2.w += e.axle.engine_impulse * w3 * inv_ratio + e.w += e.axle.engine_impulse * w1 * (1.0 - e.clutch) + wheel1.w += e.axle.engine_impulse * w2 * inv_ratio * (1.0 - e.clutch) + wheel2.w += e.axle.engine_impulse * w3 * inv_ratio * (1.0 - e.clutch) } // Warmp start diff impulse @@ -1058,9 +1107,11 @@ simulate_step :: proc(scene: ^Scene, sim_state: ^Sim_State, config: Solver_Confi substeps := config.substreps_minus_one + 1 - dt := config.timestep / f32(substeps) - inv_dt := 1.0 / dt + dt_64 := f64(config.timestep) / f64(substeps) + inv_dt_64 := f64(1.0) / dt_64 + dt := f32(dt_64) + inv_dt := f32(inv_dt_64) tlas := build_tlas(sim_state, config) @@ -1073,7 +1124,7 @@ simulate_step :: proc(scene: ^Scene, sim_state: ^Sim_State, config: Solver_Confi aabb_a := tlas.body_aabbs[int(contact.a) - 1] aabb_b := tlas.body_aabbs[int(contact.b) - 1] - if !bvh.test_aabb_vs_aabb(aabb_a, aabb_b) { + if false && !bvh.test_aabb_vs_aabb(aabb_a, aabb_b) { removed_pair := make_contact_pair(i32(contact.a) - 1, i32(contact.b) - 1) delete_key(&sim_state.contact_container.lookup, removed_pair) diff --git a/src_assets/car_convex.blend b/src_assets/car_convex.blend index 8b58a4b..3188bc9 100644 --- a/src_assets/car_convex.blend +++ b/src_assets/car_convex.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fa2e92eafb9dcf95ea196dc5e7ee2cf14034d172a6816b0cf780ef27533b7d77 -size 477254 +oid sha256:58a3c0f89125eb052fd01ab21c80cfbf60e5f2527e779ca537a5acc055070e5f +size 476544 diff --git a/src_assets/car_convex.blend1 b/src_assets/car_convex.blend1 index a703a3a..4d1c7e2 100644 --- a/src_assets/car_convex.blend1 +++ b/src_assets/car_convex.blend1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3a2bb3d3a2f97f42d63f92c93bcfb523768b1c5b61db453db9e188634dcae52c -size 477254 +oid sha256:32e2f161dc45431c698a753bbcaee06cb95c4136d5736571527451db4c66e413 +size 478070