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.

This commit is contained in:
Avi Weinstock 2021-05-11 17:43:45 -04:00
parent 937815d8c3
commit 6df2e96d25
3 changed files with 66 additions and 31 deletions

10
Cargo.lock generated
View File

@ -2415,6 +2415,15 @@ dependencies = [
"serde", "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]] [[package]]
name = "inotify" name = "inotify"
version = "0.7.1" version = "0.7.1"
@ -5833,6 +5842,7 @@ dependencies = [
"iced_native", "iced_native",
"iced_winit", "iced_winit",
"image", "image",
"inline_tweak",
"itertools 0.10.0", "itertools 0.10.0",
"keyboard-keynames", "keyboard-keynames",
"lazy_static", "lazy_static",

View File

@ -104,7 +104,7 @@ treeculler = "0.2"
tokio = { version = "1", default-features = false, features = ["rt-multi-thread"] } tokio = { version = "1", default-features = false, features = ["rt-multi-thread"] }
num_cpus = "1.0" num_cpus = "1.0"
# vec_map = { version = "0.8.2" } # vec_map = { version = "0.8.2" }
# inline_tweak = "1.0.2" inline_tweak = "1.0.2"
itertools = "0.10.0" itertools = "0.10.0"
# Tracy # Tracy

View File

@ -30,9 +30,10 @@ use std::sync::Arc;
use vek::*; use vek::*;
pub struct VoxelMinimap { pub struct VoxelMinimap {
chunk_minimaps: HashMap<Vec2<i32>, HashMap<i32, Grid<[u8; 4]>>>, chunk_minimaps: HashMap<Vec2<i32>, (i32, Vec<Grid<[u8; 4]>>)>,
composited: RgbaImage, composited: RgbaImage,
image_id: img_ids::Rotations, image_id: img_ids::Rotations,
last_pos: Vec3<i32>,
} }
const VOXEL_MINIMAP_SIDELENGTH: u32 = 512; const VOXEL_MINIMAP_SIDELENGTH: u32 = 512;
@ -51,6 +52,7 @@ impl VoxelMinimap {
Some(Rgba::from([0.0, 0.0, 0.0, 0.0])), Some(Rgba::from([0.0, 0.0, 0.0, 0.0])),
)), )),
composited, composited,
last_pos: Vec3::zero(),
} }
} }
@ -58,7 +60,7 @@ impl VoxelMinimap {
let terrain = client.state().terrain(); let terrain = client.state().terrain();
for (key, chunk) in terrain.iter() { for (key, chunk) in terrain.iter() {
if !self.chunk_minimaps.contains_key(&key) { 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() { for z in chunk.get_min_z()..chunk.get_max_z() {
let grid = Grid::populate_from(Vec2::new(32, 32), |v| { let grid = Grid::populate_from(Vec2::new(32, 32), |v| {
chunk chunk
@ -68,35 +70,56 @@ impl VoxelMinimap {
.map(|rgb| [rgb.r, rgb.g, rgb.b, 192]) .map(|rgb| [rgb.r, rgb.g, rgb.b, 192])
.unwrap_or([0, 0, 0, 0]) .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(); let player = client.entity();
if let Some(pos) = client.state().ecs().read_storage::<comp::Pos>().get(player) { if let Some(pos) = client.state().ecs().read_storage::<comp::Pos>().get(player) {
let pos = pos.0; 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<i32> = 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 { for y in 0..VOXEL_MINIMAP_SIDELENGTH {
let vpos = pos.xy() + Vec2::new(x as f32, y as f32) for x in 0..VOXEL_MINIMAP_SIDELENGTH {
- VOXEL_MINIMAP_SIDELENGTH as f32 / 2.0; let voff = Vec2::new(x as f32, y as f32);
let cpos: Vec2<i32> = (vpos / 32.0).as_(); let coff: Vec2<i32> = voff.map(|i| (i as i32).div_euclid(32));
let cmod: Vec2<i32> = (vpos % 32.0).as_(); let cmod: Vec2<i32> = voff.map(|i| (i as i32).rem_euclid(32));
if let Some(grid) = self let mut rgba = Vec4::<u16>::zero();
.chunk_minimaps let (weights, zoff) =
.get(&cpos) if (x as i32 - VOXEL_MINIMAP_SIDELENGTH as i32 / 2).abs() < 96
.and_then(|l| l.get(&(pos.z as i32))) && (y as i32 - VOXEL_MINIMAP_SIDELENGTH as i32 / 2).abs() < 96
{ {
self.composited.put_pixel( (&[2, 4, 1, 1, 1][..], -1)
x, } else {
VOXEL_MINIMAP_SIDELENGTH - y - 1, (&[1][..], 0)
grid.get(cmod) };
.map(|c| image::Rgba(*c)) for z in 0..weights.len() {
.unwrap_or(image::Rgba([0, 0, 0, 0])), 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<u16> = grid
.get(cmod)
.map(|c| Vec4::<u8>::from(*c).as_())
.unwrap_or(Vec4::one());
rgba += tmp.as_() * weights[z];
} }
} }
let color = {
let rgba: Vec4<u8> = (rgba / weights.iter().sum::<u16>()).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( ui.replace_graphic(
self.image_id.none, self.image_id.none,
Graphic::Image( Graphic::Image(
@ -107,6 +130,7 @@ impl VoxelMinimap {
} }
} }
} }
}
widget_ids! { widget_ids! {
struct Ids { struct Ids {
@ -378,10 +402,11 @@ impl<'a> Widget for MiniMap<'a> {
self.voxel_minimap.image_id.source_north self.voxel_minimap.image_id.source_north
}; };
let scaling = (VOXEL_MINIMAP_SIDELENGTH as f64 / 32.0) * max_zoom / zoom; let scaling = (VOXEL_MINIMAP_SIDELENGTH as f64 / 32.0) * max_zoom / zoom;
let cmod: Vec2<f64> = (player_pos.xy() % 32.0).as_();
let rect_src = position::Rect::from_xy_dim( let rect_src = position::Rect::from_xy_dim(
[ [
VOXEL_MINIMAP_SIDELENGTH as f64 / 2.0, cmod.x + VOXEL_MINIMAP_SIDELENGTH as f64 / 2.0,
VOXEL_MINIMAP_SIDELENGTH as f64 / 2.0, -cmod.y + VOXEL_MINIMAP_SIDELENGTH as f64 / 2.0,
], ],
[scaling, scaling], [scaling, scaling],
); );