From 754fd681c039ba8fe6279d281ca3d2ee9005c6da Mon Sep 17 00:00:00 2001 From: _ <> Date: Wed, 11 Mar 2020 03:41:58 +0000 Subject: [PATCH] I guess it makes more sense with all-quadratic drag --- src/bin/pumpkin.rs | 170 +++++++++++++++++++++++++++++---------------- 1 file changed, 110 insertions(+), 60 deletions(-) diff --git a/src/bin/pumpkin.rs b/src/bin/pumpkin.rs index e992af5..0428ede 100644 --- a/src/bin/pumpkin.rs +++ b/src/bin/pumpkin.rs @@ -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 , + 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 , } 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| {