Merge branch 'imbris/vd_circle' into 'master'

Circular View Distance

See merge request veloren/veloren!248
This commit is contained in:
Forest Anderson 2019-06-24 23:13:02 +00:00
commit 81932ac509
4 changed files with 75 additions and 40 deletions

View File

@ -45,6 +45,7 @@ pub struct Client {
tick: u64,
state: State,
entity: EcsEntity,
view_distance: Option<u32>,
loaded_distance: Option<u32>,
@ -230,9 +231,9 @@ impl Client {
let mut chunks_to_remove = Vec::new();
self.state.terrain().iter().for_each(|(key, _)| {
if (Vec2::from(chunk_pos) - Vec2::from(key))
.map(|e: i32| e.abs() as u32)
.reduce_max()
> view_distance + 1
.map(|e: i32| (e.abs() as u32).checked_sub(2).unwrap_or(0))
.magnitude_squared()
> view_distance.pow(2)
{
chunks_to_remove.push(key);
}
@ -242,18 +243,39 @@ impl Client {
}
// Request chunks from the server.
// TODO: This is really inefficient.
let mut all_loaded = true;
'outer: for dist in 0..=view_distance as i32 {
for i in chunk_pos.x - dist..=chunk_pos.x + 1 + dist {
for j in chunk_pos.y - dist..=chunk_pos.y + 1 + dist {
let key = Vec2::new(i, j);
if self.state.terrain().get_key(key).is_none() {
if !self.pending_chunks.contains_key(&key) {
// Only iterate through chunks that need to be loaded for circular vd
// The (dist - 2) explained:
// -0.5 because a chunk is visible if its corner is within the view distance
// -0.5 for being able to move to the corner of the current chunk
// -1 because chunks are not meshed if they don't have all their neighbors
// (notice also that view_distance is decreased by 1)
// (this subtraction on vd is ommitted elsewhere in order to provide a buffer layer of loaded chunks)
let top = if 2 * (dist - 2).max(0).pow(2) > (view_distance - 1).pow(2) as i32 {
((view_distance - 1).pow(2) as f32 - (dist - 2).pow(2) as f32)
.sqrt()
.round() as i32
+ 1
} else {
dist
};
for i in -top..=top {
let keys = [
chunk_pos + Vec2::new(dist, i),
chunk_pos + Vec2::new(i, dist),
chunk_pos + Vec2::new(-dist, i),
chunk_pos + Vec2::new(i, -dist),
];
for key in keys.iter() {
if self.state.terrain().get_key(*key).is_none() {
if !self.pending_chunks.contains_key(key) {
if self.pending_chunks.len() < 4 {
self.postbox
.send_message(ClientMsg::TerrainChunkRequest { key });
self.pending_chunks.insert(key, Instant::now());
.send_message(ClientMsg::TerrainChunkRequest { key: *key });
self.pending_chunks.insert(*key, Instant::now());
} else {
break 'outer;
}

View File

@ -295,11 +295,11 @@ impl Server {
})
{
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;
let adjusted_dist_sqr = (Vec2::from(chunk_pos) - Vec2::from(key))
.map(|e: i32| (e.abs() as u32).checked_sub(2).unwrap_or(0))
.magnitude_squared();
if dist <= view_distance + 1 {
if adjusted_dist_sqr <= view_distance.pow(2) {
self.clients.notify(
entity,
ServerMsg::TerrainChunkUpdate {
@ -327,13 +327,14 @@ impl Server {
.join()
{
let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32));
let dist = Vec2::from(chunk_pos - key)
.map(|e: i32| e.abs() as u32)
.reduce_max();
let adjusted_dist_sqr = Vec2::from(chunk_pos - key)
.map(|e: i32| (e.abs() as u32).checked_sub(2).unwrap_or(0))
.magnitude_squared();
if player
.view_distance
.map(|vd| dist <= vd + 1)
.map(|vd| adjusted_dist_sqr <= vd.pow(2))
.unwrap_or(false)
{
should_drop = false;
@ -696,9 +697,10 @@ impl Server {
(pos.0 - client_pos)
.map2(TerrainChunkSize::SIZE, |d, sz| {
(d.abs() as u32) < client_vd * sz as u32
(d.abs() as u32 / sz).checked_sub(2).unwrap_or(0)
})
.reduce_and()
.magnitude_squared()
< client_vd.pow(2)
};
match force_update {

View File

@ -55,11 +55,13 @@ widget_ids! {
// Test
bag_space_add,
// Debug
debug_bg,
fps_counter,
ping,
coordinates,
loaded_distance,
// Game Version
version,
@ -336,10 +338,9 @@ impl Hud {
// 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()
.map2(TerrainChunkSize::SIZE, |d, sz| d.abs() as f32 / sz as f32)
.magnitude()
< view_distance as f32
})
.map(|(_, pos, actor, _, player)| match actor {
comp::Actor::Character {
@ -380,10 +381,9 @@ impl Hud {
// 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()
.map2(TerrainChunkSize::SIZE, |d, sz| d.abs() as f32 / sz as f32)
.magnitude()
< view_distance as f32
})
{
let back_id = health_back_id_walker.next(
@ -425,18 +425,21 @@ impl Hud {
.font_id(self.fonts.opensans)
.color(TEXT_COLOR)
.set(self.ids.version, ui_widgets);
// Ticks per second
Text::new(&format!("FPS: {:.1}", debug_info.tps))
.color(TEXT_COLOR)
.down_from(self.ids.version, 5.0)
.font_id(self.fonts.opensans)
.font_size(14)
.set(self.ids.fps_counter, ui_widgets);
// Ping
Text::new(&format!("Ping: {:.1}ms", debug_info.ping_ms))
.color(TEXT_COLOR)
.down_from(self.ids.fps_counter, 5.0)
.font_id(self.fonts.opensans)
.font_size(14)
.set(self.ids.ping, ui_widgets);
// Players position
let coordinates_text = match debug_info.coordinates {
Some(coordinates) => format!("Coordinates: {:.1}", coordinates.0),
None => "Player has no Pos component".to_owned(),
@ -447,6 +450,16 @@ impl Hud {
.font_id(self.fonts.opensans)
.font_size(14)
.set(self.ids.coordinates, ui_widgets);
// Loaded distance
Text::new(&format!(
"View distance: {} chunks",
client.loaded_distance().unwrap_or(0)
))
.color(TEXT_COLOR)
.down_from(self.ids.coordinates, 5.0)
.font_id(self.fonts.opensans)
.font_size(14)
.set(self.ids.loaded_distance, ui_widgets);
}
// Add Bag-Space Button.

View File

@ -493,13 +493,12 @@ 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();
let vd_frac = (pos.0 - player_pos)
.map2(TerrainChunkSize::SIZE, |d, sz| d.abs() as f32 / sz as f32)
.magnitude()
/ view_distance as f32;
// Keep from re-adding/removing entities on the border of the vd
if vd_percent > 120 {
if vd_frac > 1.2 {
match actor {
comp::Actor::Character { body, .. } => match body {
Body::Humanoid(_) => {
@ -514,7 +513,7 @@ impl FigureMgr {
},
}
continue;
} else if vd_percent > 100 {
} else if vd_frac > 1.0 {
continue;
}
@ -693,10 +692,9 @@ impl FigureMgr {
// 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()
.map2(TerrainChunkSize::SIZE, |d, sz| d.abs() as f32 / sz as f32)
.magnitude()
< view_distance as f32
})
// Don't render dead entities
.filter(|(_, _, _, _, _, _, stats)| stats.map_or(true, |s| !s.is_dead))