mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Simplified replacement drag logic, special-case volume entities to improve movement
This commit is contained in:
parent
7b4bb2de99
commit
610d1d8497
@ -69,9 +69,9 @@ impl Body {
|
||||
match self {
|
||||
Body::DefaultAirship | Body::Volume => Vec3::new(25.0, 50.0, 40.0),
|
||||
Body::AirBalloon => Vec3::new(25.0, 50.0, 40.0),
|
||||
Body::SailBoat => Vec3::new(12.0, 17.0, 6.0),
|
||||
Body::Galleon => Vec3::new(14.0, 32.0, 10.0),
|
||||
Body::Skiff => Vec3::new(7.0, 32.0, 10.0),
|
||||
Body::SailBoat => Vec3::new(12.0, 32.0, 6.0),
|
||||
Body::Galleon => Vec3::new(14.0, 48.0, 10.0),
|
||||
Body::Skiff => Vec3::new(7.0, 15.0, 10.0),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -211,81 +211,11 @@ impl Body {
|
||||
}
|
||||
}
|
||||
|
||||
/// Physically incorrect (but relatively dt-independent) way to calculate drag coefficients for liquids.
|
||||
/// Physically incorrect (but relatively dt-independent) way to calculate
|
||||
/// drag coefficients for liquids.
|
||||
// TODO: Remove this in favour of `aerodynamic_forces` (see: `integrate_forces`)
|
||||
pub fn drag_coefficient_liquid(
|
||||
&self,
|
||||
rel_flow: &Vel,
|
||||
fluid_density: f32,
|
||||
wings: Option<&Wings>,
|
||||
scale: f32,
|
||||
) -> Vec3<f32> {
|
||||
let v_sq = rel_flow.0.magnitude_squared();
|
||||
let rel_flow_dir = Dir::new(rel_flow.0 / v_sq.sqrt());
|
||||
0.5 * fluid_density
|
||||
* match wings {
|
||||
Some(&Wings {
|
||||
aspect_ratio,
|
||||
planform_area,
|
||||
ori,
|
||||
}) => {
|
||||
if aspect_ratio > 25.0 {
|
||||
tracing::warn!(
|
||||
"Calculating lift for wings with an aspect ratio of {}. The \
|
||||
formulas are only valid for aspect ratios below 25.",
|
||||
aspect_ratio
|
||||
)
|
||||
};
|
||||
let ar = aspect_ratio.min(24.0);
|
||||
// We have an elliptical wing; proceed to calculate its lift and drag
|
||||
|
||||
// aoa will be positive when we're pitched up and negative otherwise
|
||||
let aoa = angle_of_attack(&ori, &rel_flow_dir);
|
||||
// c_l will be positive when aoa is positive (we have positive lift,
|
||||
// producing an upward force) and negative otherwise
|
||||
let c_l = lift_coefficient(ar, aoa);
|
||||
|
||||
// lift dir will be orthogonal to the local relative flow vector.
|
||||
// Local relative flow is the resulting vector of (relative) freestream
|
||||
// flow + downwash (created by the vortices
|
||||
// of the wing tips)
|
||||
let lift_dir: Dir = {
|
||||
// induced angle of attack
|
||||
let aoa_i = c_l / (PI * ar);
|
||||
// effective angle of attack; the aoa as seen by aerofoil after
|
||||
// downwash
|
||||
let aoa_eff = aoa - aoa_i;
|
||||
// Angle between chord line and local relative wind is aoa_eff
|
||||
// radians. Direction of lift is
|
||||
// perpendicular to local relative wind.
|
||||
// At positive lift, local relative wind will be below our cord line
|
||||
// at an angle of aoa_eff. Thus if
|
||||
// we pitch down by aoa_eff radians then
|
||||
// our chord line will be colinear with local relative wind vector
|
||||
// and our up will be the direction
|
||||
// of lift.
|
||||
ori.pitched_down(aoa_eff).up()
|
||||
};
|
||||
|
||||
// induced drag coefficient (drag due to lift)
|
||||
let cdi = {
|
||||
// Oswald's efficiency factor (empirically derived--very magical)
|
||||
// (this definition should not be used for aspect ratios > 25)
|
||||
let e = 1.78 * (1.0 - 0.045 * ar.powf(0.68)) - 0.64;
|
||||
c_l.powi(2) / (PI * e * ar)
|
||||
};
|
||||
|
||||
// drag coefficient
|
||||
let c_d = zero_lift_drag_coefficient() + cdi;
|
||||
debug_assert!(c_d.is_sign_positive());
|
||||
debug_assert!(c_l.is_sign_positive() || aoa.is_sign_negative());
|
||||
|
||||
planform_area * scale.powf(2.0) * (c_l * *lift_dir + c_d * *rel_flow_dir)
|
||||
+ self.parasite_drag(scale).powf(0.75) * *rel_flow_dir
|
||||
},
|
||||
|
||||
_ => self.parasite_drag(scale).powf(0.75) * *rel_flow_dir,
|
||||
}
|
||||
pub fn drag_coefficient_liquid(&self, fluid_density: f32, scale: f32) -> f32 {
|
||||
fluid_density * self.parasite_drag(scale)
|
||||
}
|
||||
|
||||
/// Parasite drag is the sum of pressure drag and skin friction.
|
||||
|
@ -220,8 +220,8 @@ impl Body {
|
||||
quadruped_low::Species::Mossdrake => 1.7,
|
||||
_ => 2.0,
|
||||
},
|
||||
Body::Ship(ship) if ship.has_water_thrust() => 0.35,
|
||||
Body::Ship(_) => 0.12,
|
||||
Body::Ship(ship) if ship.has_water_thrust() => 5.0 / self.dimensions().y,
|
||||
Body::Ship(_) => 6.0 / self.dimensions().y,
|
||||
Body::Arthropod(_) => 3.5,
|
||||
}
|
||||
}
|
||||
@ -236,15 +236,15 @@ impl Body {
|
||||
match self {
|
||||
Body::Object(_) => return None,
|
||||
Body::ItemDrop(_) => return None,
|
||||
Body::Ship(ship) if ship.has_water_thrust() => 350.0 * self.mass().0,
|
||||
Body::Ship(ship) if ship.has_water_thrust() => 500.0 * self.mass().0,
|
||||
Body::Ship(_) => return None,
|
||||
Body::BipedLarge(_) => 120.0 * self.mass().0,
|
||||
Body::Golem(_) => 100.0 * self.mass().0,
|
||||
Body::BipedSmall(_) => 1000.0 * self.mass().0,
|
||||
Body::BirdMedium(_) => 400.0 * self.mass().0,
|
||||
Body::BirdLarge(_) => 400.0 * self.mass().0,
|
||||
Body::FishMedium(_) => 900.0 * self.mass().0,
|
||||
Body::FishSmall(_) => 1000.0 * self.mass().0,
|
||||
Body::FishMedium(_) => 200.0 * self.mass().0,
|
||||
Body::FishSmall(_) => 300.0 * self.mass().0,
|
||||
Body::Dragon(_) => 50.0 * self.mass().0,
|
||||
// Humanoids are a bit different: we try to give them thrusts that result in similar
|
||||
// speeds for gameplay reasons
|
||||
@ -844,7 +844,7 @@ pub fn handle_climb(data: &JoinData<'_>, update: &mut StateUpdate) -> bool {
|
||||
.map(|depth| depth > 1.0)
|
||||
.unwrap_or(false)
|
||||
//&& update.vel.0.z < 0.0
|
||||
// *All* entities can climb when in liquids, to let them get out of
|
||||
// *All* entities can climb when in liquids, to let them climb out near the surface
|
||||
&& (data.body.can_climb() || data.physics.in_liquid().is_some())
|
||||
&& update.energy.current() > 1.0
|
||||
{
|
||||
|
@ -11,7 +11,7 @@ use common::{
|
||||
link::Is,
|
||||
mounting::{Rider, VolumeRider},
|
||||
outcome::Outcome,
|
||||
resources::DeltaTime,
|
||||
resources::{DeltaTime, GameMode},
|
||||
states,
|
||||
terrain::{Block, BlockKind, TerrainGrid},
|
||||
uid::Uid,
|
||||
@ -62,20 +62,23 @@ fn integrate_forces(
|
||||
// Aerodynamic/hydrodynamic forces
|
||||
if !rel_flow.0.is_approx_zero() {
|
||||
debug_assert!(!rel_flow.0.map(|a| a.is_nan()).reduce_or());
|
||||
// HACK: We should really use the latter logic (i.e: `aerodynamic_forces`) for liquids, but it results in
|
||||
// pretty serious dt-dependent problems that are extremely difficult to resolve. This is a compromise: for
|
||||
// liquids only, we calculate drag using an incorrect (but still visually plausible) model that is much more
|
||||
// resistant to differences in dt. Because it's physically incorrect anyway, there are magic coefficients that
|
||||
// HACK: We should really use the latter logic (i.e: `aerodynamic_forces`) for
|
||||
// liquids, but it results in pretty serious dt-dependent problems that
|
||||
// are extremely difficult to resolve. This is a compromise: for liquids
|
||||
// only, we calculate drag using an incorrect (but still visually plausible)
|
||||
// model that is much more resistant to differences in dt. Because it's
|
||||
// physically incorrect anyway, there are magic coefficients that
|
||||
// exist simply to get us closer to what water 'should' feel like.
|
||||
if fluid.is_liquid() {
|
||||
let fric = body.drag_coefficient_liquid(
|
||||
&rel_flow,
|
||||
fluid_density.0,
|
||||
wings,
|
||||
scale.map_or(1.0, |s| s.0),
|
||||
).magnitude() * 0.02;
|
||||
let fric = body
|
||||
.drag_coefficient_liquid(fluid_density.0, scale.map_or(1.0, |s| s.0))
|
||||
.powf(0.75)
|
||||
* 0.02;
|
||||
|
||||
vel.0 *= (1.0 / (1.0 + fric)).powf(dt.0 * 10.0);
|
||||
let fvel = fluid.flow_vel();
|
||||
|
||||
// Drag is relative to fluid velocity, so compensate before applying drag
|
||||
vel.0 = (vel.0 - fvel.0) * (1.0 / (1.0 + fric)).powf(dt.0 * 10.0) + fvel.0;
|
||||
} else {
|
||||
let impulse = dt.0
|
||||
* body.aerodynamic_forces(
|
||||
@ -132,6 +135,7 @@ pub struct PhysicsRead<'a> {
|
||||
terrain: ReadExpect<'a, TerrainGrid>,
|
||||
dt: Read<'a, DeltaTime>,
|
||||
event_bus: Read<'a, EventBus<ServerEvent>>,
|
||||
game_mode: ReadExpect<'a, GameMode>,
|
||||
scales: ReadStorage<'a, Scale>,
|
||||
stickies: ReadStorage<'a, Sticky>,
|
||||
immovables: ReadStorage<'a, Immovable>,
|
||||
@ -667,6 +671,12 @@ impl<'a> PhysicsData<'a> {
|
||||
if in_loaded_chunk
|
||||
// And not already stuck on a block (e.g., for arrows)
|
||||
&& !(physics_state.on_surface().is_some() && sticky.is_some())
|
||||
// HACK: Special-case boats. Experimentally, clients are *bad* at making guesses about movement,
|
||||
// and this is a particular problem for volume entities since careful control of velocity is
|
||||
// required for nice movement of entities on top of the volume. Special-case volume entities here
|
||||
// to prevent weird drag/gravity guesses messing with movement, relying on the client's hermite
|
||||
// interpolation instead.
|
||||
&& !(matches!(body, Body::Ship(_)) && matches!(&*read.game_mode, GameMode::Client))
|
||||
{
|
||||
// Clamp dt to an effective 10 TPS, to prevent gravity
|
||||
// from slamming the players into the floor when
|
||||
@ -1748,7 +1758,9 @@ fn box_voxel_collision<T: BaseVol<Vox = Block> + ReadVol>(
|
||||
(old_depth + old_pos.z - pos.0.z).max(depth)
|
||||
});
|
||||
|
||||
physics_state.ground_vel = ground_vel;
|
||||
if depth > 0.0 {
|
||||
physics_state.ground_vel = ground_vel;
|
||||
}
|
||||
|
||||
Fluid::Liquid {
|
||||
kind,
|
||||
|
@ -1651,13 +1651,7 @@ fn handle_spawn_airship(
|
||||
let ori = comp::Ori::from(common::util::Dir::new(dir.unwrap_or(Vec3::unit_y())));
|
||||
let mut builder = server
|
||||
.state
|
||||
.create_ship(pos, ori, ship, |ship| ship.make_collider())
|
||||
.with(LightEmitter {
|
||||
col: Rgb::new(1.0, 0.65, 0.2),
|
||||
strength: 2.0,
|
||||
flicker: 1.0,
|
||||
animated: true,
|
||||
});
|
||||
.create_ship(pos, ori, ship, |ship| ship.make_collider());
|
||||
if let Some(pos) = destination {
|
||||
let (kp, ki, kd) =
|
||||
comp::agent::pid_coefficients(&comp::Body::Ship(ship)).unwrap_or((1.0, 0.0, 0.0));
|
||||
@ -1695,13 +1689,7 @@ fn handle_spawn_ship(
|
||||
let ori = comp::Ori::from(common::util::Dir::new(dir.unwrap_or(Vec3::unit_y())));
|
||||
let mut builder = server
|
||||
.state
|
||||
.create_ship(pos, ori, ship, |ship| ship.make_collider())
|
||||
.with(LightEmitter {
|
||||
col: Rgb::new(1.0, 0.65, 0.2),
|
||||
strength: 2.0,
|
||||
flicker: 1.0,
|
||||
animated: true,
|
||||
});
|
||||
.create_ship(pos, ori, ship, |ship| ship.make_collider());
|
||||
if let Some(pos) = destination {
|
||||
let (kp, ki, kd) =
|
||||
comp::agent::pid_coefficients(&comp::Body::Ship(ship)).unwrap_or((1.0, 0.0, 0.0));
|
||||
|
Loading…
Reference in New Issue
Block a user