From cc61925486f463cc1be50c80ae606dde5d44d33d Mon Sep 17 00:00:00 2001 From: Yusuf Bera Ertan Date: Wed, 20 Nov 2019 02:29:34 +0300 Subject: [PATCH] Use temporal coherence for figure frustum culling, don't process figures if they are not in view frustum --- Cargo.lock | 2 +- voxygen/src/menu/char_selection/scene.rs | 2 + voxygen/src/scene/figure/mod.rs | 173 ++++++++++++++++++----- voxygen/src/scene/mod.rs | 2 +- 4 files changed, 143 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 06b2135261..73f02110b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1064,7 +1064,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "frustum_culling" version = "0.1.0" -source = "git+https://gitlab.com/yusdacra/frustum_culling#735aef658c7e5eb0c4d543cacbea2f3e06216438" +source = "git+https://gitlab.com/yusdacra/frustum_culling#7145dbee5eadf4df0cbe5aeda856a0967f47ace4" [[package]] name = "fsevent" diff --git a/voxygen/src/menu/char_selection/scene.rs b/voxygen/src/menu/char_selection/scene.rs index 906e60c0a4..e5712f956e 100644 --- a/voxygen/src/menu/char_selection/scene.rs +++ b/voxygen/src/menu/char_selection/scene.rs @@ -169,6 +169,8 @@ impl Scene { 1.0 / 60.0, // TODO: Use actual deltatime here? 1.0, 1.0, + 0, + true, ); } diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index 7d94eb92cd..cdf1be2e24 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -43,8 +43,6 @@ pub struct FigureMgr { fish_small_states: HashMap>, biped_large_states: HashMap>, object_states: HashMap>, - figure_count: usize, - visible_figure_count: usize, } impl FigureMgr { @@ -61,8 +59,6 @@ impl FigureMgr { fish_small_states: HashMap::new(), biped_large_states: HashMap::new(), object_states: HashMap::new(), - figure_count: 0, - visible_figure_count: 0, } } @@ -70,7 +66,7 @@ impl FigureMgr { self.model_cache.clean(tick); } - pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client) { + pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client, camera: &Camera) { let time = client.state().get_time(); let tick = client.get_tick(); let ecs = client.state().ecs(); @@ -82,8 +78,6 @@ impl FigureMgr { .get(client.entity()) .map_or(Vec3::zero(), |pos| pos.0); - self.figure_count = 0; - for (entity, pos, ori, scale, body, character, last_character, stats) in ( &ecs.entities(), &ecs.read_storage::(), @@ -96,7 +90,79 @@ impl FigureMgr { ) .join() { - self.figure_count += 1; + // Don't process figures outside the frustum spectrum + let frustum = camera.frustum(client); + + let (in_frustum, lpindex) = frustum.test_sphere_coherence( + pos.0.into_array(), + scale.unwrap_or(&Scale(1.0)).0 * 2.0, + match body { + Body::Humanoid(_) => { + self.character_states.get(&entity).map(|state| state.lpindex) + } + Body::QuadrupedSmall(_) => { + self.quadruped_small_states.get(&entity).map(|state| state.lpindex) + } + Body::QuadrupedMedium(_) => { + self.quadruped_medium_states.get(&entity).map(|state| state.lpindex) + } + Body::BirdMedium(_) => { + self.bird_medium_states.get(&entity).map(|state| state.lpindex) + } + Body::FishMedium(_) => { + self.fish_medium_states.get(&entity).map(|state| state.lpindex) + } + Body::Dragon(_) => { + self.dragon_states.get(&entity).map(|state| state.lpindex) + } + Body::BirdSmall(_) => { + self.bird_small_states.get(&entity).map(|state| state.lpindex) + } + Body::FishSmall(_) => { + self.fish_small_states.get(&entity).map(|state| state.lpindex) + } + Body::BipedLarge(_) => { + self.biped_large_states.get(&entity).map(|state| state.lpindex) + } + Body::Object(_) => { + self.object_states.get(&entity).map(|state| state.lpindex) + } + }.unwrap_or(0), + ); + + match body { + Body::Humanoid(_) => { + self.character_states.get_mut(&entity).map(|state| state.visible = in_frustum); + } + Body::QuadrupedSmall(_) => { + self.quadruped_small_states.get_mut(&entity).map(|state| state.visible = in_frustum); + } + Body::QuadrupedMedium(_) => { + self.quadruped_medium_states.get_mut(&entity).map(|state| state.visible = in_frustum); + } + Body::BirdMedium(_) => { + self.bird_medium_states.get_mut(&entity).map(|state| state.visible = in_frustum); + } + Body::FishMedium(_) => { + self.fish_medium_states.get_mut(&entity).map(|state| state.visible = in_frustum); + } + Body::Dragon(_) => { + self.dragon_states.get_mut(&entity).map(|state| state.visible = in_frustum); + } + Body::BirdSmall(_) => { + self.bird_small_states.get_mut(&entity).map(|state| state.visible = in_frustum); + } + Body::FishSmall(_) => { + self.fish_small_states.get_mut(&entity).map(|state| state.visible = in_frustum); + } + Body::BipedLarge(_) => { + self.biped_large_states.get_mut(&entity).map(|state| state.visible = in_frustum); + } + Body::Object(_) => { + self.object_states.get_mut(&entity).map(|state| state.visible = in_frustum); + } + } + // Don't process figures outside the vd let vd_frac = Vec2::from(pos.0 - player_pos) .map2(TerrainChunk::RECT_SIZE, |d: f32, sz| { @@ -139,7 +205,7 @@ impl FigureMgr { } } continue; - } else if vd_frac > 1.0 { + } else if vd_frac > 1.0 || !in_frustum { continue; } @@ -320,6 +386,8 @@ impl FigureMgr { dt, movement_animation_rate, action_animation_rate, + lpindex, + true, ); } Body::QuadrupedSmall(_) => { @@ -377,6 +445,8 @@ impl FigureMgr { dt, movement_animation_rate, action_animation_rate, + lpindex, + true, ); } Body::QuadrupedMedium(_) => { @@ -434,6 +504,8 @@ impl FigureMgr { dt, movement_animation_rate, action_animation_rate, + lpindex, + true, ); } Body::BirdMedium(_) => { @@ -489,6 +561,8 @@ impl FigureMgr { dt, movement_animation_rate, action_animation_rate, + lpindex, + true, ); } Body::FishMedium(_) => { @@ -544,6 +618,8 @@ impl FigureMgr { dt, movement_animation_rate, action_animation_rate, + lpindex, + true, ); } Body::Dragon(_) => { @@ -599,6 +675,8 @@ impl FigureMgr { dt, movement_animation_rate, action_animation_rate, + lpindex, + true, ); } Body::BirdSmall(_) => { @@ -654,6 +732,8 @@ impl FigureMgr { dt, movement_animation_rate, action_animation_rate, + lpindex, + true, ); } Body::FishSmall(_) => { @@ -709,6 +789,8 @@ impl FigureMgr { dt, movement_animation_rate, action_animation_rate, + lpindex, + true, ); } Body::BipedLarge(_) => { @@ -764,6 +846,8 @@ impl FigureMgr { dt, movement_animation_rate, action_animation_rate, + lpindex, + true, ); } Body::Object(_) => { @@ -783,6 +867,8 @@ impl FigureMgr { dt, movement_animation_rate, action_animation_rate, + lpindex, + true, ); } } @@ -823,15 +909,11 @@ impl FigureMgr { let tick = client.get_tick(); let ecs = client.state().ecs(); - let frustum = camera.frustum(client); - let character_state_storage = client .state() .read_storage::(); let character_state = character_state_storage.get(client.entity()); - self.visible_figure_count = 0; - for (entity, _, _, body, stats, _) in ( &ecs.entities(), &ecs.read_storage::(), @@ -841,59 +923,55 @@ impl FigureMgr { ecs.read_storage::().maybe(), ) .join() - // Don't render figures outside of frustum (camera viewport, max draw distance is farplane) - .filter(|(_, pos, _, _, _, scale)| { - frustum.test_sphere( - pos.0.into_array(), - scale.unwrap_or(&Scale(1.0)).0 * 2.0, - ) - }) // Don't render dead entities .filter(|(_, _, _, _, stats, _)| stats.map_or(true, |s| !s.is_dead)) { - self.visible_figure_count += 1; - if let Some((locals, bone_consts)) = match body { + if let Some((locals, bone_consts, visible)) = match body { Body::Humanoid(_) => self .character_states .get(&entity) - .map(|state| (state.locals(), state.bone_consts())), + .map(|state| (state.locals(), state.bone_consts(), state.visible)), Body::QuadrupedSmall(_) => self .quadruped_small_states .get(&entity) - .map(|state| (state.locals(), state.bone_consts())), + .map(|state| (state.locals(), state.bone_consts(), state.visible)), Body::QuadrupedMedium(_) => self .quadruped_medium_states .get(&entity) - .map(|state| (state.locals(), state.bone_consts())), + .map(|state| (state.locals(), state.bone_consts(), state.visible)), Body::BirdMedium(_) => self .bird_medium_states .get(&entity) - .map(|state| (state.locals(), state.bone_consts())), + .map(|state| (state.locals(), state.bone_consts(), state.visible)), Body::FishMedium(_) => self .fish_medium_states .get(&entity) - .map(|state| (state.locals(), state.bone_consts())), + .map(|state| (state.locals(), state.bone_consts(), state.visible)), Body::Dragon(_) => self .dragon_states .get(&entity) - .map(|state| (state.locals(), state.bone_consts())), + .map(|state| (state.locals(), state.bone_consts(), state.visible)), Body::BirdSmall(_) => self .bird_small_states .get(&entity) - .map(|state| (state.locals(), state.bone_consts())), + .map(|state| (state.locals(), state.bone_consts(), state.visible)), Body::FishSmall(_) => self .fish_small_states .get(&entity) - .map(|state| (state.locals(), state.bone_consts())), + .map(|state| (state.locals(), state.bone_consts(), state.visible)), Body::BipedLarge(_) => self .biped_large_states .get(&entity) - .map(|state| (state.locals(), state.bone_consts())), + .map(|state| (state.locals(), state.bone_consts(), state.visible)), Body::Object(_) => self .object_states .get(&entity) - .map(|state| (state.locals(), state.bone_consts())), + .map(|state| (state.locals(), state.bone_consts(), state.visible)), } { + if !visible { + continue; + } + let is_player = entity == client.entity(); let player_camera_mode = if is_player { @@ -922,11 +1000,30 @@ impl FigureMgr { } pub fn figure_count(&self) -> usize { - self.figure_count + self.character_states.len() + + self.quadruped_small_states.len() + + self.character_states.len() + + self.quadruped_medium_states.len() + + self.bird_medium_states.len() + + self.fish_medium_states.len() + + self.dragon_states.len() + + self.bird_small_states.len() + + self.fish_small_states.len() + + self.biped_large_states.len() + + self.object_states.len() } pub fn figure_count_visible(&self) -> usize { - self.visible_figure_count + self.character_states.iter().filter(|(_, c)| c.visible).count() + + self.quadruped_small_states.iter().filter(|(_, c)| c.visible).count() + + self.quadruped_medium_states.iter().filter(|(_, c)| c.visible).count() + + self.bird_medium_states.iter().filter(|(_, c)| c.visible).count() + + self.fish_medium_states.iter().filter(|(_, c)| c.visible).count() + + self.dragon_states.iter().filter(|(_, c)| c.visible).count() + + self.bird_small_states.iter().filter(|(_, c)| c.visible).count() + + self.fish_small_states.iter().filter(|(_, c)| c.visible).count() + + self.biped_large_states.iter().filter(|(_, c)| c.visible).count() + + self.object_states.iter().filter(|(_, c)| c.visible).count() } } @@ -939,6 +1036,8 @@ pub struct FigureState { pos: Vec3, ori: Vec3, last_ori: Vec3, + lpindex: u8, + visible: bool, } impl FigureState { @@ -954,6 +1053,8 @@ impl FigureState { pos: Vec3::zero(), ori: Vec3::zero(), last_ori: Vec3::zero(), + lpindex: 0, + visible: false, } } @@ -968,7 +1069,11 @@ impl FigureState { dt: f32, movement_rate: f32, action_rate: f32, + lpindex: u8, + visible: bool, ) { + self.visible = visible; + self.lpindex = lpindex; self.last_ori = Lerp::lerp(self.last_ori, ori, 15.0 * dt); // Update interpolation values diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index 28c9f97abc..66aaacf40c 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -301,7 +301,7 @@ impl Scene { ); // Maintain the figures. - self.figure_mgr.maintain(renderer, client); + self.figure_mgr.maintain(renderer, client, &self.camera); // Remove unused figures. self.figure_mgr.clean(client.get_tick());