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 --"
|
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"
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
(
|
(
|
||||||
|
@ -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() });
|
||||||
});
|
});
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user