diff --git a/client/src/lib.rs b/client/src/lib.rs index e2f1b8d95c..fe153ecc54 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -1685,7 +1685,7 @@ impl Client { self.invite = None; } - // TODO: put this somewhere else? Otherwise update comments here. + // Lerp the clientside weather. self.weather.update(&mut self.state.weather_grid_mut()); // Lerp towards the target time of day - this ensures a smooth transition for diff --git a/common/src/weather.rs b/common/src/weather.rs index 069d3d3163..08cddcb767 100644 --- a/common/src/weather.rs +++ b/common/src/weather.rs @@ -12,7 +12,7 @@ pub struct Weather { pub cloud: f32, /// Rain per time, between 0 and 1 pub rain: f32, - // Wind direction in block / second + /// Wind velocity in block / second pub wind: Vec2, } @@ -75,8 +75,6 @@ pub const CELL_SIZE: u32 = CHUNKS_PER_CELL * TerrainChunkSize::RECT_SIZE.x; /// How often the weather is updated, in seconds pub const WEATHER_DT: f32 = 5.0; -// pub const MAX_WIND_SPEED: f32 = CELL_SIZE as f32 / WEATHER_DT; - #[derive(Debug, Clone, Serialize, Deserialize)] pub struct WeatherGrid { weather: Grid, diff --git a/server/src/weather/sim.rs b/server/src/weather/sim.rs index 51ad06621d..e46060cb92 100644 --- a/server/src/weather/sim.rs +++ b/server/src/weather/sim.rs @@ -6,148 +6,17 @@ use noise::{NoiseFn, SuperSimplex, Turbulence}; use vek::*; use world::World; -/* -#[derive(Clone, Copy, Default)] -struct Cell { - wind: Vec2, - temperature: f32, - moisture: f32, - cloud: f32, -} -#[derive(Default)] -pub struct Constants { - alt: f32, - normal: Vec3, - humid: f32, - temp: f32, -} - -/// Used to sample weather that isn't simulated -fn sample_cell(_p: Vec2, _time: f64) -> Cell { - Cell { - wind: Vec2::new(20.0, 20.0), - temperature: 0.5, - moisture: 0.1, - cloud: 0.0, - } -} - -#[derive(Clone, Copy, Default)] -pub struct WeatherInfo { - pub lightning_chance: f32, -} - -fn sample_plane_normal(points: &[Vec3]) -> Option> { - if points.len() < 3 { - return None; - } - let sum = points.iter().cloned().sum::>(); - let centroid = sum / (points.len() as f32); - - let (xx, xy, xz, yy, yz, zz) = { - let (mut xx, mut xy, mut xz, mut yy, mut yz, mut zz) = (0.0, 0.0, 0.0, 0.0, 0.0, 0.0); - for p in points { - let p = *p - centroid; - xx += p.x * p.x; - xy += p.x * p.y; - xz += p.x * p.z; - yy += p.y * p.y; - yz += p.y * p.z; - zz += p.z * p.z; - } - (xx, xy, xz, yy, yz, zz) - }; - - let det_x: f32 = yy * zz - yz * yz; - let det_y: f32 = xx * zz - xz * xz; - let det_z: f32 = xx * yy - xy * xy; - - let det_max = det_x.max(det_y).max(det_z); - if det_max <= 0.0 { - None - } else if det_max == det_x { - Some(Vec3::new(det_x, xz * yz - xy * zz, xy * yz - xz * yy).normalized()) - } else if det_max == det_y { - Some(Vec3::new(xy * yz - xy * zz, det_y, xy * xz - yz * xx).normalized()) - } else { - Some(Vec3::new(xy * yz - xz * yy, xy * xz - yz * xx, det_z).normalized()) - } -} -*/ fn cell_to_wpos(p: Vec2) -> Vec2 { p * CELL_SIZE as i32 } pub struct WeatherSim { - // cells: Grid, // The variables used for simulation - // consts: Grid, // The constants from the world used for simulation - // info: Grid, size: Vec2, } impl WeatherSim { pub fn new(size: Vec2, _world: &World) -> Self { - /* - let size = size.as_(); - let this = Self { - cells: Grid::new(size, Cell::default()), - consts: Grid::from_raw( - size, - (0..size.x * size.y) - .map(|i| Vec2::new(i % size.x, i / size.x)) - .map(|p| { - let mut temp_sum = 0.0; - - let mut alt_sum = 0.0; - - let mut humid_sum = 1000.0; - - let mut points: Vec> = - Vec::with_capacity((CHUNKS_PER_CELL * CHUNKS_PER_CELL) as usize); - 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) { - let wpos = chunk_pos * TerrainChunkSize::RECT_SIZE.as_(); - let a = world.sim().get_alt_approx(wpos).unwrap_or(0.0); - alt_sum += a; - - let wpos = wpos.as_().with_z(a); - points.push(wpos); - - let height_p = 1.0 - a / world.sim().max_height; - - let env = chunk.get_environment(); - temp_sum += env.temp; - humid_sum += (env.humid * (1.0 + env.near_water)) * height_p; - } - } - } - Constants { - alt: alt_sum / (CHUNKS_PER_CELL * CHUNKS_PER_CELL) as f32, - humid: humid_sum / (CHUNKS_PER_CELL * CHUNKS_PER_CELL) as f32, - temp: temp_sum / (CHUNKS_PER_CELL * CHUNKS_PER_CELL) as f32, - normal: sample_plane_normal(&points).unwrap(), - } - }) - .collect_vec(), - ), - info: Grid::new(size, WeatherInfo::default()), - }; - this.cells.iter_mut().for_each(|(point, cell)| { - let time = 0.0; - *cell = sample_cell(point, time); - }); - this - */ Self { size } } - /* - fn get_cell(&self, p: Vec2, time: f64) -> Cell { - *self.cells.get(p).unwrap_or(&sample_cell(p, time)) - } - */ - - // https://minds.wisconsin.edu/bitstream/handle/1793/66950/LitzauSpr2013.pdf // Time step is cell size / maximum wind speed pub fn tick(&mut self, time_of_day: &TimeOfDay, out: &mut WeatherGrid) { let time = time_of_day.0; @@ -182,203 +51,6 @@ impl WeatherSim { ) * 200.0 * (1.0 - pressure); } - /* - let mut swap = Grid::new(self.cells.size(), Cell::default()); - // Dissipate wind, humidty and pressure - // 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, 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), 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, 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.009); - cell.temperature += c.temperature * factor(0.01); - cell.moisture += c.moisture * factor(0.008); - cell.cloud += c.cloud * factor(0.005); - } - cell - } - } - self.cells = swap.clone(); - swap.iter_mut().for_each(|(_, cell)| { - *cell = Cell::default(); - }); - - // Wind spread - // Wind is modeled by taking the target cell - // contents and moving it to different cells. - // We assume wind will not travel more than 1 cell per tick. - - // Need to spread wind from outside simulation to simulate that the veloren - // island is not in a box. - 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); - 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 * CELL_SIZE)), - ( - point + wind_dir.with_y(0), - (CELL_SIZE - a) * b / (CELL_SIZE * CELL_SIZE), - ), - ( - point + wind_dir.with_x(0), - a * (CELL_SIZE - b) / (CELL_SIZE * CELL_SIZE), - ), - ( - point + wind_dir, - (CELL_SIZE - a) * (CELL_SIZE - b) / (CELL_SIZE * CELL_SIZE), - ), - ]; - for (p, factor) in spread { - if let Some(c) = swap.get_mut(p) { - c.wind += cell.wind * factor; - c.temperature += cell.temperature * factor; - c.moisture += cell.moisture * factor; - c.cloud += cell.cloud * factor; - } - } - } - } - - // Evaporate moisture and condense clouds - for (point, cell) in swap.iter() { - let dt = 1.0 - 0.96f32.powf(DT); - let day_light = ((2.0 - * ((time_of_day.0 as f32 / (24.0 * 60.0 * 60.0)) % 1.0 - 0.5).abs()) - * (1.0 - self.weather[point].cloud / CLOUD_MAX)) - .clamp(0.0, 1.0); - - self.cells[point].temperature = - cell.temperature * (1.0 - dt) + dt * (self.consts[point].temp + 0.1 * day_light); - - let temp_part = ((cell.temperature + WATER_BOILING_POINT) - / (WATER_BOILING_POINT * 2.0)) - .powf(4.0) - .clamp(0.0, 1.0); - - // Drag wind based on pressure difference. - // note: pressure scales linearly with temperature in this simulation - self.cells[point].wind = cell.wind - + [ - Vec2::new(1, 0), - Vec2::new(1, 1), - Vec2::new(0, 1), - Vec2::new(-1, 0), - Vec2::new(-1, -1), - Vec2::new(0, -1), - Vec2::new(1, -1), - Vec2::new(-1, 1), - ] - .iter() - .filter(|&&p| swap.get(p).is_some()) - .map(|&p| { - let diff = - (swap.get(p).unwrap().temperature - cell.temperature) / WATER_BOILING_POINT; - p.as_().normalized() * diff * DT - }) - .sum::>(); - - // Curve wind based on topography - if let Some(xy) = if self.consts[point].normal.z < 1.0 { - Some(self.consts[point].normal.xy().normalized()) - } else { - None - } { - if self.cells[point].wind.dot(xy) > 0.0 { - const WIND_CHECK_START: f32 = 500.0; - const WIND_CHECK_STOP: f32 = 3000.0; - let alt_m = (self.consts[point].alt - WIND_CHECK_START) - / (WIND_CHECK_STOP - WIND_CHECK_START); - let reflected = self.cells[point].wind.reflected(xy) * (1.0 - 0.9f32.powf(DT)); - if reflected.x.is_nan() || reflected.y.is_nan() { - panic!("ref is nan"); - } - if self.cells[point].wind.x.is_nan() || self.cells[point].wind.y.is_nan() { - panic!("wind is nan"); - } - let drag = (1.0 - alt_m) * self.consts[point].normal.z; - self.cells[point].wind = self.cells[point].wind * drag - + reflected * alt_m * (1.0 - self.consts[point].normal.z); - } - } - - // If positive condense moisture to clouds, if negative evaporate clouds into - // moisture. - let condensation = cell.moisture * (1.0 - 0.99f32.powf((1.0 - temp_part) * DT)) - - cell.cloud * (1.0 - 0.98f32.powf(temp_part * DT)) / CLOUD_MAX; - - 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)); - - // Evaporate from ground. - self.cells[point].moisture = - cell.moisture + (self.consts[point].humid / 100.0) * temp_part * DT - condensation; - - self.cells[point].cloud = (cell.cloud + condensation - rain).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; - - self.info[point].lightning_chance = self.weather[point].cloud.powf(2.0) - * (self.weather[point].rain * 0.9 + 0.1) - * temp_part; - } - */ } pub fn size(&self) -> Vec2 { self.size } diff --git a/voxygen/src/audio/mod.rs b/voxygen/src/audio/mod.rs index 92f6808493..a03aa7871e 100644 --- a/voxygen/src/audio/mod.rs +++ b/voxygen/src/audio/mod.rs @@ -354,7 +354,7 @@ impl AudioFrontend { } } - // Retrieves the channel currently having the given tag + /// Retrieves the channel currently having the given tag fn get_ambient_channel( &mut self, channel_tag: AmbientChannelTag, @@ -452,13 +452,13 @@ impl AudioFrontend { } } - // Retrieves the current setting for sfx volume + /// Retrieves the current setting for sfx volume pub fn get_sfx_volume(&self) -> f32 { self.sfx_volume * self.master_volume } - // Retrieves the current setting for ambience volume + /// Retrieves the current setting for ambience volume pub fn get_ambience_volume(&self) -> f32 { self.ambience_volume * self.master_volume } - // Retrieves the current setting for music volume + /// Retrieves the current setting for music volume pub fn get_music_volume(&self) -> f32 { self.music_volume * self.master_volume } pub fn sfx_enabled(&self) -> bool { self.get_sfx_volume() > 0.0 } diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index c22ab247f4..34a48b5f9f 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -50,7 +50,7 @@ const QUAD_INDEX_BUFFER_U16_START_VERT_LEN: u16 = 3000; const QUAD_INDEX_BUFFER_U32_START_VERT_LEN: u32 = 3000; // For rain occlusion we only need to render the closest chunks. -// TODO: Is this a good value? +/// How many chunks are maximally rendered for rain occlusion. pub const RAIN_OCCLUSION_CHUNKS: usize = 9; /// A type that stores all the layouts associated with this renderer that never diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index 70c58a7b8d..c663799531 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -814,7 +814,6 @@ impl Terrain { loaded_distance: f32, camera: &Camera, ) -> ( - // TODO: Better return type? Aabb, Vec>, math::Aabr, @@ -1406,10 +1405,7 @@ impl Terrain { // Check if there is rain near the camera let max_weather = scene_data.state.max_weather_near(focus_pos.xy()); let visible_occlusion_volume = if max_weather.rain > 0.0 { - let occlusion_box = visible_bounding_box/*.intersection(Aabb { - min: focus_pos + camera.dependents().cam_pos - 100.0, - max: focus_pos + camera.dependents().cam_pos + 100.0, - })*/; + let occlusion_box = visible_bounding_box; let visible_bounding_box = math::Aabb:: { min: math::Vec3::from(occlusion_box.min - focus_off), max: math::Vec3::from(occlusion_box.max - focus_off),