Initial work for gliding lift, updraft, thermals

This commit is contained in:
laundmo 2023-06-08 17:28:07 +02:00 committed by juliancoffee
parent 3ac2729b99
commit 761953513b
5 changed files with 208 additions and 11 deletions

View File

@ -32,3 +32,6 @@ dbg-voxygen = "run --bin veloren-voxygen --profile debuginfo"
swarm = "run --bin swarm --features client/bin_bot,client/tick_network --" swarm = "run --bin swarm --features client/bin_bot,client/tick_network --"
ci-clippy = "clippy --all-targets --locked --features=bin_cmd_doc_gen,bin_compression,bin_csv,bin_graphviz,bin_bot,bin_asset_migrate,asset_tweak,bin,stat" ci-clippy = "clippy --all-targets --locked --features=bin_cmd_doc_gen,bin_compression,bin_csv,bin_graphviz,bin_bot,bin_asset_migrate,asset_tweak,bin,stat"
ci-clippy2 = "clippy -p veloren-voxygen --locked --no-default-features --features=default-publish" ci-clippy2 = "clippy -p veloren-voxygen --locked --no-default-features --features=default-publish"
[target.x86_64-pc-windows-msvc]
linker = "rust-lld.exe"

View File

@ -141,6 +141,10 @@ pub struct TerrainChunkMeta {
contains_cave: bool, contains_cave: bool,
contains_river: bool, contains_river: bool,
river_velocity: Vec3<f32>, river_velocity: Vec3<f32>,
downhill_chunk: Option<Vec2<i32>>,
gradient: Option<f32>,
rockiness: f32,
cliff_height: f32,
temp: f32, temp: f32,
humidity: f32, humidity: f32,
site: Option<SiteKindMeta>, site: Option<SiteKindMeta>,
@ -162,6 +166,10 @@ impl TerrainChunkMeta {
temp: f32, temp: f32,
humidity: f32, humidity: f32,
site: Option<SiteKindMeta>, site: Option<SiteKindMeta>,
downhill_chunk: Option<Vec2<i32>>,
gradient: Option<f32>,
rockiness: f32,
cliff_height: f32,
) -> Self { ) -> Self {
Self { Self {
name, name,
@ -178,6 +186,10 @@ impl TerrainChunkMeta {
debug_points: Vec::new(), debug_points: Vec::new(),
debug_lines: Vec::new(), debug_lines: Vec::new(),
sprite_cfgs: HashMap::default(), sprite_cfgs: HashMap::default(),
downhill_chunk,
rockiness,
cliff_height,
gradient,
} }
} }
@ -197,6 +209,10 @@ impl TerrainChunkMeta {
debug_points: Vec::new(), debug_points: Vec::new(),
debug_lines: Vec::new(), debug_lines: Vec::new(),
sprite_cfgs: HashMap::default(), sprite_cfgs: HashMap::default(),
downhill_chunk: None,
gradient: None,
rockiness: 0.0,
cliff_height: 0.0,
} }
} }
@ -204,6 +220,7 @@ impl TerrainChunkMeta {
pub fn biome(&self) -> BiomeKind { self.biome } pub fn biome(&self) -> BiomeKind { self.biome }
/// Altitude in blocks
pub fn alt(&self) -> f32 { self.alt } pub fn alt(&self) -> f32 { self.alt }
pub fn tree_density(&self) -> f32 { self.tree_density } pub fn tree_density(&self) -> f32 { self.tree_density }
@ -216,6 +233,7 @@ impl TerrainChunkMeta {
pub fn site(&self) -> Option<SiteKindMeta> { self.site } pub fn site(&self) -> Option<SiteKindMeta> { self.site }
/// Temperature from 0 to 1 (possibly -1 to 1)
pub fn temp(&self) -> f32 { self.temp } pub fn temp(&self) -> f32 { self.temp }
pub fn humidity(&self) -> f32 { self.humidity } pub fn humidity(&self) -> f32 { self.humidity }
@ -239,6 +257,14 @@ impl TerrainChunkMeta {
pub fn set_sprite_cfg_at(&mut self, rpos: Vec3<i32>, sprite_cfg: SpriteCfg) { pub fn set_sprite_cfg_at(&mut self, rpos: Vec3<i32>, sprite_cfg: SpriteCfg) {
self.sprite_cfgs.insert(rpos, sprite_cfg); self.sprite_cfgs.insert(rpos, sprite_cfg);
} }
pub fn downhill_chunk(&self) -> Option<Vec2<i32>> { self.downhill_chunk }
pub fn gradient(&self) -> Option<f32> { self.gradient }
pub fn rockiness(&self) -> f32 { self.rockiness }
pub fn cliff_height(&self) -> f32 { self.cliff_height }
} }
// Terrain type aliases // Terrain type aliases

View File

@ -12,22 +12,25 @@ use common::{
link::Is, link::Is,
mounting::{Rider, VolumeRider}, mounting::{Rider, VolumeRider},
outcome::Outcome, outcome::Outcome,
resources::{DeltaTime, GameMode}, resources::{DeltaTime, GameMode, TimeOfDay},
states, states,
terrain::{Block, BlockKind, TerrainGrid}, terrain::{Block, BlockKind, CoordinateConversions, TerrainGrid, NEIGHBOR_DELTA},
time::DayPeriod,
uid::Uid, uid::Uid,
util::{Projection, SpatialGrid}, util::{Projection, SpatialGrid},
vol::{BaseVol, ReadVol}, vol::{BaseVol, ReadVol},
weather::WeatherGrid,
}; };
use common_base::{prof_span, span}; use common_base::{prof_span, span};
use common_ecs::{Job, Origin, ParMode, Phase, PhysicsMetrics, System}; use common_ecs::{Job, Origin, ParMode, Phase, PhysicsMetrics, System};
use itertools::Itertools;
use rayon::iter::ParallelIterator; use rayon::iter::ParallelIterator;
use specs::{ use specs::{
shred, Entities, Entity, Join, LendJoin, ParJoin, Read, ReadExpect, ReadStorage, SystemData, shred, Entities, Entity, Join, LendJoin, ParJoin, Read, ReadExpect, ReadStorage, SystemData,
Write, WriteExpect, WriteStorage, Write, WriteExpect, WriteStorage,
}; };
use std::ops::Range; use std::ops::Range;
use vek::*; use vek::{num_traits::Signed, *};
/// The density of the fluid as a function of submersion ratio in given fluid /// The density of the fluid as a function of submersion ratio in given fluid
/// where it is assumed that any unsubmersed part is is air. /// where it is assumed that any unsubmersed part is is air.
@ -150,11 +153,12 @@ pub struct PhysicsRead<'a> {
is_ridings: ReadStorage<'a, Is<Rider>>, is_ridings: ReadStorage<'a, Is<Rider>>,
is_volume_ridings: ReadStorage<'a, Is<VolumeRider>>, is_volume_ridings: ReadStorage<'a, Is<VolumeRider>>,
projectiles: ReadStorage<'a, Projectile>, projectiles: ReadStorage<'a, Projectile>,
char_states: ReadStorage<'a, CharacterState>,
bodies: ReadStorage<'a, Body>,
character_states: ReadStorage<'a, CharacterState>, character_states: ReadStorage<'a, CharacterState>,
bodies: ReadStorage<'a, Body>,
densities: ReadStorage<'a, Density>, densities: ReadStorage<'a, Density>,
stats: ReadStorage<'a, Stats>, stats: ReadStorage<'a, Stats>,
weather: Option<Read<'a, WeatherGrid>>,
time_of_day: Read<'a, TimeOfDay>,
} }
#[derive(SystemData)] #[derive(SystemData)]
@ -236,7 +240,7 @@ impl<'a> PhysicsData<'a> {
&mut self.write.previous_phys_cache, &mut self.write.previous_phys_cache,
&self.read.colliders, &self.read.colliders,
self.read.scales.maybe(), self.read.scales.maybe(),
self.read.char_states.maybe(), self.read.character_states.maybe(),
) )
.join() .join()
{ {
@ -369,7 +373,7 @@ impl<'a> PhysicsData<'a> {
// moving whether it should interact into the collider component // moving whether it should interact into the collider component
// or into a separate component. // or into a separate component.
read.projectiles.maybe(), read.projectiles.maybe(),
read.char_states.maybe(), read.character_states.maybe(),
) )
.par_join() .par_join()
.map_init( .map_init(
@ -438,7 +442,7 @@ impl<'a> PhysicsData<'a> {
previous_cache, previous_cache,
mass, mass,
collider, collider,
read.char_states.get(entity), read.character_states.get(entity),
read.is_ridings.get(entity), read.is_ridings.get(entity),
)) ))
}) })
@ -601,6 +605,154 @@ impl<'a> PhysicsData<'a> {
ref mut write, ref mut write,
} = self; } = self;
prof_span!(guard, "Apply Weather");
if let Some(weather) = &read.weather {
let day_period: DayPeriod = read.time_of_day.0.into();
for (_, state, pos, phys) in (
&read.entities,
&read.character_states,
&write.positions,
&mut write.physics_states,
)
.join()
{
if !state.is_glide() {
continue;
}
prof_span!(guard, "Apply Weather INIT");
let chunk_pos: Vec2<i32> = pos.0.xy().wpos_to_cpos().as_();
let Some(current_chunk) = read.terrain.get_key(chunk_pos) else { continue; };
let meta = current_chunk.meta();
let interp_weather = weather.get_interpolated(pos.0.xy());
// TODO: use tree density to scale down surface wind
// TODO: use temperature to estimate updraft
// TODO: use slope to estimate updraft (wind direction goes up along slope)
// TODO: use biome to refine
// Weather sim wind
let above_ground = pos.0.z - meta.alt();
let wind_velocity = interp_weather.wind_vel();
let surrounding_chunks_metas = {
NEIGHBOR_DELTA
.iter()
.map(move |&(x, y)| chunk_pos + Vec2::new(x, y))
.filter(|pos| read.terrain.contains_key(*pos))
.filter_map(|cpos| read.terrain.get_key(cpos).map(|c| c.meta()))
};
drop(guard);
prof_span!(guard, "Apply Weather THERMALS");
// === THERMALS ===
let mut lift = dbg!(dbg!(meta.temp()) * 4.0);
let temperatures = surrounding_chunks_metas.clone().map(|m| m.temp()).minmax();
// more thermals if hot chunks border cold chunks
lift *= match temperatures {
itertools::MinMaxResult::NoElements
| itertools::MinMaxResult::OneElement(_) => 1.0,
itertools::MinMaxResult::MinMax(a, b) => 0.8 + ((a - b).abs() * 1.1),
}
.clamp(0.1, 2.0);
// way less thermals at after/night - long term heat.
lift *= match day_period {
DayPeriod::Night => 0.0,
DayPeriod::Morning => 0.9,
DayPeriod::Noon => 1.8,
DayPeriod::Evening => 1.2,
};
// way more thermals in strong rain (its often caused by strong thermals). less
// in weak rain or cloudy..
lift *= if interp_weather.rain.is_between(0.5, 1.0)
&& interp_weather.cloud.is_between(0.6, 1.0)
{
1.5
} else if interp_weather.rain.is_between(0.0, 0.5)
&& interp_weather.cloud.is_between(0.01, 1.0)
{
0.1
} else {
1.0
};
lift *= (above_ground / 60.).max(1.); // the first 60 blocks are weaker. starting from the ground should be difficult.
// biome thermals modifiers
use common::terrain::BiomeKind::*;
lift *= match meta.biome() {
Void => 0.0,
Lake => 2.0,
Ocean => 1.5,
Swamp => 0.8, // swamps are usually not hot
Taiga | Grassland | Savannah => 1.0,
Mountain => 0.8,
Snowland => 0.9,
Desert => 0.2, // deserts are too uniformly hot for nice thermal columns
Jungle | Forest => 0.5, // trees cool the air by evaporation
};
drop(guard);
prof_span!(guard, "Apply Weather RIDGE_WAVE");
// === Ridge/Wave lift ===
let mut ridge_lift = meta
.downhill_chunk()
.map(|cpos| {
let upwards: Vec2<f32> = -(chunk_pos.cpos_to_wpos_center().as_()
- cpos.cpos_to_wpos_center().as_());
match read.terrain.get_key(cpos).map(|c| c.meta()) {
Some(other_meta) => {
let wind = dbg!(
(upwards + wind_velocity)
.clamped(Vec2::zero(), Vec2::broadcast(1.0))
);
let vertical_distance =
((meta.alt() - other_meta.alt()) / 20.0).clamp(0.0, 14.0); // just guessing, 50 blocks seems like a decent max
if wind.magnitude_squared().is_positive() {
0.5 + dbg!(wind.magnitude()).clamp(0.0, 1.0)
+ dbg!(vertical_distance)
} else {
1.0
}
},
None => 0.0,
}
})
.unwrap_or(0.0);
dbg!(ridge_lift);
// Cliffs mean more lift
ridge_lift *= 0.9 + (meta.cliff_height() / 44.0) * 1.2; // 44 seems to be max, according to a lerp in WorldSim::generate_cliffs
// trees mean less lift
ridge_lift *= (1.0 - meta.tree_density()).max(0.7); // probably 0. to 1. src: SiteKind::is_suitable_loc comparisons
drop(guard);
// see: https://en.wikipedia.org/wiki/Lift_(soaring)
// ridge/wave lift:
// - along the ridge of a mountain if wind is blowing against and up it
// - around the tops of large mountains
// convergence zones:
// - where hot and cold meet.
// - could use biomes/temp difference between chunks to model this
let wind_vel = (wind_velocity, dbg!(lift) + dbg!(ridge_lift)).into();
phys.in_fluid = phys.in_fluid.map(|f| match f {
Fluid::Air { elevation, .. } => Fluid::Air {
vel: Vel(wind_vel),
elevation,
},
fluid => fluid,
});
}
}
drop(guard);
prof_span!(guard, "insert PosVelOriDefer"); prof_span!(guard, "insert PosVelOriDefer");
// NOTE: keep in sync with join below // NOTE: keep in sync with join below
( (

View File

@ -754,8 +754,7 @@ fn selected_entity_window(
two_col_row(ui, "On Wall", physics_state.on_wall.map_or("-".to_owned(), |x| format!("{:.1},{:.1},{:.1}", x.x, x.y, x.z ))); two_col_row(ui, "On Wall", physics_state.on_wall.map_or("-".to_owned(), |x| format!("{:.1},{:.1},{:.1}", x.x, x.y, x.z )));
two_col_row(ui, "Touching Entities", physics_state.touch_entities.len().to_string()); two_col_row(ui, "Touching Entities", physics_state.touch_entities.len().to_string());
two_col_row(ui, "In Fluid", match physics_state.in_fluid { two_col_row(ui, "In Fluid", match physics_state.in_fluid {
Some(Fluid::Air { elevation, vel, .. }) => format!("Air (Elevation: {:.1}), vel: ({:.1},{:.1},{:.1}) ({:.1} u/s)", elevation, vel.0.x, vel.0.y, vel.0.z, vel.0.magnitude()),
Some(Fluid::Air { elevation, .. }) => format!("Air (Elevation: {:.1})", elevation),
Some(Fluid::Liquid { depth, kind, .. }) => format!("{:?} (Depth: {:.1})", kind, depth), Some(Fluid::Liquid { depth, kind, .. }) => format!("{:?} (Depth: {:.1})", kind, depth),
_ => "None".to_owned() }); _ => "None".to_owned() });
}); });

View File

@ -390,7 +390,24 @@ impl World {
.get_origin() .get_origin()
.distance_squared(chunk_center_wpos2d) .distance_squared(chunk_center_wpos2d)
}) })
.map(|id| index.sites[*id].kind.convert_to_meta().unwrap_or_default()), .map(|id| index.sites[*id].kind.convert_to_meta().unwrap_or_default())
.or_else(|| sim_chunk.poi.map(|poi| self.civs.pois[poi].name.clone())),
sim_chunk.get_biome(),
sim_chunk.alt,
sim_chunk.tree_density,
sim_chunk.cave.1.alt != 0.0,
sim_chunk.river.is_river(),
sim_chunk.river.velocity,
sim_chunk.temp,
sim_chunk.humidity,
sim_chunk
.sites
.iter()
.find_map(|site| index.sites[*site].kind.convert_to_meta()),
sim_chunk.downhill.map(|e| e.wpos_to_cpos()),
self.sim.get_gradient_approx(chunk_pos),
sim_chunk.rockiness,
sim_chunk.cliff_height,
); );
let mut chunk = TerrainChunk::new(base_z, stone, air, meta); let mut chunk = TerrainChunk::new(base_z, stone, air, meta);