I guess it makes more sense with all-quadratic drag
parent
ae19b8bb93
commit
754fd681c0
|
@ -60,15 +60,25 @@ impl EulerAngles {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Use iota macro
|
mod keys {
|
||||||
const KEY_LEFT: usize = 0;
|
use iota::iota;
|
||||||
const KEY_RIGHT: usize = KEY_LEFT + 1;
|
iota! {
|
||||||
const KEY_UP: usize = KEY_RIGHT + 1;
|
pub const
|
||||||
const KEY_DOWN: usize = KEY_UP + 1;
|
KEY_LEFT: usize = iota;
|
||||||
|
, KEY_RIGHT
|
||||||
|
, KEY_UP
|
||||||
|
, KEY_DOWN
|
||||||
|
, YAW_LEFT
|
||||||
|
, YAW_RIGHT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct ControllerState {
|
struct ControllerState {
|
||||||
keys: Vec <bool>,
|
keys: Vec <bool>,
|
||||||
|
|
||||||
|
analog_left_x: i16,
|
||||||
|
analog_left_y: i16,
|
||||||
|
|
||||||
trigger_left: i16,
|
trigger_left: i16,
|
||||||
trigger_right: i16,
|
trigger_right: i16,
|
||||||
}
|
}
|
||||||
|
@ -87,6 +97,7 @@ impl ControllerState {
|
||||||
};
|
};
|
||||||
|
|
||||||
let key_or_gamepad = |key, button| f (key) || b (button);
|
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 {
|
Self {
|
||||||
keys: vec! [
|
keys: vec! [
|
||||||
|
@ -95,14 +106,10 @@ impl ControllerState {
|
||||||
key_or_gamepad (Scancode::Up, Button::DPadUp),
|
key_or_gamepad (Scancode::Up, Button::DPadUp),
|
||||||
key_or_gamepad (Scancode::Down, Button::DPadDown),
|
key_or_gamepad (Scancode::Down, Button::DPadDown),
|
||||||
],
|
],
|
||||||
trigger_left: match c {
|
analog_left_x: axis_or_zero (Axis::LeftX),
|
||||||
None => 0,
|
analog_left_y: axis_or_zero (Axis::LeftY),
|
||||||
Some (c) => c.axis (Axis::TriggerLeft),
|
trigger_left: axis_or_zero (Axis::TriggerLeft),
|
||||||
},
|
trigger_right: axis_or_zero (Axis::TriggerRight),
|
||||||
trigger_right: match c {
|
|
||||||
None => 0,
|
|
||||||
Some (c) => c.axis (Axis::TriggerRight),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,6 +123,7 @@ impl ControllerState {
|
||||||
spin_speed: i32
|
spin_speed: i32
|
||||||
) -> i32
|
) -> i32
|
||||||
{
|
{
|
||||||
|
use keys::*;
|
||||||
const SPIN_RAMP_TIME: i32 = 30;
|
const SPIN_RAMP_TIME: i32 = 30;
|
||||||
let spin_f = 4.0 * spin_speed as f32 / SPIN_RAMP_TIME as f32;
|
let spin_f = 4.0 * spin_speed as f32 / SPIN_RAMP_TIME as f32;
|
||||||
|
|
||||||
|
@ -144,6 +152,7 @@ impl ControllerState {
|
||||||
spin_speed: i32
|
spin_speed: i32
|
||||||
) -> i32
|
) -> i32
|
||||||
{
|
{
|
||||||
|
use keys::*;
|
||||||
const SPIN_RAMP_TIME: i32 = 30;
|
const SPIN_RAMP_TIME: i32 = 30;
|
||||||
let spin_f = 2.0 * (spin_speed + 1) as f32 / SPIN_RAMP_TIME as f32;
|
let spin_f = 2.0 * (spin_speed + 1) as f32 / SPIN_RAMP_TIME as f32;
|
||||||
let spin_f = spin_f.to_radians ();
|
let spin_f = spin_f.to_radians ();
|
||||||
|
@ -166,6 +175,12 @@ impl ControllerState {
|
||||||
//println! ("spin_f {}, Quat {:?}", spin_f, delta);
|
//println! ("spin_f {}, Quat {:?}", spin_f, delta);
|
||||||
|
|
||||||
if delta == Quat::default () {
|
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
|
0
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -207,17 +222,19 @@ struct Airplane {
|
||||||
struct FlightState {
|
struct FlightState {
|
||||||
airplane: Airplane,
|
airplane: Airplane,
|
||||||
spin_speed: i32,
|
spin_speed: i32,
|
||||||
|
arrows: Vec <Arrow>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FlightState {
|
impl Default for FlightState {
|
||||||
fn default () -> Self {
|
fn default () -> Self {
|
||||||
Self {
|
Self {
|
||||||
airplane: Airplane {
|
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 (),
|
vel: (0.0, 0.0, 0.0).into (),
|
||||||
ori: Default::default (),
|
ori: Default::default (),
|
||||||
},
|
},
|
||||||
spin_speed: 0,
|
spin_speed: 0,
|
||||||
|
arrows: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -230,54 +247,86 @@ impl FlightState {
|
||||||
pub fn step (&mut self, controller: &ControllerState) {
|
pub fn step (&mut self, controller: &ControllerState) {
|
||||||
self.spin_speed = controller.control_quat (&mut self.airplane.ori, self.spin_speed);
|
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;
|
let throttle = 1.0 + (controller.trigger_right as f32 - controller.trigger_left as f32) / 32767.0;
|
||||||
|
|
||||||
// Forces
|
let airplane = &mut self.airplane;
|
||||||
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 turbulent_dir = Vec3::from ((0.5 * object_space_dir.x (), 0.0, object_space_dir.z ()));
|
let microsteps = 4;
|
||||||
let turbulent_drag = -speed * speed * airplane.ori.mul_vec3 (turbulent_dir);
|
|
||||||
|
|
||||||
let air_drag = laminar_drag + turbulent_drag;
|
for microstep in 0..microsteps {
|
||||||
|
// Info
|
||||||
// Accumulate forces and run an Euler integration step
|
let nose = airplane.ori.mul_vec3 ((0.0, 1.0, 0.0).into ());
|
||||||
let dt = 1.0 / 60.0;
|
let speed = airplane.vel.length ();
|
||||||
airplane.vel += dt * (thrust + gravity + air_drag);
|
// Different from nose since planes are always drifting
|
||||||
airplane.pos += dt * airplane.vel;
|
let direction = if speed == 0.0 {
|
||||||
|
Vec3::from ((0.0, 0.0, 0.0))
|
||||||
if airplane.pos.z () < 0.0 {
|
}
|
||||||
airplane.vel.set_z (0.0);
|
else {
|
||||||
airplane.pos.set_z (0.0);
|
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 ()))
|
Vec3::from ((v4.x (), v4.y (), v4.z ()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive (Clone)]
|
||||||
struct Arrow {
|
struct Arrow {
|
||||||
origin: Vec3,
|
origin: Vec3,
|
||||||
direction: Vec3,
|
direction: Vec3,
|
||||||
|
@ -667,7 +717,7 @@ impl GameGraphics {
|
||||||
flags: hashmap! {
|
flags: hashmap! {
|
||||||
gl::CULL_FACE => true,
|
gl::CULL_FACE => true,
|
||||||
gl::DEPTH_TEST => true,
|
gl::DEPTH_TEST => true,
|
||||||
gl::TEXTURE_2D => true,
|
gl::TEXTURE_2D => false,
|
||||||
gl::STENCIL_TEST => false,
|
gl::STENCIL_TEST => false,
|
||||||
},
|
},
|
||||||
front_face: Some (FrontFace::Cw),
|
front_face: Some (FrontFace::Cw),
|
||||||
|
@ -911,7 +961,7 @@ impl GameGraphics {
|
||||||
let unis = &shader_vars.unis;
|
let unis = &shader_vars.unis;
|
||||||
|
|
||||||
glezz::uniform_3fv (unis [&MIN_BRIGHT], &black);
|
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 () {
|
for arrow in arrows.iter () {
|
||||||
let mvp = view_mat * arrow.model_mat;
|
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| {
|
let renderable_arrows: Vec <_> = arrows.iter ().map (|arrow| {
|
||||||
|
|
Loading…
Reference in New Issue