diff --git a/.cargo/config b/.cargo/config index 87d684cbbf..7409c9c2cf 100644 --- a/.cargo/config +++ b/.cargo/config @@ -32,3 +32,6 @@ dbg-voxygen = "run --bin veloren-voxygen --profile debuginfo" 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-clippy2 = "clippy -p veloren-voxygen --locked --no-default-features --features=default-publish" + +[target.x86_64-pc-windows-msvc] +linker = "rust-lld.exe" diff --git a/common/src/terrain/mod.rs b/common/src/terrain/mod.rs index 5e6e0fd8e6..3c9f57dfcf 100644 --- a/common/src/terrain/mod.rs +++ b/common/src/terrain/mod.rs @@ -141,6 +141,10 @@ pub struct TerrainChunkMeta { contains_cave: bool, contains_river: bool, river_velocity: Vec3, + downhill_chunk: Option>, + gradient: Option, + rockiness: f32, + cliff_height: f32, temp: f32, humidity: f32, site: Option, @@ -162,6 +166,10 @@ impl TerrainChunkMeta { temp: f32, humidity: f32, site: Option, + downhill_chunk: Option>, + gradient: Option, + rockiness: f32, + cliff_height: f32, ) -> Self { Self { name, @@ -178,6 +186,10 @@ impl TerrainChunkMeta { debug_points: Vec::new(), debug_lines: Vec::new(), sprite_cfgs: HashMap::default(), + downhill_chunk, + rockiness, + cliff_height, + gradient, } } @@ -197,6 +209,10 @@ impl TerrainChunkMeta { debug_points: Vec::new(), debug_lines: Vec::new(), 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 } + /// Altitude in blocks pub fn alt(&self) -> f32 { self.alt } pub fn tree_density(&self) -> f32 { self.tree_density } @@ -216,6 +233,7 @@ impl TerrainChunkMeta { pub fn site(&self) -> Option { self.site } + /// Temperature from 0 to 1 (possibly -1 to 1) pub fn temp(&self) -> f32 { self.temp } pub fn humidity(&self) -> f32 { self.humidity } @@ -239,6 +257,14 @@ impl TerrainChunkMeta { pub fn set_sprite_cfg_at(&mut self, rpos: Vec3, sprite_cfg: SpriteCfg) { self.sprite_cfgs.insert(rpos, sprite_cfg); } + + pub fn downhill_chunk(&self) -> Option> { self.downhill_chunk } + + pub fn gradient(&self) -> Option { self.gradient } + + pub fn rockiness(&self) -> f32 { self.rockiness } + + pub fn cliff_height(&self) -> f32 { self.cliff_height } } // Terrain type aliases diff --git a/common/systems/src/phys.rs b/common/systems/src/phys.rs index 89377b3f6a..975e153fb9 100644 --- a/common/systems/src/phys.rs +++ b/common/systems/src/phys.rs @@ -12,22 +12,25 @@ use common::{ link::Is, mounting::{Rider, VolumeRider}, outcome::Outcome, - resources::{DeltaTime, GameMode}, + resources::{DeltaTime, GameMode, TimeOfDay}, states, - terrain::{Block, BlockKind, TerrainGrid}, + terrain::{Block, BlockKind, CoordinateConversions, TerrainGrid, NEIGHBOR_DELTA}, + time::DayPeriod, uid::Uid, util::{Projection, SpatialGrid}, vol::{BaseVol, ReadVol}, + weather::WeatherGrid, }; use common_base::{prof_span, span}; use common_ecs::{Job, Origin, ParMode, Phase, PhysicsMetrics, System}; +use itertools::Itertools; use rayon::iter::ParallelIterator; use specs::{ shred, Entities, Entity, Join, LendJoin, ParJoin, Read, ReadExpect, ReadStorage, SystemData, Write, WriteExpect, WriteStorage, }; 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 /// where it is assumed that any unsubmersed part is is air. @@ -150,11 +153,12 @@ pub struct PhysicsRead<'a> { is_ridings: ReadStorage<'a, Is>, is_volume_ridings: ReadStorage<'a, Is>, projectiles: ReadStorage<'a, Projectile>, - char_states: ReadStorage<'a, CharacterState>, - bodies: ReadStorage<'a, Body>, character_states: ReadStorage<'a, CharacterState>, + bodies: ReadStorage<'a, Body>, densities: ReadStorage<'a, Density>, stats: ReadStorage<'a, Stats>, + weather: Option>, + time_of_day: Read<'a, TimeOfDay>, } #[derive(SystemData)] @@ -236,7 +240,7 @@ impl<'a> PhysicsData<'a> { &mut self.write.previous_phys_cache, &self.read.colliders, self.read.scales.maybe(), - self.read.char_states.maybe(), + self.read.character_states.maybe(), ) .join() { @@ -369,7 +373,7 @@ impl<'a> PhysicsData<'a> { // moving whether it should interact into the collider component // or into a separate component. read.projectiles.maybe(), - read.char_states.maybe(), + read.character_states.maybe(), ) .par_join() .map_init( @@ -438,7 +442,7 @@ impl<'a> PhysicsData<'a> { previous_cache, mass, collider, - read.char_states.get(entity), + read.character_states.get(entity), read.is_ridings.get(entity), )) }) @@ -601,6 +605,154 @@ impl<'a> PhysicsData<'a> { ref mut write, } = 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 = 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 = -(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"); // NOTE: keep in sync with join below ( diff --git a/voxygen/egui/src/lib.rs b/voxygen/egui/src/lib.rs index 9ac3d7642b..5a8a279e38 100644 --- a/voxygen/egui/src/lib.rs +++ b/voxygen/egui/src/lib.rs @@ -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, "Touching Entities", physics_state.touch_entities.len().to_string()); two_col_row(ui, "In Fluid", match physics_state.in_fluid { - - Some(Fluid::Air { elevation, .. }) => format!("Air (Elevation: {:.1})", elevation), + 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::Liquid { depth, kind, .. }) => format!("{:?} (Depth: {:.1})", kind, depth), _ => "None".to_owned() }); }); diff --git a/world/src/lib.rs b/world/src/lib.rs index 95a7fb9b3f..f38fa19fe4 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -390,7 +390,24 @@ impl World { .get_origin() .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);