From 5d3acb0c2eb23d99c8da86d41349d70377944e80 Mon Sep 17 00:00:00 2001 From: James Melkonian Date: Fri, 2 Apr 2021 19:00:37 -0700 Subject: [PATCH 1/5] Topographic map --- Cargo.toml | 2 +- client/src/lib.rs | 105 +++++++++++++++++++++++++++++++------ common/src/terrain/map.rs | 16 +++++- voxygen/src/hud/map.rs | 41 ++++++++++++++- voxygen/src/hud/minimap.rs | 6 ++- voxygen/src/hud/mod.rs | 17 ++++++ voxygen/src/session.rs | 4 ++ voxygen/src/settings.rs | 2 + world/src/sim/map.rs | 1 + 9 files changed, 173 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2c7b8a0bbe..362bccde47 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ debug = false codegen-units = 8 lto = false # TEMP false to avoid fingerprints bug -incremental = false +incremental = true # All dependencies (but not this crate itself) [profile.dev.package."*"] opt-level = 3 diff --git a/client/src/lib.rs b/client/src/lib.rs index 0c2a1b59fb..6d34132d5d 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -34,7 +34,7 @@ use common::{ outcome::Outcome, recipe::RecipeBook, resources::{DeltaTime, PlayerEntity, TimeOfDay}, - terrain::{block::Block, neighbors, BiomeKind, SitesKind, TerrainChunk, TerrainChunkSize}, + terrain::{block::Block, map::MapConfig, neighbors, BiomeKind, SitesKind, TerrainChunk, TerrainChunkSize}, trade::{PendingTrade, SitePrices, TradeAction, TradeId, TradeResult}, uid::{Uid, UidAllocator}, vol::RectVolSize, @@ -112,21 +112,22 @@ pub struct WorldData { /// map data (e.g. with shadow map data or river data), but at present /// we opt not to do this. /// - /// The second element of the tuple is the world size (as a 2D grid, - /// in chunks), and the third element holds the minimum height for any land - /// chunk (i.e. the sea level) in its x coordinate, and the maximum land - /// height above this height (i.e. the max height) in its y coordinate. - map: (Arc, Vec2, Vec2), + /// The first two elements of the tuple are the regular and topographic maps + /// respectively. The third element of the tuple is the world size (as a 2D + /// grid, in chunks), and the fourth element holds the minimum height for + /// any land chunk (i.e. the sea level) in its x coordinate, and the maximum + /// land height above this height (i.e. the max height) in its y coordinate. + map: (Arc, Arc, Vec2, Vec2), } impl WorldData { - pub fn chunk_size(&self) -> Vec2 { self.map.1 } + pub fn chunk_size(&self) -> Vec2 { self.map.2 } pub fn map_image(&self) -> &Arc { &self.map.0 } - pub fn min_chunk_alt(&self) -> f32 { self.map.2.x } + pub fn map_topo_image(&self) -> &Arc { &self.map.1 } - pub fn max_chunk_alt(&self) -> f32 { self.map.2.y } + pub fn max_chunk_alt(&self) -> f32 { self.map.3.y } } pub struct SiteInfoRich { @@ -324,6 +325,7 @@ impl Client { // Redraw map (with shadows this time). let mut world_map_rgba = vec![0u32; rgba.size().product() as usize]; + let mut world_map_topo = vec![0u32; rgba.size().product() as usize]; let mut map_config = common::terrain::map::MapConfig::orthographic( map_size_lg, core::ops::RangeInclusive::new(0.0, max_height), @@ -336,18 +338,40 @@ impl Client { && pos.y < map_size.y as i32 }; ping_stream.send(PingMsg::Ping)?; - map_config.generate( - |pos| { + fn sample_pos(map_config: &MapConfig, pos: Vec2, alt: &Grid, rgba: &Grid, map_size: &Vec2, map_size_lg: &common::terrain::MapSizeLg, max_height: f32) -> common::terrain::map::MapSample { + let rescale_height = |h: f32| h / max_height; + let scale_height_big = |h: u32| (h >> 3) as f32 / 8191.0 * max_height; + let bounds_check = |pos: Vec2| { + pos.reduce_partial_min() >= 0 + && pos.x < map_size.x as i32 + && pos.y < map_size.y as i32 + }; + let MapConfig { + gain, + is_contours, + is_hill_shaded, + .. + } = *map_config; + let mut is_contour_line = false; let (rgba, alt, downhill_wpos) = if bounds_check(pos) { let posi = pos.y as usize * map_size.x as usize + pos.x as usize; let [r, g, b, a] = rgba[pos].to_le_bytes(); let alti = alt[pos]; + // Compute contours (chunks are assigned in the river code below) + let altj = rescale_height(scale_height_big(alti)); + let chunk_contour = (altj * gain / 100.0) as u32; + // Compute downhill. let downhill = { let mut best = -1; let mut besth = alti; - for nposi in neighbors(map_size_lg, posi) { + for nposi in neighbors(*map_size_lg, posi) { let nbh = alt.raw()[nposi]; + let nalt = rescale_height(scale_height_big(nbh)); + let nchunk_contour = (nalt * gain / 100.0) as u32; + if chunk_contour > nchunk_contour { + is_contour_line = true; + } if nbh < besth { besth = nbh; best = nposi as isize; @@ -369,17 +393,43 @@ impl Client { } else { (Rgba::zero(), 0, None) }; + let alt = f64::from(rescale_height(scale_height_big(alt))); let wpos = pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32); let downhill_wpos = downhill_wpos .unwrap_or(wpos + TerrainChunkSize::RECT_SIZE.map(|e| e as i32)); - let alt = rescale_height(scale_height_big(alt)); + let is_path = rgba.r == 0x37 && rgba.g == 0x29 && rgba.b == 0x23; + let rgb = Rgb::from(rgba).map(|e: u8| e as f64 / 255.0); + let rgb = if is_hill_shaded { + if is_path { + // Path color is Rgb::new(0x37, 0x29, 0x23) + Rgb::new(0.9, 0.9, 0.63) + } else if rgb.r == 0.0 && rgb.b > 0.4 && rgb.g < 0.3 { + // Water + Rgb::new(0.23, 0.47, 0.53) + } else if is_contours && is_contour_line { + // Color contour lines + Rgb::new(0.15, 0.15, 0.15) + } else { + // Color hill shading + let lightness = (alt + 0.2).min(1.0) as f64; + Rgb::new(lightness, 0.9 * lightness, 0.5 * lightness) + } + } else if is_contours && is_contour_line { + // Color contour lines + Rgb::new(0.15, 0.15, 0.15) + } else { + rgb + }.map(|e| (e * 255.0) as u8); common::terrain::map::MapSample { - rgb: Rgb::from(rgba), - alt: f64::from(alt), + rgb, + alt, downhill_wpos, connections: None, + is_path, } - }, + } + map_config.generate( + |pos| sample_pos(&map_config, pos, &alt, &rgba, &map_size, &map_size_lg, max_height), |wpos| { let pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |e, f| e / f as i32); rescale_height(if bounds_check(pos) { @@ -393,6 +443,26 @@ impl Client { u32::from_le_bytes([r, g, b, a]); }, ); + + // Generate topographic map + map_config.is_hill_shaded = true; + map_config.is_contours = true; + map_config.is_shaded = false; + map_config.generate( + |pos| sample_pos(&map_config, pos, &alt, &rgba, &map_size, &map_size_lg, max_height), + |wpos| { + let pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |e, f| e / f as i32); + rescale_height(if bounds_check(pos) { + scale_height_big(alt[pos]) + } else { + 0.0 + }) + }, + |pos, (r, g, b, a)| { + world_map_topo[pos.y * map_size.x as usize + pos.x] = + u32::from_le_bytes([r, g, b, a]); + }, + ); ping_stream.send(PingMsg::Ping)?; let make_raw = |rgba| -> Result<_, Error> { let mut raw = vec![0u8; 4 * world_map_rgba.len()]; @@ -413,6 +483,7 @@ impl Client { let lod_base = rgba; let lod_alt = alt; let world_map_img = make_raw(&world_map_rgba)?; + let world_map_topo_img = make_raw(&world_map_topo)?; let horizons = (west.0, west.1, east.0, east.1) .into_par_iter() .map(|(wa, wh, ea, eh)| u32::from_le_bytes([wa, wh, ea, eh])) @@ -426,7 +497,7 @@ impl Client { lod_base, lod_alt, Grid::from_raw(map_size.map(|e| e as i32), lod_horizon), - (world_map_img, map_size, map_bounds), + (world_map_img, world_map_topo_img, map_size, map_bounds), world_map.sites, recipe_book, max_group_size, diff --git a/common/src/terrain/map.rs b/common/src/terrain/map.rs index dcde0db0e3..587b34fec9 100644 --- a/common/src/terrain/map.rs +++ b/common/src/terrain/map.rs @@ -317,6 +317,16 @@ pub struct MapConfig<'a> { /// /// Defaults to false. pub is_debug: bool, + /// If true, contour lines are drawn on top of the base rbg + /// + /// Defaults to false. + pub is_contours: bool, + /// If true, hill shading is applied to the terrain and all the + /// colors are different. Is incompatible with humidity/temperature/shaded + /// maps. + /// + /// Defaults to false + pub is_hill_shaded: bool, } pub const QUADRANTS: usize = 4; @@ -366,6 +376,8 @@ pub struct MapSample { /// Connections at each index correspond to the same index in /// NEIGHBOR_DELTA. pub connections: Option<[Option; 8]>, + /// If the chunk contains a path + pub is_path: bool, } impl<'a> MapConfig<'a> { @@ -395,6 +407,8 @@ impl<'a> MapConfig<'a> { is_temperature: false, is_humidity: false, is_debug: false, + is_contours: false, + is_hill_shaded: false, } } @@ -432,7 +446,6 @@ impl<'a> MapConfig<'a> { scale, light_direction, horizons, - is_shaded, // is_debug, .. @@ -712,3 +725,4 @@ impl<'a> MapConfig<'a> { } } } + diff --git a/voxygen/src/hud/map.rs b/voxygen/src/hud/map.rs index 5cda0b59c1..2e8588e7f2 100644 --- a/voxygen/src/hud/map.rs +++ b/voxygen/src/hud/map.rs @@ -72,6 +72,7 @@ const SHOW_ECONOMY: bool = false; // turn this display off (for 0.9) until we ha pub struct Map<'a> { client: &'a Client, world_map: &'a (img_ids::Rotations, Vec2), + world_map_topo: &'a (img_ids::Rotations, Vec2), imgs: &'a Imgs, fonts: &'a Fonts, #[conrod(common_builder)] @@ -89,6 +90,7 @@ impl<'a> Map<'a> { imgs: &'a Imgs, rot_imgs: &'a ImgsRot, world_map: &'a (img_ids::Rotations, Vec2), + world_map_topo: &'a (img_ids::Rotations, Vec2), fonts: &'a Fonts, pulse: f32, localized_strings: &'a Localization, @@ -99,6 +101,7 @@ impl<'a> Map<'a> { imgs, rot_imgs, world_map, + world_map_topo, client, fonts, common: widget::CommonBuilder::default(), @@ -123,6 +126,7 @@ pub enum Event { ShowDungeons(bool), ShowCaves(bool), ShowTrees(bool), + ShowTopoMap(bool), Close, RequestSiteInfo(SiteId), } @@ -184,6 +188,7 @@ impl<'a> Widget for Map<'a> { let show_castles = self.global_state.settings.interface.map_show_castles; let show_caves = self.global_state.settings.interface.map_show_caves; let show_trees = self.global_state.settings.interface.map_show_trees; + let show_topo_map = self.global_state.settings.interface.map_show_topo_map; let mut events = Vec::new(); let i18n = &self.localized_strings; // Tooltips @@ -271,7 +276,7 @@ impl<'a> Widget for Map<'a> { .parent(state.ids.bg) .set(state.ids.grid, ui); // Map Image - let (world_map, worldsize) = self.world_map; + let (world_map, worldsize) = if !show_topo_map { self.world_map } else { self.world_map_topo }; // Coordinates let player_pos = self @@ -539,6 +544,40 @@ impl<'a> Widget for Map<'a> { { events.push(Event::ShowTrees(!show_trees)); } + Text::new(i18n.get("hud.map.trees")) + .right_from(state.ids.show_trees_box, 10.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .graphics_for(state.ids.show_trees_box) + .color(TEXT_COLOR) + .set(state.ids.show_trees_text, ui); + // Topographical Map + Image::new(self.imgs.mmap_site_tree) + .down_from(state.ids.show_caves_img, 10.0) + .w_h(20.0, 20.0) + .set(state.ids.show_trees_img, ui); + if Button::image(if show_topo_map { + self.imgs.checkbox_checked + } else { + self.imgs.checkbox + }) + .w_h(18.0, 18.0) + .hover_image(if show_topo_map { + self.imgs.checkbox_checked_mo + } else { + self.imgs.checkbox_mo + }) + .press_image(if show_topo_map { + self.imgs.checkbox_checked + } else { + self.imgs.checkbox_press + }) + .right_from(state.ids.show_trees_img, 10.0) + .set(state.ids.show_trees_box, ui) + .was_clicked() + { + events.push(Event::ShowTopoMap(!show_topo_map)); + } Text::new(i18n.get("hud.map.trees")) .right_from(state.ids.show_trees_box, 10.0) .font_size(self.fonts.cyri.scale(14)) diff --git a/voxygen/src/hud/minimap.rs b/voxygen/src/hud/minimap.rs index ceee112fba..3107e4afce 100644 --- a/voxygen/src/hud/minimap.rs +++ b/voxygen/src/hud/minimap.rs @@ -48,6 +48,7 @@ pub struct MiniMap<'a> { imgs: &'a Imgs, rot_imgs: &'a ImgsRot, world_map: &'a (img_ids::Rotations, Vec2), + world_map_topo: &'a (img_ids::Rotations, Vec2), fonts: &'a Fonts, #[conrod(common_builder)] common: widget::CommonBuilder, @@ -61,6 +62,7 @@ impl<'a> MiniMap<'a> { imgs: &'a Imgs, rot_imgs: &'a ImgsRot, world_map: &'a (img_ids::Rotations, Vec2), + world_map_topo: &'a (img_ids::Rotations, Vec2), fonts: &'a Fonts, ori: Vec3, global_state: &'a GlobalState, @@ -70,6 +72,7 @@ impl<'a> MiniMap<'a> { imgs, rot_imgs, world_map, + world_map_topo, fonts, common: widget::CommonBuilder::default(), ori, @@ -140,7 +143,8 @@ impl<'a> Widget for MiniMap<'a> { .set(state.ids.mmap_frame_bg, ui); // Map size in chunk coords - let (world_map, worldsize) = self.world_map; + let show_topo_map = self.global_state.settings.interface.map_show_topo_map; + let (world_map, worldsize) = if !show_topo_map { self.world_map } else { self.world_map_topo }; // Zoom Buttons diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index acd945ae14..516de25c5d 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -250,6 +250,7 @@ widget_ids! { chat, map, world_map, + world_map_topo, character_window, popup, minimap, @@ -380,6 +381,7 @@ pub enum Event { MapShowCastles(bool), MapShowCaves(bool), MapShowTrees(bool), + MapShowTopo(bool), AdjustWindowSize([u16; 2]), ChangeFullscreenMode(FullScreenSettings), ToggleParticlesEnabled(bool), @@ -778,6 +780,7 @@ pub struct Hud { ui: Ui, ids: Ids, world_map: (/* Id */ Rotations, Vec2), + world_map_topo: (/* Id */ Rotations, Vec2), imgs: Imgs, item_imgs: ItemImgs, fonts: Fonts, @@ -825,6 +828,14 @@ impl Hud { )), client.world_data().chunk_size().map(|e| e as u32), ); + // Load world topo map + let world_map_topo = ( + ui.add_graphic_with_rotations(Graphic::Image( + Arc::clone(client.world_data().map_topo_image()), + Some(water_color), + )), + client.world_data().chunk_size().map(|e| e as u32), + ); // Load images. let imgs = Imgs::load(&mut ui).expect("Failed to load images!"); // Load rotation images. @@ -862,6 +873,7 @@ impl Hud { ui, imgs, world_map, + world_map_topo, rot_imgs, item_imgs, fonts, @@ -2230,6 +2242,7 @@ impl Hud { &self.imgs, &self.rot_imgs, &self.world_map, + &self.world_map_topo, &self.fonts, camera.get_orientation(), &global_state, @@ -2783,6 +2796,7 @@ impl Hud { &self.imgs, &self.rot_imgs, &self.world_map, + &self.world_map_topo, &self.fonts, self.pulse, i18n, @@ -2821,6 +2835,9 @@ impl Hud { map::Event::ShowTrees(map_show_trees) => { events.push(Event::MapShowTrees(map_show_trees)); }, + map::Event::ShowTopoMap(map_show_topo_map) => { + events.push(Event::MapShowTopo(map_show_topo_map)); + }, map::Event::RequestSiteInfo(id) => { events.push(Event::RequestSiteInfo(id)); }, diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index ff7c946075..c6d9a1c924 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -1356,6 +1356,10 @@ impl PlayState for SessionState { global_state.settings.interface.map_show_trees = map_show_trees; global_state.settings.save_to_file_warn(); }, + HudEvent::MapShowTopo(map_show_topo) => { + global_state.settings.interface.map_show_topo_map = map_show_topo; + global_state.settings.save_to_file_warn(); + }, HudEvent::RequestSiteInfo(id) => { let mut client = self.client.borrow_mut(); client.request_site_economy(id); diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs index 55dcd9851a..311920c5e9 100644 --- a/voxygen/src/settings.rs +++ b/voxygen/src/settings.rs @@ -451,6 +451,7 @@ pub struct InterfaceSettings { pub loading_tips: bool, pub map_show_caves: bool, pub map_show_trees: bool, + pub map_show_topo_map: bool, pub minimap_show: bool, pub minimap_face_north: bool, } @@ -483,6 +484,7 @@ impl Default for InterfaceSettings { loading_tips: true, map_show_caves: true, map_show_trees: true, + map_show_topo_map: false, minimap_show: true, minimap_face_north: false, } diff --git a/world/src/sim/map.rs b/world/src/sim/map.rs index 8ac6747b97..8f03a53263 100644 --- a/world/src/sim/map.rs +++ b/world/src/sim/map.rs @@ -260,5 +260,6 @@ pub fn sample_pos( } else { None }, + is_path, } } From 3578116eeed82ea27f8076b64185e8ec0b03bef0 Mon Sep 17 00:00:00 2001 From: James Melkonian Date: Sat, 3 Apr 2021 16:27:51 -0700 Subject: [PATCH 2/5] Added map layers --- client/src/lib.rs | 87 +++++++++++++++++++++++++---------- common/src/terrain/map.rs | 21 ++++++--- voxygen/src/hud/map.rs | 94 ++++++++++++++++---------------------- voxygen/src/hud/minimap.rs | 12 ++--- voxygen/src/hud/mod.rs | 32 +++++-------- world/src/sim/map.rs | 2 +- 6 files changed, 136 insertions(+), 112 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index 6d34132d5d..ac164702d6 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -117,17 +117,19 @@ pub struct WorldData { /// grid, in chunks), and the fourth element holds the minimum height for /// any land chunk (i.e. the sea level) in its x coordinate, and the maximum /// land height above this height (i.e. the max height) in its y coordinate. - map: (Arc, Arc, Vec2, Vec2), + map: (Vec>, Vec2, Vec2), } impl WorldData { - pub fn chunk_size(&self) -> Vec2 { self.map.2 } + pub fn chunk_size(&self) -> Vec2 { self.map.1 } - pub fn map_image(&self) -> &Arc { &self.map.0 } + pub fn map_layers(&self) -> &Vec> { &self.map.0 } - pub fn map_topo_image(&self) -> &Arc { &self.map.1 } + pub fn map_image(&self) -> &Arc { &self.map.0[0] } - pub fn max_chunk_alt(&self) -> f32 { self.map.3.y } + pub fn min_chunk_alt(&self) -> f32 { self.map.2.x } + + pub fn max_chunk_alt(&self) -> f32 { self.map.2.y } } pub struct SiteInfoRich { @@ -324,6 +326,7 @@ impl Client { let horizons = [unzip_horizons(&west), unzip_horizons(&east)]; // Redraw map (with shadows this time). + let mut world_map_political = vec![0u32; rgba.size().product() as usize]; let mut world_map_rgba = vec![0u32; rgba.size().product() as usize]; let mut world_map_topo = vec![0u32; rgba.size().product() as usize]; let mut map_config = common::terrain::map::MapConfig::orthographic( @@ -350,16 +353,19 @@ impl Client { gain, is_contours, is_hill_shaded, + is_political, .. } = *map_config; let mut is_contour_line = false; + let mut is_border = false; let (rgba, alt, downhill_wpos) = if bounds_check(pos) { let posi = pos.y as usize * map_size.x as usize + pos.x as usize; let [r, g, b, a] = rgba[pos].to_le_bytes(); + let is_water = r == 0 && b > 102 && g < 77; let alti = alt[pos]; // Compute contours (chunks are assigned in the river code below) let altj = rescale_height(scale_height_big(alti)); - let chunk_contour = (altj * gain / 100.0) as u32; + let chunk_contour = (altj * gain / 150.0) as u32; // Compute downhill. let downhill = { @@ -368,10 +374,17 @@ impl Client { for nposi in neighbors(*map_size_lg, posi) { let nbh = alt.raw()[nposi]; let nalt = rescale_height(scale_height_big(nbh)); - let nchunk_contour = (nalt * gain / 100.0) as u32; - if chunk_contour > nchunk_contour { + let nchunk_contour = (nalt * gain / 150.0) as u32; + if !is_contour_line && chunk_contour > nchunk_contour { is_contour_line = true; } + let [nr, ng, nb, _na] = rgba.raw()[nposi].to_le_bytes(); + let n_is_water = nr == 0 && nb > 102 && ng < 77; + + if !is_border && is_political && is_water && !n_is_water { + is_border = true; + } + if nbh < besth { besth = nbh; best = nposi as isize; @@ -398,36 +411,64 @@ impl Client { let downhill_wpos = downhill_wpos .unwrap_or(wpos + TerrainChunkSize::RECT_SIZE.map(|e| e as i32)); let is_path = rgba.r == 0x37 && rgba.g == 0x29 && rgba.b == 0x23; - let rgb = Rgb::from(rgba).map(|e: u8| e as f64 / 255.0); - let rgb = if is_hill_shaded { + let rgba = rgba.map(|e: u8| e as f64 / 255.0); + let rgba = if is_hill_shaded { if is_path { // Path color is Rgb::new(0x37, 0x29, 0x23) - Rgb::new(0.9, 0.9, 0.63) - } else if rgb.r == 0.0 && rgb.b > 0.4 && rgb.g < 0.3 { + Rgba::new(0.9, 0.9, 0.63, 1.0) + } else if rgba.r == 0.0 && rgba.b > 0.4 && rgba.g < 0.3 { // Water - Rgb::new(0.23, 0.47, 0.53) + Rgba::new(0.23, 0.47, 0.53, 0.5) } else if is_contours && is_contour_line { // Color contour lines - Rgb::new(0.15, 0.15, 0.15) + Rgba::new(0.15, 0.15, 0.15, 0.5) } else { // Color hill shading let lightness = (alt + 0.2).min(1.0) as f64; - Rgb::new(lightness, 0.9 * lightness, 0.5 * lightness) + Rgba::new(lightness, 0.9 * lightness, 0.5 * lightness, 0.5) } } else if is_contours && is_contour_line { // Color contour lines - Rgb::new(0.15, 0.15, 0.15) + Rgba::new(0.15, 0.15, 0.15, 0.9) + } else if is_political { + if is_path { + Rgba::new(0.3, 0.3, 0.3, 1.0) + } else if is_border { + Rgba::new(0.0, 0.0, 0.0, 1.0) + } else { + Rgba::new(1.0, 0.9, 0.6, 1.0) + } } else { - rgb + Rgba::new(rgba.r, rgba.g, rgba.b, 0.5) }.map(|e| (e * 255.0) as u8); common::terrain::map::MapSample { - rgb, + rgba, alt, downhill_wpos, connections: None, is_path, } } + map_config.is_shaded = true; + map_config.is_political = true; + map_config.generate( + |pos| sample_pos(&map_config, pos, &alt, &rgba, &map_size, &map_size_lg, max_height), + |wpos| { + let pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |e, f| e / f as i32); + rescale_height(if bounds_check(pos) { + scale_height_big(alt[pos]) + } else { + 0.0 + }) + }, + |pos, (r, g, b, a)| { + world_map_political[pos.y * map_size.x as usize + pos.x] = + u32::from_le_bytes([r, g, b, a]); + }, + ); + + map_config.is_shaded = false; + map_config.is_political = false; map_config.generate( |pos| sample_pos(&map_config, pos, &alt, &rgba, &map_size, &map_size_lg, max_height), |wpos| { @@ -443,11 +484,9 @@ impl Client { u32::from_le_bytes([r, g, b, a]); }, ); - // Generate topographic map - map_config.is_hill_shaded = true; + //map_config.is_hill_shaded = true; map_config.is_contours = true; - map_config.is_shaded = false; map_config.generate( |pos| sample_pos(&map_config, pos, &alt, &rgba, &map_size, &map_size_lg, max_height), |wpos| { @@ -482,8 +521,10 @@ impl Client { ping_stream.send(PingMsg::Ping)?; let lod_base = rgba; let lod_alt = alt; - let world_map_img = make_raw(&world_map_rgba)?; + let world_map_rgba_img = make_raw(&world_map_rgba)?; let world_map_topo_img = make_raw(&world_map_topo)?; + let world_map_political_img = make_raw(&world_map_political)?; + let world_map_layers = vec!(world_map_political_img, world_map_rgba_img, world_map_topo_img); let horizons = (west.0, west.1, east.0, east.1) .into_par_iter() .map(|(wa, wh, ea, eh)| u32::from_le_bytes([wa, wh, ea, eh])) @@ -497,7 +538,7 @@ impl Client { lod_base, lod_alt, Grid::from_raw(map_size.map(|e| e as i32), lod_horizon), - (world_map_img, world_map_topo_img, map_size, map_bounds), + (world_map_layers, map_size, map_bounds), world_map.sites, recipe_book, max_group_size, diff --git a/common/src/terrain/map.rs b/common/src/terrain/map.rs index 587b34fec9..f62075182e 100644 --- a/common/src/terrain/map.rs +++ b/common/src/terrain/map.rs @@ -327,6 +327,10 @@ pub struct MapConfig<'a> { /// /// Defaults to false pub is_hill_shaded: bool, + /// If true, terrain is white, rivers, borders, and roads are black. + /// + /// Defaults to false + pub is_political: bool, } pub const QUADRANTS: usize = 4; @@ -363,7 +367,7 @@ pub struct Connection { pub struct MapSample { /// the base RGB color for a particular map pixel using the current settings /// (i.e. the color *without* lighting). - pub rgb: Rgb, + pub rgba: Rgba, /// Surface altitude information /// (correctly reflecting settings like is_basement and is_water) pub alt: f64, @@ -409,6 +413,7 @@ impl<'a> MapConfig<'a> { is_debug: false, is_contours: false, is_hill_shaded: false, + is_political: false, } } @@ -497,7 +502,7 @@ impl<'a> MapConfig<'a> { }; let MapSample { - rgb, + rgba, alt, downhill_wpos, .. @@ -505,7 +510,8 @@ impl<'a> MapConfig<'a> { let alt = alt as f32; let wposi = pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32); - let mut rgb = rgb.map(|e| e as f64 / 255.0); + let rgb = Rgb::new(rgba.r, rgba.g, rgba.b).map(|e| e as f64 / 255.0); + let rgba = rgba.map(|e| e as f64 / 255.0); // Material properties: // @@ -581,7 +587,7 @@ impl<'a> MapConfig<'a> { if has_river { let water_rgb = Rgb::new(0, ((g_water) * 1.0) as u8, ((b_water) * 1.0) as u8) .map(|e| e as f64 / 255.0); - rgb = water_rgb; + //rgba = Rgba::new(water_rgb.r, water_rgb.g, water_rgb.b, 1.0); k_s = Rgb::new(1.0, 1.0, 1.0); k_d = water_rgb; k_a = water_rgb; @@ -707,13 +713,14 @@ impl<'a> MapConfig<'a> { let ambient = k_a * i_a; let diffuse = k_d * lambertian * i_m_d; let specular = k_s * spec_angle.powf(alpha) * i_m_s; - (ambient + shadow * (diffuse + specular)).map(|e| e.min(1.0)) + let shadow_rgb = (ambient + shadow * (diffuse + specular)).map(|e| e.min(1.0)); + Rgba::new(shadow_rgb.r, shadow_rgb.g, shadow_rgb.b, 1.0) } else { - rgb + rgba } .map(|e| (e * 255.0) as u8); - let rgba = (rgb.r, rgb.g, rgb.b, 255); + let rgba = (rgb.r, rgb.g, rgb.b, rgb.a); write_pixel(Vec2::new(i, j), rgba); }); diff --git a/voxygen/src/hud/map.rs b/voxygen/src/hud/map.rs index 2e8588e7f2..172208894c 100644 --- a/voxygen/src/hud/map.rs +++ b/voxygen/src/hud/map.rs @@ -31,7 +31,9 @@ widget_ids! { location_name, indicator, indicator_overlay, - grid, + grid_layer_0, + grid_layer_1, + grid_layer_2, map_title, qlog_title, zoom_slider, @@ -71,8 +73,7 @@ const SHOW_ECONOMY: bool = false; // turn this display off (for 0.9) until we ha #[derive(WidgetCommon)] pub struct Map<'a> { client: &'a Client, - world_map: &'a (img_ids::Rotations, Vec2), - world_map_topo: &'a (img_ids::Rotations, Vec2), + world_map_layers: &'a (Vec, Vec2), imgs: &'a Imgs, fonts: &'a Fonts, #[conrod(common_builder)] @@ -89,8 +90,7 @@ impl<'a> Map<'a> { client: &'a Client, imgs: &'a Imgs, rot_imgs: &'a ImgsRot, - world_map: &'a (img_ids::Rotations, Vec2), - world_map_topo: &'a (img_ids::Rotations, Vec2), + world_map_layers: &'a (Vec, Vec2), fonts: &'a Fonts, pulse: f32, localized_strings: &'a Localization, @@ -100,8 +100,7 @@ impl<'a> Map<'a> { Self { imgs, rot_imgs, - world_map, - world_map_topo, + world_map_layers, client, fonts, common: widget::CommonBuilder::default(), @@ -274,9 +273,13 @@ impl<'a> Widget for Map<'a> { .mid_top_with_margin_on(state.ids.map_align, 5.0) .w_h(765.0, 765.0) .parent(state.ids.bg) - .set(state.ids.grid, ui); + .set(state.ids.grid_layer_0, ui); // Map Image - let (world_map, worldsize) = if !show_topo_map { self.world_map } else { self.world_map_topo }; + //let (world_map, worldsize) = if !show_topo_map { self.world_map } else { self.world_map_topo }; + let world_map_layer_0 = &self.world_map_layers.0[0]; + let world_map_layer_1 = &self.world_map_layers.0[1]; + let world_map_layer_2 = &self.world_map_layers.0[2]; + let worldsize = self.world_map_layers.1; // Coordinates let player_pos = self @@ -297,7 +300,7 @@ impl<'a> Widget for Map<'a> { // Handle dragging let drag = self.global_state.settings.interface.map_drag; let dragged: Vec2 = ui - .widget_input(state.ids.grid) + .widget_input(state.ids.grid_layer_0) .drags() .left() .map(|drag| Vec2::::from(drag.delta_xy)) @@ -325,15 +328,32 @@ impl<'a> Widget for Map<'a> { { events.push(Event::Close); } - Image::new(world_map.none) + // Map layer 0 + Image::new(world_map_layer_0.none) .mid_top_with_margin_on(state.ids.map_align, 10.0) .w_h(map_size.x, map_size.y) .parent(state.ids.bg) .source_rectangle(rect_src) - .set(state.ids.grid, ui); + .set(state.ids.grid_layer_0, ui); + // Map layer 1 + Image::new(world_map_layer_1.none) + .mid_top_with_margin_on(state.ids.map_align, 10.0) + .w_h(map_size.x, map_size.y) + .parent(state.ids.bg) + .source_rectangle(rect_src) + .graphics_for(state.ids.grid_layer_0) + .set(state.ids.grid_layer_1, ui); + // Map layer 2 + Image::new(world_map_layer_2.none) + .mid_top_with_margin_on(state.ids.map_align, 10.0) + .w_h(map_size.x, map_size.y) + .parent(state.ids.bg) + .source_rectangle(rect_src) + .graphics_for(state.ids.grid_layer_0) + .set(state.ids.grid_layer_2, ui); // Handle zooming with the mousewheel let scrolled: f64 = ui - .widget_input(state.ids.grid) + .widget_input(state.ids.grid_layer_0) .scrolls() .map(|scroll| scroll.y) .sum(); @@ -544,40 +564,6 @@ impl<'a> Widget for Map<'a> { { events.push(Event::ShowTrees(!show_trees)); } - Text::new(i18n.get("hud.map.trees")) - .right_from(state.ids.show_trees_box, 10.0) - .font_size(self.fonts.cyri.scale(14)) - .font_id(self.fonts.cyri.conrod_id) - .graphics_for(state.ids.show_trees_box) - .color(TEXT_COLOR) - .set(state.ids.show_trees_text, ui); - // Topographical Map - Image::new(self.imgs.mmap_site_tree) - .down_from(state.ids.show_caves_img, 10.0) - .w_h(20.0, 20.0) - .set(state.ids.show_trees_img, ui); - if Button::image(if show_topo_map { - self.imgs.checkbox_checked - } else { - self.imgs.checkbox - }) - .w_h(18.0, 18.0) - .hover_image(if show_topo_map { - self.imgs.checkbox_checked_mo - } else { - self.imgs.checkbox_mo - }) - .press_image(if show_topo_map { - self.imgs.checkbox_checked - } else { - self.imgs.checkbox_press - }) - .right_from(state.ids.show_trees_img, 10.0) - .set(state.ids.show_trees_box, ui) - .was_clicked() - { - events.push(Event::ShowTopoMap(!show_topo_map)); - } Text::new(i18n.get("hud.map.trees")) .right_from(state.ids.show_trees_box, 10.0) .font_size(self.fonts.cyri.scale(14)) @@ -649,7 +635,7 @@ impl<'a> Widget for Map<'a> { SiteKind::Tree => self.imgs.mmap_site_tree, }) .x_y_position_relative_to( - state.ids.grid, + state.ids.grid_layer_0, position::Relative::Scalar(rpos.x as f64), position::Relative::Scalar(rpos.y as f64), ) @@ -824,7 +810,7 @@ impl<'a> Widget for Map<'a> { _ => self.imgs.indicator_group, }) .x_y_position_relative_to( - state.ids.grid, + state.ids.grid_layer_0, position::Relative::Scalar(rpos.x as f64), position::Relative::Scalar(rpos.y as f64), ) @@ -858,7 +844,7 @@ impl<'a> Widget for Map<'a> { { Image::new(self.rot_imgs.indicator_mmap_small.target_north) .x_y_position_relative_to( - state.ids.grid, + state.ids.grid_layer_0, position::Relative::Scalar(rpos.x as f64), position::Relative::Scalar(rpos.y as f64), ) @@ -877,7 +863,7 @@ impl<'a> Widget for Map<'a> { }; if Button::image(self.imgs.button) .w_h(92.0, icon_size.y) - .mid_bottom_with_margin_on(state.ids.grid, -36.0) + .mid_bottom_with_margin_on(state.ids.grid_layer_0, -36.0) .hover_image(if recenter { self.imgs.button_hover } else { @@ -909,7 +895,7 @@ impl<'a> Widget for Map<'a> { }; Image::new(self.imgs.m_move_ico) - .bottom_left_with_margins_on(state.ids.grid, -36.0, 0.0) + .bottom_left_with_margins_on(state.ids.grid_layer_0, -36.0, 0.0) .w_h(icon_size.x, icon_size.y) .color(Some(UI_HIGHLIGHT_0)) .set(state.ids.drag_ico, ui); @@ -917,7 +903,7 @@ impl<'a> Widget for Map<'a> { .right_from(state.ids.drag_ico, 5.0) .font_size(self.fonts.cyri.scale(14)) .font_id(self.fonts.cyri.conrod_id) - .graphics_for(state.ids.grid) + .graphics_for(state.ids.grid_layer_0) .color(TEXT_COLOR) .set(state.ids.drag_txt, ui); Image::new(self.imgs.m_scroll_ico) @@ -929,7 +915,7 @@ impl<'a> Widget for Map<'a> { .right_from(state.ids.zoom_ico, 5.0) .font_size(self.fonts.cyri.scale(14)) .font_id(self.fonts.cyri.conrod_id) - .graphics_for(state.ids.grid) + .graphics_for(state.ids.grid_layer_0) .color(TEXT_COLOR) .set(state.ids.zoom_txt, ui); diff --git a/voxygen/src/hud/minimap.rs b/voxygen/src/hud/minimap.rs index 3107e4afce..36b88b7512 100644 --- a/voxygen/src/hud/minimap.rs +++ b/voxygen/src/hud/minimap.rs @@ -47,8 +47,7 @@ pub struct MiniMap<'a> { imgs: &'a Imgs, rot_imgs: &'a ImgsRot, - world_map: &'a (img_ids::Rotations, Vec2), - world_map_topo: &'a (img_ids::Rotations, Vec2), + world_map: &'a (&'a img_ids::Rotations, Vec2), fonts: &'a Fonts, #[conrod(common_builder)] common: widget::CommonBuilder, @@ -61,8 +60,7 @@ impl<'a> MiniMap<'a> { client: &'a Client, imgs: &'a Imgs, rot_imgs: &'a ImgsRot, - world_map: &'a (img_ids::Rotations, Vec2), - world_map_topo: &'a (img_ids::Rotations, Vec2), + world_map: &'a (&'a img_ids::Rotations, Vec2), fonts: &'a Fonts, ori: Vec3, global_state: &'a GlobalState, @@ -72,7 +70,6 @@ impl<'a> MiniMap<'a> { imgs, rot_imgs, world_map, - world_map_topo, fonts, common: widget::CommonBuilder::default(), ori, @@ -143,8 +140,9 @@ impl<'a> Widget for MiniMap<'a> { .set(state.ids.mmap_frame_bg, ui); // Map size in chunk coords - let show_topo_map = self.global_state.settings.interface.map_show_topo_map; - let (world_map, worldsize) = if !show_topo_map { self.world_map } else { self.world_map_topo }; + //let show_topo_map = self.global_state.settings.interface.map_show_topo_map; + //let (world_map, worldsize) = if !show_topo_map { self.world_map } else { self.world_map_topo }; + let (world_map, worldsize) = self.world_map; // Zoom Buttons diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 516de25c5d..b106263d2d 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -779,8 +779,7 @@ impl PromptDialogSettings { pub struct Hud { ui: Ui, ids: Ids, - world_map: (/* Id */ Rotations, Vec2), - world_map_topo: (/* Id */ Rotations, Vec2), + world_map_layers: (/* Id */ Vec, Vec2), imgs: Imgs, item_imgs: ItemImgs, fonts: Fonts, @@ -821,19 +820,15 @@ impl Hud { // translucent alpha since UI have transparency and LOD doesn't). let water_color = srgba_to_linear(Rgba::new(0.0, 0.18, 0.37, 1.0)); // Load world map - let world_map = ( - ui.add_graphic_with_rotations(Graphic::Image( - Arc::clone(client.world_data().map_image()), + let mut layers = Vec::new(); + for layer in client.world_data().map_layers() { + layers.push(ui.add_graphic_with_rotations(Graphic::Image( + Arc::clone(layer), Some(water_color), - )), - client.world_data().chunk_size().map(|e| e as u32), - ); - // Load world topo map - let world_map_topo = ( - ui.add_graphic_with_rotations(Graphic::Image( - Arc::clone(client.world_data().map_topo_image()), - Some(water_color), - )), + ))); + } + let world_map_layers = ( + layers, client.world_data().chunk_size().map(|e| e as u32), ); // Load images. @@ -872,8 +867,7 @@ impl Hud { Self { ui, imgs, - world_map, - world_map_topo, + world_map_layers, rot_imgs, item_imgs, fonts, @@ -2241,8 +2235,7 @@ impl Hud { client, &self.imgs, &self.rot_imgs, - &self.world_map, - &self.world_map_topo, + &(&self.world_map_layers.0[1], self.world_map_layers.1), &self.fonts, camera.get_orientation(), &global_state, @@ -2795,8 +2788,7 @@ impl Hud { client, &self.imgs, &self.rot_imgs, - &self.world_map, - &self.world_map_topo, + &self.world_map_layers, &self.fonts, self.pulse, i18n, diff --git a/world/src/sim/map.rs b/world/src/sim/map.rs index 8f03a53263..09bf61c6db 100644 --- a/world/src/sim/map.rs +++ b/world/src/sim/map.rs @@ -248,7 +248,7 @@ pub fn sample_pos( }; MapSample { - rgb, + rgba: Rgba::new(rgb.r, rgb.g, rgb.b, 255), alt: if is_water { true_alt.max(true_water_alt) } else { From 44715100c18a5b5655d4fb9ac64553db316a6dd1 Mon Sep 17 00:00:00 2001 From: James Melkonian Date: Sat, 3 Apr 2021 23:01:40 -0700 Subject: [PATCH 3/5] Add layers to minimap --- client/src/lib.rs | 19 ++++---- common/src/terrain/map.rs | 18 +++++--- voxygen/src/hud/map.rs | 88 +++++++++++++++++++------------------- voxygen/src/hud/minimap.rs | 69 +++++++++++++++++++----------- voxygen/src/hud/mod.rs | 7 +-- voxygen/src/session.rs | 4 -- voxygen/src/settings.rs | 2 - 7 files changed, 113 insertions(+), 94 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index ac164702d6..161caab118 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -352,8 +352,9 @@ impl Client { let MapConfig { gain, is_contours, - is_hill_shaded, + is_height_map, is_political, + is_roads, .. } = *map_config; let mut is_contour_line = false; @@ -365,7 +366,8 @@ impl Client { let alti = alt[pos]; // Compute contours (chunks are assigned in the river code below) let altj = rescale_height(scale_height_big(alti)); - let chunk_contour = (altj * gain / 150.0) as u32; + let contour_interval = 150.0; + let chunk_contour = (altj * gain / contour_interval) as u32; // Compute downhill. let downhill = { @@ -374,7 +376,7 @@ impl Client { for nposi in neighbors(*map_size_lg, posi) { let nbh = alt.raw()[nposi]; let nalt = rescale_height(scale_height_big(nbh)); - let nchunk_contour = (nalt * gain / 150.0) as u32; + let nchunk_contour = (nalt * gain / contour_interval) as u32; if !is_contour_line && chunk_contour > nchunk_contour { is_contour_line = true; } @@ -412,7 +414,7 @@ impl Client { .unwrap_or(wpos + TerrainChunkSize::RECT_SIZE.map(|e| e as i32)); let is_path = rgba.r == 0x37 && rgba.g == 0x29 && rgba.b == 0x23; let rgba = rgba.map(|e: u8| e as f64 / 255.0); - let rgba = if is_hill_shaded { + let rgba = if is_height_map { if is_path { // Path color is Rgb::new(0x37, 0x29, 0x23) Rgba::new(0.9, 0.9, 0.63, 1.0) @@ -427,9 +429,8 @@ impl Client { let lightness = (alt + 0.2).min(1.0) as f64; Rgba::new(lightness, 0.9 * lightness, 0.5 * lightness, 0.5) } - } else if is_contours && is_contour_line { - // Color contour lines - Rgba::new(0.15, 0.15, 0.15, 0.9) + } else if is_roads && is_path { + Rgba::new(0.9, 0.9, 0.63, 1.0) } else if is_political { if is_path { Rgba::new(0.3, 0.3, 0.3, 1.0) @@ -438,6 +439,8 @@ impl Client { } else { Rgba::new(1.0, 0.9, 0.6, 1.0) } + } else if is_contours && is_contour_line { + Rgba::new(0.15, 0.15, 0.15, 0.9) } else { Rgba::new(rgba.r, rgba.g, rgba.b, 0.5) }.map(|e| (e * 255.0) as u8); @@ -485,8 +488,8 @@ impl Client { }, ); // Generate topographic map - //map_config.is_hill_shaded = true; map_config.is_contours = true; + map_config.is_roads = true; map_config.generate( |pos| sample_pos(&map_config, pos, &alt, &rgba, &map_size, &map_size_lg, max_height), |wpos| { diff --git a/common/src/terrain/map.rs b/common/src/terrain/map.rs index f62075182e..74313a9bc5 100644 --- a/common/src/terrain/map.rs +++ b/common/src/terrain/map.rs @@ -321,16 +321,19 @@ pub struct MapConfig<'a> { /// /// Defaults to false. pub is_contours: bool, - /// If true, hill shading is applied to the terrain and all the - /// colors are different. Is incompatible with humidity/temperature/shaded - /// maps. + /// If true, a yellow/terracotta heightmap shading is applied to the + /// terrain and water is a faded blue. /// /// Defaults to false - pub is_hill_shaded: bool, + pub is_height_map: bool, /// If true, terrain is white, rivers, borders, and roads are black. /// /// Defaults to false pub is_political: bool, + /// If true, roads are colored on top of everything else + /// + /// Defaults to false + pub is_roads: bool, } pub const QUADRANTS: usize = 4; @@ -412,8 +415,9 @@ impl<'a> MapConfig<'a> { is_humidity: false, is_debug: false, is_contours: false, - is_hill_shaded: false, + is_height_map: false, is_political: false, + is_roads: false, } } @@ -511,7 +515,7 @@ impl<'a> MapConfig<'a> { let alt = alt as f32; let wposi = pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32); let rgb = Rgb::new(rgba.r, rgba.g, rgba.b).map(|e| e as f64 / 255.0); - let rgba = rgba.map(|e| e as f64 / 255.0); + let mut rgba = rgba.map(|e| e as f64 / 255.0); // Material properties: // @@ -587,7 +591,7 @@ impl<'a> MapConfig<'a> { if has_river { let water_rgb = Rgb::new(0, ((g_water) * 1.0) as u8, ((b_water) * 1.0) as u8) .map(|e| e as f64 / 255.0); - //rgba = Rgba::new(water_rgb.r, water_rgb.g, water_rgb.b, 1.0); + rgba = Rgba::new(water_rgb.r, water_rgb.g, water_rgb.b, rgba.a); k_s = Rgb::new(1.0, 1.0, 1.0); k_d = water_rgb; k_a = water_rgb; diff --git a/voxygen/src/hud/map.rs b/voxygen/src/hud/map.rs index 172208894c..2205df6b66 100644 --- a/voxygen/src/hud/map.rs +++ b/voxygen/src/hud/map.rs @@ -31,9 +31,7 @@ widget_ids! { location_name, indicator, indicator_overlay, - grid_layer_0, - grid_layer_1, - grid_layer_2, + map_layers[], map_title, qlog_title, zoom_slider, @@ -125,7 +123,6 @@ pub enum Event { ShowDungeons(bool), ShowCaves(bool), ShowTrees(bool), - ShowTopoMap(bool), Close, RequestSiteInfo(SiteId), } @@ -187,7 +184,6 @@ impl<'a> Widget for Map<'a> { let show_castles = self.global_state.settings.interface.map_show_castles; let show_caves = self.global_state.settings.interface.map_show_caves; let show_trees = self.global_state.settings.interface.map_show_trees; - let show_topo_map = self.global_state.settings.interface.map_show_topo_map; let mut events = Vec::new(); let i18n = &self.localized_strings; // Tooltips @@ -269,16 +265,24 @@ impl<'a> Widget for Map<'a> { .color(TEXT_COLOR) .set(state.ids.location_name, ui), }*/ + // Map Layers + // It is assumed that there is at least one layer + if state.ids.map_layers.len() < self.world_map_layers.0.len() { + state.update(|state| { + state + .ids + .map_layers + .resize(self.world_map_layers.0.len(), &mut ui.widget_id_generator()) + }); + } + Image::new(self.imgs.map_frame_art) .mid_top_with_margin_on(state.ids.map_align, 5.0) .w_h(765.0, 765.0) .parent(state.ids.bg) - .set(state.ids.grid_layer_0, ui); - // Map Image - //let (world_map, worldsize) = if !show_topo_map { self.world_map } else { self.world_map_topo }; - let world_map_layer_0 = &self.world_map_layers.0[0]; - let world_map_layer_1 = &self.world_map_layers.0[1]; - let world_map_layer_2 = &self.world_map_layers.0[2]; + .set(state.ids.map_layers[0], ui); + + // Map Size let worldsize = self.world_map_layers.1; // Coordinates @@ -300,7 +304,7 @@ impl<'a> Widget for Map<'a> { // Handle dragging let drag = self.global_state.settings.interface.map_drag; let dragged: Vec2 = ui - .widget_input(state.ids.grid_layer_0) + .widget_input(state.ids.map_layers[0]) .drags() .left() .map(|drag| Vec2::::from(drag.delta_xy)) @@ -328,32 +332,30 @@ impl<'a> Widget for Map<'a> { { events.push(Event::Close); } - // Map layer 0 - Image::new(world_map_layer_0.none) - .mid_top_with_margin_on(state.ids.map_align, 10.0) - .w_h(map_size.x, map_size.y) - .parent(state.ids.bg) - .source_rectangle(rect_src) - .set(state.ids.grid_layer_0, ui); - // Map layer 1 - Image::new(world_map_layer_1.none) - .mid_top_with_margin_on(state.ids.map_align, 10.0) - .w_h(map_size.x, map_size.y) - .parent(state.ids.bg) - .source_rectangle(rect_src) - .graphics_for(state.ids.grid_layer_0) - .set(state.ids.grid_layer_1, ui); - // Map layer 2 - Image::new(world_map_layer_2.none) - .mid_top_with_margin_on(state.ids.map_align, 10.0) - .w_h(map_size.x, map_size.y) - .parent(state.ids.bg) - .source_rectangle(rect_src) - .graphics_for(state.ids.grid_layer_0) - .set(state.ids.grid_layer_2, ui); + + // Map Layer Images + for (index, layer) in self.world_map_layers.0.iter().enumerate() { + if index == 0 { + Image::new(layer.none) + .mid_top_with_margin_on(state.ids.map_align, 10.0) + .w_h(map_size.x, map_size.y) + .parent(state.ids.bg) + .source_rectangle(rect_src) + .set(state.ids.map_layers[index], ui); + } else { + Image::new(layer.none) + .mid_top_with_margin_on(state.ids.map_align, 10.0) + .w_h(map_size.x, map_size.y) + .parent(state.ids.bg) + .source_rectangle(rect_src) + .graphics_for(state.ids.map_layers[0]) + .set(state.ids.map_layers[index], ui); + } + } + // Handle zooming with the mousewheel let scrolled: f64 = ui - .widget_input(state.ids.grid_layer_0) + .widget_input(state.ids.map_layers[0]) .scrolls() .map(|scroll| scroll.y) .sum(); @@ -635,7 +637,7 @@ impl<'a> Widget for Map<'a> { SiteKind::Tree => self.imgs.mmap_site_tree, }) .x_y_position_relative_to( - state.ids.grid_layer_0, + state.ids.map_layers[0], position::Relative::Scalar(rpos.x as f64), position::Relative::Scalar(rpos.y as f64), ) @@ -810,7 +812,7 @@ impl<'a> Widget for Map<'a> { _ => self.imgs.indicator_group, }) .x_y_position_relative_to( - state.ids.grid_layer_0, + state.ids.map_layers[0], position::Relative::Scalar(rpos.x as f64), position::Relative::Scalar(rpos.y as f64), ) @@ -844,7 +846,7 @@ impl<'a> Widget for Map<'a> { { Image::new(self.rot_imgs.indicator_mmap_small.target_north) .x_y_position_relative_to( - state.ids.grid_layer_0, + state.ids.map_layers[0], position::Relative::Scalar(rpos.x as f64), position::Relative::Scalar(rpos.y as f64), ) @@ -863,7 +865,7 @@ impl<'a> Widget for Map<'a> { }; if Button::image(self.imgs.button) .w_h(92.0, icon_size.y) - .mid_bottom_with_margin_on(state.ids.grid_layer_0, -36.0) + .mid_bottom_with_margin_on(state.ids.map_layers[0], -36.0) .hover_image(if recenter { self.imgs.button_hover } else { @@ -895,7 +897,7 @@ impl<'a> Widget for Map<'a> { }; Image::new(self.imgs.m_move_ico) - .bottom_left_with_margins_on(state.ids.grid_layer_0, -36.0, 0.0) + .bottom_left_with_margins_on(state.ids.map_layers[0], -36.0, 0.0) .w_h(icon_size.x, icon_size.y) .color(Some(UI_HIGHLIGHT_0)) .set(state.ids.drag_ico, ui); @@ -903,7 +905,7 @@ impl<'a> Widget for Map<'a> { .right_from(state.ids.drag_ico, 5.0) .font_size(self.fonts.cyri.scale(14)) .font_id(self.fonts.cyri.conrod_id) - .graphics_for(state.ids.grid_layer_0) + .graphics_for(state.ids.map_layers[0]) .color(TEXT_COLOR) .set(state.ids.drag_txt, ui); Image::new(self.imgs.m_scroll_ico) @@ -915,7 +917,7 @@ impl<'a> Widget for Map<'a> { .right_from(state.ids.zoom_ico, 5.0) .font_size(self.fonts.cyri.scale(14)) .font_id(self.fonts.cyri.conrod_id) - .graphics_for(state.ids.grid_layer_0) + .graphics_for(state.ids.map_layers[0]) .color(TEXT_COLOR) .set(state.ids.zoom_txt, ui); diff --git a/voxygen/src/hud/minimap.rs b/voxygen/src/hud/minimap.rs index 36b88b7512..e8c9dc8a70 100644 --- a/voxygen/src/hud/minimap.rs +++ b/voxygen/src/hud/minimap.rs @@ -29,7 +29,7 @@ widget_ids! { mmap_plus, mmap_minus, mmap_north_button, - grid, + map_layers[], indicator, mmap_north, mmap_east, @@ -47,7 +47,7 @@ pub struct MiniMap<'a> { imgs: &'a Imgs, rot_imgs: &'a ImgsRot, - world_map: &'a (&'a img_ids::Rotations, Vec2), + world_map_layers: &'a (Vec, Vec2), fonts: &'a Fonts, #[conrod(common_builder)] common: widget::CommonBuilder, @@ -60,7 +60,7 @@ impl<'a> MiniMap<'a> { client: &'a Client, imgs: &'a Imgs, rot_imgs: &'a ImgsRot, - world_map: &'a (&'a img_ids::Rotations, Vec2), + world_map_layers: &'a (Vec, Vec2), fonts: &'a Fonts, ori: Vec3, global_state: &'a GlobalState, @@ -69,7 +69,7 @@ impl<'a> MiniMap<'a> { client, imgs, rot_imgs, - world_map, + world_map_layers, fonts, common: widget::CommonBuilder::default(), ori, @@ -99,7 +99,7 @@ impl<'a> Widget for MiniMap<'a> { ids: Ids::new(id_gen), zoom: { - let min_world_dim = self.world_map.1.reduce_partial_min() as f64; + let min_world_dim = self.world_map_layers.1.reduce_partial_min() as f64; min_world_dim.min( min_world_dim * (TerrainChunkSize::RECT_SIZE.reduce_partial_max() as f64 / 32.0) @@ -140,9 +140,17 @@ impl<'a> Widget for MiniMap<'a> { .set(state.ids.mmap_frame_bg, ui); // Map size in chunk coords - //let show_topo_map = self.global_state.settings.interface.map_show_topo_map; - //let (world_map, worldsize) = if !show_topo_map { self.world_map } else { self.world_map_topo }; - let (world_map, worldsize) = self.world_map; + let worldsize = self.world_map_layers.1; + // Map Layers + // It is assumed that there is at least one layer + if state.ids.map_layers.len() < self.world_map_layers.0.len() { + state.update(|state| { + state + .ids + .map_layers + .resize(self.world_map_layers.0.len(), &mut ui.widget_id_generator()) + }); + } // Zoom Buttons @@ -250,17 +258,30 @@ impl<'a> Widget for MiniMap<'a> { let map_size = Vec2::new(170.0 * SCALE, 170.0 * SCALE); // Map Image - let world_map_rotation = if is_facing_north { - world_map.none - } else { - world_map.source_north - }; - Image::new(world_map_rotation) - .middle_of(state.ids.mmap_frame_bg) - .w_h(map_size.x, map_size.y) - .parent(state.ids.mmap_frame_bg) - .source_rectangle(rect_src) - .set(state.ids.grid, ui); + // Map Layer Images + for (index, layer) in self.world_map_layers.0.iter().enumerate() { + let world_map_rotation = if is_facing_north { + layer.none + } else { + layer.source_north + }; + if index == 0 { + Image::new(world_map_rotation) + .middle_of(state.ids.mmap_frame_bg) + .w_h(map_size.x, map_size.y) + .parent(state.ids.mmap_frame_bg) + .source_rectangle(rect_src) + .set(state.ids.map_layers[index], ui); + } else { + Image::new(world_map_rotation) + .middle_of(state.ids.mmap_frame_bg) + .w_h(map_size.x, map_size.y) + .parent(state.ids.mmap_frame_bg) + .source_rectangle(rect_src) + .graphics_for(state.ids.map_layers[0]) + .set(state.ids.map_layers[index], ui); + } + } // Map icons if state.ids.mmap_site_icons.len() < self.client.sites().len() { @@ -309,7 +330,7 @@ impl<'a> Widget for MiniMap<'a> { SiteKind::Tree => self.imgs.mmap_site_tree, }) .x_y_position_relative_to( - state.ids.grid, + state.ids.map_layers[0], position::Relative::Scalar(rpos.x as f64), position::Relative::Scalar(rpos.y as f64), ) @@ -329,7 +350,7 @@ impl<'a> Widget for MiniMap<'a> { SiteKind::Cave => Color::Rgba(1.0, 1.0, 1.0, 0.0), SiteKind::Tree => Color::Rgba(1.0, 1.0, 1.0, 0.0), })) - .parent(state.ids.grid) + .parent(state.ids.map_layers[0]) .set(state.ids.mmap_site_icons_bgs[i], ui); Image::new(match &site.kind { SiteKind::Town => self.imgs.mmap_site_town, @@ -400,7 +421,7 @@ impl<'a> Widget for MiniMap<'a> { _ => self.imgs.indicator_group, }) .x_y_position_relative_to( - state.ids.grid, + state.ids.map_layers[0], position::Relative::Scalar(rpos.x as f64), position::Relative::Scalar(rpos.y as f64), ) @@ -418,7 +439,7 @@ impl<'a> Widget for MiniMap<'a> { self.rot_imgs.indicator_mmap_small.none }; Image::new(ind_rotation) - .middle_of(state.ids.grid) + .middle_of(state.ids.map_layers[0]) .w_h(32.0 * ind_scale, 37.0 * ind_scale) .color(Some(UI_HIGHLIGHT_0)) .floating(true) @@ -438,7 +459,7 @@ impl<'a> Widget for MiniMap<'a> { let pos = clamped * (map_size / 2.0 - 10.0); Text::new(name) .x_y_position_relative_to( - state.ids.grid, + state.ids.map_layers[0], position::Relative::Scalar(pos.x), position::Relative::Scalar(pos.y), ) diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index b106263d2d..53f64343cf 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -250,7 +250,6 @@ widget_ids! { chat, map, world_map, - world_map_topo, character_window, popup, minimap, @@ -381,7 +380,6 @@ pub enum Event { MapShowCastles(bool), MapShowCaves(bool), MapShowTrees(bool), - MapShowTopo(bool), AdjustWindowSize([u16; 2]), ChangeFullscreenMode(FullScreenSettings), ToggleParticlesEnabled(bool), @@ -2235,7 +2233,7 @@ impl Hud { client, &self.imgs, &self.rot_imgs, - &(&self.world_map_layers.0[1], self.world_map_layers.1), + &self.world_map_layers, &self.fonts, camera.get_orientation(), &global_state, @@ -2827,9 +2825,6 @@ impl Hud { map::Event::ShowTrees(map_show_trees) => { events.push(Event::MapShowTrees(map_show_trees)); }, - map::Event::ShowTopoMap(map_show_topo_map) => { - events.push(Event::MapShowTopo(map_show_topo_map)); - }, map::Event::RequestSiteInfo(id) => { events.push(Event::RequestSiteInfo(id)); }, diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index c6d9a1c924..ff7c946075 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -1356,10 +1356,6 @@ impl PlayState for SessionState { global_state.settings.interface.map_show_trees = map_show_trees; global_state.settings.save_to_file_warn(); }, - HudEvent::MapShowTopo(map_show_topo) => { - global_state.settings.interface.map_show_topo_map = map_show_topo; - global_state.settings.save_to_file_warn(); - }, HudEvent::RequestSiteInfo(id) => { let mut client = self.client.borrow_mut(); client.request_site_economy(id); diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs index 311920c5e9..55dcd9851a 100644 --- a/voxygen/src/settings.rs +++ b/voxygen/src/settings.rs @@ -451,7 +451,6 @@ pub struct InterfaceSettings { pub loading_tips: bool, pub map_show_caves: bool, pub map_show_trees: bool, - pub map_show_topo_map: bool, pub minimap_show: bool, pub minimap_face_north: bool, } @@ -484,7 +483,6 @@ impl Default for InterfaceSettings { loading_tips: true, map_show_caves: true, map_show_trees: true, - map_show_topo_map: false, minimap_show: true, minimap_face_north: false, } From 9c7bfb72ea7ea805ef9efea2d33850b47c29ab57 Mon Sep 17 00:00:00 2001 From: James Melkonian Date: Mon, 5 Apr 2021 16:44:00 -0700 Subject: [PATCH 4/5] Map icon scaling --- client/src/lib.rs | 2 +- voxygen/src/hud/map.rs | 29 +++++++++++++++-------------- voxygen/src/hud/minimap.rs | 18 +++++++++--------- voxygen/src/settings.rs | 2 ++ 4 files changed, 27 insertions(+), 24 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index 161caab118..b0b30b8c8e 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -440,7 +440,7 @@ impl Client { Rgba::new(1.0, 0.9, 0.6, 1.0) } } else if is_contours && is_contour_line { - Rgba::new(0.15, 0.15, 0.15, 0.9) + Rgba::new(0.15, 0.15, 0.15, 0.8) } else { Rgba::new(rgba.r, rgba.g, rgba.b, 0.5) }.map(|e| (e * 255.0) as u8); diff --git a/voxygen/src/hud/map.rs b/voxygen/src/hud/map.rs index 2205df6b66..d3ee0b2f73 100644 --- a/voxygen/src/hud/map.rs +++ b/voxygen/src/hud/map.rs @@ -71,7 +71,7 @@ const SHOW_ECONOMY: bool = false; // turn this display off (for 0.9) until we ha #[derive(WidgetCommon)] pub struct Map<'a> { client: &'a Client, - world_map_layers: &'a (Vec, Vec2), + world_map: &'a (Vec, Vec2), imgs: &'a Imgs, fonts: &'a Fonts, #[conrod(common_builder)] @@ -88,7 +88,7 @@ impl<'a> Map<'a> { client: &'a Client, imgs: &'a Imgs, rot_imgs: &'a ImgsRot, - world_map_layers: &'a (Vec, Vec2), + world_map: &'a (Vec, Vec2), fonts: &'a Fonts, pulse: f32, localized_strings: &'a Localization, @@ -98,7 +98,7 @@ impl<'a> Map<'a> { Self { imgs, rot_imgs, - world_map_layers, + world_map, client, fonts, common: widget::CommonBuilder::default(), @@ -267,12 +267,12 @@ impl<'a> Widget for Map<'a> { }*/ // Map Layers // It is assumed that there is at least one layer - if state.ids.map_layers.len() < self.world_map_layers.0.len() { + if state.ids.map_layers.len() < self.world_map.0.len() { state.update(|state| { state .ids .map_layers - .resize(self.world_map_layers.0.len(), &mut ui.widget_id_generator()) + .resize(self.world_map.0.len(), &mut ui.widget_id_generator()) }); } @@ -283,7 +283,7 @@ impl<'a> Widget for Map<'a> { .set(state.ids.map_layers[0], ui); // Map Size - let worldsize = self.world_map_layers.1; + let worldsize = self.world_map.1; // Coordinates let player_pos = self @@ -334,7 +334,7 @@ impl<'a> Widget for Map<'a> { } // Map Layer Images - for (index, layer) in self.world_map_layers.0.iter().enumerate() { + for (index, layer) in self.world_map.0.iter().enumerate() { if index == 0 { Image::new(layer.none) .mid_top_with_margin_on(state.ids.map_align, 10.0) @@ -603,6 +603,7 @@ impl<'a> Widget for Map<'a> { // Convert to relative pixel coordinates from the center of the map // Accounting for zooming let rpos = rfpos.map2(map_size, |e, sz| e * sz as f32 * zoom as f32); + let rside = zoom * 6.0; if rpos .map2(map_size, |e, sz| e.abs() > sz as f32 / 2.0) @@ -641,7 +642,7 @@ impl<'a> Widget for Map<'a> { position::Relative::Scalar(rpos.x as f64), position::Relative::Scalar(rpos.y as f64), ) - .w_h(20.0 * 1.2, 20.0 * 1.2) + .w_h(rside * 1.2, rside * 1.2) .hover_image(match &site.kind { SiteKind::Town => self.imgs.mmap_site_town_hover, SiteKind::Dungeon { .. } => self.imgs.mmap_site_dungeon_hover, @@ -700,16 +701,16 @@ impl<'a> Widget for Map<'a> { _ => self.imgs.nothing, }) .mid_top_with_margin_on(state.ids.mmap_site_icons[i], match difficulty { - 5 => -12.0 * size, - _ => -4.0 * size, + 5 => -2.0 * zoom * size, + _ => -1.0 * zoom * size, }) .w(match difficulty { - 5 => 12.0 * size, - _ => 4.0 * size * difficulty as f64, + 5 => 2.0 * zoom * size, + _ => 1.0 * zoom * size * difficulty as f64, }) .h(match difficulty { - 5 => 12.0 * size, - _ => 4.0 * size, + 5 => 2.0 * size * zoom, + _ => 1.0 * zoom * size, }) .color(Some(match difficulty { 0 => QUALITY_LOW, diff --git a/voxygen/src/hud/minimap.rs b/voxygen/src/hud/minimap.rs index e8c9dc8a70..b9e481c50a 100644 --- a/voxygen/src/hud/minimap.rs +++ b/voxygen/src/hud/minimap.rs @@ -47,7 +47,7 @@ pub struct MiniMap<'a> { imgs: &'a Imgs, rot_imgs: &'a ImgsRot, - world_map_layers: &'a (Vec, Vec2), + world_map: &'a (Vec, Vec2), fonts: &'a Fonts, #[conrod(common_builder)] common: widget::CommonBuilder, @@ -60,7 +60,7 @@ impl<'a> MiniMap<'a> { client: &'a Client, imgs: &'a Imgs, rot_imgs: &'a ImgsRot, - world_map_layers: &'a (Vec, Vec2), + world_map: &'a (Vec, Vec2), fonts: &'a Fonts, ori: Vec3, global_state: &'a GlobalState, @@ -69,7 +69,7 @@ impl<'a> MiniMap<'a> { client, imgs, rot_imgs, - world_map_layers, + world_map, fonts, common: widget::CommonBuilder::default(), ori, @@ -99,7 +99,7 @@ impl<'a> Widget for MiniMap<'a> { ids: Ids::new(id_gen), zoom: { - let min_world_dim = self.world_map_layers.1.reduce_partial_min() as f64; + let min_world_dim = self.world_map.1.reduce_partial_min() as f64; min_world_dim.min( min_world_dim * (TerrainChunkSize::RECT_SIZE.reduce_partial_max() as f64 / 32.0) @@ -140,15 +140,15 @@ impl<'a> Widget for MiniMap<'a> { .set(state.ids.mmap_frame_bg, ui); // Map size in chunk coords - let worldsize = self.world_map_layers.1; + let worldsize = self.world_map.1; // Map Layers // It is assumed that there is at least one layer - if state.ids.map_layers.len() < self.world_map_layers.0.len() { + if state.ids.map_layers.len() < self.world_map.0.len() { state.update(|state| { state .ids .map_layers - .resize(self.world_map_layers.0.len(), &mut ui.widget_id_generator()) + .resize(self.world_map.0.len(), &mut ui.widget_id_generator()) }); } @@ -259,7 +259,7 @@ impl<'a> Widget for MiniMap<'a> { // Map Image // Map Layer Images - for (index, layer) in self.world_map_layers.0.iter().enumerate() { + for (index, layer) in self.world_map.0.iter().enumerate() { let world_map_rotation = if is_facing_north { layer.none } else { @@ -350,7 +350,7 @@ impl<'a> Widget for MiniMap<'a> { SiteKind::Cave => Color::Rgba(1.0, 1.0, 1.0, 0.0), SiteKind::Tree => Color::Rgba(1.0, 1.0, 1.0, 0.0), })) - .parent(state.ids.map_layers[0]) + .parent(state.ids.map_layers[2]) .set(state.ids.mmap_site_icons_bgs[i], ui); Image::new(match &site.kind { SiteKind::Town => self.imgs.mmap_site_town, diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs index 55dcd9851a..11c3a826ea 100644 --- a/voxygen/src/settings.rs +++ b/voxygen/src/settings.rs @@ -451,6 +451,7 @@ pub struct InterfaceSettings { pub loading_tips: bool, pub map_show_caves: bool, pub map_show_trees: bool, + pub map_show_topo: bool, pub minimap_show: bool, pub minimap_face_north: bool, } @@ -483,6 +484,7 @@ impl Default for InterfaceSettings { loading_tips: true, map_show_caves: true, map_show_trees: true, + map_show_topo: false, minimap_show: true, minimap_face_north: false, } From 7bb62615df68e4926dbdde083437668b6110cea7 Mon Sep 17 00:00:00 2001 From: James Melkonian Date: Mon, 5 Apr 2021 22:15:42 -0700 Subject: [PATCH 5/5] Add topographic option to map --- CHANGELOG.md | 1 + Cargo.toml | 2 +- assets/voxygen/element/map/topographic.png | Bin 0 -> 11585 bytes assets/voxygen/i18n/en/hud/map.ron | 1 + client/src/lib.rs | 303 +++++++++++++-------- common/src/terrain/map.rs | 6 +- voxygen/src/hud/img_ids.rs | 1 + voxygen/src/hud/map.rs | 44 ++- voxygen/src/hud/minimap.rs | 5 +- voxygen/src/hud/mod.rs | 24 +- voxygen/src/session.rs | 4 + voxygen/src/settings.rs | 4 +- world/examples/water.rs | 8 +- 13 files changed, 269 insertions(+), 134 deletions(-) create mode 100644 assets/voxygen/element/map/topographic.png diff --git a/CHANGELOG.md b/CHANGELOG.md index f13320ea09..50b0d3fd0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Attacks now emit sound effects from the target on hit. - Crafting menu tabs - Auto camera setting, making the game easier to play with one hand +- Topographic map option ### Changed diff --git a/Cargo.toml b/Cargo.toml index 362bccde47..2c7b8a0bbe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ debug = false codegen-units = 8 lto = false # TEMP false to avoid fingerprints bug -incremental = true +incremental = false # All dependencies (but not this crate itself) [profile.dev.package."*"] opt-level = 3 diff --git a/assets/voxygen/element/map/topographic.png b/assets/voxygen/element/map/topographic.png new file mode 100644 index 0000000000000000000000000000000000000000..289f64b1580f3cae310e3eede48c9d61ba28a433 GIT binary patch literal 11585 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 CnK{7# literal 0 HcmV?d00001 diff --git a/assets/voxygen/i18n/en/hud/map.ron b/assets/voxygen/i18n/en/hud/map.ron index 8482ecfbc2..4fbf02bc25 100644 --- a/assets/voxygen/i18n/en/hud/map.ron +++ b/assets/voxygen/i18n/en/hud/map.ron @@ -6,6 +6,7 @@ // Map and Questlog "hud.map.map_title": "Map", "hud.map.qlog_title": "Quests", + "hud.map.topo_map": "Topographic", "hud.map.difficulty": "Difficulty", "hud.map.towns": "Towns", "hud.map.castles": "Castles", diff --git a/client/src/lib.rs b/client/src/lib.rs index b0b30b8c8e..2c9f44a113 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -34,7 +34,10 @@ use common::{ outcome::Outcome, recipe::RecipeBook, resources::{DeltaTime, PlayerEntity, TimeOfDay}, - terrain::{block::Block, map::MapConfig, neighbors, BiomeKind, SitesKind, TerrainChunk, TerrainChunkSize}, + terrain::{ + block::Block, map::MapConfig, neighbors, BiomeKind, SitesKind, TerrainChunk, + TerrainChunkSize, + }, trade::{PendingTrade, SitePrices, TradeAction, TradeId, TradeResult}, uid::{Uid, UidAllocator}, vol::RectVolSize, @@ -326,8 +329,9 @@ impl Client { let horizons = [unzip_horizons(&west), unzip_horizons(&east)]; // Redraw map (with shadows this time). - let mut world_map_political = vec![0u32; rgba.size().product() as usize]; let mut world_map_rgba = vec![0u32; rgba.size().product() as usize]; + let mut world_map_political = vec![0u32; rgba.size().product() as usize]; + let mut world_map_rgba_half_alpha = vec![0u32; rgba.size().product() as usize]; let mut world_map_topo = vec![0u32; rgba.size().product() as usize]; let mut map_config = common::terrain::map::MapConfig::orthographic( map_size_lg, @@ -341,121 +345,167 @@ impl Client { && pos.y < map_size.y as i32 }; ping_stream.send(PingMsg::Ping)?; - fn sample_pos(map_config: &MapConfig, pos: Vec2, alt: &Grid, rgba: &Grid, map_size: &Vec2, map_size_lg: &common::terrain::MapSizeLg, max_height: f32) -> common::terrain::map::MapSample { - let rescale_height = |h: f32| h / max_height; - let scale_height_big = |h: u32| (h >> 3) as f32 / 8191.0 * max_height; - let bounds_check = |pos: Vec2| { - pos.reduce_partial_min() >= 0 - && pos.x < map_size.x as i32 - && pos.y < map_size.y as i32 - }; - let MapConfig { - gain, - is_contours, - is_height_map, - is_political, - is_roads, - .. - } = *map_config; - let mut is_contour_line = false; - let mut is_border = false; - let (rgba, alt, downhill_wpos) = if bounds_check(pos) { - let posi = pos.y as usize * map_size.x as usize + pos.x as usize; - let [r, g, b, a] = rgba[pos].to_le_bytes(); - let is_water = r == 0 && b > 102 && g < 77; - let alti = alt[pos]; - // Compute contours (chunks are assigned in the river code below) - let altj = rescale_height(scale_height_big(alti)); - let contour_interval = 150.0; - let chunk_contour = (altj * gain / contour_interval) as u32; + fn sample_pos( + map_config: &MapConfig, + pos: Vec2, + alt: &Grid, + rgba: &Grid, + map_size: &Vec2, + map_size_lg: &common::terrain::MapSizeLg, + max_height: f32, + ) -> common::terrain::map::MapSample { + let rescale_height = |h: f32| h / max_height; + let scale_height_big = |h: u32| (h >> 3) as f32 / 8191.0 * max_height; + let bounds_check = |pos: Vec2| { + pos.reduce_partial_min() >= 0 + && pos.x < map_size.x as i32 + && pos.y < map_size.y as i32 + }; + let MapConfig { + gain, + is_contours, + is_height_map, + is_political, + is_roads, + rgba_alpha, + .. + } = *map_config; + let mut is_contour_line = false; + let mut is_border = false; + let (rgba, alt, downhill_wpos) = if bounds_check(pos) { + let posi = pos.y as usize * map_size.x as usize + pos.x as usize; + let [r, g, b, a] = rgba[pos].to_le_bytes(); + let is_water = r == 0 && b > 102 && g < 77; + let alti = alt[pos]; + // Compute contours (chunks are assigned in the river code below) + let altj = rescale_height(scale_height_big(alti)); + let contour_interval = 150.0; + let chunk_contour = (altj * gain / contour_interval) as u32; - // Compute downhill. - let downhill = { - let mut best = -1; - let mut besth = alti; - for nposi in neighbors(*map_size_lg, posi) { - let nbh = alt.raw()[nposi]; - let nalt = rescale_height(scale_height_big(nbh)); - let nchunk_contour = (nalt * gain / contour_interval) as u32; - if !is_contour_line && chunk_contour > nchunk_contour { - is_contour_line = true; - } - let [nr, ng, nb, _na] = rgba.raw()[nposi].to_le_bytes(); - let n_is_water = nr == 0 && nb > 102 && ng < 77; - - if !is_border && is_political && is_water && !n_is_water { - is_border = true; - } - - if nbh < besth { - besth = nbh; - best = nposi as isize; - } + // Compute downhill. + let downhill = { + let mut best = -1; + let mut besth = alti; + for nposi in neighbors(*map_size_lg, posi) { + let nbh = alt.raw()[nposi]; + let nalt = rescale_height(scale_height_big(nbh)); + let nchunk_contour = (nalt * gain / contour_interval) as u32; + if !is_contour_line && chunk_contour > nchunk_contour { + is_contour_line = true; } - best - }; - let downhill_wpos = if downhill < 0 { - None - } else { - Some( - Vec2::new( - (downhill as usize % map_size.x as usize) as i32, - (downhill as usize / map_size.x as usize) as i32, - ) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32), - ) - }; - (Rgba::new(r, g, b, a), alti, downhill_wpos) - } else { - (Rgba::zero(), 0, None) + let [nr, ng, nb, _na] = rgba.raw()[nposi].to_le_bytes(); + let n_is_water = nr == 0 && nb > 102 && ng < 77; + + if !is_border && is_political && is_water && !n_is_water { + is_border = true; + } + + if nbh < besth { + besth = nbh; + best = nposi as isize; + } + } + best }; - let alt = f64::from(rescale_height(scale_height_big(alt))); - let wpos = pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32); - let downhill_wpos = downhill_wpos - .unwrap_or(wpos + TerrainChunkSize::RECT_SIZE.map(|e| e as i32)); - let is_path = rgba.r == 0x37 && rgba.g == 0x29 && rgba.b == 0x23; - let rgba = rgba.map(|e: u8| e as f64 / 255.0); - let rgba = if is_height_map { - if is_path { - // Path color is Rgb::new(0x37, 0x29, 0x23) - Rgba::new(0.9, 0.9, 0.63, 1.0) - } else if rgba.r == 0.0 && rgba.b > 0.4 && rgba.g < 0.3 { - // Water - Rgba::new(0.23, 0.47, 0.53, 0.5) - } else if is_contours && is_contour_line { - // Color contour lines - Rgba::new(0.15, 0.15, 0.15, 0.5) - } else { - // Color hill shading - let lightness = (alt + 0.2).min(1.0) as f64; - Rgba::new(lightness, 0.9 * lightness, 0.5 * lightness, 0.5) - } - } else if is_roads && is_path { - Rgba::new(0.9, 0.9, 0.63, 1.0) - } else if is_political { - if is_path { - Rgba::new(0.3, 0.3, 0.3, 1.0) - } else if is_border { - Rgba::new(0.0, 0.0, 0.0, 1.0) - } else { - Rgba::new(1.0, 0.9, 0.6, 1.0) - } - } else if is_contours && is_contour_line { - Rgba::new(0.15, 0.15, 0.15, 0.8) + let downhill_wpos = if downhill < 0 { + None } else { - Rgba::new(rgba.r, rgba.g, rgba.b, 0.5) - }.map(|e| (e * 255.0) as u8); - common::terrain::map::MapSample { - rgba, - alt, - downhill_wpos, - connections: None, - is_path, + Some( + Vec2::new( + (downhill as usize % map_size.x as usize) as i32, + (downhill as usize / map_size.x as usize) as i32, + ) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32), + ) + }; + (Rgba::new(r, g, b, a), alti, downhill_wpos) + } else { + (Rgba::zero(), 0, None) + }; + let alt = f64::from(rescale_height(scale_height_big(alt))); + let wpos = pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32); + let downhill_wpos = downhill_wpos + .unwrap_or(wpos + TerrainChunkSize::RECT_SIZE.map(|e| e as i32)); + let is_path = rgba.r == 0x37 && rgba.g == 0x29 && rgba.b == 0x23; + let rgba = rgba.map(|e: u8| e as f64 / 255.0); + let rgba = if is_height_map { + if is_path { + // Path color is Rgb::new(0x37, 0x29, 0x23) + Rgba::new(0.9, 0.9, 0.63, 1.0) + } else if rgba.r == 0.0 && rgba.b > 0.4 && rgba.g < 0.3 { + // Water + Rgba::new(0.23, 0.47, 0.53, 0.5) + } else if is_contours && is_contour_line { + // Color contour lines + Rgba::new(0.15, 0.15, 0.15, 0.5) + } else { + // Color hill shading + let lightness = (alt + 0.2).min(1.0) as f64; + Rgba::new(lightness, 0.9 * lightness, 0.5 * lightness, 0.5) } + } else if is_roads && is_path { + Rgba::new(0.9, 0.9, 0.63, 1.0) + } else if is_political { + if is_path { + Rgba::new(0.3, 0.3, 0.3, 1.0) + } else if is_border { + Rgba::new(0.0, 0.0, 0.0, 1.0) + } else { + Rgba::new(1.0, 0.9, 0.6, 1.0) + } + } else if is_contours && is_contour_line { + Rgba::new(0.15, 0.15, 0.15, 0.8) + } else { + Rgba::new(rgba.r, rgba.g, rgba.b, rgba_alpha) + } + .map(|e| (e * 255.0) as u8); + common::terrain::map::MapSample { + rgba, + alt, + downhill_wpos, + connections: None, + is_path, + } } map_config.is_shaded = true; + map_config.rgba_alpha = 1.0; + map_config.generate( + |pos| { + sample_pos( + &map_config, + pos, + &alt, + &rgba, + &map_size, + &map_size_lg, + max_height, + ) + }, + |wpos| { + let pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |e, f| e / f as i32); + rescale_height(if bounds_check(pos) { + scale_height_big(alt[pos]) + } else { + 0.0 + }) + }, + |pos, (r, g, b, a)| { + world_map_rgba[pos.y * map_size.x as usize + pos.x] = + u32::from_le_bytes([r, g, b, a]); + }, + ); map_config.is_political = true; map_config.generate( - |pos| sample_pos(&map_config, pos, &alt, &rgba, &map_size, &map_size_lg, max_height), + |pos| { + sample_pos( + &map_config, + pos, + &alt, + &rgba, + &map_size, + &map_size_lg, + max_height, + ) + }, |wpos| { let pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |e, f| e / f as i32); rescale_height(if bounds_check(pos) { @@ -471,9 +521,20 @@ impl Client { ); map_config.is_shaded = false; + map_config.rgba_alpha = 0.5; map_config.is_political = false; map_config.generate( - |pos| sample_pos(&map_config, pos, &alt, &rgba, &map_size, &map_size_lg, max_height), + |pos| { + sample_pos( + &map_config, + pos, + &alt, + &rgba, + &map_size, + &map_size_lg, + max_height, + ) + }, |wpos| { let pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |e, f| e / f as i32); rescale_height(if bounds_check(pos) { @@ -483,7 +544,7 @@ impl Client { }) }, |pos, (r, g, b, a)| { - world_map_rgba[pos.y * map_size.x as usize + pos.x] = + world_map_rgba_half_alpha[pos.y * map_size.x as usize + pos.x] = u32::from_le_bytes([r, g, b, a]); }, ); @@ -491,7 +552,17 @@ impl Client { map_config.is_contours = true; map_config.is_roads = true; map_config.generate( - |pos| sample_pos(&map_config, pos, &alt, &rgba, &map_size, &map_size_lg, max_height), + |pos| { + sample_pos( + &map_config, + pos, + &alt, + &rgba, + &map_size, + &map_size_lg, + max_height, + ) + }, |wpos| { let pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |e, f| e / f as i32); rescale_height(if bounds_check(pos) { @@ -525,9 +596,15 @@ impl Client { let lod_base = rgba; let lod_alt = alt; let world_map_rgba_img = make_raw(&world_map_rgba)?; - let world_map_topo_img = make_raw(&world_map_topo)?; let world_map_political_img = make_raw(&world_map_political)?; - let world_map_layers = vec!(world_map_political_img, world_map_rgba_img, world_map_topo_img); + let world_map_rgba_half_alpha_img = make_raw(&world_map_rgba_half_alpha)?; + let world_map_topo_img = make_raw(&world_map_topo)?; + let world_map_layers = vec![ + world_map_rgba_img, + world_map_political_img, + world_map_rgba_half_alpha_img, + world_map_topo_img, + ]; let horizons = (west.0, west.1, east.0, east.1) .into_par_iter() .map(|(wa, wh, ea, eh)| u32::from_le_bytes([wa, wh, ea, eh])) diff --git a/common/src/terrain/map.rs b/common/src/terrain/map.rs index 74313a9bc5..a323f20e31 100644 --- a/common/src/terrain/map.rs +++ b/common/src/terrain/map.rs @@ -334,6 +334,10 @@ pub struct MapConfig<'a> { /// /// Defaults to false pub is_roads: bool, + /// Alpha value for rgba. Handled by the sample_pos closure + /// + /// Defaults to 1.0 + pub rgba_alpha: f64, } pub const QUADRANTS: usize = 4; @@ -418,6 +422,7 @@ impl<'a> MapConfig<'a> { is_height_map: false, is_political: false, is_roads: false, + rgba_alpha: 1.0, } } @@ -736,4 +741,3 @@ impl<'a> MapConfig<'a> { } } } - diff --git a/voxygen/src/hud/img_ids.rs b/voxygen/src/hud/img_ids.rs index 024c762fda..5a746d9c83 100644 --- a/voxygen/src/hud/img_ids.rs +++ b/voxygen/src/hud/img_ids.rs @@ -336,6 +336,7 @@ image_ids! { crosshair_bg_pressed: "voxygen.element.misc_bg.crosshair_bg_pressed", // Map + map_topo: "voxygen.element.map.topographic", map_bg: "voxygen.element.misc_bg.map_bg", map_frame: "voxygen.element.misc_bg.map_frame", map_frame_art: "voxygen.element.misc_bg.map_frame_art", diff --git a/voxygen/src/hud/map.rs b/voxygen/src/hud/map.rs index d3ee0b2f73..07ac6d207e 100644 --- a/voxygen/src/hud/map.rs +++ b/voxygen/src/hud/map.rs @@ -40,6 +40,9 @@ widget_ids! { member_indicators[], member_height_indicators[], map_settings_align, + show_topo_map_img, + show_topo_map_box, + show_topo_map_text, show_towns_img, show_towns_box, show_towns_text, @@ -123,6 +126,7 @@ pub enum Event { ShowDungeons(bool), ShowCaves(bool), ShowTrees(bool), + ShowTopoMap(bool), Close, RequestSiteInfo(SiteId), } @@ -184,6 +188,7 @@ impl<'a> Widget for Map<'a> { let show_castles = self.global_state.settings.interface.map_show_castles; let show_caves = self.global_state.settings.interface.map_show_caves; let show_trees = self.global_state.settings.interface.map_show_trees; + let show_topo_map = self.global_state.settings.interface.map_show_topo_map; let mut events = Vec::new(); let i18n = &self.localized_strings; // Tooltips @@ -342,7 +347,7 @@ impl<'a> Widget for Map<'a> { .parent(state.ids.bg) .source_rectangle(rect_src) .set(state.ids.map_layers[index], ui); - } else { + } else if show_topo_map { Image::new(layer.none) .mid_top_with_margin_on(state.ids.map_align, 10.0) .w_h(map_size.x, map_size.y) @@ -369,9 +374,44 @@ impl<'a> Widget for Map<'a> { .top_right_with_margins_on(state.ids.frame, 55.0, 10.0) .set(state.ids.map_settings_align, ui); // Checkboxes + // Show topographic map + Image::new(self.imgs.map_topo) + .top_left_with_margins_on(state.ids.map_settings_align, 5.0, 5.0) + .w_h(20.0, 20.0) + .set(state.ids.show_topo_map_img, ui); + if Button::image(if show_topo_map { + self.imgs.checkbox_checked + } else { + self.imgs.checkbox + }) + .w_h(18.0, 18.0) + .hover_image(if show_topo_map { + self.imgs.checkbox_checked_mo + } else { + self.imgs.checkbox_mo + }) + .press_image(if show_topo_map { + self.imgs.checkbox_checked + } else { + self.imgs.checkbox_press + }) + .right_from(state.ids.show_topo_map_img, 10.0) + .set(state.ids.show_topo_map_box, ui) + .was_clicked() + { + events.push(Event::ShowTopoMap(!show_topo_map)); + } + Text::new(i18n.get("hud.map.topo_map")) + .right_from(state.ids.show_topo_map_box, 10.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .graphics_for(state.ids.show_topo_map_box) + .color(TEXT_COLOR) + .set(state.ids.show_topo_map_text, ui); + // Show difficulties Image::new(self.imgs.map_dif_6) - .top_left_with_margins_on(state.ids.map_settings_align, 5.0, 5.0) + .down_from(state.ids.show_topo_map_img, 10.0) .w_h(20.0, 20.0) .set(state.ids.show_difficulty_img, ui); if Button::image(if show_difficulty { diff --git a/voxygen/src/hud/minimap.rs b/voxygen/src/hud/minimap.rs index b9e481c50a..7eed945091 100644 --- a/voxygen/src/hud/minimap.rs +++ b/voxygen/src/hud/minimap.rs @@ -118,6 +118,7 @@ impl<'a> Widget for MiniMap<'a> { const SCALE: f64 = 1.5; // TODO Make this a setting 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 orientation = if is_facing_north { Vec3::new(0.0, 1.0, 0.0) } else { @@ -272,7 +273,7 @@ impl<'a> Widget for MiniMap<'a> { .parent(state.ids.mmap_frame_bg) .source_rectangle(rect_src) .set(state.ids.map_layers[index], ui); - } else { + } else if show_topo_map { Image::new(world_map_rotation) .middle_of(state.ids.mmap_frame_bg) .w_h(map_size.x, map_size.y) @@ -350,7 +351,7 @@ impl<'a> Widget for MiniMap<'a> { SiteKind::Cave => Color::Rgba(1.0, 1.0, 1.0, 0.0), SiteKind::Tree => Color::Rgba(1.0, 1.0, 1.0, 0.0), })) - .parent(state.ids.map_layers[2]) + .parent(state.ids.map_layers[3]) .set(state.ids.mmap_site_icons_bgs[i], ui); Image::new(match &site.kind { SiteKind::Town => self.imgs.mmap_site_town, diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 53f64343cf..6d4532f566 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -374,6 +374,7 @@ pub enum Event { ChangeAmbiance(f32), MapZoom(f64), MapDrag(Vec2), + MapShowTopoMap(bool), MapShowDifficulty(bool), MapShowTowns(bool), MapShowDungeons(bool), @@ -777,7 +778,7 @@ impl PromptDialogSettings { pub struct Hud { ui: Ui, ids: Ids, - world_map_layers: (/* Id */ Vec, Vec2), + world_map: (/* Id */ Vec, Vec2), imgs: Imgs, item_imgs: ItemImgs, fonts: Fonts, @@ -820,15 +821,11 @@ impl Hud { // Load world map let mut layers = Vec::new(); for layer in client.world_data().map_layers() { - layers.push(ui.add_graphic_with_rotations(Graphic::Image( - Arc::clone(layer), - Some(water_color), - ))); + layers.push( + ui.add_graphic_with_rotations(Graphic::Image(Arc::clone(layer), Some(water_color))), + ); } - let world_map_layers = ( - layers, - client.world_data().chunk_size().map(|e| e as u32), - ); + let world_map = (layers, client.world_data().chunk_size().map(|e| e as u32)); // Load images. let imgs = Imgs::load(&mut ui).expect("Failed to load images!"); // Load rotation images. @@ -865,7 +862,7 @@ impl Hud { Self { ui, imgs, - world_map_layers, + world_map, rot_imgs, item_imgs, fonts, @@ -2233,7 +2230,7 @@ impl Hud { client, &self.imgs, &self.rot_imgs, - &self.world_map_layers, + &self.world_map, &self.fonts, camera.get_orientation(), &global_state, @@ -2786,7 +2783,7 @@ impl Hud { client, &self.imgs, &self.rot_imgs, - &self.world_map_layers, + &self.world_map, &self.fonts, self.pulse, i18n, @@ -2801,6 +2798,9 @@ impl Hud { self.show.want_grab = true; self.force_ungrab = false; }, + map::Event::ShowTopoMap(map_show_topo_map) => { + events.push(Event::MapShowTopoMap(map_show_topo_map)); + }, map::Event::ShowDifficulties(map_show_difficulties) => { events.push(Event::MapShowDifficulty(map_show_difficulties)); }, diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index ff7c946075..e12d806ce3 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -1332,6 +1332,10 @@ impl PlayState for SessionState { global_state.settings.interface.map_drag = map_drag; global_state.settings.save_to_file_warn(); }, + HudEvent::MapShowTopoMap(map_show_topo_map) => { + global_state.settings.interface.map_show_topo_map = map_show_topo_map; + global_state.settings.save_to_file_warn(); + }, HudEvent::MapShowDifficulty(map_show_difficulty) => { global_state.settings.interface.map_show_difficulty = map_show_difficulty; global_state.settings.save_to_file_warn(); diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs index 11c3a826ea..90531dba08 100644 --- a/voxygen/src/settings.rs +++ b/voxygen/src/settings.rs @@ -444,6 +444,7 @@ pub struct InterfaceSettings { pub ui_scale: ScaleMode, pub map_zoom: f64, pub map_drag: Vec2, + pub map_show_topo_map: bool, pub map_show_difficulty: bool, pub map_show_towns: bool, pub map_show_dungeons: bool, @@ -451,7 +452,6 @@ pub struct InterfaceSettings { pub loading_tips: bool, pub map_show_caves: bool, pub map_show_trees: bool, - pub map_show_topo: bool, pub minimap_show: bool, pub minimap_face_north: bool, } @@ -477,6 +477,7 @@ impl Default for InterfaceSettings { ui_scale: ScaleMode::RelativeToWindow([1920.0, 1080.0].into()), map_zoom: 10.0, map_drag: Vec2 { x: 0.0, y: 0.0 }, + map_show_topo_map: false, map_show_difficulty: true, map_show_towns: true, map_show_dungeons: true, @@ -484,7 +485,6 @@ impl Default for InterfaceSettings { loading_tips: true, map_show_caves: true, map_show_trees: true, - map_show_topo: false, minimap_show: true, minimap_face_north: false, } diff --git a/world/examples/water.rs b/world/examples/water.rs index e4243b7cb1..8c722dddb8 100644 --- a/world/examples/water.rs +++ b/world/examples/water.rs @@ -91,9 +91,10 @@ fn main() { } else { MapSample { alt: 0.0, - rgb: Rgb::new(0, 0, 0), + rgba: Rgba::new(0, 0, 0, 255), connections: None, downhill_wpos: (pos + 1) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32), + is_path: false, } } }; @@ -178,6 +179,11 @@ fn main() { is_temperature, is_humidity, is_debug: true, + is_contours: false, + is_height_map: false, + is_political: false, + is_roads: false, + rgba_alpha: 1.0, }; if samples_changed {