more correct occlusion

This commit is contained in:
IsseW 2022-03-28 15:22:57 +02:00
parent 83ee54001e
commit 54f958acc7
15 changed files with 246 additions and 121 deletions

View File

@ -102,7 +102,7 @@ void main() {
#ifdef EXPERIMENTAL_RAIN
vec3 old_color = color.rgb;
// If this value is changed also change it in voxygen/src/scene/mod.rs
// If this value is changed also change it in common/src/weather.rs
float fall_rate = 70.0;
dir.xy += wind_vel * dir.z / fall_rate;
dir = normalize(dir);
@ -118,7 +118,6 @@ void main() {
rain_dist *= 0.3;
vec2 drop_density = vec2(30, 1);
vec2 drop_size = vec2(0.0008, 0.05);
vec2 rain_pos = (view_pos * rain_dist);
rain_pos += vec2(0, tick.x * fall_rate + cam_wpos.z);
@ -140,6 +139,7 @@ void main() {
break;
}
float rain_density = rain_density_at(cam_wpos.xy + rpos.xy) * rain_occlusion_at(cam_pos.xyz + rpos.xyz) * 10.0;
vec2 drop_size = vec2(0.0008, 0.05);
if (fract(hash(fract(vec4(cell, rain_dist, 0) * 0.01))) > rain_density) {
continue;

View File

@ -48,7 +48,7 @@ use common::{
trade::{PendingTrade, SitePrices, TradeAction, TradeId, TradeResult},
uid::{Uid, UidAllocator},
vol::RectVolSize,
weather::{self, Weather},
weather::{self, Weather, WeatherGrid},
};
#[cfg(feature = "tracy")] use common_base::plot;
use common_base::{prof_span, span};
@ -153,24 +153,23 @@ pub struct SiteInfoRich {
}
struct WeatherLerp {
old: (Grid<Weather>, Instant),
new: (Grid<Weather>, Instant),
current: Grid<Weather>,
old: (WeatherGrid, Instant),
new: (WeatherGrid, Instant),
}
impl WeatherLerp {
fn weather_update(&mut self, weather: Grid<Weather>) {
fn weather_update(&mut self, weather: WeatherGrid) {
self.old = mem::replace(&mut self.new, (weather, Instant::now()));
}
fn update(&mut self) {
fn update(&mut self, to_update: &mut WeatherGrid) {
let old = &self.old.0;
let new = &self.new.0;
if new.size() == Vec2::zero() {
return;
}
if self.current.size() != new.size() {
self.current = new.clone();
if to_update.size() != new.size() {
*to_update = new.clone();
}
if old.size() == new.size() {
// Assume updates are regular
@ -178,9 +177,12 @@ impl WeatherLerp {
/ self.new.1.duration_since(self.old.1).as_secs_f32())
.clamp(0.0, 1.0);
old.iter().zip(new.iter()).for_each(|((p, old), (_, new))| {
self.current[p] = Weather::lerp(old, new, t);
});
to_update
.iter_mut()
.zip(old.iter().zip(new.iter()))
.for_each(|((_, current), ((_, old), (_, new)))| {
*current = Weather::lerp(old, new, t);
});
}
}
}
@ -188,15 +190,8 @@ impl WeatherLerp {
impl Default for WeatherLerp {
fn default() -> Self {
Self {
old: (
Grid::new(Vec2::new(0, 0), Weather::default()),
Instant::now(),
),
new: (
Grid::new(Vec2::new(0, 0), Weather::default()),
Instant::now(),
),
current: Grid::new(Vec2::new(0, 0), Weather::default()),
old: (WeatherGrid::new(Vec2::broadcast(0)), Instant::now()),
new: (WeatherGrid::new(Vec2::broadcast(0)), Instant::now()),
}
}
}
@ -1465,47 +1460,12 @@ impl Client {
.map(|v| v.0)
}
pub fn get_weather(&self) -> &Grid<Weather> { &self.weather.current }
pub fn current_weather(&self) -> Weather {
pub fn weather_at_player(&self) -> Weather {
self.position()
.map(|wpos| self.current_weather_wpos(wpos.xy()))
.map(|wpos| self.state.weather_at(wpos.xy()))
.unwrap_or_default()
}
pub fn current_weather_wpos(&self, wpos: Vec2<f32>) -> Weather {
let cell_pos = wpos / ((TerrainChunkSize::RECT_SIZE * weather::CHUNKS_PER_CELL).as_());
let rpos = cell_pos.map(|e| e.fract());
let cell_pos = cell_pos.map(|e| e.floor());
let wpos = cell_pos.as_::<i32>();
Weather::lerp(
&Weather::lerp(
self.weather
.current
.get(wpos)
.unwrap_or(&Weather::default()),
self.weather
.current
.get(wpos + Vec2::unit_x())
.unwrap_or(&Weather::default()),
rpos.x,
),
&Weather::lerp(
self.weather
.current
.get(wpos + Vec2::unit_x())
.unwrap_or(&Weather::default()),
self.weather
.current
.get(wpos + Vec2::one())
.unwrap_or(&Weather::default()),
rpos.x,
),
rpos.y,
)
}
pub fn current_chunk(&self) -> Option<Arc<TerrainChunk>> {
let chunk_pos = Vec2::from(self.position()?)
.map2(TerrainChunkSize::RECT_SIZE, |e: f32, sz| {
@ -1724,6 +1684,9 @@ impl Client {
self.invite = None;
}
// TODO: put this somewhere else? Otherwise update comments here.
self.weather.update(&mut self.state.weather_grid_mut());
// Lerp towards the target time of day - this ensures a smooth transition for
// large jumps in TimeOfDay such as when using /time
if let Some(target_tod) = self.target_time_of_day {
@ -1751,8 +1714,6 @@ impl Client {
// 5) Terrain
self.tick_terrain()?;
// TODO: put this somewhere else?
self.weather.update();
// Send a ping to the server once every second
if self.state.get_time() - self.last_server_ping > 1. {

View File

@ -15,7 +15,7 @@ use common::{
trade::{PendingTrade, SitePrices, TradeId, TradeResult},
uid::Uid,
uuid::Uuid,
weather::Weather,
weather::{Weather, WeatherGrid},
};
use hashbrown::HashMap;
use serde::{Deserialize, Serialize};
@ -198,7 +198,7 @@ pub enum ServerGeneral {
/// Economic information about sites
SiteEconomy(EconomyInfo),
MapMarker(comp::MapMarkerUpdate),
WeatherUpdate(common::grid::Grid<Weather>),
WeatherUpdate(WeatherGrid),
}
impl ServerGeneral {

View File

@ -1,10 +1,11 @@
use std::fmt;
use serde::{Deserialize, Serialize};
use vek::{Lerp, Vec2};
use vek::{Lerp, Vec2, Vec3};
pub const CHUNKS_PER_CELL: u32 = 16;
// Weather::default is Clear, 0 degrees C and no wind
use crate::{grid::Grid, terrain::TerrainChunkSize, vol::RectVolSize};
/// 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
@ -39,6 +40,13 @@ impl Weather {
wind: Vec2::<f32>::lerp(from.wind, to.wind, t),
}
}
// Get the rain direction for this weather
pub fn rain_dir(&self) -> Vec3<f32> {
// If this value is changed also change it in cloud-frag.glsl
const FALL_RATE: f32 = 70.0;
(-Vec3::unit_z() + self.wind / FALL_RATE).normalized()
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
@ -59,3 +67,62 @@ impl fmt::Display for WeatherKind {
}
}
}
pub const CHUNKS_PER_CELL: u32 = 16;
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<Weather>,
}
impl WeatherGrid {
pub fn new(size: Vec2<u32>) -> Self {
Self {
weather: Grid::new(size.as_(), Weather::default()),
}
}
pub fn iter(&self) -> impl Iterator<Item = (Vec2<i32>, &Weather)> { self.weather.iter() }
pub fn iter_mut(&mut self) -> impl Iterator<Item = (Vec2<i32>, &mut Weather)> {
self.weather.iter_mut()
}
pub fn size(&self) -> Vec2<u32> { self.weather.size().as_() }
/// Get the weather at a given world position by doing bilinear
/// interpolation between four cells.
pub fn get_interpolated(&self, wpos: Vec2<f32>) -> Weather {
let cell_pos = wpos / CELL_SIZE as f32;
let rpos = cell_pos.map(|e| e.fract());
let cell_pos = cell_pos.map(|e| e.floor());
let wpos = cell_pos.as_::<i32>();
Weather::lerp(
&Weather::lerp(
self.weather.get(wpos).unwrap_or(&Weather::default()),
self.weather
.get(wpos + Vec2::unit_x())
.unwrap_or(&Weather::default()),
rpos.x,
),
&Weather::lerp(
self.weather
.get(wpos + Vec2::unit_x())
.unwrap_or(&Weather::default()),
self.weather
.get(wpos + Vec2::one())
.unwrap_or(&Weather::default()),
rpos.x,
),
rpos.y,
)
}
}

View File

@ -21,6 +21,7 @@ use common::{
time::DayPeriod,
trade::Trades,
vol::{ReadVol, WriteVol},
weather::{Weather, WeatherGrid},
};
use common_base::span;
use common_ecs::{PhysicsMetrics, SysMetrics};
@ -206,6 +207,7 @@ impl State {
// Register synced resources used by the ECS.
ecs.insert(TimeOfDay(0.0));
ecs.insert(Calendar::default());
ecs.insert(WeatherGrid::new(Vec2::zero()));
// Register unsynced resources used by the ECS.
ecs.insert(Time(0.0));
@ -346,13 +348,23 @@ impl State {
/// last game tick.
pub fn terrain_changes(&self) -> Fetch<TerrainChanges> { self.ecs.read_resource() }
/// Get a reference the current in-game weather grid.
pub fn weather_grid(&self) -> Fetch<WeatherGrid> { self.ecs.read_resource() }
/// Get a mutable reference the current in-game weather grid.
pub fn weather_grid_mut(&mut self) -> FetchMut<WeatherGrid> { self.ecs.write_resource() }
/// Get the current weather at a position.
pub fn weather_at(&self, pos: Vec2<f32>) -> Weather {
self.weather_grid().get_interpolated(pos)
}
/// Get the current in-game time of day.
///
/// Note that this should not be used for physics, animations or other such
/// localised timings.
pub fn get_time_of_day(&self) -> f64 { self.ecs.read_resource::<TimeOfDay>().0 }
/// Get the current in-game day period (period of the day/night cycle)
/// Get the current in-game day period (period of the day/night cycle)
pub fn get_day_period(&self) -> DayPeriod { self.get_time_of_day().into() }

View File

@ -1,4 +1,4 @@
use common::weather::CHUNKS_PER_CELL;
use common::weather::{WeatherGrid, CHUNKS_PER_CELL, WEATHER_DT};
use common_ecs::{dispatch, System};
use common_state::State;
use specs::DispatcherBuilder;
@ -18,17 +18,18 @@ pub fn add_server_systems(dispatch_builder: &mut DispatcherBuilder) {
pub fn init(state: &mut State, world: &world::World) {
// How many chunks wide a weather cell is.
// 16 here means that a weather cell is 16x16 chunks.
let sim = sim::WeatherSim::new(world.sim().get_size() / CHUNKS_PER_CELL, world);
let weather_size = world.sim().get_size() / CHUNKS_PER_CELL;
let sim = sim::WeatherSim::new(weather_size, world);
state.ecs_mut().insert(sim);
// Tick weather every 2 seconds
state
.ecs_mut()
.insert(SysScheduler::<tick::Sys>::every(Duration::from_secs_f32(
sim::DT,
WEATHER_DT,
)));
state
.ecs_mut()
.insert(SysScheduler::<sync::Sys>::every(Duration::from_secs_f32(
sim::DT,
WEATHER_DT,
)));
}

View File

@ -3,7 +3,7 @@ use common::{
resources::TimeOfDay,
terrain::TerrainChunkSize,
vol::RectVolSize,
weather::{Weather, CHUNKS_PER_CELL},
weather::{Weather, WeatherGrid, CELL_SIZE, CHUNKS_PER_CELL},
};
use itertools::Itertools;
use noise::{NoiseFn, SuperSimplex, Turbulence};
@ -43,18 +43,9 @@ pub struct WeatherInfo {
pub struct WeatherSim {
cells: Grid<Cell>, // The variables used for simulation
consts: Grid<Constants>, // The constants from the world used for simulation
weather: Grid<Weather>, // The current weather.
info: Grid<WeatherInfo>,
}
/*
const MAX_WIND_SPEED: f32 = 128.0;
*/
pub(crate) const CELL_SIZE: u32 = CHUNKS_PER_CELL * TerrainChunkSize::RECT_SIZE.x;
/// How often the weather is updated, in seconds
pub(crate) const DT: f32 = 5.0; // CELL_SIZE as f32 / MAX_WIND_SPEED;
fn sample_plane_normal(points: &[Vec3<f32>]) -> Option<Vec3<f32>> {
if points.len() < 3 {
return None;
@ -140,7 +131,6 @@ impl WeatherSim {
})
.collect_vec(),
),
weather: Grid::new(size, Weather::default()),
info: Grid::new(size, WeatherInfo::default()),
};
this.cells.iter_mut().for_each(|(point, cell)| {
@ -150,8 +140,6 @@ impl WeatherSim {
this
}
pub fn get_weather(&self) -> &Grid<Weather> { &self.weather }
/*
fn get_cell(&self, p: Vec2<i32>, time: f64) -> Cell {
*self.cells.get(p).unwrap_or(&sample_cell(p, time))
@ -160,7 +148,7 @@ 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, time_of_day: &TimeOfDay) {
pub fn tick(&mut self, time_of_day: &TimeOfDay, out: &mut WeatherGrid) {
let time = time_of_day.0;
let base_nz = Turbulence::new(
@ -173,7 +161,7 @@ impl WeatherSim {
let rain_nz = SuperSimplex::new();
for (point, cell) in self.weather.iter_mut() {
for (point, cell) in out.iter_mut() {
let wpos = cell_to_wpos(point);
let pos = wpos.as_::<f64>() + time as f64 * 0.1;
@ -391,4 +379,6 @@ impl WeatherSim {
}
*/
}
pub fn size(&self) -> Vec2<u32> { self.cells.size().as_() }
}

View File

@ -1,3 +1,4 @@
use common::weather::WeatherGrid;
use common_ecs::{Origin, Phase, System};
use common_net::msg::ServerGeneral;
use specs::{Join, ReadExpect, ReadStorage, Write};
@ -11,7 +12,7 @@ pub struct Sys;
impl<'a> System<'a> for Sys {
type SystemData = (
ReadExpect<'a, WeatherSim>,
ReadExpect<'a, WeatherGrid>,
Write<'a, SysScheduler<Self>>,
ReadStorage<'a, Client>,
);
@ -20,14 +21,16 @@ impl<'a> System<'a> for Sys {
const ORIGIN: Origin = Origin::Server;
const PHASE: Phase = Phase::Create;
fn run(_job: &mut common_ecs::Job<Self>, (sim, mut scheduler, clients): Self::SystemData) {
fn run(
_job: &mut common_ecs::Job<Self>,
(weather_grid, mut scheduler, clients): Self::SystemData,
) {
if scheduler.should_run() {
let mut lazy_msg = None;
for client in clients.join() {
if lazy_msg.is_none() {
lazy_msg = Some(
client.prepare(ServerGeneral::WeatherUpdate(sim.get_weather().clone())),
);
lazy_msg =
Some(client.prepare(ServerGeneral::WeatherUpdate(weather_grid.clone())));
}
lazy_msg.as_ref().map(|msg| client.send_prepared(msg));
}

View File

@ -1,4 +1,4 @@
use common::resources::TimeOfDay;
use common::{resources::TimeOfDay, weather::WeatherGrid};
use common_ecs::{Origin, Phase, System};
use specs::{Read, Write, WriteExpect};
@ -13,6 +13,7 @@ impl<'a> System<'a> for Sys {
type SystemData = (
Read<'a, TimeOfDay>,
WriteExpect<'a, WeatherSim>,
WriteExpect<'a, WeatherGrid>,
Write<'a, SysScheduler<Self>>,
);
@ -22,10 +23,13 @@ impl<'a> System<'a> for Sys {
fn run(
_job: &mut common_ecs::Job<Self>,
(game_time, mut sim, mut scheduler): Self::SystemData,
(game_time, mut sim, mut grid, mut scheduler): Self::SystemData,
) {
if scheduler.should_run() {
sim.tick(&*game_time);
if grid.size() != sim.size() {
*grid = WeatherGrid::new(sim.size());
}
sim.tick(&game_time, &mut grid);
}
}
}

View File

@ -157,11 +157,11 @@ impl AmbientMgr {
}
fn check_rain_necessity(&mut self, client: &Client) -> bool {
client.current_weather().rain > 0.001
client.weather_at_player().rain > 0.001
}
fn check_thunder_necessity(&mut self, client: &Client) -> bool {
client.current_weather().rain * 500.0 > 0.7
client.weather_at_player().rain * 500.0 > 0.7
}
fn check_leaves_necessity(&mut self, client: &Client, camera: &Camera) -> bool {
@ -238,7 +238,7 @@ impl AmbientChannel {
let focus_off = camera.get_focus_pos().map(f32::trunc);
let cam_pos = camera.dependents().cam_pos + focus_off;
// Float from around -30.0 to 30.0
let client_wind_speed_sq = client.current_weather().wind.magnitude_squared();
let client_wind_speed_sq = client.weather_at_player().wind.magnitude_squared();
let (terrain_alt, tree_density) = if let Some(chunk) = client.current_chunk() {
(chunk.meta().alt(), chunk.meta().tree_density())
@ -268,13 +268,13 @@ impl AmbientChannel {
// multipler at end will have to change depending on how intense rain normally
// is
// TODO: make rain diminish with distance above terrain
let rain_intensity = client.current_weather().rain * 500.0;
let rain_intensity = client.weather_at_player().rain * 500.0;
return rain_intensity.min(0.9);
}
fn get_thunder_volume(&mut self, client: &Client) -> f32 {
let thunder_intensity = client.current_weather().rain * 500.0;
let thunder_intensity = client.weather_at_player().rain * 500.0;
if thunder_intensity < 0.7 {
0.0

View File

@ -332,7 +332,7 @@ impl MusicMgr {
let is_dark = (state.get_day_period().is_dark()) as bool;
let current_period_of_day = Self::get_current_day_period(is_dark);
let current_weather = client.current_weather();
let current_weather = client.weather_at_player();
let current_biome = client.current_biome();
let current_site = client.current_site();

View File

@ -2388,7 +2388,7 @@ impl Hud {
.font_size(self.fonts.cyri.scale(14))
.set(self.ids.time, ui_widgets);
let weather = client.current_weather();
let weather = client.weather_at_player();
Text::new(&format!(
"Weather({kind:.5}): {{cloud: {cloud:.5}, rain: {rain:.5}, wind: <{wind_x:.5}, \
{wind_y:.2}>}}",

View File

@ -182,7 +182,7 @@ impl Lod {
}
}
// Update weather texture
let weather = client.get_weather();
let weather = client.state().weather_grid();
let size = weather.size().as_::<u32>();
renderer.update_texture(
&self.data.weather,

View File

@ -692,7 +692,10 @@ 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,
client
.state()
.weather_at(focus_off.xy() + 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));
@ -704,13 +707,14 @@ impl Scene {
self.debug.maintain(renderer);
// Maintain the terrain.
let (_visible_bounds, visible_light_volume, visible_psr_bounds) = self.terrain.maintain(
renderer,
scene_data,
focus_pos,
self.loaded_distance,
&self.camera,
);
let (_visible_bounds, visible_light_volume, visible_psr_bounds, visible_occlusion_volume) =
self.terrain.maintain(
renderer,
scene_data,
focus_pos,
self.loaded_distance,
&self.camera,
);
// Maintain the figures.
let _figure_bounds = self.figure_mgr.maintain(
@ -752,7 +756,8 @@ impl Scene {
* Mat4::translation_3d(Vec3::new(1.0, -1.0, 0.0));
let directed_mats = |d_view_mat: math::Mat4<f32>,
d_dir: math::Vec3<f32>|
d_dir: math::Vec3<f32>,
volume: &Vec<math::Vec3<f32>>|
-> (Mat4<f32>, Mat4<f32>) {
// NOTE: Light view space, right-handed.
let v_p_orig = math::Vec3::from(d_view_mat * math::Vec4::from_direction(new_dir));
@ -765,7 +770,7 @@ impl Scene {
// (right-handed).
let bounds1 = math::fit_psr(
view_mat.map_cols(math::Vec4::from),
visible_light_volume.iter().copied(),
volume.iter().copied(),
math::Vec4::homogenized,
);
let n_e = f64::from(-bounds1.max.z);
@ -997,17 +1002,15 @@ impl Scene {
)
};
let weather = client.current_weather_wpos(focus_off.xy() + cam_pos.xy());
let weather = client.state().weather_at(focus_off.xy() + cam_pos.xy());
if true || weather.rain > 0.001
// TODO: check if rain map mode is on
{
// If this value is changed also change it in cloud-frag.glsl
const FALL_RATE: f32 = 70.0;
let rain_dir =
math::Vec3::from(-Vec3::unit_z() + weather.wind / FALL_RATE).normalized();
let rain_dir = math::Vec3::from(weather.rain_dir());
let rain_view_mat = math::Mat4::look_at_rh(look_at, look_at + rain_dir, up);
let (shadow_mat, texture_mat) = directed_mats(rain_view_mat, rain_dir);
let (shadow_mat, texture_mat) =
directed_mats(rain_view_mat, rain_dir, &visible_occlusion_volume);
let rain_occlusion_locals = RainOcclusionLocals::new(shadow_mat, texture_mat);
renderer.update_consts(&mut self.data.rain_occlusion_mats, &[rain_occlusion_locals]);
@ -1029,7 +1032,8 @@ impl Scene {
let mut directed_shadow_mats = Vec::with_capacity(6);
let light_view_mat = math::Mat4::look_at_rh(look_at, look_at + directed_light_dir, up);
let (shadow_mat, texture_mat) = directed_mats(light_view_mat, directed_light_dir);
let (shadow_mat, texture_mat) =
directed_mats(light_view_mat, directed_light_dir, &visible_light_volume);
let shadow_locals = ShadowLocals::new(shadow_mat, texture_mat);
@ -1166,7 +1170,7 @@ impl Scene {
prof_span!("rain occlusion");
if let Some(mut occlusion_pass) = drawer.rain_occlusion_pass() {
self.terrain
.render_shadows(&mut occlusion_pass.draw_terrain_shadows(), focus_pos);
.render_occlusion(&mut occlusion_pass.draw_terrain_shadows(), cam_pos);
self.figure_mgr.render_shadows(
&mut occlusion_pass.draw_figure_shadows(),

View File

@ -812,7 +812,13 @@ impl<V: RectRasterableVol> Terrain<V> {
focus_pos: Vec3<f32>,
loaded_distance: f32,
camera: &Camera,
) -> (Aabb<f32>, Vec<math::Vec3<f32>>, math::Aabr<f32>) {
) -> (
// TODO: Better return type?
Aabb<f32>,
Vec<math::Vec3<f32>>,
math::Aabr<f32>,
Vec<math::Vec3<f32>>,
) {
let camera::Dependents {
view_mat,
proj_mat_treeculler,
@ -1395,11 +1401,56 @@ impl<V: RectRasterableVol> Terrain<V> {
})
};
drop(guard);
span!(guard, "Rain occlusion magic");
let weather = scene_data.state.weather_at(focus_pos.xy());
let visible_occlusion_volume = if weather.rain > 0.0 {
let occlusion_box = Aabb {
min: visible_bounding_box
.min
.map2(focus_pos - 10.0, |a, b| a.max(b)),
max: visible_bounding_box
.max
.map2(focus_pos + 10.0, |a, b| a.min(b)),
};
let visible_bounding_box = math::Aabb::<f32> {
min: math::Vec3::from(occlusion_box.min - focus_off),
max: math::Vec3::from(occlusion_box.max - focus_off),
};
let visible_bounds_fine = math::Aabb {
min: math::Vec3::from(visible_bounding_box.min.as_::<f64>()),
max: math::Vec3::from(visible_bounding_box.max.as_::<f64>()),
};
// TODO: move out shared calculations
let inv_proj_view =
math::Mat4::from_col_arrays((proj_mat_treeculler * view_mat).into_col_arrays())
.as_::<f64>()
.inverted();
let ray_direction = math::Vec3::<f32>::from(weather.rain_dir());
// NOTE: We use proj_mat_treeculler here because
// calc_focused_light_volume_points makes the assumption that the
// near plane lies before the far plane.
let visible_occlusion_volume = math::calc_focused_light_volume_points(
inv_proj_view,
ray_direction.as_::<f64>(),
visible_bounds_fine,
1e-6,
)
.map(|v| v.as_::<f32>())
.collect::<Vec<_>>();
visible_occlusion_volume
} else {
Vec::new()
};
drop(guard);
(
visible_bounding_box,
visible_light_volume,
visible_psr_bounds,
visible_occlusion_volume,
)
}
@ -1452,6 +1503,38 @@ impl<V: RectRasterableVol> Terrain<V> {
.for_each(|(model, locals)| drawer.draw(model, locals));
}
pub fn render_occlusion<'a>(
&'a self,
drawer: &mut TerrainShadowDrawer<'_, 'a>,
focus_pos: Vec3<f32>,
) {
span!(_guard, "render_occlusion", "Terrain::render_occlusion");
let focus_chunk = Vec2::from(focus_pos).map2(TerrainChunk::RECT_SIZE, |e: f32, sz| {
(e as i32).div_euclid(sz as i32)
});
// For rain occlusion we only need to render the closest chunks.
// TODO: Is this a good value?
const RAIN_OCCLUSION_CHUNKS: usize = 16;
let chunk_iter = Spiral2d::new()
.filter_map(|rpos| {
let pos = focus_chunk + rpos;
self.chunks.get(&pos)
})
.take(self.chunks.len().min(RAIN_OCCLUSION_CHUNKS));
chunk_iter
// Find a way to keep this?
// .filter(|chunk| chunk.can_shadow_sun())
.filter_map(|chunk| {
// TODO: Should the fuid model also be considered here?
chunk
.opaque_model
.as_ref()
.map(|model| (model, &chunk.locals))
})
.for_each(|(model, locals)| drawer.draw(model, locals));
}
pub fn chunks_for_point_shadows(
&self,
focus_pos: Vec3<f32>,