looking good
parent
3aeced7c2f
commit
3c2f247167
|
@ -31,9 +31,33 @@ pub const
|
|||
}
|
||||
}
|
||||
|
||||
#[derive (Default)]
|
||||
struct GameState {
|
||||
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 {
|
||||
|
@ -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 <Vec3> = 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 ();
|
||||
|
||||
|
|
154
src/physics.rs
154
src/physics.rs
|
@ -10,7 +10,7 @@ pub struct PhysicsBody {
|
|||
#[derive (Debug, PartialEq)]
|
||||
pub struct PhysicsResult {
|
||||
pub body: PhysicsBody,
|
||||
pub triangles_hit: Vec <usize>,
|
||||
pub normals_hit: Vec <Vec3>,
|
||||
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 <PrimCollision>
|
||||
{
|
||||
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 {
|
||||
|
|
Loading…
Reference in New Issue