diff --git a/common/src/terrain/map.rs b/common/src/terrain/map.rs index 887cd89990..34c3d434b1 100644 --- a/common/src/terrain/map.rs +++ b/common/src/terrain/map.rs @@ -299,6 +299,11 @@ pub struct MapConfig<'a> { /// /// Defaults to true. pub is_water: bool, + /// When `is_water` is true, controls whether an ice layer should appear on + /// that water. + /// + /// Defaults to true. + pub is_ice: bool, /// If true, 3D lighting and shading are turned on. Otherwise, a plain /// altitude map is used. /// @@ -403,6 +408,7 @@ impl<'a> MapConfig<'a> { is_basement: false, is_water: true, + is_ice: true, is_shaded: true, is_temperature: false, is_humidity: false, @@ -581,13 +587,16 @@ impl<'a> MapConfig<'a> { let g_water = 32.0 * water_color_factor; let b_water = 64.0 * water_color_factor; 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; - k_s = Rgb::new(1.0, 1.0, 1.0); - k_d = water_rgb; - k_a = water_rgb; - alpha = 0.255; + // Rudimentary ice check + if !rgb.map(|e| e > 0.6).reduce_and() { + 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; + k_s = Rgb::new(1.0, 1.0, 1.0); + k_d = water_rgb; + k_a = water_rgb; + alpha = 0.255; + } } let downhill_alt = sample_wpos(downhill_wpos); diff --git a/world/examples/water.rs b/world/examples/water.rs index c25451bf90..b66ea79cc2 100644 --- a/world/examples/water.rs +++ b/world/examples/water.rs @@ -177,6 +177,7 @@ fn main() { is_basement, is_water, + is_ice: true, is_shaded, is_temperature, is_humidity, diff --git a/world/src/block/mod.rs b/world/src/block/mod.rs index 0e2aff6eba..e9e40d8ab8 100644 --- a/world/src/block/mod.rs +++ b/world/src/block/mod.rs @@ -54,7 +54,7 @@ impl<'a> BlockGen<'a> { // Main sample let sample = column_gen.get((wpos, index, calendar))?; - Some(ZCache { sample }) + Some(ZCache { sample, calendar }) } pub fn get_with_z_cache(&mut self, wpos: Vec3, z_cache: Option<&ZCache>) -> Option { @@ -74,8 +74,9 @@ impl<'a> BlockGen<'a> { //tree_density, //forest_kind, //close_structures, - // marble, - // marble_small, + marble, + marble_mid, + marble_small, rock, // temp, // humidity, @@ -83,6 +84,8 @@ impl<'a> BlockGen<'a> { snow_cover, cliff_offset, cliff_height, + // water_vel, + ice_depth, .. } = sample; @@ -204,8 +207,12 @@ impl<'a> BlockGen<'a> { } }) .or_else(|| { + let over_water = height < water_height; // Water - if (wposf.z as f32) < water_height { + if over_water && (wposf.z as f32 - water_height).abs() < ice_depth { + // TODO: Ice block + Some(Block::new(BlockKind::WeakRock, Rgb::new(200, 225, 255))) + } else if (wposf.z as f32) < water_height { // Ocean Some(water) } else { @@ -217,6 +224,7 @@ impl<'a> BlockGen<'a> { pub struct ZCache<'a> { pub sample: ColumnSample<'a>, + pub calendar: Option<&'a Calendar>, } impl<'a> ZCache<'a> { @@ -232,7 +240,7 @@ impl<'a> ZCache<'a> { let ground_max = self.sample.alt + warp + rocks + 2.0; - let max = ground_max.max(self.sample.water_level + 2.0); + let max = ground_max.max(self.sample.water_level + 2.0 + self.sample.ice_depth); (min, max) } diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index 357a373a74..5a3a4fd6bb 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -91,6 +91,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { wpos, |chunk| if chunk.river.near_water() { 1.0 } else { 0.0 }, )?; + let water_vel = sim.get_interpolated(wpos, |chunk| chunk.river.velocity)?; let alt = sim.get_interpolated_monotone(wpos, |chunk| chunk.alt)?; let surface_veg = sim.get_interpolated_monotone(wpos, |chunk| chunk.surface_veg)?; let sim_chunk = sim.get(chunk_pos)?; @@ -1081,7 +1082,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { // Snow covering let thematic_snow = calendar.map_or(false, |c| c.is_event(CalendarEvent::Christmas)); - let snow_cover = temp + let snow_factor = temp .sub(if thematic_snow { CONFIG.tropical_temp } else { @@ -1092,17 +1093,17 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { .add(((marble - 0.5) / 0.5) * 0.5) .add(((marble_mid - 0.5) / 0.5) * 0.25) .add(((marble_small - 0.5) / 0.5) * 0.175); - let (alt, ground, sub_surface_color) = if snow_cover <= 0.0 && alt > water_level { + let (alt, ground, sub_surface_color) = if snow_factor <= 0.0 && alt > water_level { // Allow snow cover. ( - alt + 1.0 - snow_cover.max(0.0), - Rgb::lerp(snow, ground, snow_cover), + alt + 1.0 - snow_factor.max(0.0), + Rgb::lerp(snow, ground, snow_factor), Lerp::lerp(sub_surface_color, ground, alt.sub(basement).mul(0.15)), ) } else { (alt, ground, sub_surface_color) }; - let snow_cover = snow_cover <= 0.0; + let snow_cover = snow_factor <= 0.0; // Make river banks not have grass let ground = water_dist @@ -1116,6 +1117,16 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { let path = sim.get_nearest_path(wpos); let cave = sim.get_nearest_cave(wpos); + let ice_depth = if snow_factor < -0.25 + && water_vel.magnitude_squared() < (0.1f32 + marble_mid * 0.2).powi(2) + { + ((1.0 - Lerp::lerp(marble, Lerp::lerp(marble_mid, marble_small, 0.25), 0.5)) * 5.0 + - 1.5) + .max(0.0) + } else { + 0.0 + }; + Some(ColumnSample { alt, riverless_alt, @@ -1149,6 +1160,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { }, forest_kind: sim_chunk.forest_kind, marble, + marble_mid, marble_small, rock, temp, @@ -1162,6 +1174,8 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { snow_cover, cliff_offset, cliff_height, + water_vel, + ice_depth, chunk: sim_chunk, }) @@ -1181,6 +1195,7 @@ pub struct ColumnSample<'a> { pub tree_density: f32, pub forest_kind: ForestKind, pub marble: f32, + pub marble_mid: f32, pub marble_small: f32, pub rock: f32, pub temp: f32, @@ -1194,6 +1209,8 @@ pub struct ColumnSample<'a> { pub snow_cover: bool, pub cliff_offset: f32, pub cliff_height: f32, + pub water_vel: Vec3, + pub ice_depth: f32, pub chunk: &'a SimChunk, } diff --git a/world/src/sim/map.rs b/world/src/sim/map.rs index f2c3b8d3a4..c4599f12bd 100644 --- a/world/src/sim/map.rs +++ b/world/src/sim/map.rs @@ -81,6 +81,7 @@ pub fn sample_pos( is_basement, is_water, + is_ice, is_shaded, is_temperature, is_humidity, @@ -133,7 +134,7 @@ pub fn sample_pos( let humidity = humidity.min(1.0).max(0.0); let temperature = temperature.min(1.0).max(-1.0) * 0.5 + 0.5; let wpos = pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32); - let column_rgb_alt = samples + let column_data = samples .and_then(|samples| { chunk_idx .and_then(|chunk_idx| samples.get(chunk_idx)) @@ -162,14 +163,14 @@ pub fn sample_pos( .map(|e| e as f64) }; - (rgb, alt) + (rgb, alt, sample.ice_depth) }); let downhill_wpos = downhill.unwrap_or(wpos + TerrainChunkSize::RECT_SIZE.map(|e| e as i32)); let alt = if is_basement { basement } else { - column_rgb_alt.map_or(alt, |(_, alt)| alt) + column_data.map_or(alt, |(_, alt, _)| alt) }; let true_water_alt = (alt.max(water_alt) as f64 - focus.z) / gain as f64; @@ -189,7 +190,7 @@ pub fn sample_pos( if is_shaded { 1.0 } else { alt }, if is_shaded || is_humidity { 1.0 } else { 0.0 }, ); - let column_rgb = column_rgb_alt.map(|(rgb, _)| rgb).unwrap_or(default_rgb); + let column_rgb = column_data.map(|(rgb, _, _)| rgb).unwrap_or(default_rgb); let mut connections = [None; 8]; let mut has_connections = false; // TODO: Support non-river connections. @@ -213,33 +214,38 @@ pub fn sample_pos( }); }); }; - let rgb = match (river_kind, (is_water, true_alt >= true_sea_level)) { - (_, (false, _)) | (None, (_, true)) | (Some(RiverKind::River { .. }), _) => { - let (r, g, b) = ( - (column_rgb.r - * if is_temperature { - temperature as f64 - } else { - column_rgb.r - }) - .sqrt(), - column_rgb.g, - (column_rgb.b - * if is_humidity { - humidity as f64 - } else { - column_rgb.b - }) - .sqrt(), - ); - Rgb::new((r * 255.0) as u8, (g * 255.0) as u8, (b * 255.0) as u8) - }, - (None | Some(RiverKind::Lake { .. } | RiverKind::Ocean), _) => Rgb::new( - 0, - ((g_water - water_depth * g_water) * 1.0) as u8, - ((b_water - water_depth * b_water) * 1.0) as u8, - ), - }; + let rgb = + if is_water && is_ice && column_data.map_or(false, |(_, _, ice_depth)| ice_depth > 0.0) { + Rgb::new(200, 225, 255) + } else { + match (river_kind, (is_water, true_alt >= true_sea_level)) { + (_, (false, _)) | (None, (_, true)) | (Some(RiverKind::River { .. }), _) => { + let (r, g, b) = ( + (column_rgb.r + * if is_temperature { + temperature as f64 + } else { + column_rgb.r + }) + .sqrt(), + column_rgb.g, + (column_rgb.b + * if is_humidity { + humidity as f64 + } else { + column_rgb.b + }) + .sqrt(), + ); + Rgb::new((r * 255.0) as u8, (g * 255.0) as u8, (b * 255.0) as u8) + }, + (None | Some(RiverKind::Lake { .. } | RiverKind::Ocean), _) => Rgb::new( + 0, + ((g_water - water_depth * g_water) * 1.0) as u8, + ((b_water - water_depth * b_water) * 1.0) as u8, + ), + } + }; // TODO: Make principled. let rgb = if is_path { Rgb::new(0x37, 0x29, 0x23)