mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
More accurate weather sim
This commit is contained in:
parent
6585ef1513
commit
78c1de19cf
@ -19,6 +19,7 @@ layout(std140, set = 0, binding = 0) uniform u_globals {
|
||||
uvec4 medium;
|
||||
ivec4 select_pos;
|
||||
vec4 gamma_exposure;
|
||||
vec2 wind_vel;
|
||||
float ambiance;
|
||||
// 0 - FirstPerson
|
||||
// 1 - ThirdPerson
|
||||
@ -37,6 +38,4 @@ mat4 threshold_matrix = mat4(
|
||||
float distance_divider = 2;
|
||||
float shadow_dithering = 0.5;
|
||||
|
||||
vec2 wind_vel = vec2(0.0);
|
||||
|
||||
#endif
|
||||
|
@ -5,8 +5,6 @@
|
||||
#include <sky.glsl>
|
||||
#include <srgb.glsl>
|
||||
|
||||
layout(set = 0, binding = 5) uniform texture2D t_alt;
|
||||
layout(set = 0, binding = 6) uniform sampler s_alt;
|
||||
layout(set = 0, binding = 7) uniform texture2D t_horizon;
|
||||
layout(set = 0, binding = 8) uniform sampler s_horizon;
|
||||
|
||||
|
@ -97,6 +97,9 @@ vec2 wind_offset = vec2(time_of_day.x * wind_speed);
|
||||
|
||||
float cloud_scale = view_distance.z / 150.0;
|
||||
|
||||
layout(set = 0, binding = 5) uniform texture2D t_alt;
|
||||
layout(set = 0, binding = 6) uniform sampler s_alt;
|
||||
|
||||
vec2 pos_to_uv(texture2D tex, sampler s, vec2 pos) {
|
||||
// Want: (pixel + 0.5) / W
|
||||
vec2 texSize = textureSize(sampler2D(tex, s), 0);
|
||||
@ -105,19 +108,22 @@ vec2 pos_to_uv(texture2D tex, sampler s, vec2 pos) {
|
||||
}
|
||||
|
||||
// Weather texture
|
||||
layout(set = 0, binding = 12) uniform texture2D t_clouds;
|
||||
layout(set = 0, binding = 13) uniform sampler s_clouds;
|
||||
layout(set = 0, binding = 12) uniform texture2D t_weather;
|
||||
layout(set = 0, binding = 13) uniform sampler s_weather;
|
||||
|
||||
vec4 sample_weather(vec2 wpos) {
|
||||
return textureLod(sampler2D(t_weather, s_weather), pos_to_uv(t_alt, s_alt, wpos - focus_off.xy), 0); // TODO: make this work for any world size
|
||||
}
|
||||
|
||||
float cloud_tendency_at(vec2 pos) {
|
||||
float nz = textureLod/*textureBicubic16*/(sampler2D(t_clouds, s_clouds), pos / 33500 , 0).r;
|
||||
//float nz = textureLod(sampler2D(t_noise, s_noise), (pos + wind_offset) / 60000.0 / cloud_scale, 0).x - 0.3;
|
||||
nz = pow(nz, 3);
|
||||
return nz;
|
||||
return sample_weather(pos).r;
|
||||
}
|
||||
|
||||
const float RAIN_CLOUD = 0.05;
|
||||
float rain_density_at(vec2 pos) {
|
||||
return clamp((cloud_tendency_at(pos) - RAIN_CLOUD) * 10, 0, 1);
|
||||
return sample_weather(pos).g;
|
||||
//return clamp((cloud_tendency_at(pos) - RAIN_CLOUD) * 10, 0, 1);
|
||||
}
|
||||
|
||||
float cloud_shadow(vec3 pos, vec3 light_dir) {
|
||||
|
@ -92,6 +92,7 @@ void main() {
|
||||
#endif
|
||||
|
||||
#ifndef EXPERIMENTAL_BAREMINIMUM
|
||||
// TODO: take wind_vel into account
|
||||
// Wind sway effect
|
||||
f_pos += model_wind_sway * vec3(
|
||||
sin(tick.x * 1.5 + f_pos.y * 0.1) * sin(tick.x * 0.35),
|
||||
|
@ -1430,6 +1430,12 @@ impl Client {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_weather_wpos(&self, wpos: Vec2<f32>) -> Weather {
|
||||
let cell_pos =
|
||||
(wpos / ((TerrainChunkSize::RECT_SIZE * weather::CHUNKS_PER_CELL).as_())).as_();
|
||||
*self.weather.get(cell_pos).unwrap_or(&Weather::default())
|
||||
}
|
||||
|
||||
pub fn current_chunk(&self) -> Option<Arc<TerrainChunk>> {
|
||||
let chunk_pos = Vec2::from(self.position()?)
|
||||
.map2(TerrainChunkSize::RECT_SIZE, |e: f32, sz| {
|
||||
|
@ -1,3 +1,5 @@
|
||||
use std::fmt;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use vek::Vec2;
|
||||
|
||||
@ -5,8 +7,11 @@ pub const CHUNKS_PER_CELL: u32 = 16;
|
||||
// Weather::default is Clear, 0 degrees C and no wind
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)]
|
||||
pub struct Weather {
|
||||
/// Clouds currently in the area between 0 and 1
|
||||
pub cloud: f32,
|
||||
/// Rain per time, between 0 and 1
|
||||
pub rain: f32,
|
||||
// Wind direction in block / second
|
||||
pub wind: Vec2<f32>,
|
||||
}
|
||||
|
||||
@ -35,3 +40,14 @@ pub enum WeatherKind {
|
||||
Rain,
|
||||
Storm,
|
||||
}
|
||||
|
||||
impl fmt::Display for WeatherKind {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
WeatherKind::Clear => write!(f, "Clear"),
|
||||
WeatherKind::Cloudy => write!(f, "Cloudy"),
|
||||
WeatherKind::Rain => write!(f, "Rain"),
|
||||
WeatherKind::Storm => write!(f, "Storm"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,11 +24,11 @@ pub fn init(state: &mut State, world: &world::World) {
|
||||
state
|
||||
.ecs_mut()
|
||||
.insert(SysScheduler::<tick::Sys>::every(Duration::from_secs_f32(
|
||||
0.1,
|
||||
sim::DT / 100.0,
|
||||
)));
|
||||
state
|
||||
.ecs_mut()
|
||||
.insert(SysScheduler::<sync::Sys>::every(Duration::from_secs_f32(
|
||||
0.1,
|
||||
sim::DT / 100.0,
|
||||
)));
|
||||
}
|
||||
|
@ -1,12 +1,8 @@
|
||||
use std::{
|
||||
ops::{Add, Deref, DerefMut, Div, Mul},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use common::{
|
||||
grid::Grid,
|
||||
resources::TimeOfDay,
|
||||
terrain::{BiomeKind, TerrainChunkSize},
|
||||
time::DayPeriod,
|
||||
vol::RectVolSize,
|
||||
weather::{Weather, CHUNKS_PER_CELL},
|
||||
};
|
||||
@ -19,10 +15,10 @@ use world::{
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Constants {
|
||||
drag: f32, // How much drag therfe is on wind. Caused by slopes.
|
||||
humidity: Option<f32>,
|
||||
temperature_day: Option<f32>,
|
||||
temperature_night: Option<f32>,
|
||||
altitude: f32,
|
||||
water: f32,
|
||||
temperature_day: f32,
|
||||
temperature_night: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default)]
|
||||
@ -30,83 +26,173 @@ struct Cell {
|
||||
wind: Vec2<f32>,
|
||||
temperature: f32,
|
||||
moisture: f32,
|
||||
rain: f32,
|
||||
cloud: f32,
|
||||
}
|
||||
/// Used to sample weather that isn't simulated
|
||||
fn sample_cell(p: Vec2<i32>, time: f64) -> Cell {
|
||||
let noise = FastNoise::new(0b10110_101_1100_1111_10010_101_1110);
|
||||
|
||||
/*let noise = FastNoise::new(0b10110_101_1100_1111_10010_101_1110);
|
||||
// Feeding sensible values into the simulation
|
||||
Cell {
|
||||
wind: Vec2::new(noise.get(p.with_z(0).as_()), noise.get(p.with_z(100).as_()))
|
||||
* ((noise.get(p.with_z(200).as_()) + 1.0) * 0.5).powf(4.0)
|
||||
* 30.0,
|
||||
temperature: noise.get(p.with_z(300).as_()).powf(3.0) * 10.0 + 20.0, // 10 -> 30
|
||||
moisture: BASE_HUMIDITY,
|
||||
rain: ((noise.get(p.with_z(400).as_()) + 1.0) * 0.5).powf(4.0) * MAX_RAIN,
|
||||
wind: Vec2::new(
|
||||
noise.get(p.as_().with_z(time / 1000.0)),
|
||||
noise.get(p.as_().with_z(-time / 1000.0)),
|
||||
) * ((noise.get(p.as_().with_z(time / 1000.0 + 200.0)) + 1.0) * 0.5).powf(2.0)
|
||||
* 30.0
|
||||
+ 5.0,
|
||||
temperature: noise.get(p.as_().with_z(time / 1000.0 + 300.0)).powf(3.0) * 10.0
|
||||
+ BASE_TEMPERATURE
|
||||
+ 273.3, /* 10 -> 30 C */
|
||||
moisture: BASE_MOISTURE + noise.get(p.as_().with_z(time / 100.0 + 400.0)).powf(3.0) * 0.2,
|
||||
cloud: noise
|
||||
.get((p.as_() / 2.0).with_z(time / 100.0 + 400.0))
|
||||
.powf(2.0)
|
||||
* 1.0,
|
||||
} */
|
||||
Cell {
|
||||
wind: Vec2::new(10.0, 0.0),
|
||||
temperature: 20.0,
|
||||
moisture: 0.0,
|
||||
cloud: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WeatherSim {
|
||||
cells: Grid<Cell>,
|
||||
constants: Grid<Constants>,
|
||||
|
||||
weather: Grid<Weather>, // The current weather.
|
||||
cells: Grid<Cell>, // The variables used for simulation
|
||||
constants: Grid<Constants>, // The constants from the world used for simulation
|
||||
weather: Grid<Weather>, // The current weather.
|
||||
}
|
||||
|
||||
const BASE_HUMIDITY: f32 = 1.6652;
|
||||
const BASE_MOISTURE: f32 = 1.0;
|
||||
const BASE_TEMPERATURE: f32 = 20.0;
|
||||
const MAX_RAIN: f32 = 100.0;
|
||||
const WATER_BOILING_POINT: f32 = 373.3;
|
||||
const MAX_WIND_SPEED: f32 = 60.0;
|
||||
const CELL_SIZE: f32 = (CHUNKS_PER_CELL * TerrainChunkSize::RECT_SIZE.x) as f32;
|
||||
pub(crate) const DT: f32 = CELL_SIZE / MAX_WIND_SPEED;
|
||||
|
||||
impl WeatherSim {
|
||||
pub fn new(size: Vec2<u32>, world: &World) -> Self {
|
||||
let size = size.as_();
|
||||
Self {
|
||||
let mut this = Self {
|
||||
cells: Grid::new(size, Cell::default()),
|
||||
constants: Grid::from_raw(
|
||||
size,
|
||||
(0..size.x * size.y)
|
||||
.map(|i| Vec2::new(i as i32 % size.x, i as i32 / size.x))
|
||||
.map(|p| {
|
||||
let mut c = Constants::default();
|
||||
let add = |member: &mut f32, v| {
|
||||
*member += v;
|
||||
*member /= 2.0;
|
||||
};
|
||||
let mut temperature_night_r = 0.0;
|
||||
let mut temperature_night = |v| {
|
||||
add(&mut temperature_night_r, v);
|
||||
};
|
||||
let mut temperature_day_r = 0.0;
|
||||
let mut temperature_day = |v| {
|
||||
add(&mut temperature_day_r, v);
|
||||
};
|
||||
let mut altitude_r = 0.0;
|
||||
let mut altitude = |v| {
|
||||
add(&mut altitude_r, v);
|
||||
};
|
||||
let mut water_r = 0.0;
|
||||
for y in 0..CHUNKS_PER_CELL as i32 {
|
||||
for x in 0..CHUNKS_PER_CELL as i32 {
|
||||
let chunk_pos = p * CHUNKS_PER_CELL as i32 + Vec2::new(x, y);
|
||||
if let Some(chunk) = world.sim().get(chunk_pos) {
|
||||
c.drag +=
|
||||
let a =
|
||||
world.sim().get_gradient_approx(chunk_pos).unwrap_or(0.0);
|
||||
if chunk.is_underwater() {
|
||||
c.humidity =
|
||||
Some(c.humidity.unwrap_or(0.0) + BASE_HUMIDITY);
|
||||
c.temperature_night =
|
||||
Some(c.temperature_night.unwrap_or(0.0) + 0.01);
|
||||
} else {
|
||||
c.temperature_day =
|
||||
Some(c.temperature_day.unwrap_or(0.0) + 0.01);
|
||||
}
|
||||
altitude(a);
|
||||
|
||||
let height_p = 1.0
|
||||
- world.sim().get_alt_approx(chunk_pos).unwrap_or(0.0)
|
||||
/ world.sim().max_height;
|
||||
|
||||
let mut water = |v| {
|
||||
add(&mut water_r, v * height_p);
|
||||
};
|
||||
|
||||
match chunk.get_biome() {
|
||||
BiomeKind::Desert => {
|
||||
c.temperature_day =
|
||||
Some(c.temperature_day.unwrap_or(0.0) + 0.01);
|
||||
c.humidity = Some(
|
||||
c.humidity.unwrap_or(0.0) - 10.0 * BASE_HUMIDITY,
|
||||
);
|
||||
water(0.0);
|
||||
temperature_night(11.0);
|
||||
temperature_day(30.0);
|
||||
},
|
||||
BiomeKind::Savannah => {
|
||||
water(0.02);
|
||||
temperature_night(13.0);
|
||||
temperature_day(26.0);
|
||||
},
|
||||
BiomeKind::Swamp => {
|
||||
c.humidity = Some(
|
||||
c.humidity.unwrap_or(0.0) + 2.0 * BASE_HUMIDITY,
|
||||
);
|
||||
water(0.8);
|
||||
temperature_night(16.0);
|
||||
temperature_day(24.0);
|
||||
},
|
||||
BiomeKind::Mountain => {
|
||||
water(0.01);
|
||||
temperature_night(13.0);
|
||||
temperature_day(14.0);
|
||||
},
|
||||
BiomeKind::Grassland => {
|
||||
water(0.05);
|
||||
temperature_night(15.0);
|
||||
temperature_day(20.0);
|
||||
},
|
||||
BiomeKind::Snowland => {
|
||||
water(0.005);
|
||||
temperature_night(-8.0);
|
||||
temperature_day(-1.0);
|
||||
},
|
||||
BiomeKind::Jungle => {
|
||||
water(0.4);
|
||||
temperature_night(20.0);
|
||||
temperature_day(27.0);
|
||||
},
|
||||
BiomeKind::Forest => {
|
||||
water(0.1);
|
||||
temperature_night(16.0);
|
||||
temperature_day(19.0);
|
||||
},
|
||||
BiomeKind::Taiga => {
|
||||
water(0.02);
|
||||
temperature_night(1.0);
|
||||
temperature_day(10.0);
|
||||
},
|
||||
BiomeKind::Lake => {
|
||||
water(1.0);
|
||||
temperature_night(20.0);
|
||||
temperature_day(18.0);
|
||||
},
|
||||
BiomeKind::Ocean => {
|
||||
water(0.98);
|
||||
temperature_night(19.0);
|
||||
temperature_day(17.0);
|
||||
},
|
||||
BiomeKind::Void => {
|
||||
water(0.0);
|
||||
temperature_night(20.0);
|
||||
temperature_day(20.0);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
c
|
||||
Constants {
|
||||
altitude: altitude_r,
|
||||
water: water_r,
|
||||
temperature_day: temperature_day_r,
|
||||
temperature_night: temperature_day_r,
|
||||
}
|
||||
})
|
||||
.collect_vec(),
|
||||
),
|
||||
weather: Grid::new(size, Weather::default()),
|
||||
}
|
||||
};
|
||||
this.cells.iter_mut().for_each(|(point, cell)| {
|
||||
let time = 0.0;
|
||||
*cell = sample_cell(point, time);
|
||||
});
|
||||
this
|
||||
}
|
||||
|
||||
pub fn get_weather(&self) -> &Grid<Weather> { &self.weather }
|
||||
@ -121,61 +207,67 @@ impl WeatherSim {
|
||||
|
||||
// https://minds.wisconsin.edu/bitstream/handle/1793/66950/LitzauSpr2013.pdf
|
||||
// Time step is cell size / maximum wind speed
|
||||
pub fn tick(&mut self, world: &World, time_of_day: &TimeOfDay, dt: f32) {
|
||||
const MAX_WIND_SPEED: f32 = 127.0;
|
||||
let cell_size: Vec2<f32> = (CHUNKS_PER_CELL * TerrainChunkSize::RECT_SIZE).as_();
|
||||
let dt = cell_size.x / MAX_WIND_SPEED;
|
||||
pub fn tick(&mut self, time_of_day: &TimeOfDay, dt: f32) {
|
||||
let time = time_of_day.0;
|
||||
let mut swap = Grid::new(self.cells.size(), Cell::default());
|
||||
// Dissipate wind, humidty and pressure
|
||||
//Dispersion is represented by the target cell expanding into the 8 adjacent
|
||||
// Dissipation is represented by the target cell expanding into the 8 adjacent
|
||||
// cells. The target cell’s contents are then distributed based the
|
||||
// percentage that overlaps the surrounding cell.
|
||||
for (point, cell) in self.cells.iter() {
|
||||
swap[point] = {
|
||||
let spread = [
|
||||
(*cell, 1. / 4.),
|
||||
(
|
||||
self.get_cell(point + Vec2::new(1, 0), time_of_day.0),
|
||||
1. / 8.,
|
||||
),
|
||||
(
|
||||
self.get_cell(point + Vec2::new(-1, 0), time_of_day.0),
|
||||
1. / 8.,
|
||||
),
|
||||
(
|
||||
self.get_cell(point + Vec2::new(0, 1), time_of_day.0),
|
||||
1. / 8.,
|
||||
),
|
||||
(
|
||||
self.get_cell(point + Vec2::new(0, -1), time_of_day.0),
|
||||
1. / 8.,
|
||||
),
|
||||
(*cell, 0),
|
||||
(self.get_cell(point + Vec2::new(1, 0), time), 1),
|
||||
(self.get_cell(point + Vec2::new(-1, 0), time), 1),
|
||||
(self.get_cell(point + Vec2::new(0, 1), time), 1),
|
||||
(self.get_cell(point + Vec2::new(0, -1), time), 1),
|
||||
// Diagonal so less overlap
|
||||
(
|
||||
self.get_cell(point + Vec2::new(1, 1), time_of_day.0),
|
||||
1. / 16.,
|
||||
),
|
||||
(
|
||||
self.get_cell(point + Vec2::new(1, -1), time_of_day.0),
|
||||
1. / 16.,
|
||||
),
|
||||
(
|
||||
self.get_cell(point + Vec2::new(-1, 1), time_of_day.0),
|
||||
1. / 16.,
|
||||
),
|
||||
(
|
||||
self.get_cell(point + Vec2::new(-1, -1), time_of_day.0),
|
||||
1. / 16.,
|
||||
),
|
||||
(self.get_cell(point + Vec2::new(1, 1), time), 2),
|
||||
(self.get_cell(point + Vec2::new(1, -1), time), 2),
|
||||
(self.get_cell(point + Vec2::new(-1, 1), time), 2),
|
||||
(self.get_cell(point + Vec2::new(-1, -1), time), 2),
|
||||
];
|
||||
let mut cell = Cell::default();
|
||||
|
||||
for (c, factor) in spread {
|
||||
cell.wind += c.wind * factor;
|
||||
cell.temperature += c.temperature * factor;
|
||||
cell.moisture += c.moisture * factor;
|
||||
}
|
||||
for (c, p) in spread {
|
||||
let factor = |rate: f32| {
|
||||
let rate = (1.0 + rate).powf(DT);
|
||||
// 1.0
|
||||
// ___________________
|
||||
// / \
|
||||
// +---+-------------------+---+
|
||||
// | 2 | 1 | 2 |
|
||||
// +---+-------------------+---+
|
||||
// | | | |
|
||||
// | | | |
|
||||
// | | | |
|
||||
// | 1 | 0 | 1 |
|
||||
// | | | |
|
||||
// | | | |
|
||||
// | | | |
|
||||
// +---+-------------------+---+ \
|
||||
// | 2 | 1 | 2 | | rate
|
||||
// +---+-------------------+---+ /
|
||||
// \___________________________/
|
||||
// 2.0 * rate + 1.0
|
||||
// area_0 = 1.0 * 1.0
|
||||
// area_1 = rate * 1.0
|
||||
// area_2 = rate * rate
|
||||
|
||||
let area = (1.0 + 2.0 * rate).powf(2.0);
|
||||
match p {
|
||||
0 => 1.0 * 1.0 / area, // area_0 / area
|
||||
1 => rate * 1.0 / area, // area_1 / area
|
||||
_ => rate * rate / area, // area_2/ area
|
||||
}
|
||||
};
|
||||
// 0.0 <= dissipation rate <= 1.0 because we only spread to direct neighbours.
|
||||
cell.wind += c.wind * factor(0.0055);
|
||||
cell.temperature += c.temperature * factor(0.007);
|
||||
cell.moisture += c.moisture * factor(0.003);
|
||||
cell.cloud += c.cloud * factor(0.001);
|
||||
}
|
||||
cell
|
||||
}
|
||||
}
|
||||
@ -194,23 +286,23 @@ impl WeatherSim {
|
||||
for y in -1..=self.cells.size().y {
|
||||
for x in -1..=self.cells.size().x {
|
||||
let point = Vec2::new(x, y);
|
||||
let cell = self.get_cell(point, time_of_day.0);
|
||||
let a = cell_size.x - cell.wind.x.abs();
|
||||
let b = cell_size.y - cell.wind.y.abs();
|
||||
let cell = self.get_cell(point, time);
|
||||
let a = CELL_SIZE - cell.wind.x.abs() * DT;
|
||||
let b = CELL_SIZE - cell.wind.y.abs() * DT;
|
||||
let wind_dir = Vec2::new(cell.wind.x.signum(), cell.wind.y.signum()).as_();
|
||||
let spread = [
|
||||
(point, a * b / (cell_size.x * cell_size.y)),
|
||||
(point, a * b / (CELL_SIZE * CELL_SIZE)),
|
||||
(
|
||||
point + wind_dir.with_y(0),
|
||||
(cell_size.x - a) * b / (cell_size.x * cell_size.y),
|
||||
(CELL_SIZE - a) * b / (CELL_SIZE * CELL_SIZE),
|
||||
),
|
||||
(
|
||||
point + wind_dir.with_x(0),
|
||||
a * (cell_size.y - b) / (cell_size.x * cell_size.y),
|
||||
a * (CELL_SIZE - b) / (CELL_SIZE * CELL_SIZE),
|
||||
),
|
||||
(
|
||||
point + wind_dir,
|
||||
(cell_size.x - a) * (cell_size.y - b) / (cell_size.x * cell_size.y),
|
||||
(CELL_SIZE - a) * (CELL_SIZE - b) / (CELL_SIZE * CELL_SIZE),
|
||||
),
|
||||
];
|
||||
for (p, factor) in spread {
|
||||
@ -218,79 +310,58 @@ impl WeatherSim {
|
||||
c.wind += cell.wind * factor;
|
||||
c.temperature += cell.temperature * factor;
|
||||
c.moisture += cell.moisture * factor;
|
||||
c.rain += cell.rain * factor;
|
||||
c.cloud += cell.cloud * factor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.cells = swap.clone();
|
||||
|
||||
// TODO: wind curl, rain condesnsing from moisture. And interacting with world
|
||||
// elements.
|
||||
// TODO: wind curl
|
||||
|
||||
// Evaporate moisture and condense clouds
|
||||
for (point, cell) in self.cells.iter_mut() {
|
||||
// r = rain, m = moisture
|
||||
// ∆r = Rcond − Rrain.
|
||||
// ∆m = −Rcond + V.
|
||||
// Rcond = δ(T0(m) − T)H(T0(m) − T) − γ(T − T0(m))H(T − T0(m)).
|
||||
// T0(m) = (100−m) / 5
|
||||
let dt = 1.0 - 0.96f32.powf(DT);
|
||||
let day_light = (1.0 - (1.0 - time_of_day.0 as f32 / 12.0 * 60.0 * 60.0).abs())
|
||||
* self.weather[point].cloud;
|
||||
|
||||
// V = B(T − Tv(h))H(T − Tv(h))
|
||||
// γ = 0.5, δ = 0.25, B = 0.5 and Tv(h) = 20◦C
|
||||
cell.temperature = cell.temperature * (1.0 - dt)
|
||||
+ dt * (self.constants[point].temperature_day * day_light
|
||||
+ self.constants[point].temperature_night * (1.0 - day_light));
|
||||
|
||||
// TODO: make these parameters depend on the world.
|
||||
// TODO: figure out what these variables mean.
|
||||
let gamma = 0.5;
|
||||
let delta = 0.25;
|
||||
let b = 0.5;
|
||||
let h = 1.0;
|
||||
let t_v = BASE_TEMPERATURE;
|
||||
// Evaporate from ground.
|
||||
let temp_part = (cell.temperature / WATER_BOILING_POINT)
|
||||
//.powf(2.0)
|
||||
.clamp(0.0, 1.0);
|
||||
cell.moisture += (self.constants[point].water / 10.0) * temp_part * DT;
|
||||
|
||||
let rain_fall_max = 0.05;
|
||||
|
||||
let evaporation = b * (cell.temperature - t_v) * h * (cell.temperature - t_v);
|
||||
|
||||
let dew_point = (100.0 - cell.moisture) / 5.0;
|
||||
|
||||
let condensation =
|
||||
delta * (dew_point - cell.temperature) * h * (dew_point - cell.temperature)
|
||||
- gamma * (cell.temperature - dew_point) * h * (cell.temperature - dew_point);
|
||||
cell.rain += condensation;
|
||||
if cell.rain > rain_fall_max {
|
||||
cell.rain -= rain_fall_max;
|
||||
self.weather[point].rain = 1.0;
|
||||
// If positive condense moisture to clouds, if negative evaporate clouds into
|
||||
// moisture.
|
||||
let condensation = if cell.moisture > 1.0 {
|
||||
cell.moisture.powf(2.0 / 3.0)
|
||||
} else {
|
||||
self.weather[point].rain = 0.5;
|
||||
}
|
||||
cell.moisture += evaporation - condensation;
|
||||
cell.moisture
|
||||
} * temp_part
|
||||
* DT;
|
||||
|
||||
self.weather[point].cloud = (cell.rain / MAX_RAIN).clamp(0.0, 1.0);
|
||||
cell.moisture -= condensation;
|
||||
cell.cloud += condensation;
|
||||
|
||||
const CLOUD_MAX: f32 = 15.0;
|
||||
const RAIN_P: f32 = 0.96;
|
||||
let rain_cloud = temp_part * CLOUD_MAX;
|
||||
|
||||
let rain_p_t = ((cell.cloud - rain_cloud) / (CLOUD_MAX - rain_cloud)).max(0.0);
|
||||
let rain = rain_p_t * (1.0 - RAIN_P.powf(DT));
|
||||
cell.cloud -= rain;
|
||||
cell.cloud = cell.cloud.max(0.0);
|
||||
|
||||
self.weather[point].cloud = (cell.cloud / CLOUD_MAX).clamp(0.0, 1.0);
|
||||
self.weather[point].rain = rain_p_t;
|
||||
self.weather[point].wind = cell.wind;
|
||||
}
|
||||
|
||||
// Maybe moisture condenses to clouds, which if they have a certain
|
||||
// amount they will release rain.
|
||||
}
|
||||
|
||||
fn update_info(&mut self) {
|
||||
let w = self
|
||||
.cells
|
||||
.iter()
|
||||
.map(|(p, c)| {
|
||||
(
|
||||
p,
|
||||
Weather::new(
|
||||
//if p.x % 2 == p.y % 2 { 1.0 } else { 0.0 },
|
||||
(self.constants[p].humidity.unwrap_or(0.0) * 100.0).clamp(0.0, 1.0),
|
||||
0.0,
|
||||
c.wind,
|
||||
),
|
||||
)
|
||||
})
|
||||
.collect_vec();
|
||||
w.iter().for_each(|&(p, w)| {
|
||||
self.weather[p] = w;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,6 @@ pub struct Sys;
|
||||
impl<'a> System<'a> for Sys {
|
||||
type SystemData = (
|
||||
Read<'a, TimeOfDay>,
|
||||
ReadExpect<'a, Arc<world::World>>,
|
||||
WriteExpect<'a, WeatherSim>,
|
||||
Write<'a, SysScheduler<Self>>,
|
||||
);
|
||||
@ -23,12 +22,9 @@ impl<'a> System<'a> for Sys {
|
||||
const ORIGIN: Origin = Origin::Server;
|
||||
const PHASE: Phase = Phase::Create;
|
||||
|
||||
fn run(
|
||||
job: &mut common_ecs::Job<Self>,
|
||||
(game_time, world, mut sim, mut scheduler): Self::SystemData,
|
||||
) {
|
||||
fn run(job: &mut common_ecs::Job<Self>, (game_time, mut sim, mut scheduler): Self::SystemData) {
|
||||
if scheduler.should_run() {
|
||||
sim.tick(&*world, &*game_time, 1.0);
|
||||
sim.tick(&*game_time, 1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -101,6 +101,7 @@ use common::{
|
||||
uid::Uid,
|
||||
util::{srgba_to_linear, Dir},
|
||||
vol::RectRasterableVol,
|
||||
weather::Weather,
|
||||
};
|
||||
use common_base::{prof_span, span};
|
||||
use common_net::{
|
||||
@ -264,6 +265,7 @@ widget_ids! {
|
||||
current_site,
|
||||
graphics_backend,
|
||||
gpu_timings[],
|
||||
weather,
|
||||
|
||||
// Game Version
|
||||
version,
|
||||
@ -492,6 +494,7 @@ pub struct DebugInfo {
|
||||
pub num_figures_visible: u32,
|
||||
pub num_particles: u32,
|
||||
pub num_particles_visible: u32,
|
||||
pub weather: Weather,
|
||||
}
|
||||
|
||||
pub struct HudInfo {
|
||||
@ -2386,11 +2389,27 @@ impl Hud {
|
||||
.font_size(self.fonts.cyri.scale(14))
|
||||
.set(self.ids.time, ui_widgets);
|
||||
|
||||
let weather = client.current_weather();
|
||||
Text::new(&format!(
|
||||
"Weather({kind:.5}): {{cloud: {cloud:.5}, rain: {rain:.5}, wind: <{wind_x:.5}, \
|
||||
{wind_y:.2}>}}",
|
||||
kind = weather.get_kind(),
|
||||
cloud = weather.cloud,
|
||||
rain = weather.rain,
|
||||
wind_x = weather.wind.x,
|
||||
wind_y = weather.wind.y
|
||||
))
|
||||
.color(TEXT_COLOR)
|
||||
.down_from(self.ids.time, V_PAD)
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.font_size(self.fonts.cyri.scale(14))
|
||||
.set(self.ids.weather, ui_widgets);
|
||||
|
||||
// Number of entities
|
||||
let entity_count = client.state().ecs().entities().join().count();
|
||||
Text::new(&format!("Entity count: {}", entity_count))
|
||||
.color(TEXT_COLOR)
|
||||
.down_from(self.ids.time, V_PAD)
|
||||
.down_from(self.ids.weather, V_PAD)
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.font_size(self.fonts.cyri.scale(14))
|
||||
.set(self.ids.entity_count, ui_widgets);
|
||||
@ -2503,6 +2522,7 @@ impl Hud {
|
||||
|
||||
// Set debug box dimensions, only timings height is dynamic
|
||||
// TODO: Make the background box size fully dynamic
|
||||
|
||||
let debug_bg_size = [320.0, 370.0 + timings_height];
|
||||
|
||||
Rectangle::fill(debug_bg_size)
|
||||
|
@ -63,6 +63,7 @@ pub struct Globals {
|
||||
medium: [u32; 4],
|
||||
select_pos: [i32; 4],
|
||||
gamma_exposure: [f32; 4],
|
||||
wind_vel: [f32; 2],
|
||||
ambiance: f32,
|
||||
cam_mode: u32,
|
||||
sprite_render_distance: f32,
|
||||
@ -108,6 +109,7 @@ impl Globals {
|
||||
ambiance: f32,
|
||||
cam_mode: CameraMode,
|
||||
sprite_render_distance: f32,
|
||||
wind_vel: Vec2<f32>,
|
||||
) -> Self {
|
||||
Self {
|
||||
view_mat: view_mat.into_col_arrays(),
|
||||
@ -153,6 +155,7 @@ impl Globals {
|
||||
.unwrap_or_else(Vec4::zero)
|
||||
.into_array(),
|
||||
gamma_exposure: [gamma, exposure, 0.0, 0.0],
|
||||
wind_vel: [wind_vel.x, wind_vel.y],
|
||||
ambiance: ambiance.clamped(0.0, 1.0),
|
||||
cam_mode: cam_mode as u32,
|
||||
sprite_render_distance,
|
||||
@ -202,6 +205,7 @@ impl Default for Globals {
|
||||
1.0,
|
||||
CameraMode::ThirdPerson,
|
||||
250.0,
|
||||
Vec2::zero(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -401,7 +405,7 @@ impl GlobalsLayouts {
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
// clouds t_clouds
|
||||
// clouds t_weather
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 12,
|
||||
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
|
||||
|
@ -677,6 +677,7 @@ impl Scene {
|
||||
scene_data.ambiance,
|
||||
self.camera.get_mode(),
|
||||
scene_data.sprite_render_distance as f32 - 20.0,
|
||||
client.current_weather_wpos(cam_pos.xy()).wind,
|
||||
)]);
|
||||
renderer.update_clouds_locals(CloudsLocals::new(proj_mat_inv, view_mat_inv));
|
||||
renderer.update_postprocess_locals(PostProcessLocals::new(proj_mat_inv, view_mat_inv));
|
||||
|
@ -274,6 +274,7 @@ impl Scene {
|
||||
scene_data.ambiance,
|
||||
self.camera.get_mode(),
|
||||
250.0,
|
||||
Vec2::zero(),
|
||||
)]);
|
||||
|
||||
self.figure_model_cache
|
||||
|
@ -338,9 +338,10 @@ impl SessionState {
|
||||
.get_weather()
|
||||
.iter()
|
||||
.map(|(_, w)| {
|
||||
//println!("{}, ", w.cloud);
|
||||
[
|
||||
(w.cloud * 255.0) as u8,
|
||||
(w.rain + 128.0).clamp(0.0, 255.0) as u8,
|
||||
(w.rain * 255.0) as u8,
|
||||
(w.wind.x + 128.0).clamp(0.0, 255.0) as u8,
|
||||
(w.wind.y + 128.0).clamp(0.0, 255.0) as u8,
|
||||
]
|
||||
@ -1126,6 +1127,7 @@ impl PlayState for SessionState {
|
||||
num_particles: self.scene.particle_mgr().particle_count() as u32,
|
||||
num_particles_visible: self.scene.particle_mgr().particle_count_visible()
|
||||
as u32,
|
||||
weather: client.current_weather(),
|
||||
}
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user