diff --git a/voxygen/src/mesh/terrain.rs b/voxygen/src/mesh/terrain.rs index 70a8e29cea..6116776a69 100644 --- a/voxygen/src/mesh/terrain.rs +++ b/voxygen/src/mesh/terrain.rs @@ -30,6 +30,7 @@ enum FaceKind { } pub const SUNLIGHT: u8 = 24; +pub const SUNLIGHT_INV: f32 = 1.0 / SUNLIGHT as f32; pub const MAX_LIGHT_DIST: i32 = SUNLIGHT as i32; fn calc_light + ReadVol + Debug>( @@ -217,7 +218,7 @@ fn calc_light + ReadVol + Debug>( .unwrap_or(default_light); if l != OPAQUE && l != UNKNOWN { - l as f32 / SUNLIGHT as f32 + l as f32 * SUNLIGHT_INV } else { 0.0 } diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index b7a0ec5f5e..fb9a51ce8f 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -6411,6 +6411,8 @@ impl FigureState { // get the rider offset skel_body: S::Body, ) { + span!(_guard, "update", "FigureState::update"); + // NOTE: As long as update() always gets called after get_or_create_model(), and // visibility is not set again until after the model is rendered, we // know we don't pair the character model with invalid model state. @@ -6475,6 +6477,11 @@ impl FigureState { let (light, glow) = terrain .map(|t| { + span!( + _guard, + "light_glow", + "FigureState::update (fetch light/glow)" + ); // Sample the location a little above to avoid clipping into terrain // TODO: Try to make this faster? It might be fine though let wpos = Vec3::from(pos.into_array()) + Vec3::unit_z(); diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index e9b6b3ce34..63704f31f3 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -6,7 +6,7 @@ use crate::{ mesh::{ greedy::{GreedyMesh, SpriteAtlasAllocator}, segment::generate_mesh_base_vol_sprite, - terrain::{generate_mesh, SUNLIGHT}, + terrain::{generate_mesh, SUNLIGHT_INV}, }, render::{ pipelines::{self, ColLights}, @@ -775,9 +775,8 @@ impl Terrain { let chunk_pos = wpos_chunk + rpos; self.chunks .get(&chunk_pos) - .map(|c| c.blocks_of_interest.lights.iter()) .into_iter() - .flatten() + .flat_map(|c| c.blocks_of_interest.lights.iter()) .map(move |(lpos, level)| { ( Vec3::::from( @@ -791,7 +790,7 @@ impl Terrain { (Vec3::broadcast(0.001), 0.0), |(bias, total), (lpos, level)| { let rpos = lpos.map(|e| e as f32 + 0.5) - wpos; - let level = (*level as f32 - rpos.magnitude()).max(0.0) / SUNLIGHT as f32; + let level = (*level as f32 - rpos.magnitude()).max(0.0) * SUNLIGHT_INV; ( bias + rpos.try_normalized().unwrap_or_else(Vec3::zero) * level, total + level, diff --git a/voxygen/src/scene/terrain/watcher.rs b/voxygen/src/scene/terrain/watcher.rs index 0ca420bef8..ddffed60f1 100644 --- a/voxygen/src/scene/terrain/watcher.rs +++ b/voxygen/src/scene/terrain/watcher.rs @@ -71,6 +71,9 @@ impl BlocksOfInterest { let mut flowers = Vec::new(); let mut interactables = Vec::new(); let mut lights = Vec::new(); + // Lights that can be omitted at random if we have too many and need to cull + // some of them + let mut minor_lights = Vec::new(); let mut fire_bowls = Vec::new(); let mut snow = Vec::new(); let mut cricket1 = Vec::new(); @@ -168,10 +171,26 @@ impl BlocksOfInterest { interactables.push((pos, Interaction::Collect)); } if let Some(glow) = block.get_glow() { - lights.push((pos, glow)); + // Currently, we count filled blocks as 'minor' lights, and sprites as + // non-minor. + if block.get_sprite().is_none() { + minor_lights.push((pos, glow)); + } else { + lights.push((pos, glow)); + } } }); + // TODO: Come up with a better way to prune many light sources: grouping them + // into larger lights with k-means clustering, perhaps? + const MAX_MINOR_LIGHTS: usize = 64; + let minor_light_count = minor_lights.len(); + lights.extend( + minor_lights + .choose_multiple(&mut rng, MAX_MINOR_LIGHTS) + .copied(), + ); + Self { leaves, drip,