diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index d95acc9db6..195663991f 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -199,7 +199,8 @@ impl Block { pub fn get_max_sunlight(&self) -> (u8, u8) { match self.kind() { BlockKind::Water => (1, 1), - BlockKind::Leaves => (10, 255), + BlockKind::Leaves => (9, 255), + BlockKind::Wood => (6, 2), _ if self.is_opaque() => (0, 255), _ => (0, 0), } diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index 2b9ca633e6..1966e1df0c 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -188,7 +188,6 @@ impl Civs { chunk.alt += diff; chunk.basement += diff; chunk.rockiness = 0.0; - chunk.warp_factor = 0.0; chunk.surface_veg *= 1.0 - factor * rng.gen_range(0.25..0.9); }); } diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index 9a5061f2db..0437477640 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -69,7 +69,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { let sim = &self.sim; - let _turb = Vec2::new( + let turb = Vec2::new( sim.gen_ctx.turb_x_nz.get((wposf.div(48.0)).into_array()) as f32, sim.gen_ctx.turb_y_nz.get((wposf.div(48.0)).into_array()) as f32, ) * 12.0; @@ -83,7 +83,6 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { let spawn_rate = sim.get_interpolated(wpos, |chunk| chunk.spawn_rate)?; let alt = sim.get_interpolated_monotone(wpos, |chunk| chunk.alt)?; let surface_veg = sim.get_interpolated_monotone(wpos, |chunk| chunk.surface_veg)?; - let chunk_warp_factor = sim.get_interpolated_monotone(wpos, |chunk| chunk.warp_factor)?; let sim_chunk = sim.get(chunk_pos)?; let neighbor_coef = TerrainChunkSize::RECT_SIZE.map(|e| e as f64); let my_chunk_idx = vec2_as_uniform_idx(self.sim.map_size_lg(), chunk_pos); @@ -93,6 +92,12 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { let neighbor_chunk = sim.get(neighbor_pos)?; Some((neighbor_pos, neighbor_chunk, &neighbor_chunk.river)) }); + let cliff_height = sim.get_interpolated(wpos, |chunk| chunk.cliff_height)?; + let cliff_scale = 1.0; + let cliff_factor = (alt + self.sim.gen_ctx.hill_nz.get(wposf.div(48.0).into_array()) as f32 * 16.0).rem_euclid(128.0) / 64.0 - 1.0; + let cliff_offset = cliff_factor.abs().powf(if cliff_factor < 0.0 { 1.0 } else { 64.0 }) * cliff_height; + let cliff_scale = (self.sim.gen_ctx.hill_nz.get(wposf.div(128.0).into_array()) + self.sim.gen_ctx.hill_nz.get(wposf.div(48.0).into_array()) * 0.125 + 0.75).max(0.0) as f32; + let alt = alt + cliff_offset * cliff_scale; let lake_width = (TerrainChunkSize::RECT_SIZE.x as f64 * (2.0f64.sqrt())) + 12.0; let neighbor_river_data = neighbor_river_data.map(|(posj, chunkj, river)| { @@ -701,7 +706,6 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { 1.0, ) }; - let warp_factor = warp_factor * chunk_warp_factor; // NOTE: To disable warp, uncomment this line. // let warp_factor = 0.0; @@ -1054,6 +1058,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { path, cave, snow_cover, + cliff_offset, chunk: sim_chunk, }) @@ -1084,6 +1089,7 @@ pub struct ColumnSample<'a> { pub path: Option<(f32, Vec2, Path, Vec2)>, pub cave: Option<(f32, Vec2, Cave, Vec2)>, pub snow_cover: bool, + pub cliff_offset: f32, pub chunk: &'a SimChunk, } diff --git a/world/src/sim/map.rs b/world/src/sim/map.rs index 59b9a0f947..7541757361 100644 --- a/world/src/sim/map.rs +++ b/world/src/sim/map.rs @@ -133,7 +133,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 = samples + let column_rgb_alt = samples .and_then(|samples| { chunk_idx .and_then(|chunk_idx| samples.get(chunk_idx)) @@ -146,7 +146,7 @@ pub fn sample_pos( let basement = sample.basement; let grass_depth = (1.5 + 2.0 * sample.chaos).min(alt - basement); let wposz = if is_basement { basement } else { alt }; - if is_basement && wposz < alt - grass_depth { + let rgb = if is_basement && wposz < alt - grass_depth { Lerp::lerp( sample.sub_surface_color, sample.stone_col.map(|e| e as f32 / 255.0), @@ -160,11 +160,13 @@ pub fn sample_pos( ((wposz as f32 - (alt - grass_depth)) / grass_depth).sqrt(), ) .map(|e| e as f64) - } + }; + + (rgb, alt) }); let downhill_wpos = downhill.unwrap_or(wpos + TerrainChunkSize::RECT_SIZE.map(|e| e as i32)); - let alt = if is_basement { basement } else { alt }; + let alt = if is_basement { basement } else { column_rgb_alt.map_or(alt, |(_, alt)| alt) }; let true_water_alt = (alt.max(water_alt) as f64 - focus.z) / gain as f64; let true_alt = (alt as f64 - focus.z) / gain as f64; @@ -183,7 +185,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.unwrap_or(default_rgb); + let column_rgb = column_rgb_alt.map(|(rgb, _)| rgb).unwrap_or(default_rgb); let mut connections = [None; 8]; let mut has_connections = false; // TODO: Support non-river connections. diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index 2c210ba6df..78d8bc1145 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -30,7 +30,7 @@ use crate::{ site::Site, util::{ seed_expan, FastNoise, FastNoise2d, RandomField, RandomPerm, Sampler, StructureGen2d, LOCALITY, - NEIGHBORS, + NEIGHBORS, CARDINALS, DHashSet, }, IndexRef, CONFIG, }; @@ -44,6 +44,7 @@ use common::{ }, vol::RectVolSize, lottery::Lottery, + spiral::Spiral2d, }; use common_net::msg::WorldMapMsg; use enum_iterator::IntoEnumIterator; @@ -1401,6 +1402,8 @@ impl WorldSim { rng, }; + this.generate_cliffs(); + if opts.seed_elements { this.seed_elements(); } @@ -1513,6 +1516,72 @@ impl WorldSim { } } + pub fn generate_cliffs(&mut self) { + let mut rng = self.rng.clone(); + + for _ in 0..self.get_size().product() / 10 { + let mut pos = self.get_size().map(|e| rng.gen_range(0..e) as i32); + + let mut cliffs = DHashSet::default(); + let mut cliff_path = Vec::new(); + + for _ in 0..64 { + if self.get_gradient_approx(pos).map_or(false, |g| g > 1.5) { + if !cliffs.insert(pos) { + break; + } + cliff_path.push((pos, 0.0)); + + pos += CARDINALS + .iter() + .copied() + .max_by_key(|rpos| self.get_gradient_approx(pos + rpos).map_or(0, |g| (g * 1000.0) as i32)) + .unwrap(); // Can't fail + } else { + break; + } + } + + // for locs in cliff_path.windows(3) { + // let to_prev_idx = NEIGHBORS + // .iter() + // .enumerate() + // .find(|(_, dir)| **dir == locs[0].0 - locs[1].0) + // .expect("Track locations must be neighbors") + // .0; + // let to_next_idx = NEIGHBORS + // .iter() + // .enumerate() + // .find(|(_, dir)| **dir == locs[2].0 - locs[1].0) + // .expect("Track locations must be neighbors") + // .0; + + // self.get_mut(locs[0].0).unwrap().cliff.0.neighbors |= + // 1 << ((to_prev_idx as u8 + 4) % 8); + // self.get_mut(locs[1].0).unwrap().cliff.0.neighbors |= + // (1 << (to_prev_idx as u8)) | (1 << (to_next_idx as u8)); + // self.get_mut(locs[2].0).unwrap().cliff.0.neighbors |= + // 1 << ((to_next_idx as u8 + 4) % 8); + + // self.get_mut(locs[1].0).unwrap().cliff.0.offset = Vec2::new(rng.gen_range(-16..17), rng.gen_range(-16..17)); + // } + + for cliff in cliffs { + let alt = self.get(cliff).map_or(0.0, |c| c.alt); + Spiral2d::new() + .take((4usize * 2 + 1).pow(2)) + .for_each(|rpos| { + let dist = rpos.map(|e| e as f32).magnitude(); + if let Some(c) = self.get_mut(cliff + rpos) { + let warp = 1.0 / (1.0 + dist); + c.tree_density *= (1.0 - warp); + c.cliff_height = Lerp::lerp(44.0, 0.0, (-1.0 + dist / 3.5)); + } + }); + } + } + } + /// Prepare the world for simulation pub fn seed_elements(&mut self) { let mut rng = self.rng.clone(); @@ -2056,7 +2125,6 @@ pub struct SimChunk { pub forest_kind: ForestKind, pub spawn_rate: f32, pub river: RiverData, - pub warp_factor: f32, pub surface_veg: f32, pub sites: Vec>, @@ -2064,6 +2132,7 @@ pub struct SimChunk { pub path: (Way, Path), pub cave: (Way, Cave), + pub cliff_height: f32, pub contains_waypoint: bool, } @@ -2284,13 +2353,14 @@ impl SimChunk { }, spawn_rate: 1.0, river, - warp_factor: 1.0, surface_veg: 1.0, sites: Vec::new(), place: None, path: Default::default(), cave: Default::default(), + cliff_height: 0.0, + contains_waypoint: false, } }