From d1d7147aa69dcd450f46a93bdf1b58ac0d2f88b8 Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Mon, 10 May 2021 23:09:14 -0400 Subject: [PATCH] Add a minimap overlay based on voxel data. --- Cargo.lock | 11 +++- voxygen/Cargo.toml | 2 +- voxygen/src/hud/minimap.rs | 131 ++++++++++++++++++++++++++++++++++++- voxygen/src/hud/mod.rs | 6 +- 4 files changed, 145 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1ff27e939b..d9e477859a 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" @@ -5644,7 +5653,6 @@ dependencies = [ name = "veloren-i18n" version = "0.9.0" dependencies = [ - "clap", "deunicode", "git2", "hashbrown", @@ -5826,6 +5834,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 0fd79bdca8..3aa3ca502b 100644 --- a/voxygen/src/hud/minimap.rs +++ b/voxygen/src/hud/minimap.rs @@ -4,22 +4,113 @@ use super::{ TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN, }; use crate::{ + hud::{Graphic, Ui}, session::settings_change::{Interface as InterfaceChange, Interface::*}, ui::{fonts::Fonts, img_ids}, GlobalState, }; use client::{self, Client}; -use common::{comp, comp::group::Role, terrain::TerrainChunkSize, vol::RectVolSize}; +use common::{ + comp, + comp::group::Role, + grid::Grid, + terrain::TerrainChunkSize, + vol::{ReadVol, RectVolSize}, +}; use common_net::msg::world_msg::SiteKind; use conrod_core::{ color, position, widget::{self, Button, Image, Rectangle, Text}, widget_ids, Color, Colorable, Positionable, Sizeable, Widget, WidgetCommon, }; - +use hashbrown::HashMap; +use image::{DynamicImage, RgbaImage}; use specs::{saveload::MarkerAllocator, WorldExt}; +use std::sync::Arc; use vek::*; +pub struct VoxelMinimap { + chunk_minimaps: HashMap, HashMap>>, + composited: RgbaImage, + image_id: img_ids::Rotations, +} + +impl VoxelMinimap { + pub fn new(ui: &mut Ui) -> Self { + let mut composited = RgbaImage::new(96, 96); + for x in 0..96 { + for y in 0..96 { + composited.put_pixel( + x, + y, + image::Rgba([255 - 2 * x as u8, 255 - 2 * y as u8, 0, 64]), + ); + } + } + Self { + chunk_minimaps: HashMap::new(), + image_id: ui.add_graphic_with_rotations(Graphic::Image( + Arc::new(DynamicImage::ImageRgba8(composited.clone())), + Some(Rgba::from([0.0, 0.0, 0.0, 0.0])), + )), + composited, + } + } + + pub fn maintain(&mut self, client: &Client, ui: &mut Ui) { + let terrain = client.state().terrain(); + for (key, chunk) in terrain.iter() { + if !self.chunk_minimaps.contains_key(&key) { + let mut layers = HashMap::new(); + for z in chunk.get_min_z()..chunk.get_max_z() { + let grid = Grid::populate_from(Vec2::new(32, 32), |v| { + chunk + .get(Vec3::new(v.x, v.y, z)) + .ok() + .and_then(|block| block.get_color()) + .map(|rgb| [rgb.r, rgb.g, rgb.b, 128]) + .unwrap_or([0, 0, 0, 0]) + }); + layers.insert(z, grid); + } + self.chunk_minimaps.insert(key, layers); + } + } + let player = client.entity(); + if let Some(pos) = client.state().ecs().read_storage::().get(player) { + let pos = pos.0; + let cpos: Vec2 = (pos.xy() / 32.0).as_(); + for i in -1..=1 { + for j in -1..=1 { + let coff = Vec2::new(i, j); + if let Some(grid) = self + .chunk_minimaps + .get(&(cpos + coff)) + .and_then(|l| l.get(&(pos.z as i32))) + { + for x in 0..32 { + for y in 0..32 { + self.composited.put_pixel( + (i + 1) as u32 * 32 + x, + (j + 1) as u32 * 32 + y, + grid.get(Vec2::new(x, y).as_()) + .map(|c| image::Rgba(*c)) + .unwrap_or(image::Rgba([0, 0, 0, 0])), + ); + } + } + } + } + } + // TODO: don't leak memory, replace + self.image_id = ui.add_graphic_with_rotations(Graphic::Image( + Arc::new(DynamicImage::ImageRgba8(self.composited.clone())), + Some(Rgba::from([0.0, 0.0, 0.0, 0.0])), + )); + } + } +} + widget_ids! { struct Ids { mmap_frame, @@ -40,6 +131,7 @@ widget_ids! { mmap_site_icons[], member_indicators[], location_marker, + voxel_minimap, } } @@ -56,6 +148,7 @@ pub struct MiniMap<'a> { ori: Vec3, global_state: &'a GlobalState, location_marker: Option>, + voxel_minimap: &'a VoxelMinimap, } impl<'a> MiniMap<'a> { @@ -69,6 +162,7 @@ impl<'a> MiniMap<'a> { ori: Vec3, global_state: &'a GlobalState, location_marker: Option>, + voxel_minimap: &'a VoxelMinimap, ) -> Self { Self { show, @@ -81,6 +175,7 @@ impl<'a> MiniMap<'a> { ori, global_state, location_marker, + voxel_minimap, } } } @@ -116,6 +211,8 @@ impl<'a> Widget for MiniMap<'a> { let show_minimap = self.global_state.settings.interface.minimap_show; let is_facing_north = self.global_state.settings.interface.minimap_face_north; let show_topo_map = self.global_state.settings.interface.map_show_topo_map; + //let show_voxel_map = self.global_state.settings.interface.map_show_voxel_map; + let show_voxel_map = true; let orientation = if is_facing_north { Vec3::new(0.0, 1.0, 0.0) } else { @@ -277,6 +374,36 @@ impl<'a> Widget for MiniMap<'a> { .set(state.ids.map_layers[index], ui); } } + if show_voxel_map { + let voxelmap_rotation = if is_facing_north { + self.voxel_minimap.image_id.none + } else { + self.voxel_minimap.image_id.source_north + }; + use inline_tweak::tweak; + /*let rect_src = position::Rect::from_xy_dim( + [ + player_pos.x as f64 / TerrainChunkSize::RECT_SIZE.x as f64 + tweak!(0.0), + worldsize.y as f64 - + (player_pos.y as f64 / TerrainChunkSize::RECT_SIZE.y as f64) + tweak!(0.0), + ], + [w_src / tweak!(32768.0), h_src / tweak!(32768.0)], + );*/ + let rect_src = position::Rect::from_xy_dim([tweak!(48.0), tweak!(48.0)], [ + tweak!(96.0), + tweak!(96.0), + ]); + Image::new(voxelmap_rotation) + .middle_of(state.ids.mmap_frame_bg) + .w_h( + map_size.x * 3.0 * (zoom / max_zoom), + map_size.y * 3.0 * zoom / max_zoom, + ) + .parent(state.ids.mmap_frame_bg) + .source_rectangle(rect_src) + .graphics_for(state.ids.map_layers[0]) + .set(state.ids.voxel_minimap, ui); + } // Map icons if state.ids.mmap_site_icons.len() < self.client.sites().len() { diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index c4c8cf2a7d..4fb30ea2a9 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -39,7 +39,7 @@ use group::Group; use img_ids::Imgs; use item_imgs::ItemImgs; use map::Map; -use minimap::MiniMap; +use minimap::{MiniMap, VoxelMinimap}; use popup::Popup; use prompt_dialog::PromptDialog; use serde::{Deserialize, Serialize}; @@ -785,6 +785,7 @@ pub struct Hud { events: Vec, crosshair_opacity: f32, floaters: Floaters, + voxel_minimap: VoxelMinimap, } impl Hud { @@ -841,6 +842,7 @@ impl Hud { ); Self { + voxel_minimap: VoxelMinimap::new(&mut ui), ui, imgs, world_map, @@ -928,6 +930,7 @@ impl Hud { ) -> Vec { span!(_guard, "update_layout", "Hud::update_layout"); let mut events = std::mem::replace(&mut self.events, Vec::new()); + self.voxel_minimap.maintain(&client, &mut self.ui); let (ref mut ui_widgets, ref mut item_tooltip_manager, ref mut tooltip_manager) = &mut self.ui.set_widgets(); // self.ui.set_item_widgets(); pulse time for pulsating elements @@ -2316,6 +2319,7 @@ impl Hud { camera.get_orientation(), &global_state, self.show.location_marker, + &self.voxel_minimap, ) .set(self.ids.minimap, ui_widgets) {