looking good

main
_ 2021-12-19 00:54:04 +00:00
parent 3aeced7c2f
commit 3c2f247167
2 changed files with 210 additions and 53 deletions

View File

@ -31,9 +31,33 @@ pub const
} }
} }
#[derive (Default)]
struct GameState { struct GameState {
player: PhysicsBody, player: PhysicsBody,
aabbs: Vec <opengl_rust::physics::Aabb>,
}
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 { struct GameGraphics {
@ -70,7 +94,9 @@ impl GameGraphics {
let screen_size = (1280.0, 720.0); 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 * let view_mat = proj_mat *
Mat4::from_translation ((0.0, 0.0, -20.0).into ()) * 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 * let mvp = view_mat *
Mat4::from_scale ((4.0, 4.0, 1.0).into ()) * Mat4::from_translation (center) *
Mat4::from_translation ((0.0, 0.0, -2.0).into ()); Mat4::from_scale (scale);
glezz::uniform_matrix_4fv (unis [&u::MVP], &mvp); glezz::uniform_matrix_4fv (unis [&u::MVP], &mvp);
self.mesh_cube.draw_all (attrs, |_| { self.mesh_cube.draw_all (attrs, |_| {
@ -257,32 +288,14 @@ async fn main () -> Result <()> {
gravity: (0.0, 0.0, -0.25).into (), gravity: (0.0, 0.0, -0.25).into (),
margin: 0.00125, 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_speed = 2.0;
let player_jump_speed = 8.0;
let mut player_jump_vec: Option <Vec3> = None;
'running: loop { 'running: loop {
let _frames_to_do = time_step.step (); let _frames_to_do = time_step.step ();
let mut player_wants_to_jump = false;
for event in event_pump.poll_iter () { for event in event_pump.poll_iter () {
match event { match event {
@ -293,14 +306,25 @@ async fn main () -> Result <()> {
Event::KeyDown { keycode: Some (Keycode::R), .. } => { Event::KeyDown { keycode: Some (Keycode::R), .. } => {
game_state = Default::default (); game_state = Default::default ();
}, },
Event::KeyDown { keycode: Some (Keycode::Space), .. } => {
player_wants_to_jump = true;
},
_ => (), _ => (),
} }
} }
let kb_state = event_pump.keyboard_state (); let kb_state = event_pump.keyboard_state ();
let player_speed = if player_jump_vec.is_some () {
game_state.player.vel.x = 0.0; game_state.player.vel.x = 0.0;
game_state.player.vel.y = 0.0; game_state.player.vel.y = 0.0;
player_speed
}
else {
0.125
};
if kb_state.is_scancode_pressed (Scancode::Left) { if kb_state.is_scancode_pressed (Scancode::Left) {
game_state.player.vel.x -= player_speed; 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.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 (); window.gl_make_current (&gl_ctx).unwrap ();

View File

@ -10,7 +10,7 @@ pub struct PhysicsBody {
#[derive (Debug, PartialEq)] #[derive (Debug, PartialEq)]
pub struct PhysicsResult { pub struct PhysicsResult {
pub body: PhysicsBody, pub body: PhysicsBody,
pub triangles_hit: Vec <usize>, pub normals_hit: Vec <Vec3>,
pub kill: bool, pub kill: bool,
} }
@ -19,6 +19,12 @@ pub struct Triangle {
pub verts: [Vec3; 3], pub verts: [Vec3; 3],
} }
#[derive (Copy, Clone)]
pub struct Aabb {
pub min: Vec3,
pub max: Vec3,
}
fn vec_min (a: &Vec3, b: &Vec3) -> Vec3 { fn vec_min (a: &Vec3, b: &Vec3) -> Vec3 {
Vec3::from (( Vec3::from ((
min (a.x, b.x), min (a.x, b.x),
@ -103,7 +109,8 @@ pub struct Params {
} }
pub fn step ( pub fn step (
params: &Params, world: &[Triangle], params: &Params,
tris: &[Triangle], aabbs: &[Aabb],
radius: f32, input: &PhysicsBody, radius: f32, input: &PhysicsBody,
) -> PhysicsResult ) -> PhysicsResult
{ {
@ -116,14 +123,14 @@ pub fn step (
let mut new_vel = input.vel + params.gravity; let mut new_vel = input.vel + params.gravity;
let mut new_pos = old_pos + new_vel * dt * t_remaining; 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 // Do 5 iterations of the sub-step, trying to converge on a valid state
for _ in 0..5 { 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 { 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; t_remaining *= 1.0 - candidate.t;
let speed_towards_normal = -Vec3::dot (new_vel, candidate.normal); 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 // But also compensate for the slide distance it lost
new_pos = push_out_pos + new_vel * dt * t_remaining; new_pos = push_out_pos + new_vel * dt * t_remaining;
triangles_hit.push (candidate.i); normals_hit.push (candidate.normal);
} }
else { else {
t_remaining = 0.0; t_remaining = 0.0;
@ -153,12 +160,17 @@ pub fn step (
pos: old_pos, pos: old_pos,
vel: new_vel, vel: new_vel,
}, },
triangles_hit, normals_hit,
kill: false, 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 -> Collision
{ {
let radius3 = Vec3::from (( let radius3 = Vec3::from ((
@ -176,12 +188,106 @@ pub fn get_candidate (world: &[Triangle], p0: Vec3, p1: Vec3, radius: f32)
c_type: CollisionType::Face, c_type: CollisionType::Face,
}; };
for (i, tri) in world.iter ().enumerate () { let ray_min = p0.min (p1) - radius3;
let tri_min = tri.min () - radius3; let ray_max = p0.max (p1) + radius3;
let tri_max = tri.max () + radius3;
let ray_min = p0.min (p1); for b in aabbs {
let ray_max = p0.max (p1); 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 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 if
ray_max.x < tri_min.x || ray_min.x > tri_max.x || 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 // Edge collisions
// Vertex 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 { candidate = candidate.take_if_closer (&Collision {
t: c.t, t: c.t,
p_impact: c.p_impact, p_impact: c.p_impact,
@ -245,7 +353,9 @@ pub fn get_candidate (world: &[Triangle], p0: Vec3, p1: Vec3, radius: f32)
candidate 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 <PrimCollision> -> Option <PrimCollision>
{ {
let radius3 = Vec3::from (( 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 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 - verts [0]) - radius;
let distance_to_face1 = Vec3::dot (normal, p1 - verts [0]) - radius;
let distance_to_face0 = Vec3::dot (normal, p0 - tri.verts [0]) - radius;
let distance_to_face1 = Vec3::dot (normal, p1 - tri.verts [0]) - radius;
if distance_to_face0 < 0.0 || distance_to_face1 > 0.0 { 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; 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); let p_impact_times_denom = p0 * (denom - t_times_denom) + p1 * (t_times_denom);
for j in 0..3 { for j in 0..verts.len () {
let a = tri.verts [j]; let a = verts [j];
let b = tri.verts [(j + 1) % 3]; let b = verts [(j + 1) % verts.len ()];
let tangent = Vec3::cross (b - a, normal); let tangent = Vec3::cross (b - a, normal);
if Vec3::dot (tangent, p_impact_times_denom - a * denom) < 0.0 { if Vec3::dot (tangent, p_impact_times_denom - a * denom) < 0.0 {