From 54eb97b9de41dd526ca0c280454131980457269d Mon Sep 17 00:00:00 2001 From: James Melkonian Date: Sat, 24 Jul 2021 19:31:31 -0700 Subject: [PATCH] Reduce number of map layers and remove unneeded alpha channel --- client/src/lib.rs | 141 +++++++++++--------------------------- common/src/terrain/map.rs | 95 ++++++++++++------------- world/examples/water.rs | 7 +- world/src/sim/map.rs | 3 +- 4 files changed, 87 insertions(+), 159 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index 935aeec197..4a6a823c1e 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -350,8 +350,6 @@ impl Client { // Redraw map (with shadows this time). 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, @@ -385,16 +383,14 @@ impl Client { gain, is_contours, is_height_map, - is_political, - is_roads, - rgba_alpha, + is_stylized_topo, .. } = *map_config; let mut is_contour_line = false; let mut is_border = false; - let (rgba, alt, downhill_wpos) = if bounds_check(pos) { + let (rgb, 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 [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) @@ -416,7 +412,7 @@ impl Client { 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 { + if !is_border && is_water && !n_is_water { is_border = true; } @@ -437,57 +433,64 @@ impl Client { ) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32), ) }; - (Rgba::new(r, g, b, a), alti, downhill_wpos) + (Rgb::new(r, g, b), alti, downhill_wpos) } else { - (Rgba::zero(), 0, None) + (Rgb::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 { + let is_path = rgb.r == 0x37 && rgb.g == 0x29 && rgb.b == 0x23; + let rgb = rgb.map(|e: u8| e as f64 / 255.0); + let is_water = rgb.r == 0.0 && rgb.b > 0.4 && rgb.g < 0.3; + + let rgb = 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) + Rgb::new(0.9, 0.9, 0.63) + } else if is_water { + Rgb::new(0.23, 0.47, 0.53) } else if is_contours && is_contour_line { // Color contour lines - Rgba::new(0.15, 0.15, 0.15, 0.5) + Rgb::new(0.15, 0.15, 0.15) } 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) + Rgb::new(lightness, 0.9 * lightness, 0.5 * lightness) } - } else if is_roads && is_path { - Rgba::new(0.9, 0.9, 0.63, 1.0) - } else if is_political { + } else if is_stylized_topo { 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) + Rgb::new(0.9, 0.9, 0.63) + } else if is_water { + if is_border { + Rgb::new(0.10, 0.34, 0.50) + } else { + Rgb::new(0.23, 0.47, 0.63) + } + } else if is_contour_line { + Rgb::new(0.25, 0.25, 0.25) } else { - Rgba::new(1.0, 0.9, 0.6, 1.0) + // Stylized colors + Rgb::new( + (rgb.r + 0.25).min(1.0), + (rgb.g + 0.23).min(1.0), + (rgb.b + 0.10).min(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) + Rgb::new(rgb.r, rgb.g, rgb.b) } .map(|e| (e * 255.0) as u8); common::terrain::map::MapSample { - rgba, + rgb, alt, downhill_wpos, connections: None, - is_path, } } + // Generate standard shaded map map_config.is_shaded = true; - map_config.rgba_alpha = 1.0; map_config.generate( |pos| { sample_pos( @@ -513,64 +516,9 @@ impl Client { 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, - ) - }, - |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.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, - ) - }, - |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_half_alpha[pos.y * map_size.x as usize + pos.x] = - u32::from_le_bytes([r, g, b, a]); - }, - ); - // Generate topographic map + // Generate map with topographical lines and stylized colors map_config.is_contours = true; - map_config.is_roads = true; + map_config.is_stylized_topo = true; map_config.generate( |pos| { sample_pos( @@ -597,9 +545,9 @@ impl Client { }, ); ping_stream.send(PingMsg::Ping)?; - let make_raw = |rgba| -> Result<_, Error> { + let make_raw = |rgb| -> Result<_, Error> { let mut raw = vec![0u8; 4 * world_map_rgba.len()]; - LittleEndian::write_u32_into(rgba, &mut raw); + LittleEndian::write_u32_into(rgb, &mut raw); Ok(Arc::new( image::DynamicImage::ImageRgba8({ // Should not fail if the dimensions are correct. @@ -615,16 +563,9 @@ impl Client { ping_stream.send(PingMsg::Ping)?; let lod_base = rgba; let lod_alt = alt; - let world_map_rgba_img = make_raw(&world_map_rgba)?; - let world_map_political_img = make_raw(&world_map_political)?; - let world_map_rgba_half_alpha_img = make_raw(&world_map_rgba_half_alpha)?; + let world_map_rgb_img = make_raw(&world_map_rgba)?; 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 world_map_layers = vec![world_map_rgb_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 16e513da73..7f6550e02f 100644 --- a/common/src/terrain/map.rs +++ b/common/src/terrain/map.rs @@ -326,18 +326,10 @@ pub struct MapConfig<'a> { /// /// Defaults to false pub is_height_map: bool, - /// If true, terrain is white, rivers, borders, and roads are black. + /// Applies contour lines as well as color modifications /// /// Defaults to false - pub is_political: bool, - /// If true, roads are colored on top of everything else - /// - /// 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 is_stylized_topo: bool, } pub const QUADRANTS: usize = 4; @@ -374,7 +366,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 rgba: Rgba, + pub rgb: Rgb, /// Surface altitude information /// (correctly reflecting settings like is_basement and is_water) pub alt: f64, @@ -387,8 +379,6 @@ 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> { @@ -420,9 +410,7 @@ impl<'a> MapConfig<'a> { is_debug: false, is_contours: false, is_height_map: false, - is_political: false, - is_roads: false, - rgba_alpha: 1.0, + is_stylized_topo: false, } } @@ -459,6 +447,7 @@ impl<'a> MapConfig<'a> { light_direction, horizons, is_shaded, + is_stylized_topo, // is_debug, .. } = *self; @@ -509,7 +498,7 @@ impl<'a> MapConfig<'a> { }; let MapSample { - rgba, + rgb, alt, downhill_wpos, .. @@ -517,8 +506,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 mut rgba = rgba.map(|e| e as f64 / 255.0); + let mut rgb = rgb.map(|e| e as f64 / 255.0); // Material properties: // @@ -594,7 +582,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, rgba.a); + rgb = water_rgb; k_s = Rgb::new(1.0, 1.0, 1.0); k_d = water_rgb; k_a = water_rgb; @@ -652,34 +640,34 @@ impl<'a> MapConfig<'a> { } } */ - let shade_frac = horizon_map - .and_then(|(angles, heights)| { - chunk_idx - .and_then(|chunk_idx| angles.get(chunk_idx)) - .map(|&e| (e as f64, heights)) - }) - .and_then(|(e, heights)| { - chunk_idx - .and_then(|chunk_idx| heights.get(chunk_idx)) - .map(|&f| (e, f as f64)) - }) - .map(|(angle, height)| { - let w = 0.1; - let height = (height - f64::from(alt * gain)).max(0.0); - if angle != 0.0 && light_direction.x != 0.0 && height != 0.0 { - let deltax = height / angle; - let lighty = (light_direction.y / light_direction.x * deltax).abs(); - let deltay = lighty - height; - let s = (deltay / deltax / w).min(1.0).max(0.0); - // Smoothstep - s * s * (3.0 - 2.0 * s) - } else { - 1.0 - } - }) - .unwrap_or(1.0); - let rgb = if is_shaded { + let shade_frac = horizon_map + .and_then(|(angles, heights)| { + chunk_idx + .and_then(|chunk_idx| angles.get(chunk_idx)) + .map(|&e| (e as f64, heights)) + }) + .and_then(|(e, heights)| { + chunk_idx + .and_then(|chunk_idx| heights.get(chunk_idx)) + .map(|&f| (e, f as f64)) + }) + .map(|(angle, height)| { + let w = 0.1; + let height = (height - f64::from(alt * gain)).max(0.0); + if angle != 0.0 && light_direction.x != 0.0 && height != 0.0 { + let deltax = height / angle; + let lighty = (light_direction.y / light_direction.x * deltax).abs(); + let deltay = lighty - height; + let s = (deltay / deltax / w).min(1.0).max(0.0); + // Smoothstep + s * s * (3.0 - 2.0 * s) + } else { + 1.0 + } + }) + .unwrap_or(1.0); + // Phong reflection model with shadows: // // I_p = k_a i_a + shadow * Σ {m ∈ lights} (k_d (L_m ⋅ N) i_m,d + k_s (R_m ⋅ @@ -687,7 +675,11 @@ impl<'a> MapConfig<'a> { // // where for the whole scene, // i_a = (RGB) intensity of ambient lighting component - let i_a = Rgb::new(0.1, 0.1, 0.1); + let i_a = if is_stylized_topo { + Rgb::new(0.4, 0.4, 0.4) + } else { + Rgb::new(0.1, 0.1, 0.1) + }; // V = direction pointing towards the viewer (e.g. virtual camera). let v = Vec3::new(0.0, 0.0, -1.0).normalized(); @@ -720,14 +712,13 @@ 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; - 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) + (ambient + shadow * (diffuse + specular)).map(|e| e.min(1.0)) } else { - rgba + rgb } .map(|e| (e * 255.0) as u8); - let rgba = (rgb.r, rgb.g, rgb.b, rgb.a); + let rgba = (rgb.r, rgb.g, rgb.b, 255); write_pixel(Vec2::new(i, j), rgba); }); diff --git a/world/examples/water.rs b/world/examples/water.rs index df5da313ba..a2803dffb2 100644 --- a/world/examples/water.rs +++ b/world/examples/water.rs @@ -96,10 +96,9 @@ fn main() { } else { MapSample { alt: 0.0, - rgba: Rgba::new(0, 0, 0, 255), + rgb: Rgb::new(0, 0, 0), connections: None, downhill_wpos: (pos + 1) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32), - is_path: false, } } }; @@ -186,9 +185,7 @@ fn main() { is_debug: true, is_contours: false, is_height_map: false, - is_political: false, - is_roads: false, - rgba_alpha: 1.0, + is_stylized_topo: false, }; if samples_changed { diff --git a/world/src/sim/map.rs b/world/src/sim/map.rs index 09bf61c6db..f2c3b8d3a4 100644 --- a/world/src/sim/map.rs +++ b/world/src/sim/map.rs @@ -248,7 +248,7 @@ pub fn sample_pos( }; MapSample { - rgba: Rgba::new(rgb.r, rgb.g, rgb.b, 255), + rgb: Rgb::new(rgb.r, rgb.g, rgb.b), alt: if is_water { true_alt.max(true_water_alt) } else { @@ -260,6 +260,5 @@ pub fn sample_pos( } else { None }, - is_path, } }