update `glam`, add tests to the physics
parent
f5da7b5188
commit
96436d6c36
|
@ -1,5 +1,7 @@
|
||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "adler32"
|
name = "adler32"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
|
@ -297,9 +299,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glam"
|
name = "glam"
|
||||||
version = "0.8.5"
|
version = "0.20.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bb24d4e1b92ceed0450bbf803ac894b597c5b8d0e16f175f7ef28c42024d8cbd"
|
checksum = "68270e16582ea40f9c5b2fcd588fbc9cb696577222e04a64d9085cc314806a8a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
|
|
|
@ -15,7 +15,8 @@ float-ord = "0.2.0"
|
||||||
futures = "0.3.8"
|
futures = "0.3.8"
|
||||||
futures-util = "0.3.9"
|
futures-util = "0.3.9"
|
||||||
gl = "0.14.0"
|
gl = "0.14.0"
|
||||||
glam = "0.8.5"
|
# glam = "0.8.5"
|
||||||
|
glam = "0.20.1"
|
||||||
iota = "0.2.1"
|
iota = "0.2.1"
|
||||||
maplit = "1.0.2"
|
maplit = "1.0.2"
|
||||||
partial-min-max = "0.4.0"
|
partial-min-max = "0.4.0"
|
||||||
|
|
|
@ -24,5 +24,62 @@ async fn main () -> Result <()> {
|
||||||
|
|
||||||
window.gl_make_current (&gl_ctx).map_err (|e| anyhow! ("Can't make OpenGL context current: {}", e))?;
|
window.gl_make_current (&gl_ctx).map_err (|e| anyhow! ("Can't make OpenGL context current: {}", e))?;
|
||||||
|
|
||||||
|
let mut event_pump = sdl_context.event_pump ().unwrap ();
|
||||||
|
|
||||||
|
let mut time_step = TimeStep::new (60, 1000);
|
||||||
|
let mut graphics_frames = 0;
|
||||||
|
|
||||||
|
let shader_program = shader::shader_from_files ("shaders/terrain-vert.glsl", "shaders/terrain-frag.glsl");
|
||||||
|
let shader_locations = ShaderLocations::new (&shader_program)?;
|
||||||
|
|
||||||
|
let mesh_cube = renderable_from_iqm_file ("cube.iqm");
|
||||||
|
|
||||||
|
'running: loop {
|
||||||
|
let _frames_to_do = time_step.step ();
|
||||||
|
|
||||||
|
for event in event_pump.poll_iter () {
|
||||||
|
match event {
|
||||||
|
Event::Quit {..} |
|
||||||
|
Event::KeyDown { keycode: Some (Keycode::Escape), .. } => {
|
||||||
|
break 'running
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.gl_make_current (&gl_ctx).unwrap ();
|
||||||
|
|
||||||
|
glezz::enable (gl::DEPTH_TEST);
|
||||||
|
|
||||||
|
glezz::clear_color (0.392f32, 0.710f32, 0.965f32, 1.0f32);
|
||||||
|
glezz::clear (gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT);
|
||||||
|
|
||||||
|
window.gl_swap_window ();
|
||||||
|
|
||||||
|
tokio::time::sleep (Duration::from_millis (15)).await;
|
||||||
|
}
|
||||||
|
|
||||||
Ok (())
|
Ok (())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ShaderLocations {
|
||||||
|
attr_pos: u32,
|
||||||
|
attr_normal: u32,
|
||||||
|
attr_color: u32,
|
||||||
|
uni_mvp: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShaderLocations {
|
||||||
|
pub fn new (shader_program: &shader::ShaderProgram) -> anyhow::Result <Self>
|
||||||
|
{
|
||||||
|
let attr = |name: &str| shader_program.get_attribute_location (&CString::new (name.as_bytes ())?).try_into ().context ("Attribute location negative");
|
||||||
|
let uni = |name: &str| shader_program.get_uniform_location (&CString::new (name.as_bytes ())?).try_into ().context ("Uniform location bad");
|
||||||
|
|
||||||
|
Ok (Self {
|
||||||
|
attr_pos: attr ("attr_pos")?,
|
||||||
|
attr_normal: attr ("attr_normal")?,
|
||||||
|
attr_color: attr ("attr_color")?,
|
||||||
|
uni_mvp: uni ("uni_mvp")?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -25,9 +25,9 @@ where V: Into <Vec3>
|
||||||
let rgb: Vec3 = rgb.into ();
|
let rgb: Vec3 = rgb.into ();
|
||||||
|
|
||||||
Vec3::from ((
|
Vec3::from ((
|
||||||
rgb.x () / 255.0,
|
rgb.x / 255.0,
|
||||||
rgb.y () / 255.0,
|
rgb.y / 255.0,
|
||||||
rgb.z () / 255.0
|
rgb.z / 255.0
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,9 +293,9 @@ impl FlightState {
|
||||||
// Forces
|
// Forces
|
||||||
let gravity = Vec3::from ((0.0, 0.0, -0.25));
|
let gravity = Vec3::from ((0.0, 0.0, -0.25));
|
||||||
let thrust = 0.125 * nose * throttle;
|
let thrust = 0.125 * nose * throttle;
|
||||||
let linear_drag = 0.0 * 0.25 * speed * -object_space_dir.y () * nose;
|
let linear_drag = 0.0 * 0.25 * speed * -object_space_dir.y * nose;
|
||||||
|
|
||||||
let turbulent_dir = Vec3::from ((-1.0 * object_space_dir.x (), -0.03125 * object_space_dir.y (), -16.0 * object_space_dir.z ()));
|
let turbulent_dir = Vec3::from ((-1.0 * object_space_dir.x, -0.03125 * object_space_dir.y, -16.0 * object_space_dir.z));
|
||||||
let quadratic_drag = speed * speed * airplane.ori.mul_vec3 (turbulent_dir);
|
let quadratic_drag = speed * speed * airplane.ori.mul_vec3 (turbulent_dir);
|
||||||
|
|
||||||
let air_drag = linear_drag + quadratic_drag;
|
let air_drag = linear_drag + quadratic_drag;
|
||||||
|
@ -305,9 +305,9 @@ impl FlightState {
|
||||||
airplane.vel += dt * (thrust + gravity + air_drag);
|
airplane.vel += dt * (thrust + gravity + air_drag);
|
||||||
airplane.pos += dt * airplane.vel;
|
airplane.pos += dt * airplane.vel;
|
||||||
|
|
||||||
if airplane.pos.z () < 0.0 {
|
if airplane.pos.z < 0.0 {
|
||||||
airplane.vel.set_z (0.0);
|
airplane.vel.z = 0.0;
|
||||||
airplane.pos.set_z (0.0);
|
airplane.pos.z = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if microstep == microsteps - 1 {
|
if microstep == microsteps - 1 {
|
||||||
|
@ -325,10 +325,10 @@ impl FlightState {
|
||||||
];
|
];
|
||||||
|
|
||||||
// Gauges
|
// Gauges
|
||||||
let alti = airplane.pos.z () * 100.0;
|
let alti = airplane.pos.z * 100.0;
|
||||||
let sink_rate = -airplane.vel.z () * 100.0;
|
let sink_rate = -airplane.vel.z * 100.0;
|
||||||
let air_speed = speed * 100.0;
|
let air_speed = speed * 100.0;
|
||||||
let ground_vel = Vec3::from ((airplane.vel.x (), airplane.vel.y (), 0.0));
|
let ground_vel = Vec3::from ((airplane.vel.x, airplane.vel.y, 0.0));
|
||||||
let ground_speed = ground_vel.length () * 100.0;
|
let ground_speed = ground_vel.length () * 100.0;
|
||||||
let glide_ratio = if sink_rate > 1.0 && throttle == 0.0 {
|
let glide_ratio = if sink_rate > 1.0 && throttle == 0.0 {
|
||||||
Some (ground_speed / sink_rate)
|
Some (ground_speed / sink_rate)
|
||||||
|
@ -457,8 +457,8 @@ fn make_object_space_vec (inverse_model_mat: &Mat4, world_space_vec: &Vec3)
|
||||||
-> Vec3
|
-> Vec3
|
||||||
{
|
{
|
||||||
let v = world_space_vec;
|
let v = world_space_vec;
|
||||||
let v4 = *inverse_model_mat * Vec4::from ((v.x (), v.y (), v.z (), 0.0));
|
let v4 = *inverse_model_mat * Vec4::from ((v.x, v.y, v.z, 0.0));
|
||||||
Vec3::from ((v4.x (), v4.y (), v4.z ()))
|
Vec3::from ((v4.x, v4.y, v4.z))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive (Clone)]
|
#[derive (Clone)]
|
||||||
|
@ -861,11 +861,12 @@ impl GameGraphics {
|
||||||
|
|
||||||
let light = state.wind_tunnel.sunlight.to_vec3 ();
|
let light = state.wind_tunnel.sunlight.to_vec3 ();
|
||||||
|
|
||||||
let shadow_mat = {
|
let shadow_mat = Mat4::from_cols (
|
||||||
let mut mat = Mat4::identity ();
|
(1.0, 0.0, 0.0, 0.0).into (),
|
||||||
mat.set_z_axis ((-light.x () / light.z (), -light.y () / light.z (), 0.0, 0.0).into ());
|
(0.0, 1.0, 0.0, 0.0).into (),
|
||||||
mat
|
(-light.x / light.z, -light.y / light.z, 0.0, 0.0).into (),
|
||||||
};
|
(0.0, 0.0, 0.0, 1.0).into (),
|
||||||
|
);
|
||||||
|
|
||||||
let mut passes = self.passes.iter ();
|
let mut passes = self.passes.iter ();
|
||||||
//println! ("Started frame");
|
//println! ("Started frame");
|
||||||
|
@ -931,7 +932,7 @@ impl GameGraphics {
|
||||||
let inverse_truck = truck_model_mat.inverse ();
|
let inverse_truck = truck_model_mat.inverse ();
|
||||||
let truck_model_mat = truck_model_mat * Mat4::from_scale ((airplane_scale, airplane_scale, airplane_scale).into ());
|
let truck_model_mat = truck_model_mat * Mat4::from_scale ((airplane_scale, airplane_scale, airplane_scale).into ());
|
||||||
|
|
||||||
let world_model_mat = Mat4::identity ();
|
let world_model_mat = Mat4::IDENTITY;
|
||||||
|
|
||||||
use uniforms::*;
|
use uniforms::*;
|
||||||
|
|
||||||
|
@ -1272,7 +1273,7 @@ fn main () {
|
||||||
let gravity = (0.0, 0.0, -1.0).into ();
|
let gravity = (0.0, 0.0, -1.0).into ();
|
||||||
|
|
||||||
let wind = state.wind.to_vec3 () * -1.0;
|
let wind = state.wind.to_vec3 () * -1.0;
|
||||||
let wind_force = (wind.x (), 0.125 * wind.y (), wind.z ()).into ();
|
let wind_force = (wind.x, 0.125 * wind.y, wind.z).into ();
|
||||||
|
|
||||||
let get_flash = |control_type, default_color| {
|
let get_flash = |control_type, default_color| {
|
||||||
if state.user_control == control_type {
|
if state.user_control == control_type {
|
||||||
|
@ -1314,10 +1315,10 @@ fn main () {
|
||||||
|
|
||||||
let d = arrow.direction / dir_len;
|
let d = arrow.direction / dir_len;
|
||||||
|
|
||||||
let up: Vec3 = if d.z () > 0.5 {
|
let up: Vec3 = if d.z > 0.5 {
|
||||||
(-1.0, 0.0, 0.0)
|
(-1.0, 0.0, 0.0)
|
||||||
}
|
}
|
||||||
else if d.z () < -0.5 {
|
else if d.z < -0.5 {
|
||||||
(-1.0, 0.0, 0.0)
|
(-1.0, 0.0, 0.0)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -1329,11 +1330,12 @@ fn main () {
|
||||||
let left = d.cross (up);
|
let left = d.cross (up);
|
||||||
let up = d.cross (left);
|
let up = d.cross (left);
|
||||||
|
|
||||||
let mut dir_mat = Mat4::identity ();
|
let dir_mat = Mat4::from_cols (
|
||||||
|
(left, 0.0).into (),
|
||||||
dir_mat.set_x_axis ((left.x (), left.y (), left.z (), 0.0).into ());
|
(up, 0.0).into (),
|
||||||
dir_mat.set_y_axis ((up.x (), up.y (), up.z (), 0.0).into ());
|
(d, 0.0).into (),
|
||||||
dir_mat.set_z_axis ((d.x (), d.y (), d.z (), 0.0).into ());
|
(0.0, 0.0, 0.0, 1.0).into (),
|
||||||
|
);
|
||||||
|
|
||||||
let s = dir_len * 0.0625;
|
let s = dir_len * 0.0625;
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,6 @@ use std::io::Cursor;
|
||||||
|
|
||||||
pub struct VertexBuffer {
|
pub struct VertexBuffer {
|
||||||
id: u32,
|
id: u32,
|
||||||
// Not bytes.
|
|
||||||
len: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const FLOAT_SIZE: usize = 4;
|
const FLOAT_SIZE: usize = 4;
|
||||||
|
@ -59,7 +57,6 @@ impl VertexBuffer {
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
len,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +71,6 @@ impl VertexBuffer {
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
len,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,9 +99,6 @@ pub struct IndexBuffer {
|
||||||
/// The OpenGL ID of the buffer
|
/// The OpenGL ID of the buffer
|
||||||
id: u32,
|
id: u32,
|
||||||
|
|
||||||
/// The count of 32-bit indices the buffer can store
|
|
||||||
len: usize,
|
|
||||||
|
|
||||||
/// The largest index stored in the buffer when it was created
|
/// The largest index stored in the buffer when it was created
|
||||||
max: u32,
|
max: u32,
|
||||||
}
|
}
|
||||||
|
@ -144,7 +137,6 @@ impl IndexBuffer {
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
len: slice.len () / IDX_SIZE,
|
|
||||||
max: max.unwrap (),
|
max: max.unwrap (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,7 +158,6 @@ impl IndexBuffer {
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
len: slice.len (),
|
|
||||||
max: *max.unwrap (),
|
max: *max.unwrap (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
196
src/physics.rs
196
src/physics.rs
|
@ -1,39 +1,37 @@
|
||||||
use glam::{Vec2, Vec3};
|
use glam::{Vec2, Vec3};
|
||||||
use partial_min_max::{min, max};
|
use partial_min_max::{min, max};
|
||||||
|
|
||||||
|
#[derive (Debug, PartialEq)]
|
||||||
pub struct PhysicsBody {
|
pub struct PhysicsBody {
|
||||||
pos: Vec3,
|
pos: Vec3,
|
||||||
vel: Vec3,
|
vel: Vec3,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive (Debug, PartialEq)]
|
||||||
pub struct PhysicsResult {
|
pub struct PhysicsResult {
|
||||||
body: PhysicsBody,
|
pub body: PhysicsBody,
|
||||||
triangles_hit: Vec <usize>,
|
pub triangles_hit: Vec <usize>,
|
||||||
kill: bool,
|
pub kill: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive (Copy, Clone)]
|
||||||
pub struct Triangle {
|
pub struct Triangle {
|
||||||
verts: [Vec3; 3],
|
verts: [Vec3; 3],
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait MeshBuffer {
|
|
||||||
fn num_triangles (&self) -> usize;
|
|
||||||
fn get_triangle (&self, i: usize) -> Triangle;
|
|
||||||
}
|
|
||||||
|
|
||||||
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),
|
||||||
min (a.y (), b.y ()),
|
min (a.y, b.y),
|
||||||
min (a.z (), b.z ())
|
min (a.z, b.z)
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vec_max (a: &Vec3, b: &Vec3) -> Vec3 {
|
fn vec_max (a: &Vec3, b: &Vec3) -> Vec3 {
|
||||||
Vec3::from ((
|
Vec3::from ((
|
||||||
max (a.x (), b.x ()),
|
max (a.x, b.x),
|
||||||
max (a.y (), b.y ()),
|
max (a.y, b.y),
|
||||||
max (a.z (), b.z ())
|
max (a.z, b.z)
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +51,7 @@ impl Triangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive (Clone)]
|
#[derive (Clone, Debug, PartialEq)]
|
||||||
pub struct Collision {
|
pub struct Collision {
|
||||||
t: f32,
|
t: f32,
|
||||||
p_impact: Vec3,
|
p_impact: Vec3,
|
||||||
|
@ -72,11 +70,9 @@ impl Collision {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_candidate <MB> (world: &MB, p0: Vec3, p1: Vec3)
|
pub fn get_candidate (world: &[Triangle], p0: Vec3, p1: Vec3, radius: f32)
|
||||||
-> Collision
|
-> Collision
|
||||||
where MB: MeshBuffer
|
|
||||||
{
|
{
|
||||||
let radius = 0.0625f32;
|
|
||||||
let radius3 = Vec3::from ((
|
let radius3 = Vec3::from ((
|
||||||
radius,
|
radius,
|
||||||
radius,
|
radius,
|
||||||
|
@ -91,19 +87,17 @@ where MB: MeshBuffer
|
||||||
i: 0,
|
i: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
for i in 0..world.num_triangles () {
|
for (i, tri) in world.iter ().enumerate () {
|
||||||
let tri = world.get_triangle (i);
|
|
||||||
|
|
||||||
let tri_min = tri.min () - radius3;
|
let tri_min = tri.min () - radius3;
|
||||||
let tri_max = tri.max () + radius3;
|
let tri_max = tri.max () + radius3;
|
||||||
|
|
||||||
let ray_min = min (p0, p1);
|
let ray_min = p0.min (p1);
|
||||||
let ray_max = max (p0, p1);
|
let ray_max = p0.max (p1);
|
||||||
|
|
||||||
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 ||
|
||||||
ray_max.y () < tri_min.y () || ray_min.y () > tri_max.y () ||
|
ray_max.y < tri_min.y || ray_min.y > tri_max.y ||
|
||||||
ray_max.z () < tri_min.z () || ray_min.z () > tri_max.z ()
|
ray_max.z < tri_min.z || ray_min.z > tri_max.z
|
||||||
{
|
{
|
||||||
// AABB reject
|
// AABB reject
|
||||||
continue;
|
continue;
|
||||||
|
@ -189,14 +183,14 @@ where MB: MeshBuffer
|
||||||
// and Y is the cross of those.
|
// and Y is the cross of those.
|
||||||
|
|
||||||
// I forgot the maths word for this
|
// I forgot the maths word for this
|
||||||
let discriminant = radius * radius - a_ray.y () * a_ray.y ();
|
let discriminant = radius * radius - a_ray.y * a_ray.y;
|
||||||
|
|
||||||
if discriminant < 0.0 {
|
if discriminant < 0.0 {
|
||||||
// No possible collision
|
// No possible collision
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let t = (a_ray.x () - discriminant.sqrt ()) / (p1 - p0).length ();
|
let t = (a_ray.x - discriminant.sqrt ()) / (p1 - p0).length ();
|
||||||
|
|
||||||
if t < 0.0 || t > 1.0 {
|
if t < 0.0 || t > 1.0 {
|
||||||
// The cylinder is along the line,
|
// The cylinder is along the line,
|
||||||
|
@ -280,25 +274,31 @@ where MB: MeshBuffer
|
||||||
candidate
|
candidate
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn physics_step <MB> (input: &PhysicsBody, world: &MB)
|
pub struct Params {
|
||||||
-> PhysicsResult
|
dt: f32,
|
||||||
where MB: MeshBuffer
|
gravity: Vec3,
|
||||||
|
margin: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn physics_step (
|
||||||
|
params: &Params, world: &[Triangle],
|
||||||
|
radius: f32, input: &PhysicsBody,
|
||||||
|
) -> PhysicsResult
|
||||||
{
|
{
|
||||||
let dt = 1.0 / 60.0;
|
let margin = params.margin;
|
||||||
let z = Vec3::from ((0.0, 0.0, 1.0));
|
let dt = params.dt;
|
||||||
let gravity = z * -16.0 * dt;
|
|
||||||
let margin = 0.00125;
|
|
||||||
|
|
||||||
let mut t_remaining = 1.0;
|
let mut t_remaining = 1.0;
|
||||||
|
|
||||||
let mut old_pos = input.pos;
|
let mut old_pos = input.pos;
|
||||||
let mut new_vel = input.vel + 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 triangles_hit = Vec::new ();
|
||||||
|
|
||||||
for i in 0..5 {
|
// Do 5 iterations of the sub-step, trying to converge on a valid state
|
||||||
let candidate = get_candidate (world, old_pos, new_pos);
|
for _ in 0..5 {
|
||||||
|
let candidate = get_candidate (world, old_pos, new_pos, radius);
|
||||||
|
|
||||||
if candidate.t <= 1.0 {
|
if candidate.t <= 1.0 {
|
||||||
t_remaining *= 1.0 - candidate.t;
|
t_remaining *= 1.0 - candidate.t;
|
||||||
|
@ -315,6 +315,8 @@ where MB: MeshBuffer
|
||||||
new_vel += candidate.normal * speed_towards_normal;
|
new_vel += candidate.normal * speed_towards_normal;
|
||||||
// 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);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
t_remaining = 0.0;
|
t_remaining = 0.0;
|
||||||
|
@ -332,3 +334,121 @@ where MB: MeshBuffer
|
||||||
kill: false,
|
kill: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg (test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_physics () {
|
||||||
|
// Remember, Z is up
|
||||||
|
|
||||||
|
let params = Params {
|
||||||
|
dt: 1.0,
|
||||||
|
gravity: (0.0, 0.0, 0.0).into (),
|
||||||
|
margin: 0.00125,
|
||||||
|
};
|
||||||
|
|
||||||
|
let world: Vec <_> = vec! [
|
||||||
|
(
|
||||||
|
(0.0, 0.0, 0.0),
|
||||||
|
(0.0, 2.0, 0.0),
|
||||||
|
(2.0, 0.0, 0.0),
|
||||||
|
),
|
||||||
|
].into_iter ()
|
||||||
|
.map (|(v0, v1, v2)| {
|
||||||
|
Triangle {
|
||||||
|
verts: [
|
||||||
|
v0.into (),
|
||||||
|
v1.into (),
|
||||||
|
v2.into (),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect ();
|
||||||
|
|
||||||
|
let magic_0 = f32::sqrt (f32::powf (0.5 + params.margin, 2.0) / 2.0);
|
||||||
|
|
||||||
|
for ((radius, body_before), e) in [
|
||||||
|
// Ray striking triangle from above, stops at triangle
|
||||||
|
(
|
||||||
|
(
|
||||||
|
0.0,
|
||||||
|
PhysicsBody {
|
||||||
|
pos: (0.5, 0.5, 0.5).into (),
|
||||||
|
vel: (0.0, 0.0, -1.0).into (),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
PhysicsResult {
|
||||||
|
body: PhysicsBody {
|
||||||
|
pos: (0.5, 0.5, params.margin).into (),
|
||||||
|
vel: (0.0, 0.0, 0.0).into (),
|
||||||
|
},
|
||||||
|
triangles_hit: vec! [0],
|
||||||
|
kill: false,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// Ball striking triangle from above, stops at ball radius
|
||||||
|
(
|
||||||
|
(
|
||||||
|
0.5,
|
||||||
|
PhysicsBody {
|
||||||
|
pos: (0.5, 0.5, 2.0).into (),
|
||||||
|
vel: (0.0, 0.0, -2.0).into (),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
PhysicsResult {
|
||||||
|
body: PhysicsBody {
|
||||||
|
pos: (0.5, 0.5, params.margin + 0.5).into (),
|
||||||
|
vel: (0.0, 0.0, 0.0).into (),
|
||||||
|
},
|
||||||
|
triangles_hit: vec! [0],
|
||||||
|
kill: false,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// Ball striking triangle on edge
|
||||||
|
(
|
||||||
|
(
|
||||||
|
0.5,
|
||||||
|
PhysicsBody {
|
||||||
|
pos: (-2.0, 1.0, 0.0).into (),
|
||||||
|
vel: (2.0, 0.0, 0.0).into (),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
PhysicsResult {
|
||||||
|
body: PhysicsBody {
|
||||||
|
pos: (-params.margin - 0.5, 1.0, 0.0).into (),
|
||||||
|
vel: (0.0, 0.0, 0.0).into (),
|
||||||
|
},
|
||||||
|
triangles_hit: vec! [0],
|
||||||
|
kill: false,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// Ball striking triangle on diagonal edge
|
||||||
|
(
|
||||||
|
(
|
||||||
|
0.5,
|
||||||
|
PhysicsBody {
|
||||||
|
pos: (2.0, 2.0, 0.0).into (),
|
||||||
|
vel: (-2.0, -2.0, 0.0).into (),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
PhysicsResult {
|
||||||
|
body: PhysicsBody {
|
||||||
|
pos: (1.0 + magic_0, 1.0 + magic_0, 0.0).into (),
|
||||||
|
vel: (0.0, 0.0, 0.0).into (),
|
||||||
|
},
|
||||||
|
triangles_hit: vec! [0],
|
||||||
|
kill: false,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
] {
|
||||||
|
let a = physics_step (¶ms, &world, radius, &body_before);
|
||||||
|
|
||||||
|
assert! (a.body.pos.distance_squared (e.body.pos) < 0.00125);
|
||||||
|
assert! (a.body.vel.distance_squared (e.body.vel) < 0.00125);
|
||||||
|
assert_eq! (a.triangles_hit, e.triangles_hit);
|
||||||
|
assert_eq! (a.kill, e.kill);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ pub use crate::{
|
||||||
gpu_buffers,
|
gpu_buffers,
|
||||||
network_protocol::*,
|
network_protocol::*,
|
||||||
quinn_common::make_client_endpoint,
|
quinn_common::make_client_endpoint,
|
||||||
|
renderable_model::renderable_from_iqm_file,
|
||||||
shader,
|
shader,
|
||||||
timestep::TimeStep,
|
timestep::TimeStep,
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,7 +18,6 @@ pub struct RenderableMesh {
|
||||||
pub struct RenderableModel {
|
pub struct RenderableModel {
|
||||||
num_pos: usize,
|
num_pos: usize,
|
||||||
num_uv: usize,
|
num_uv: usize,
|
||||||
num_normal: usize,
|
|
||||||
|
|
||||||
vertexes: VertexBuffer,
|
vertexes: VertexBuffer,
|
||||||
indexes: IndexBuffer,
|
indexes: IndexBuffer,
|
||||||
|
@ -89,7 +88,6 @@ impl RenderableModel {
|
||||||
Self {
|
Self {
|
||||||
num_pos,
|
num_pos,
|
||||||
num_uv,
|
num_uv,
|
||||||
num_normal,
|
|
||||||
|
|
||||||
vertexes,
|
vertexes,
|
||||||
indexes,
|
indexes,
|
||||||
|
|
Loading…
Reference in New Issue