veloren_common_systems/phys/
weather.rs1use common::{
2 comp::Pos,
3 resources::TimeOfDay,
4 terrain::{CoordinateConversions, NEIGHBOR_DELTA, SiteKindMeta, TerrainGrid},
5 weather::WeatherGrid,
6};
7use common_base::{self, prof_span};
8use itertools::Itertools;
9use vek::*;
10
11pub(super) fn simulated_wind_vel(
14 pos: &Pos,
15 weather: &WeatherGrid,
16 terrain: &TerrainGrid,
17 time_of_day: &TimeOfDay,
18) -> Result<Vec3<f32>, ()> {
19 prof_span!(guard, "Apply Weather INIT");
20
21 let pos_2d = pos.0.as_().xy();
22 let chunk_pos: Vec2<i32> = pos_2d.wpos_to_cpos();
23 let Some(current_chunk) = terrain.get_key(chunk_pos) else {
24 return Err(());
25 };
26
27 let meta = current_chunk.meta();
28
29 let interp_weather = weather.get_interpolated(pos.0.xy());
30 let interp_alt = terrain
32 .get_interpolated(pos_2d, |c| c.meta().alt())
33 .unwrap_or(0.);
34 let interp_tree_density = terrain
35 .get_interpolated(pos_2d, |c| c.meta().tree_density())
36 .unwrap_or(0.);
37 let interp_town = terrain
38 .get_interpolated(pos_2d, |c| match c.meta().site() {
39 Some(SiteKindMeta::Settlement(_)) => 2.7,
40 _ => 1.0,
41 })
42 .unwrap_or(0.);
43 let normal = terrain
44 .get_interpolated(pos_2d, |c| {
45 c.meta()
46 .approx_chunk_terrain_normal()
47 .unwrap_or(Vec3::unit_z())
48 })
49 .unwrap_or(Vec3::unit_z());
50 let above_ground = pos.0.z - interp_alt;
51 let wind_velocity = interp_weather.wind_vel();
52
53 let surrounding_chunks_metas = NEIGHBOR_DELTA
54 .iter()
55 .map(move |&(x, y)| chunk_pos + Vec2::new(x, y))
56 .filter_map(|cpos| terrain.get_key(cpos).map(|c| c.meta()))
57 .collect::<Vec<_>>();
58
59 drop(guard);
60
61 prof_span!(guard, "thermals");
62
63 let sun_dir = time_of_day.get_sun_dir().normalized();
70 let mut lift = ((sun_dir - normal.normalized()).magnitude() - 0.5).max(0.2) * 2.3;
71
72 let temperatures = surrounding_chunks_metas.iter().map(|m| m.temp()).minmax();
74
75 lift *= match temperatures {
77 itertools::MinMaxResult::NoElements | itertools::MinMaxResult::OneElement(_) => 1.0,
78 itertools::MinMaxResult::MinMax(a, b) => 0.8 + ((a - b).abs() * 1.1),
79 }
80 .min(2.0);
81
82 lift *= if interp_weather.rain.is_between(0.5, 1.0) && interp_weather.cloud.is_between(0.6, 1.0)
87 {
88 1.5
89 } else if interp_weather.rain.is_between(0.2, 0.5) && interp_weather.cloud.is_between(0.3, 0.6)
90 {
91 0.8
92 } else {
93 1.0
94 };
95
96 lift *= (above_ground / 15.).min(1.);
98 lift *= (220. - above_ground / 20.).clamp(0.0, 1.0);
99
100 if interp_alt > 500.0 {
103 lift *= 0.8;
104 }
105
106 lift *= interp_town;
108
109 lift *= terrain
111 .get_interpolated(pos_2d, |c| 1. - c.meta().near_water() as i32 as f32)
112 .unwrap_or(1.);
113
114 drop(guard);
115
116 let mut ridge_lift = {
119 const RIDGE_LIFT_COEFF: f32 = 1.0;
120
121 let steepness = normal.angle_between(Vec3::unit_z());
122
123 let mut angle = wind_velocity.angle_between(normal.xy()); angle = (angle - 1.3).max(0.0);
129
130 angle * steepness * wind_velocity.magnitude() * RIDGE_LIFT_COEFF
132 };
133
134 ridge_lift *= 0.9 + (meta.cliff_height() / 44.0) * 1.2;
137
138 ridge_lift *= 1. / (1. + (1.3f32.powf(0.1 * above_ground - 15.)));
140
141 let wind_factor = 1. / (0.25 + (0.96f32.powf(0.1 * above_ground - 15.)));
143
144 let mut wind_vel = (wind_velocity * wind_factor).with_z(lift + ridge_lift);
145
146 wind_vel *= (1.0 - interp_tree_density).max(0.7);
148
149 let magn = wind_vel.magnitude_squared().max(0.0001);
151
152 wind_vel *= magn.min(600.) / magn;
154
155 Ok(wind_vel)
156}