From 449073740532bccbf56e1fdb07c8efaa5f6e82b3 Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Tue, 11 May 2021 17:43:45 -0400 Subject: [PATCH] Improve the efficiency of the voxel minimap by only updating it when crossing a chunk boundary or changing z-level. Allows making the 9 chunks nearest to the player fancier by compositing multiple z levels. --- Cargo.lock | 10 +++++ voxygen/Cargo.toml | 2 +- voxygen/src/hud/minimap.rs | 85 ++++++++++++++++++++++++-------------- 3 files changed, 66 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3fb5eb69dd..ebe9001f23 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2415,6 +2415,15 @@ dependencies = [ "serde", ] +[[package]] +name = "inline_tweak" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7033e97b20277cc0d043226d1940fa7719ff08d2305d1fc7421e53066d00eb4b" +dependencies = [ + "lazy_static", +] + [[package]] name = "inotify" version = "0.7.1" @@ -5833,6 +5842,7 @@ dependencies = [ "iced_native", "iced_winit", "image", + "inline_tweak", "itertools 0.10.0", "keyboard-keynames", "lazy_static", diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index dfd6c62396..6bc04cbe06 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -104,7 +104,7 @@ treeculler = "0.2" tokio = { version = "1", default-features = false, features = ["rt-multi-thread"] } num_cpus = "1.0" # vec_map = { version = "0.8.2" } -# inline_tweak = "1.0.2" +inline_tweak = "1.0.2" itertools = "0.10.0" # Tracy diff --git a/voxygen/src/hud/minimap.rs b/voxygen/src/hud/minimap.rs index 8c5afaa3f4..149f476212 100644 --- a/voxygen/src/hud/minimap.rs +++ b/voxygen/src/hud/minimap.rs @@ -30,9 +30,10 @@ use std::sync::Arc; use vek::*; pub struct VoxelMinimap { - chunk_minimaps: HashMap, HashMap>>, + chunk_minimaps: HashMap, (i32, Vec>)>, composited: RgbaImage, image_id: img_ids::Rotations, + last_pos: Vec3, } const VOXEL_MINIMAP_SIDELENGTH: u32 = 512; @@ -51,6 +52,7 @@ impl VoxelMinimap { Some(Rgba::from([0.0, 0.0, 0.0, 0.0])), )), composited, + last_pos: Vec3::zero(), } } @@ -58,7 +60,7 @@ impl VoxelMinimap { let terrain = client.state().terrain(); for (key, chunk) in terrain.iter() { if !self.chunk_minimaps.contains_key(&key) { - let mut layers = HashMap::new(); + let mut layers = Vec::new(); for z in chunk.get_min_z()..chunk.get_max_z() { let grid = Grid::populate_from(Vec2::new(32, 32), |v| { chunk @@ -68,42 +70,64 @@ impl VoxelMinimap { .map(|rgb| [rgb.r, rgb.g, rgb.b, 192]) .unwrap_or([0, 0, 0, 0]) }); - layers.insert(z, grid); + layers.push(grid); } - self.chunk_minimaps.insert(key, layers); + self.chunk_minimaps.insert(key, (chunk.get_min_z(), layers)); } } let player = client.entity(); if let Some(pos) = client.state().ecs().read_storage::().get(player) { let pos = pos.0; - for x in 0..VOXEL_MINIMAP_SIDELENGTH { + let vpos = pos.xy() - VOXEL_MINIMAP_SIDELENGTH as f32 / 2.0; + let cpos: Vec2 = vpos.map(|i| (i as i32).div_euclid(32)); + if cpos.distance_squared(self.last_pos.xy()) >= 1 || self.last_pos.z != pos.z as i32 { + self.last_pos = cpos.with_z(pos.z as i32); for y in 0..VOXEL_MINIMAP_SIDELENGTH { - let vpos = pos.xy() + Vec2::new(x as f32, y as f32) - - VOXEL_MINIMAP_SIDELENGTH as f32 / 2.0; - let cpos: Vec2 = (vpos / 32.0).as_(); - let cmod: Vec2 = (vpos % 32.0).as_(); - if let Some(grid) = self - .chunk_minimaps - .get(&cpos) - .and_then(|l| l.get(&(pos.z as i32))) - { - self.composited.put_pixel( - x, - VOXEL_MINIMAP_SIDELENGTH - y - 1, - grid.get(cmod) - .map(|c| image::Rgba(*c)) - .unwrap_or(image::Rgba([0, 0, 0, 0])), - ); + for x in 0..VOXEL_MINIMAP_SIDELENGTH { + let voff = Vec2::new(x as f32, y as f32); + let coff: Vec2 = voff.map(|i| (i as i32).div_euclid(32)); + let cmod: Vec2 = voff.map(|i| (i as i32).rem_euclid(32)); + let mut rgba = Vec4::::zero(); + let (weights, zoff) = + if (x as i32 - VOXEL_MINIMAP_SIDELENGTH as i32 / 2).abs() < 96 + && (y as i32 - VOXEL_MINIMAP_SIDELENGTH as i32 / 2).abs() < 96 + { + (&[2, 4, 1, 1, 1][..], -1) + } else { + (&[1][..], 0) + }; + for z in 0..weights.len() { + if let Some(grid) = + self.chunk_minimaps + .get(&(cpos + coff)) + .and_then(|(zlo, g)| { + g.get((pos.z as i32 + z as i32 - zlo + zoff) as usize) + }) + { + let tmp: Vec4 = grid + .get(cmod) + .map(|c| Vec4::::from(*c).as_()) + .unwrap_or(Vec4::one()); + rgba += tmp.as_() * weights[z]; + } + } + let color = { + let rgba: Vec4 = (rgba / weights.iter().sum::()).as_(); + image::Rgba([rgba.x, rgba.y, rgba.z, rgba.w]) + }; + self.composited + .put_pixel(x, VOXEL_MINIMAP_SIDELENGTH - y - 1, color); } } + + ui.replace_graphic( + self.image_id.none, + Graphic::Image( + Arc::new(DynamicImage::ImageRgba8(self.composited.clone())), + Some(Rgba::from([0.0, 0.0, 0.0, 0.0])), + ), + ); } - ui.replace_graphic( - self.image_id.none, - Graphic::Image( - Arc::new(DynamicImage::ImageRgba8(self.composited.clone())), - Some(Rgba::from([0.0, 0.0, 0.0, 0.0])), - ), - ); } } } @@ -378,10 +402,11 @@ impl<'a> Widget for MiniMap<'a> { self.voxel_minimap.image_id.source_north }; let scaling = (VOXEL_MINIMAP_SIDELENGTH as f64 / 32.0) * max_zoom / zoom; + let cmod: Vec2 = (player_pos.xy() % 32.0).as_(); let rect_src = position::Rect::from_xy_dim( [ - VOXEL_MINIMAP_SIDELENGTH as f64 / 2.0, - VOXEL_MINIMAP_SIDELENGTH as f64 / 2.0, + cmod.x + VOXEL_MINIMAP_SIDELENGTH as f64 / 2.0, + -cmod.y + VOXEL_MINIMAP_SIDELENGTH as f64 / 2.0, ], [scaling, scaling], );