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 {