mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Initial work for gliding lift, updraft, thermals
This commit is contained in:
parent
3ac2729b99
commit
761953513b
@ -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"
|
||||
|
@ -141,6 +141,10 @@ pub struct TerrainChunkMeta {
|
||||
contains_cave: bool,
|
||||
contains_river: bool,
|
||||
river_velocity: Vec3<f32>,
|
||||
downhill_chunk: Option<Vec2<i32>>,
|
||||
gradient: Option<f32>,
|
||||
rockiness: f32,
|
||||
cliff_height: f32,
|
||||
temp: f32,
|
||||
humidity: f32,
|
||||
site: Option<SiteKindMeta>,
|
||||
@ -162,6 +166,10 @@ impl TerrainChunkMeta {
|
||||
temp: f32,
|
||||
humidity: f32,
|
||||
site: Option<SiteKindMeta>,
|
||||
downhill_chunk: Option<Vec2<i32>>,
|
||||
gradient: Option<f32>,
|
||||
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<SiteKindMeta> { 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<i32>, sprite_cfg: SpriteCfg) {
|
||||
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
|
||||
|
@ -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<Rider>>,
|
||||
is_volume_ridings: ReadStorage<'a, Is<VolumeRider>>,
|
||||
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<Read<'a, WeatherGrid>>,
|
||||
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<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");
|
||||
// NOTE: keep in sync with join below
|
||||
(
|
||||
|
@ -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() });
|
||||
});
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user