Gliding Debug Info

This commit is contained in:
Illia Denysenko 2021-08-07 13:19:58 +00:00 committed by Marcel
parent fab5c54253
commit 0858279810
4 changed files with 147 additions and 55 deletions

View File

@ -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>) -> f32 {

View File

@ -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;

View File

@ -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<comp::Pos>,
pub velocity: Option<comp::Vel>,
pub ori: Option<comp::Ori>,
pub character_state: Option<comp::CharacterState>,
pub look_dir: Dir,
pub in_fluid: Option<comp::Fluid>,
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!(
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.0.x,
velocity.0.y,
velocity.0.z,
velocity.0.magnitude()
),
None => "Player has no Vel component".to_owned(),
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<comp::Fluid>,
velocity: Option<comp::Vel>,
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()
}
}

View File

@ -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::<Pos>().get(entity).cloned();
let velocity = ecs.read_storage::<Vel>().get(entity).cloned();
let ori = ecs.read_storage::<comp::Ori>().get(entity).cloned();
let look_dir = self.inputs.look_dir;
let in_fluid = ecs
.read_storage::<comp::PhysicsState>()
.get(entity)
.and_then(|state| state.in_fluid);
let character_state = ecs
.read_storage::<comp::CharacterState>()
.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::<Pos>()
.get(self.client.borrow().entity())
.cloned(),
velocity: self
.client
.borrow()
.state()
.ecs()
.read_storage::<Vel>()
.get(self.client.borrow().entity())
.cloned(),
ori: self
.client
.borrow()
.state()
.ecs()
.read_storage::<comp::Ori>()
.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,6 +1019,7 @@ 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.