From e3e8afd99b93cdb7ee97e3a69c8df1e607013aac Mon Sep 17 00:00:00 2001 From: Imbris Date: Mon, 27 May 2019 13:01:00 -0400 Subject: [PATCH] Stop drawing entities outside the vd Former-commit-id: 34878fb66ec22ac77a7400724b80ad7f8321f4cf --- client/src/lib.rs | 4 +++ server/src/lib.rs | 7 ++-- voxygen/src/hud/mod.rs | 44 ++++++++++++++++++------ voxygen/src/scene/figure.rs | 68 +++++++++++++++++++++++++++++++++---- voxygen/src/scene/mod.rs | 3 +- 5 files changed, 105 insertions(+), 21 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index d68faaf8b1..dd43d40628 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -110,6 +110,10 @@ impl Client { .send_message(ClientMsg::SetViewDistance(self.view_distance.unwrap())); // Can't fail } + pub fn view_distance(&self) -> Option { + self.view_distance + } + /// Send a chat message to the server. #[allow(dead_code)] pub fn send_chat(&mut self, msg: String) { diff --git a/server/src/lib.rs b/server/src/lib.rs index 4dc494d446..1eb3af791a 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -283,19 +283,22 @@ impl Server { // Also, send the chunk data to anybody that is close by. if let Ok((key, chunk)) = self.chunk_rx.try_recv() { // Send the chunk to all nearby players. - for (entity, player, pos) in ( + for (entity, view_distance, pos) in ( &self.state.ecs().entities(), &self.state.ecs().read_storage::(), &self.state.ecs().read_storage::(), ) .join() + .filter_map(|(entity, player, pos)| { + player.view_distance.map(|vd| (entity, vd, pos)) + }) { let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32)); let dist = (Vec2::from(chunk_pos) - Vec2::from(key)) .map(|e: i32| e.abs()) .reduce_max() as u32; - if player.view_distance.map(|vd| dist <= vd).unwrap_or(false) { + if dist <= view_distance { self.clients.notify( entity, ServerMsg::TerrainChunkUpdate { diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index f27c128a9c..5bc2e971ac 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -31,8 +31,7 @@ use crate::{ GlobalState, }; use client::Client; -use common::comp; -use common::comp::phys::Pos; +use common::{comp, terrain::TerrainChunkSize, vol::VolSize}; use conrod_core::{ color, graph, widget::{self, Button, Image, Rectangle, Text}, @@ -105,7 +104,7 @@ font_ids! { pub struct DebugInfo { pub tps: f64, pub ping_ms: f64, - pub coordinates: Option, + pub coordinates: Option, } pub enum Event { @@ -305,6 +304,14 @@ impl Hud { let player = ecs.read_storage::(); let entities = ecs.entities(); let me = client.entity(); + let view_distance = client.view_distance().unwrap_or(1); + // Get player position. + let player_pos = client + .state() + .ecs() + .read_storage::() + .get(client.entity()) + .map_or(Vec3::zero(), |pos| pos.0); let mut name_id_walker = self.ids.name_tags.walk(); let mut health_id_walker = self.ids.health_bars.walk(); let mut health_back_id_walker = self.ids.health_bar_backs.walk(); @@ -313,6 +320,14 @@ impl Hud { for (pos, name) in (&entities, &pos, &actor, &stats, player.maybe()) .join() .filter(|(entity, _, _, stats, _)| *entity != me && !stats.is_dead) + // Don't process nametags outside the vd (visibility further limited by ui backend) + .filter(|(_, pos, _, _, _)| { + (pos.0 - player_pos) + .map2(TerrainChunkSize::SIZE, |d, sz| { + (d.abs() as u32) < view_distance * sz as u32 + }) + .reduce_and() + }) .map(|(entity, pos, actor, _, player)| match actor { comp::Actor::Character { name: char_name, .. @@ -342,14 +357,21 @@ impl Hud { } // Render Health Bars - for (entity, pos, stats) in - (&entities, &pos, &stats) - .join() - .filter(|(entity, _, stats)| { - *entity != me - && !stats.is_dead - && stats.hp.get_current() != stats.hp.get_maximum() - }) + for (entity, pos, stats) in (&entities, &pos, &stats) + .join() + .filter(|(entity, _, stats)| { + *entity != me + && !stats.is_dead + && stats.hp.get_current() != stats.hp.get_maximum() + }) + // Don't process health bars outside the vd (visibility further limited by ui backend) + .filter(|(_, pos, _)| { + (pos.0 - player_pos) + .map2(TerrainChunkSize::SIZE, |d, sz| { + (d.abs() as u32) < view_distance * sz as u32 + }) + .reduce_and() + }) { let back_id = health_back_id_walker.next( &mut self.ids.health_bar_backs, diff --git a/voxygen/src/scene/figure.rs b/voxygen/src/scene/figure.rs index 96f7462e52..aea1e91490 100644 --- a/voxygen/src/scene/figure.rs +++ b/voxygen/src/scene/figure.rs @@ -26,6 +26,8 @@ use common::{ figure::Segment, msg, msg::ClientState, + terrain::TerrainChunkSize, + vol::VolSize, }; use dot_vox::DotVoxData; use specs::{Component, Entity as EcsEntity, Join, VecStorage}; @@ -461,6 +463,15 @@ impl FigureMgr { pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client) { let time = client.state().get_time(); let ecs = client.state().ecs(); + let view_distance = client.view_distance().unwrap_or(1); + // Get player position. + let player_pos = client + .state() + .ecs() + .read_storage::() + .get(client.entity()) + .map_or(Vec3::zero(), |pos| pos.0); + for (entity, pos, vel, dir, actor, animation_info, stats) in ( &ecs.entities(), &ecs.read_storage::(), @@ -472,6 +483,32 @@ impl FigureMgr { ) .join() { + // Don't process figures outside the vd + let vd_percent = (pos.0 - player_pos) + .map2(TerrainChunkSize::SIZE, |d, sz| { + (100 * d.abs() as u32) / (view_distance * sz) + }) + .reduce_max(); + // Keep from re-adding/removing entities on the border of the vd + if vd_percent > 120 { + match actor { + comp::Actor::Character { body, .. } => match body { + Body::Humanoid(_) => { + self.character_states.remove(&entity); + } + Body::Quadruped(_) => { + self.quadruped_states.remove(&entity); + } + Body::QuadrupedMedium(_) => { + self.QuadrupedMedium_states.remove(&entity); + } + }, + } + continue; + } else if vd_percent > 100 { + continue; + } + // Change in health as color! let col = stats .and_then(|stats| stats.hp.last_change) @@ -610,17 +647,36 @@ impl FigureMgr { let tick = client.get_tick(); let ecs = client.state().ecs(); - for (entity, actor, stat) in ( + let view_distance = client.view_distance().unwrap_or(1); + // Get player position. + let player_pos = client + .state() + .ecs() + .read_storage::() + .get(client.entity()) + .map_or(Vec3::zero(), |pos| pos.0); + + for (entity, _, _, _, actor, _, _) in ( &ecs.entities(), + &ecs.read_storage::(), + &ecs.read_storage::(), + &ecs.read_storage::(), &ecs.read_storage::(), - &ecs.read_storage::(), // Just to make sure the entity is alive + &ecs.read_storage::(), + ecs.read_storage::().maybe(), ) .join() + // Don't render figures outside the vd + .filter(|(_, pos, _, _, _, _, _)| { + (pos.0 - player_pos) + .map2(TerrainChunkSize::SIZE, |d, sz| { + (d.abs() as u32) < view_distance * sz as u32 + }) + .reduce_and() + }) + // Don't render dead entities + .filter(|(e, _, _, _, a, _, stats)| stats.map_or(true, |s| !s.is_dead)) { - if stat.is_dead { - continue; - } - match actor { comp::Actor::Character { body, .. } => { if let Some((locals, bone_consts)) = match body { diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index 8fcb143ada..0f8d33f4ce 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -116,8 +116,7 @@ impl Scene { .ecs() .read_storage::() .get(client.entity()) - .map(|pos| pos.0) - .unwrap_or(Vec3::zero()); + .map_or(Vec3::zero(), |pos| pos.0); // Alter camera position to match player. self.camera.set_focus_pos(player_pos + Vec3::unit_z() * 2.1);