I guess it makes more sense with all-quadratic drag

main
_ 2020-03-11 03:41:58 +00:00
parent ae19b8bb93
commit 754fd681c0
1 changed files with 110 additions and 60 deletions

View File

@ -60,15 +60,25 @@ impl EulerAngles {
}
}
// TODO: Use iota macro
const KEY_LEFT: usize = 0;
const KEY_RIGHT: usize = KEY_LEFT + 1;
const KEY_UP: usize = KEY_RIGHT + 1;
const KEY_DOWN: usize = KEY_UP + 1;
mod keys {
use iota::iota;
iota! {
pub const
KEY_LEFT: usize = iota;
, KEY_RIGHT
, KEY_UP
, KEY_DOWN
, YAW_LEFT
, YAW_RIGHT
}
}
struct ControllerState {
keys: Vec <bool>,
analog_left_x: i16,
analog_left_y: i16,
trigger_left: i16,
trigger_right: i16,
}
@ -87,6 +97,7 @@ impl ControllerState {
};
let key_or_gamepad = |key, button| f (key) || b (button);
let axis_or_zero = |a| c.as_ref ().map_or (0, |c| c.axis (a));
Self {
keys: vec! [
@ -95,14 +106,10 @@ impl ControllerState {
key_or_gamepad (Scancode::Up, Button::DPadUp),
key_or_gamepad (Scancode::Down, Button::DPadDown),
],
trigger_left: match c {
None => 0,
Some (c) => c.axis (Axis::TriggerLeft),
},
trigger_right: match c {
None => 0,
Some (c) => c.axis (Axis::TriggerRight),
},
analog_left_x: axis_or_zero (Axis::LeftX),
analog_left_y: axis_or_zero (Axis::LeftY),
trigger_left: axis_or_zero (Axis::TriggerLeft),
trigger_right: axis_or_zero (Axis::TriggerRight),
}
}
@ -116,6 +123,7 @@ impl ControllerState {
spin_speed: i32
) -> i32
{
use keys::*;
const SPIN_RAMP_TIME: i32 = 30;
let spin_f = 4.0 * spin_speed as f32 / SPIN_RAMP_TIME as f32;
@ -144,6 +152,7 @@ impl ControllerState {
spin_speed: i32
) -> i32
{
use keys::*;
const SPIN_RAMP_TIME: i32 = 30;
let spin_f = 2.0 * (spin_speed + 1) as f32 / SPIN_RAMP_TIME as f32;
let spin_f = spin_f.to_radians ();
@ -166,6 +175,12 @@ impl ControllerState {
//println! ("spin_f {}, Quat {:?}", spin_f, delta);
if delta == Quat::default () {
let analog_scale = 1.0f32.to_radians () / 32768.0;
delta = delta.mul_quat (Quat::from_rotation_y (self.analog_left_x as f32 * analog_scale));
delta = delta.mul_quat (Quat::from_rotation_x (self.analog_left_y as f32 * analog_scale));
*controlled_quat = (controlled_quat.mul_quat (delta)).normalize ();
0
}
else {
@ -207,17 +222,19 @@ struct Airplane {
struct FlightState {
airplane: Airplane,
spin_speed: i32,
arrows: Vec <Arrow>,
}
impl Default for FlightState {
fn default () -> Self {
Self {
airplane: Airplane {
pos: (0.0, -10.0, 20.0).into (),
pos: (0.0, -20.0, 20.0).into (),
vel: (0.0, 0.0, 0.0).into (),
ori: Default::default (),
},
spin_speed: 0,
arrows: vec![],
}
}
}
@ -230,54 +247,86 @@ impl FlightState {
pub fn step (&mut self, controller: &ControllerState) {
self.spin_speed = controller.control_quat (&mut self.airplane.ori, self.spin_speed);
let airplane = &mut self.airplane;
// Info
let nose = airplane.ori.mul_vec3 ((0.0, 1.0, 0.0).into ());
let speed = airplane.vel.length ();
// Different from nose since planes are always drifting
let direction = if speed == 0.0 {
Vec3::from ((0.0, 0.0, 0.0))
}
else {
airplane.vel * (1.0 / speed)
};
let object_space_dir = airplane.ori.conjugate ().mul_vec3 (direction);
let throttle = 1.0 + (controller.trigger_right as f32 - controller.trigger_left as f32) / 32767.0;
// Forces
let gravity = Vec3::from ((0.0, 0.0, -0.25));
let thrust = nose * throttle;
let laminar_drag = 0.25 * speed * -object_space_dir.y () * nose;
let airplane = &mut self.airplane;
let turbulent_dir = Vec3::from ((0.5 * object_space_dir.x (), 0.0, object_space_dir.z ()));
let turbulent_drag = -speed * speed * airplane.ori.mul_vec3 (turbulent_dir);
let microsteps = 4;
let air_drag = laminar_drag + turbulent_drag;
// Accumulate forces and run an Euler integration step
let dt = 1.0 / 60.0;
airplane.vel += dt * (thrust + gravity + air_drag);
airplane.pos += dt * airplane.vel;
if airplane.pos.z () < 0.0 {
airplane.vel.set_z (0.0);
airplane.pos.set_z (0.0);
for microstep in 0..microsteps {
// Info
let nose = airplane.ori.mul_vec3 ((0.0, 1.0, 0.0).into ());
let speed = airplane.vel.length ();
// Different from nose since planes are always drifting
let direction = if speed == 0.0 {
Vec3::from ((0.0, 0.0, 0.0))
}
else {
airplane.vel * (1.0 / speed)
};
let inverse_ori = airplane.ori.conjugate ();
let object_space_dir = inverse_ori.mul_vec3 (direction);
// Forces
let gravity = Vec3::from ((0.0, 0.0, -0.25));
let thrust = 0.125 * nose * throttle;
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 quadratic_drag = speed * speed * airplane.ori.mul_vec3 (turbulent_dir);
let air_drag = linear_drag + quadratic_drag;
// Accumulate forces and run an Euler integration step
let dt = 1.0 / 60.0 / microsteps as f32;
airplane.vel += dt * (thrust + gravity + air_drag);
airplane.pos += dt * airplane.vel;
if airplane.pos.z () < 0.0 {
airplane.vel.set_z (0.0);
airplane.pos.set_z (0.0);
}
if microstep == microsteps - 1 {
let make_arrow = |direction, color| Arrow {
origin: airplane.pos.clone (),
direction: direction * 0.125,
color,
};
self.arrows = vec! [
make_arrow (gravity, color_from_255 ((128.0, 128.0, 128.0))),
make_arrow (thrust, color_from_255 ((255.0, 128.0, 0.0))),
make_arrow (linear_drag, color_from_255 ((128.0, 128.0, 128.0))),
make_arrow (quadratic_drag, color_from_255 ((0.0, 255.0, 255.0))),
];
// Gauges
let alti = airplane.pos.z () * 100.0;
let sink_rate = -airplane.vel.z () * 100.0;
let air_speed = speed * 100.0;
let ground_vel = Vec3::from ((airplane.vel.x (), airplane.vel.y (), 0.0));
let ground_speed = ground_vel.length () * 100.0;
let glide_ratio = if sink_rate > 1.0 && throttle == 0.0 {
Some (ground_speed / sink_rate)
}
else {
None
};
println! ("Alti: {}, Airspeed: {}, Groundspeed: {}, Throttle: {}, Sink Rate: {}, Glide Ratio: {:?}\nLaminar: {}, Turbulent: {}",
alti as i32,
air_speed as i32,
ground_speed as i32,
(throttle * 100.0) as i32,
sink_rate as i32,
glide_ratio,
(linear_drag.length () * 100.0) as i32,
(quadratic_drag.length () * 100.0) as i32
);
}
}
// Gauges
let alti = airplane.pos.z () * 100.0;
let air_speed = speed * 100.0;
let ground_vel = Vec3::from ((airplane.vel.x (), airplane.vel.y (), 0.0));
let ground_speed = ground_vel.length () * 100.0;
println! ("Alti: {}, Airspeed: {}, Groundspeed: {}, Throttle: {}",
alti as i32,
air_speed as i32,
ground_speed as i32,
(throttle * 100.0) as i32
);
}
}
@ -388,6 +437,7 @@ fn make_object_space_vec (inverse_model_mat: &Mat4, world_space_vec: &Vec3)
Vec3::from ((v4.x (), v4.y (), v4.z ()))
}
#[derive (Clone)]
struct Arrow {
origin: Vec3,
direction: Vec3,
@ -667,7 +717,7 @@ impl GameGraphics {
flags: hashmap! {
gl::CULL_FACE => true,
gl::DEPTH_TEST => true,
gl::TEXTURE_2D => true,
gl::TEXTURE_2D => false,
gl::STENCIL_TEST => false,
},
front_face: Some (FrontFace::Cw),
@ -911,7 +961,7 @@ impl GameGraphics {
let unis = &shader_vars.unis;
glezz::uniform_3fv (unis [&MIN_BRIGHT], &black);
glezz::uniform_3fv (unis [&MIN_ALBEDO], &black);
glezz::uniform_3fv (unis [&MIN_ALBEDO], &white);
for arrow in arrows.iter () {
let mvp = view_mat * arrow.model_mat;
@ -1036,7 +1086,7 @@ fn main () {
}
]
},
_ => vec![],
PlayMode::FreeFlight => state.flight.arrows.clone (),
};
let renderable_arrows: Vec <_> = arrows.iter ().map (|arrow| {