From 3c2f24716766a0e46f19b46491b3feb0081e476e Mon Sep 17 00:00:00 2001 From: _ <_@_> Date: Sun, 19 Dec 2021 00:54:04 +0000 Subject: [PATCH] looking good --- src/bin/platformer.rs | 109 ++++++++++++++++++++++-------- src/physics.rs | 154 +++++++++++++++++++++++++++++++++++------- 2 files changed, 210 insertions(+), 53 deletions(-) diff --git a/src/bin/platformer.rs b/src/bin/platformer.rs index aa19973..a9268e8 100644 --- a/src/bin/platformer.rs +++ b/src/bin/platformer.rs @@ -31,9 +31,33 @@ pub const } } -#[derive (Default)] struct GameState { player: PhysicsBody, + aabbs: Vec , +} + +impl Default for GameState { + fn default () -> Self { + let player = Default::default (); + let aabbs = [ + ((-4.0, -4.0, -3.0), (4.0, 4.0, -1.0)), + ((-1.5, 1.0, -1.0), (-0.5, 2.0, 0.0)), + ((-0.5, 1.0, 0.0), (0.5, 2.0, 1.0)), + ((0.5, 1.0, 1.0), (1.5, 2.0, 2.0)), + ].into_iter () + .map (|(min, max)| { + opengl_rust::physics::Aabb { + min: min.into (), + max: max.into (), + } + }) + .collect (); + + Self { + player, + aabbs, + } + } } struct GameGraphics { @@ -70,7 +94,9 @@ impl GameGraphics { let screen_size = (1280.0, 720.0); - let proj_mat = Mat4::perspective_rh_gl (30.0f32.to_radians (), screen_size.0 / screen_size.1, 0.125, 200.0); + let fov = 30.0f32; + + let proj_mat = Mat4::perspective_rh_gl (fov.to_radians (), screen_size.0 / screen_size.1, 0.125, 200.0); let view_mat = proj_mat * Mat4::from_translation ((0.0, 0.0, -20.0).into ()) * @@ -98,10 +124,15 @@ impl GameGraphics { }); } - { + for aabb in &state.aabbs { + let min = aabb.min; + let max = aabb.max; + let center = (min + max) / 2.0; + let scale = (max - min) / 2.0; + let mvp = view_mat * - Mat4::from_scale ((4.0, 4.0, 1.0).into ()) * - Mat4::from_translation ((0.0, 0.0, -2.0).into ()); + Mat4::from_translation (center) * + Mat4::from_scale (scale); glezz::uniform_matrix_4fv (unis [&u::MVP], &mvp); self.mesh_cube.draw_all (attrs, |_| { @@ -257,32 +288,14 @@ async fn main () -> Result <()> { gravity: (0.0, 0.0, -0.25).into (), margin: 0.00125, }; - let phys_world: Vec <_> = vec! [ - ( - (-4.0, -4.0, -1.0), - (-4.0, 4.0, -1.0), - (4.0, -4.0, -1.0), - ), - ( - (4.0, 4.0, -1.0), - (4.0, -4.0, -1.0), - (-4.0, 4.0, -1.0), - ), - ].into_iter () - .map (|(v0, v1, v2)| { - opengl_rust::physics::Triangle { - verts: [ - v0.into (), - v1.into (), - v2.into (), - ], - } - }) - .collect (); + let player_speed = 2.0; + let player_jump_speed = 8.0; + let mut player_jump_vec: Option = None; 'running: loop { let _frames_to_do = time_step.step (); + let mut player_wants_to_jump = false; for event in event_pump.poll_iter () { match event { @@ -293,14 +306,25 @@ async fn main () -> Result <()> { Event::KeyDown { keycode: Some (Keycode::R), .. } => { game_state = Default::default (); }, + Event::KeyDown { keycode: Some (Keycode::Space), .. } => { + player_wants_to_jump = true; + }, _ => (), } } let kb_state = event_pump.keyboard_state (); - game_state.player.vel.x = 0.0; - game_state.player.vel.y = 0.0; + + let player_speed = if player_jump_vec.is_some () { + game_state.player.vel.x = 0.0; + game_state.player.vel.y = 0.0; + player_speed + } + else { + 0.125 + }; + if kb_state.is_scancode_pressed (Scancode::Left) { game_state.player.vel.x -= player_speed; } @@ -314,7 +338,32 @@ async fn main () -> Result <()> { game_state.player.vel.y -= player_speed; } - game_state.player = opengl_rust::physics::step (&phys_params, &phys_world, 0.5, &game_state.player).body; + if player_wants_to_jump { + if let Some (normal) = player_jump_vec.clone () { + game_state.player.vel += normal * player_jump_speed; + } + } + + let phys_result = opengl_rust::physics::step (&phys_params, &[], &game_state.aabbs, 0.5, &game_state.player); + + game_state.player = phys_result.body; + // tracing::debug! ("player pos: {}", game_state.player.pos); + dbg! (player_jump_vec); + + player_jump_vec = None; + for normal in &phys_result.normals_hit { + player_jump_vec = Some (match player_jump_vec { + None => *normal, + Some (old) => { + if normal.z > old.z { + *normal + } + else { + old + } + }, + }); + } window.gl_make_current (&gl_ctx).unwrap (); diff --git a/src/physics.rs b/src/physics.rs index fd313cf..2487956 100644 --- a/src/physics.rs +++ b/src/physics.rs @@ -10,7 +10,7 @@ pub struct PhysicsBody { #[derive (Debug, PartialEq)] pub struct PhysicsResult { pub body: PhysicsBody, - pub triangles_hit: Vec , + pub normals_hit: Vec , pub kill: bool, } @@ -19,6 +19,12 @@ pub struct Triangle { pub verts: [Vec3; 3], } +#[derive (Copy, Clone)] +pub struct Aabb { + pub min: Vec3, + pub max: Vec3, +} + fn vec_min (a: &Vec3, b: &Vec3) -> Vec3 { Vec3::from (( min (a.x, b.x), @@ -103,7 +109,8 @@ pub struct Params { } pub fn step ( - params: &Params, world: &[Triangle], + params: &Params, + tris: &[Triangle], aabbs: &[Aabb], radius: f32, input: &PhysicsBody, ) -> PhysicsResult { @@ -116,14 +123,14 @@ pub fn step ( let mut new_vel = input.vel + params.gravity; let mut new_pos = old_pos + new_vel * dt * t_remaining; - let mut triangles_hit = Vec::new (); + let mut normals_hit = Vec::new (); // Do 5 iterations of the sub-step, trying to converge on a valid state for _ in 0..5 { - let candidate = get_candidate (world, old_pos, new_pos, radius); + let candidate = get_candidate (tris, aabbs, old_pos, new_pos, radius); if candidate.t <= 1.0 { - tracing::debug! ("Tri {}, type {:?}", candidate.i, candidate.c_type); + //tracing::debug! ("Tri {}, type {:?}", candidate.i, candidate.c_type); t_remaining *= 1.0 - candidate.t; let speed_towards_normal = -Vec3::dot (new_vel, candidate.normal); @@ -139,7 +146,7 @@ pub fn step ( // But also compensate for the slide distance it lost new_pos = push_out_pos + new_vel * dt * t_remaining; - triangles_hit.push (candidate.i); + normals_hit.push (candidate.normal); } else { t_remaining = 0.0; @@ -153,12 +160,17 @@ pub fn step ( pos: old_pos, vel: new_vel, }, - triangles_hit, + normals_hit, kill: false, } } -pub fn get_candidate (world: &[Triangle], p0: Vec3, p1: Vec3, radius: f32) +pub fn get_candidate ( + tris: &[Triangle], + aabbs: &[Aabb], + p0: Vec3, p1: Vec3, + radius: f32 +) -> Collision { let radius3 = Vec3::from (( @@ -176,12 +188,106 @@ pub fn get_candidate (world: &[Triangle], p0: Vec3, p1: Vec3, radius: f32) c_type: CollisionType::Face, }; - for (i, tri) in world.iter ().enumerate () { - let tri_min = tri.min () - radius3; - let tri_max = tri.max () + radius3; + let ray_min = p0.min (p1) - radius3; + let ray_max = p0.max (p1) + radius3; + + for b in aabbs { + if + ray_max.x < b.min.x || ray_min.x > b.max.x || + ray_max.y < b.min.y || ray_min.y > b.max.y || + ray_max.z < b.min.z || ray_min.z > b.max.z + { + // AABB reject + // tracing::trace! ("AABB reject"); + continue; + } - let ray_min = p0.min (p1); - let ray_max = p0.max (p1); + let verts = [ + (b.min.x, b.min.y, b.min.z).into (), + (b.max.x, b.min.y, b.min.z).into (), + (b.max.x, b.max.y, b.min.z).into (), + (b.min.x, b.max.y, b.min.z).into (), + + (b.min.x, b.min.y, b.max.z).into (), + (b.max.x, b.min.y, b.max.z).into (), + (b.max.x, b.max.y, b.max.z).into (), + (b.min.x, b.max.y, b.max.z).into (), + ]; + + for (a, b, c, d) in [ + (0, 1, 2, 3), + (4, 7, 6, 5), + + (2, 1, 5, 6), + (3, 2, 6, 7), + + (0, 3, 7, 4), + (1, 0, 4, 5), + ] { + let a = verts [a]; + let b = verts [b]; + let c = verts [c]; + let d = verts [d]; + + let normal = Vec3::cross (c - b, b - a).normalize (); + + if let Some (c) = get_candidate_face (&[a, b, c, d], normal, p0, p1, radius) { + candidate = candidate.take_if_closer (&Collision { + t: c.t, + p_impact: c.p_impact, + normal: c.normal, + i: 0, + c_type: CollisionType::Face, + }); + } + } + + for (a, b) in [ + (0, 1), + (1, 2), + (2, 3), + (3, 0), + + (4, 5), + (5, 6), + (6, 7), + (7, 4), + + (0, 4), + (1, 5), + (2, 6), + (3, 7), + ] { + let a = verts [a]; + let b = verts [b]; + + if let Some (c) = get_candidate_edge (a, b, p0, p1, radius) { + candidate = candidate.take_if_closer (&Collision { + t: c.t, + p_impact: c.p_impact, + normal: c.normal, + i: 0, + c_type: CollisionType::Edge, + }); + } + } + + for vert in &verts { + if let Some (c) = get_candidate_vert (*vert, p0, p1, radius) { + candidate = candidate.take_if_closer (&Collision { + t: c.t, + p_impact: c.p_impact, + normal: c.normal, + i: 0, + c_type: CollisionType::Vert, + }); + } + } + } + + for (i, tri) in tris.iter ().enumerate () { + let tri_min = tri.min (); + let tri_max = tri.max (); if ray_max.x < tri_min.x || ray_min.x > tri_max.x || @@ -198,7 +304,9 @@ pub fn get_candidate (world: &[Triangle], p0: Vec3, p1: Vec3, radius: f32) // Edge collisions // Vertex collisions - if let Some (c) = get_candidate_face (&tri, p0, p1, radius) { + let normal = Vec3::cross (tri.verts [2] - tri.verts [1], tri.verts [1] - tri.verts [0]).normalize (); + + if let Some (c) = get_candidate_face (&tri.verts, normal, p0, p1, radius) { candidate = candidate.take_if_closer (&Collision { t: c.t, p_impact: c.p_impact, @@ -245,7 +353,9 @@ pub fn get_candidate (world: &[Triangle], p0: Vec3, p1: Vec3, radius: f32) candidate } -fn get_candidate_face (tri: &Triangle, p0: Vec3, p1: Vec3, radius: f32) +/// Collide a ray with a convex planar face, like a triangle or a rectangle. + +fn get_candidate_face (verts: &[Vec3], normal: Vec3, p0: Vec3, p1: Vec3, radius: f32) -> Option { let radius3 = Vec3::from (( @@ -255,13 +365,11 @@ fn get_candidate_face (tri: &Triangle, p0: Vec3, p1: Vec3, radius: f32) )); let v = p1 - p0; - let normal = Vec3::cross (tri.verts [2] - tri.verts [1], tri.verts [1] - tri.verts [0]).normalize (); - - let distance_to_face0 = Vec3::dot (normal, p0 - tri.verts [0]) - radius; - let distance_to_face1 = Vec3::dot (normal, p1 - tri.verts [0]) - radius; + let distance_to_face0 = Vec3::dot (normal, p0 - verts [0]) - radius; + let distance_to_face1 = Vec3::dot (normal, p1 - verts [0]) - radius; if distance_to_face0 < 0.0 || distance_to_face1 > 0.0 { - tracing::trace! ("passed_plane {} {}", distance_to_face0, distance_to_face1); + // tracing::trace! ("passed_plane {} {}", distance_to_face0, distance_to_face1); return None; } @@ -272,9 +380,9 @@ fn get_candidate_face (tri: &Triangle, p0: Vec3, p1: Vec3, radius: f32) let p_impact_times_denom = p0 * (denom - t_times_denom) + p1 * (t_times_denom); - for j in 0..3 { - let a = tri.verts [j]; - let b = tri.verts [(j + 1) % 3]; + for j in 0..verts.len () { + let a = verts [j]; + let b = verts [(j + 1) % verts.len ()]; let tangent = Vec3::cross (b - a, normal); if Vec3::dot (tangent, p_impact_times_denom - a * denom) < 0.0 {