diff --git a/common/src/comp/fluid_dynamics.rs b/common/src/comp/fluid_dynamics.rs index 6e233545e3..72808cc505 100644 --- a/common/src/comp/fluid_dynamics.rs +++ b/common/src/comp/fluid_dynamics.rs @@ -243,8 +243,9 @@ impl Body { Body::BirdMedium(_) | Body::BirdLarge(_) | Body::Dragon(_) => { let dim = self.dimensions().map(|a| a * 0.5); let cd: f32 = match self { - // "Field Estimates of Body Drag Coefficient on the Basis of Dives in Passerine - // Birds", Anders Hedenström and Felix Liechti, 2001 + // "Field Estimates of Body Drag Coefficient + // on the Basis of Dives in Passerine Birds", + // Anders Hedenström and Felix Liechti, 2001 Body::BirdLarge(_) | Body::BirdMedium(_) => 0.2, // arbitrary _ => 0.7, @@ -370,7 +371,8 @@ pub fn zero_lift_drag_coefficient(planform_area: f32) -> f32 { planform_area * 0 /// freestream flow /// 2. up to around ~18°, at which point maximum lift has been achieved and /// thereafter falls precipitously, causing a stall (this is the stall -/// angle) 3. effective aoa, i.e. geometric aoa - induced aoa; assumes +/// angle) +/// 3. effective aoa, i.e. geometric aoa - induced aoa; assumes /// no sideslip // TODO: Look into handling tapered wings fn lift_slope(aspect_ratio: f32, sweep_angle: Option) -> f32 { diff --git a/common/systems/src/phys.rs b/common/systems/src/phys.rs index d5e294e75d..6255abc1bb 100644 --- a/common/systems/src/phys.rs +++ b/common/systems/src/phys.rs @@ -69,8 +69,8 @@ fn integrate_forces( // This way we can only ever lose velocity and will never experience a reverse // in direction from events such as falling into water at high velocities. if new_v.dot(vel.0) < 0.0 { - // Multiply by a factor to prevent full stop, as this can cause things to get - // stuck in high-density medium + // Multiply by a factor to prevent full stop, + // as this can cause things to get stuck in high-density medium vel.0 -= vel.0.projected(&impulse) * 0.9; } else { vel.0 = new_v; diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 342c94023c..a7ddfa52be 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -74,7 +74,7 @@ use client::Client; use common::{ combat, comp::{ - self, + self, fluid_dynamics, inventory::trade_pricing::TradePricing, item::{tool::ToolKind, ItemDesc, MaterialStatManifest, Quality}, skills::{Skill, SkillGroupKind}, @@ -86,7 +86,7 @@ use common::{ terrain::{SpriteKind, TerrainChunk}, trade::{ReducedInventory, TradeAction}, uid::Uid, - util::srgba_to_linear, + util::{srgba_to_linear, Dir}, vol::RectRasterableVol, }; use common_base::{prof_span, span}; @@ -233,7 +233,10 @@ widget_ids! { ping, coordinates, velocity, + glide_ratio, + glide_aoe, orientation, + look_direction, loaded_distance, time, entity_count, @@ -441,6 +444,9 @@ pub struct DebugInfo { pub coordinates: Option, pub velocity: Option, pub ori: Option, + pub character_state: Option, + pub look_dir: Dir, + pub in_fluid: Option, pub num_chunks: u32, pub num_lights: u32, pub num_visible_chunks: u32, @@ -2150,6 +2156,8 @@ impl Hud { } // Display debug window. + // TODO: + // Make it use i18n keys. if let Some(debug_info) = debug_info { prof_span!("debug info"); // Alpha Version @@ -2192,15 +2200,31 @@ impl Hud { .font_size(self.fonts.cyri.scale(14)) .set(self.ids.coordinates, ui_widgets); // Player's velocity - let velocity_text = match debug_info.velocity { - Some(velocity) => format!( - "Velocity: ({:.1}, {:.1}, {:.1}) [{:.1} u/s]", - velocity.0.x, - velocity.0.y, - velocity.0.z, - velocity.0.magnitude() - ), - None => "Player has no Vel component".to_owned(), + let (velocity_text, glide_ratio_text) = match debug_info.velocity { + Some(velocity) => { + let velocity = velocity.0; + let velocity_text = format!( + "Velocity: ({:.1}, {:.1}, {:.1}) [{:.1} u/s]", + velocity.x, + velocity.y, + velocity.z, + velocity.magnitude() + ); + let horizontal_velocity = velocity.xy().magnitude(); + let dz = velocity.z; + // don't divide by zero + let glide_ratio_text = if dz.abs() > 0.0001 { + format!("Glide Ratio: {:.1}", (-1.0) * (horizontal_velocity / dz)) + } else { + "Glide Ratio: Altitude is constant".to_owned() + }; + + (velocity_text, glide_ratio_text) + }, + None => { + let err = "Player has no Vel component"; + (err.to_owned(), err.to_owned()) + }, }; Text::new(&velocity_text) .color(TEXT_COLOR) @@ -2208,23 +2232,54 @@ impl Hud { .font_id(self.fonts.cyri.conrod_id) .font_size(self.fonts.cyri.scale(14)) .set(self.ids.velocity, ui_widgets); + Text::new(&glide_ratio_text) + .color(TEXT_COLOR) + .down_from(self.ids.velocity, 5.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(14)) + .set(self.ids.glide_ratio, ui_widgets); + let glide_angle_text = angle_of_attack_text( + debug_info.in_fluid, + debug_info.velocity, + debug_info.character_state.as_ref(), + ); + Text::new(&glide_angle_text) + .color(TEXT_COLOR) + .down_from(self.ids.glide_ratio, 5.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(14)) + .set(self.ids.glide_aoe, ui_widgets); // Player's orientation vector let orientation_text = match debug_info.ori { Some(ori) => { - let look_dir = ori.look_dir(); + let orientation = ori.look_dir(); format!( - "Orientation: ({:.1}, {:.1}, {:.1})", - look_dir.x, look_dir.y, look_dir.z, + "Orientation: ({:.2}, {:.2}, {:.2})", + orientation.x, orientation.y, orientation.z, ) }, None => "Player has no Ori component".to_owned(), }; Text::new(&orientation_text) .color(TEXT_COLOR) - .down_from(self.ids.velocity, 5.0) + .down_from(self.ids.glide_aoe, 5.0) .font_id(self.fonts.cyri.conrod_id) .font_size(self.fonts.cyri.scale(14)) .set(self.ids.orientation, ui_widgets); + let look_dir_text = { + let look_vec = debug_info.look_dir.to_vec(); + + format!( + "Look Direction: ({:.2}, {:.2}, {:.2})", + look_vec.x, look_vec.y, look_vec.z, + ) + }; + Text::new(&look_dir_text) + .color(TEXT_COLOR) + .down_from(self.ids.orientation, 5.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(14)) + .set(self.ids.look_direction, ui_widgets); // Loaded distance Text::new(&format!( "View distance: {:.2} blocks ({:.2} chunks)", @@ -2232,7 +2287,7 @@ impl Hud { client.loaded_distance() / TerrainChunk::RECT_SIZE.x as f32, )) .color(TEXT_COLOR) - .down_from(self.ids.orientation, 5.0) + .down_from(self.ids.look_direction, 5.0) .font_id(self.fonts.cyri.conrod_id) .font_size(self.fonts.cyri.scale(14)) .set(self.ids.loaded_distance, ui_widgets); @@ -4007,3 +4062,39 @@ pub fn get_buff_time(buff: BuffInfo) -> String { "".to_string() } } + +pub fn angle_of_attack_text( + fluid: Option, + velocity: Option, + character_state: Option<&comp::CharacterState>, +) -> String { + use comp::CharacterState; + + let glider_ori = if let Some(CharacterState::Glide(data)) = character_state { + data.ori + } else { + return "Angle of Attack: Not gliding".to_owned(); + }; + + let fluid = if let Some(fluid) = fluid { + fluid + } else { + return "Angle of Attack: Not in fluid".to_owned(); + }; + + let velocity = if let Some(velocity) = velocity { + velocity + } else { + return "Angle of Attack: Player has no vel component".to_owned(); + }; + let rel_flow = fluid.relative_flow(&velocity).0; + let v_sq = rel_flow.magnitude_squared(); + + if v_sq.abs() > 0.0001 { + let rel_flow_dir = Dir::new(rel_flow / v_sq.sqrt()); + let aoe = fluid_dynamics::angle_of_attack(&glider_ori, &rel_flow_dir); + format!("Angle of Attack: {:.1}", aoe.to_degrees()) + } else { + "Angle of Attack: Not moving".to_owned() + } +} diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs index 23c8bed695..07114633fb 100644 --- a/voxygen/src/session/mod.rs +++ b/voxygen/src/session/mod.rs @@ -583,9 +583,11 @@ impl PlayState for SessionState { } }, GameInput::Fly => { - // Not sure where to put comment, but I noticed when testing flight - // Syncing of inputs between mounter and mountee broke with - // controller change + // Not sure where to put comment, but I noticed + // when testing flight. + // + // Syncing of inputs between mounter and mountee + // broke with controller change self.key_state.fly ^= state; let mut client = self.client.borrow_mut(); client.handle_input( @@ -978,40 +980,36 @@ impl PlayState for SessionState { .camera_mut() .compute_dependents(&*self.client.borrow().state().terrain()); - // Generate debug info, if needed (it iterates through enough data that we might + // Generate debug info, if needed + // (it iterates through enough data that we might // as well avoid it unless we need it). - let debug_info = global_state - .settings - .interface - .toggle_debug - .then(|| DebugInfo { + let debug_info = global_state.settings.interface.toggle_debug.then(|| { + let client = self.client.borrow(); + let ecs = client.state().ecs(); + let entity = client.entity(); + let coordinates = ecs.read_storage::().get(entity).cloned(); + let velocity = ecs.read_storage::().get(entity).cloned(); + let ori = ecs.read_storage::().get(entity).cloned(); + let look_dir = self.inputs.look_dir; + let in_fluid = ecs + .read_storage::() + .get(entity) + .and_then(|state| state.in_fluid); + let character_state = ecs + .read_storage::() + .get(entity) + .cloned(); + + DebugInfo { tps: global_state.clock.stats().average_tps, frame_time: global_state.clock.stats().average_busy_dt, ping_ms: self.client.borrow().get_ping_ms_rolling_avg(), - coordinates: self - .client - .borrow() - .state() - .ecs() - .read_storage::() - .get(self.client.borrow().entity()) - .cloned(), - velocity: self - .client - .borrow() - .state() - .ecs() - .read_storage::() - .get(self.client.borrow().entity()) - .cloned(), - ori: self - .client - .borrow() - .state() - .ecs() - .read_storage::() - .get(self.client.borrow().entity()) - .cloned(), + coordinates, + velocity, + ori, + look_dir, + character_state, + in_fluid, num_chunks: self.scene.terrain().chunk_count() as u32, num_lights: self.scene.lights().len() as u32, num_visible_chunks: self.scene.terrain().visible_chunk_count() as u32, @@ -1021,7 +1019,8 @@ impl PlayState for SessionState { num_particles: self.scene.particle_mgr().particle_count() as u32, num_particles_visible: self.scene.particle_mgr().particle_count_visible() as u32, - }); + } + }); // Extract HUD events ensuring the client borrow gets dropped. let mut hud_events = self.hud.maintain(