looking good
parent
3aeced7c2f
commit
3c2f247167
|
@ -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 ();
|
||||||
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) {
|
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 ();
|
||||||
|
|
||||||
|
|
154
src/physics.rs
154
src/physics.rs
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue