diff --git a/voxygen/src/hud/settings_window/video.rs b/voxygen/src/hud/settings_window/video.rs index c0d227f82d..3bf10af9ea 100644 --- a/voxygen/src/hud/settings_window/video.rs +++ b/voxygen/src/hud/settings_window/video.rs @@ -318,7 +318,7 @@ impl<'a> Widget for Video<'a> { if let Some(new_val) = ImageSlider::discrete( self.global_state.settings.graphics.lod_distance, 0, - 250, + 500, self.imgs.slider_indicator, self.imgs.slider, ) diff --git a/voxygen/src/scene/lod.rs b/voxygen/src/scene/lod.rs index bebbcc202b..34c5e89635 100644 --- a/voxygen/src/scene/lod.rs +++ b/voxygen/src/scene/lod.rs @@ -4,6 +4,7 @@ use crate::{ FirstPassDrawer, Instances, LodObjectInstance, LodObjectVertex, LodTerrainVertex, Mesh, Model, Quad, Renderer, Tri, }, + scene::{camera, Camera}, settings::Settings, }; use client::Client; @@ -14,13 +15,26 @@ use common::{ util::srgba_to_linear, }; use hashbrown::HashMap; +use std::ops::Range; +use treeculler::{BVol, Frustum, AABB}; use vek::*; +// For culling +const MAX_OBJECT_RADIUS: i32 = 64; + +struct ObjectGroup { + instances: Instances, + // None implies no instances + z_range: Option>, + frustum_last_plane_index: u8, + visible: bool, +} + pub struct Lod { model: Option<(u32, Model)>, data: LodData, - zone_objects: HashMap, HashMap>>, + zone_objects: HashMap, HashMap>, object_data: HashMap>, } @@ -62,7 +76,13 @@ impl Lod { self.data.tgt_detail = (detail - detail % 2).max(100).min(2500); } - pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client) { + pub fn maintain( + &mut self, + renderer: &mut Renderer, + client: &Client, + focus_pos: Vec3, + camera: &Camera, + ) { // Update LoD terrain mesh according to detail if self .model @@ -78,14 +98,21 @@ impl Lod { )); } - // Maintain LoD object instances + // Create new LoD groups new a new zone has loaded for (p, zone) in client.lod_zones() { self.zone_objects.entry(*p).or_insert_with(|| { let mut objects = HashMap::<_, Vec<_>>::new(); + let mut z_range = None; for object in zone.objects.iter() { let pos = p.map(|e| lod::to_wpos(e) as f32).with_z(0.0) + object.pos.map(|e| e as f32) + Vec2::broadcast(0.5).with_z(0.0); + z_range = Some(z_range.map_or( + pos.z as i32..pos.z as i32, + |z_range: Range| { + z_range.start.min(pos.z as i32)..z_range.end.max(pos.z as i32) + }, + )); objects .entry(object.kind) .or_default() @@ -94,19 +121,53 @@ impl Lod { objects .into_iter() .map(|(kind, instances)| { - ( - kind, - renderer + (kind, ObjectGroup { + instances: renderer .create_instances(&instances) .expect("Renderer error?!"), - ) + z_range: z_range.clone(), + frustum_last_plane_index: 0, + visible: false, + }) }) .collect() }); } + // Remove zones that are unloaded self.zone_objects .retain(|p, _| client.lod_zones().contains_key(p)); + + // Determine visiblity of zones based on view frustum + let camera::Dependents { + view_mat, + proj_mat_treeculler, + .. + } = camera.dependents(); + let focus_off = focus_pos.map(|e| e.trunc()); + let frustum = Frustum::from_modelview_projection( + (proj_mat_treeculler * view_mat * Mat4::translation_3d(-focus_off)).into_col_arrays(), + ); + for (pos, groups) in &mut self.zone_objects { + for group in groups.values_mut() { + if let Some(z_range) = &group.z_range { + let group_min = (pos.map(lod::to_wpos).with_z(z_range.start) + - MAX_OBJECT_RADIUS) + .map(|e| e as f32); + let group_max = ((pos + 1).map(lod::to_wpos).with_z(z_range.end) + + MAX_OBJECT_RADIUS) + .map(|e| e as f32); + let (in_frustum, last_plane_index) = + AABB::new(group_min.into_array(), group_max.into_array()) + .coherent_test_against_frustum( + &frustum, + group.frustum_last_plane_index, + ); + group.visible = in_frustum; + group.frustum_last_plane_index = last_plane_index; + } + } + } } pub fn render<'a>(&'a self, drawer: &mut FirstPassDrawer<'a>) { @@ -116,10 +177,10 @@ impl Lod { // Draw LoD objects let mut drawer = drawer.draw_lod_objects(); - for objects in self.zone_objects.values() { - for (kind, instances) in objects { + for groups in self.zone_objects.values() { + for (kind, group) in groups.iter().filter(|(_, g)| g.visible) { if let Some(model) = self.object_data.get(kind) { - drawer.draw(model, instances); + drawer.draw(model, &group.instances); } } } diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index 846ed21e1c..4e740f8558 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -681,7 +681,7 @@ impl Scene { renderer.update_postprocess_locals(PostProcessLocals::new(proj_mat_inv, view_mat_inv)); // Maintain LoD. - self.lod.maintain(renderer, client); + self.lod.maintain(renderer, client, focus_pos, &self.camera); // Maintain debug shapes self.debug.maintain(renderer);