From 631d8c6603e08226a74b0b0f585497be64e5a9dd Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 17 Sep 2021 13:36:05 +0100 Subject: [PATCH] Cleaned up shrub implementation --- .../{shrubs.ron => shrubs/jungle.ron} | 12 +- .../{jungle-bush-0.vox => jungle/bush-0.vox} | Bin .../{jungle-bush-1.vox => jungle/bush-1.vox} | Bin .../{jungle-bush-2.vox => jungle/bush-2.vox} | Bin .../{jungle-bush-3.vox => jungle/bush-3.vox} | Bin .../{jungle-bush-4.vox => jungle/bush-4.vox} | Bin .../{jungle-bush-5.vox => jungle/bush-5.vox} | Bin common/src/terrain/map.rs | 7 +- common/src/terrain/mod.rs | 16 +- world/src/column/mod.rs | 595 ++++++++++-------- world/src/layer/shrub.rs | 21 +- world/src/sim/mod.rs | 81 ++- 12 files changed, 406 insertions(+), 326 deletions(-) rename assets/world/manifests/{shrubs.ron => shrubs/jungle.ron} (54%) rename assets/world/shrub/{jungle-bush-0.vox => jungle/bush-0.vox} (100%) rename assets/world/shrub/{jungle-bush-1.vox => jungle/bush-1.vox} (100%) rename assets/world/shrub/{jungle-bush-2.vox => jungle/bush-2.vox} (100%) rename assets/world/shrub/{jungle-bush-3.vox => jungle/bush-3.vox} (100%) rename assets/world/shrub/{jungle-bush-4.vox => jungle/bush-4.vox} (100%) rename assets/world/shrub/{jungle-bush-5.vox => jungle/bush-5.vox} (100%) diff --git a/assets/world/manifests/shrubs.ron b/assets/world/manifests/shrubs/jungle.ron similarity index 54% rename from assets/world/manifests/shrubs.ron rename to assets/world/manifests/shrubs/jungle.ron index b8cf355e61..594c8e4d22 100644 --- a/assets/world/manifests/shrubs.ron +++ b/assets/world/manifests/shrubs/jungle.ron @@ -6,27 +6,27 @@ center: (6, 6, 2), ), ( - specifier: "world.shrub.jungle-bush-0", + specifier: "world.shrub.jungle.bush-0", center: (5, 5, 3), ), ( - specifier: "world.shrub.jungle-bush-1", + specifier: "world.shrub.jungle.bush-1", center: (5, 5, 2), ), ( - specifier: "world.shrub.jungle-bush-2", + specifier: "world.shrub.jungle.bush-2", center: (5, 5, 3), ), ( - specifier: "world.shrub.jungle-bush-3", + specifier: "world.shrub.jungle.bush-3", center: (5, 5, 3), ), ( - specifier: "world.shrub.jungle-bush-4", + specifier: "world.shrub.jungle.bush-4", center: (5, 5, 4), ), ( - specifier: "world.shrub.jungle-bush-5", + specifier: "world.shrub.jungle.bush-5", center: (5, 5, 5), ), ] diff --git a/assets/world/shrub/jungle-bush-0.vox b/assets/world/shrub/jungle/bush-0.vox similarity index 100% rename from assets/world/shrub/jungle-bush-0.vox rename to assets/world/shrub/jungle/bush-0.vox diff --git a/assets/world/shrub/jungle-bush-1.vox b/assets/world/shrub/jungle/bush-1.vox similarity index 100% rename from assets/world/shrub/jungle-bush-1.vox rename to assets/world/shrub/jungle/bush-1.vox diff --git a/assets/world/shrub/jungle-bush-2.vox b/assets/world/shrub/jungle/bush-2.vox similarity index 100% rename from assets/world/shrub/jungle-bush-2.vox rename to assets/world/shrub/jungle/bush-2.vox diff --git a/assets/world/shrub/jungle-bush-3.vox b/assets/world/shrub/jungle/bush-3.vox similarity index 100% rename from assets/world/shrub/jungle-bush-3.vox rename to assets/world/shrub/jungle/bush-3.vox diff --git a/assets/world/shrub/jungle-bush-4.vox b/assets/world/shrub/jungle/bush-4.vox similarity index 100% rename from assets/world/shrub/jungle-bush-4.vox rename to assets/world/shrub/jungle/bush-4.vox diff --git a/assets/world/shrub/jungle-bush-5.vox b/assets/world/shrub/jungle/bush-5.vox similarity index 100% rename from assets/world/shrub/jungle-bush-5.vox rename to assets/world/shrub/jungle/bush-5.vox diff --git a/common/src/terrain/map.rs b/common/src/terrain/map.rs index b134e40e83..3f23b0fd53 100644 --- a/common/src/terrain/map.rs +++ b/common/src/terrain/map.rs @@ -551,8 +551,11 @@ impl<'a> MapConfig<'a> { downhill_wpos, ); let (_t, _pt, dist) = if let Some((t, pt, dist)) = - quadratic_nearest_point(&coeffs, wposf, Vec2::new(neighbor_wpos, downhill_wpos)) - { + quadratic_nearest_point( + &coeffs, + wposf, + Vec2::new(neighbor_wpos, downhill_wpos), + ) { (t, pt, dist) } else { let ndist = wposf.distance_squared(neighbor_wpos); diff --git a/common/src/terrain/mod.rs b/common/src/terrain/mod.rs index a56aa38e8d..719df4d3a3 100644 --- a/common/src/terrain/mod.rs +++ b/common/src/terrain/mod.rs @@ -304,9 +304,9 @@ pub fn quadratic_nearest_point( // end: line.y, // }; // let len_sq = line.start.distance_squared(line.end); - // let t = ((point - line.start).dot(line.end - line.start) / len_sq).clamped(0.0, 1.0); - // let pos = line.start + (line.end - line.start) * t; - // return Some((t, pos, pos.distance_squared(point))); + // let t = ((point - line.start).dot(line.end - line.start) / + // len_sq).clamped(0.0, 1.0); let pos = line.start + (line.end - line.start) + // * t; return Some((t, pos, pos.distance_squared(point))); // Quadratic @@ -406,9 +406,9 @@ pub fn quadratic_nearest_point( .unwrap() }); min_root - // .map(|(t, pt, dist)| { - // let t = t.clamped(0.0, 1.0); - // let pos = spline.x * t * t + spline.y * t + spline.z; - // (t, pos, pos.distance_squared(point)) - // }) + // .map(|(t, pt, dist)| { + // let t = t.clamped(0.0, 1.0); + // let pos = spline.x * t * t + spline.y * t + spline.z; + // (t, pos, pos.distance_squared(point)) + // }) } diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index 52761482dc..4cc7171c88 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -309,7 +309,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { // }, ); - let river_width = river_width.max(2.0f64.sqrt() + 0.1) * (1.0 + river_width_noise * 0.3); + let river_width = river_width.max(2.0) * (1.0 + river_width_noise * 0.3); // To find the distance, we just evaluate the quadratic equation at river_t and // see if it's within width (but we should be able to use it for a // lot more, and this probably isn't the very best approach anyway @@ -344,7 +344,12 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { .unwrap_or(CONFIG.sea_level); #[derive(Default)] - struct WeightedSum { sum: T, weight: T, min: Option, max: Option } + struct WeightedSum { + sum: T, + weight: T, + min: Option, + max: Option, + } impl WeightedSum { fn with(self, value: f32, weight: f32) -> Self { Self { @@ -353,14 +358,23 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { ..self } } + /// With an upper bound fn with_min(self, min: f32) -> Self { - Self { min: Some(self.min.unwrap_or(min).min(min)), ..self } + Self { + min: Some(self.min.unwrap_or(min).min(min)), + ..self + } } + /// With a lower bound fn with_max(self, max: f32) -> Self { - Self { max: Some(self.max.unwrap_or(max).max(max)), ..self } + Self { + max: Some(self.max.unwrap_or(max).max(max)), + ..self + } } + fn eval(&self) -> Option { if self.weight > 0.0 { let res = self.sum / self.weight; @@ -370,8 +384,8 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { } else { None } - } + fn eval_or(&self, default: f32) -> f32 { let res = if self.weight > 0.0 { self.sum / self.weight @@ -384,8 +398,17 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { } } - fn is_waterfall(chunk_pos: Vec2, river_chunk: &SimChunk, downhill_chunk: &SimChunk) -> bool { - (chunk_pos.sum() as u32 % 19 < 2 || matches!(downhill_chunk.river.river_kind, Some(RiverKind::Lake { .. }))) && (river_chunk.water_alt > downhill_chunk.water_alt + 8.0) + fn is_waterfall( + chunk_pos: Vec2, + river_chunk: &SimChunk, + downhill_chunk: &SimChunk, + ) -> bool { + (chunk_pos.sum() as u32 % 19 < 2 + || matches!( + downhill_chunk.river.river_kind, + Some(RiverKind::Lake { .. }) + )) + && (river_chunk.water_alt > downhill_chunk.water_alt + 8.0) } fn river_water_alt(a: f32, b: f32, t: f32, is_waterfall: bool) -> f32 { @@ -501,17 +524,28 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { }, ); let unbounded_water_level = unbounded_water_level.eval_or(actual_sea_level); - let water_level = match (river_water_level.eval(), lake_water_level.eval().filter(|_| lake_dist <= 0.0 || in_river)) { + let water_level = match ( + river_water_level.eval(), + lake_water_level + .eval() + .filter(|_| lake_dist <= 0.0 || in_river), + ) { (Some(r), Some(l)) => r.max(l), - (r, l) => r.or(l).unwrap_or(actual_sea_level).max(unbounded_water_level), + (r, l) => r + .or(l) + .unwrap_or(actual_sea_level) + .max(unbounded_water_level), }; let riverless_alt = alt; let alt = neighbor_river_data.clone().fold( WeightedSum::default().with(riverless_alt, 1.0), - |alt, (river_chunk_idx, river_chunk, river, dist_info)| match (river.river_kind, dist_info) { + |alt, (river_chunk_idx, river_chunk, river, dist_info)| match ( + river.river_kind, + dist_info, + ) { ( - Some(kind/*RiverKind::River { cross_section }*/), + Some(kind /* RiverKind::River { cross_section } */), Some((_, dist, river_width, (river_t, (river_pos, _), downhill_chunk))), ) => { // Distance from river center @@ -519,7 +553,12 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { // Distance from edge of river let river_edge_dist = (river_dist - river_width * 0.5).max(0.0) as f32; // 0.0 = not near river, 1.0 = in middle of river - let near_river = ((river_dist / river_width) as f32).min(1.0).mul(f32::consts::PI).cos().add(1.0).mul(0.5); + let near_river = ((river_dist / river_width) as f32) + .min(1.0) + .mul(f32::consts::PI) + .cos() + .add(1.0) + .mul(0.5); let water_alt = match kind { RiverKind::River { cross_section } => { @@ -540,11 +579,25 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { is_waterfall(river_chunk_idx, river_chunk, downhill_chunk), ); - let depth = water_level - Lerp::lerp(riverless_alt.min(water_level), water_level - 4.0, 0.5); + let depth = water_level + - Lerp::lerp( + riverless_alt.min(water_level), + water_level - 4.0, + 0.5, + ); - let min_alt = Lerp::lerp(riverless_alt, lake_water_alt, ((river_dist - 8.5) / (river_width * 0.5 - 8.5).max(0.01)).clamped(0.0, 1.0) as f32); + let min_alt = Lerp::lerp( + riverless_alt, + lake_water_alt, + ((river_dist - 8.5) / (river_width * 0.5 - 8.5).max(0.01)) + .clamped(0.0, 1.0) as f32, + ); - Some((lake_water_alt, /*river_width as f32 * 0.15*/ depth, Some(min_alt))) + Some(( + lake_water_alt, + /* river_width as f32 * 0.15 */ depth, + Some(min_alt), + )) }, RiverKind::Ocean => None, }; @@ -553,29 +606,48 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { if let Some((water_alt, water_depth, min_alt)) = water_alt { if river_edge_dist <= 0.0 { const MIN_DEPTH: f32 = 1.0; - let near_centre = ((river_dist / (river_width * 0.5)) as f32).min(1.0).mul(f32::consts::PI).cos().add(1.0).mul(0.5); - let waterfall_boost = if is_waterfall(river_chunk_idx, river_chunk, downhill_chunk) { - (river_chunk.alt - downhill_chunk.alt).max(0.0).powf(2.0) * (1.0 - (river_t as f32 - 0.5).abs() * 2.0).powf(3.5) / 20.0 - } else { - 0.0 - }; - // let river_depth = cross_section.y as f32 /*river_width as f32 * 0.15*/; - let riverbed_depth = near_centre * water_depth + MIN_DEPTH + waterfall_boost; - // Handle rivers debouching into the ocean nicely by 'flattening' their bottom - let riverbed_alt = (water_alt - riverbed_depth).max(riverless_alt.min(CONFIG.sea_level - MIN_DEPTH)); - alt - .with(min_alt.unwrap_or(riverbed_alt).min(riverbed_alt), near_centre * BANK_STRENGTH) - .with_min(min_alt.unwrap_or(riverbed_alt).min(riverbed_alt)) + let near_centre = ((river_dist / (river_width * 0.5)) as f32) + .min(1.0) + .mul(f32::consts::PI) + .cos() + .add(1.0) + .mul(0.5); + let waterfall_boost = + if is_waterfall(river_chunk_idx, river_chunk, downhill_chunk) { + (river_chunk.alt - downhill_chunk.alt).max(0.0).powf(2.0) + * (1.0 - (river_t as f32 - 0.5).abs() * 2.0).powf(3.5) + / 20.0 + } else { + 0.0 + }; + // let river_depth = cross_section.y as f32 /*river_width as f32 * + // 0.15*/; + let riverbed_depth = + near_centre * water_depth + MIN_DEPTH + waterfall_boost; + // Handle rivers debouching into the ocean nicely by 'flattening' their + // bottom + let riverbed_alt = (water_alt - riverbed_depth) + .max(riverless_alt.min(CONFIG.sea_level - MIN_DEPTH)); + alt.with( + min_alt.unwrap_or(riverbed_alt).min(riverbed_alt), + near_centre * BANK_STRENGTH, + ) + .with_min(min_alt.unwrap_or(riverbed_alt).min(riverbed_alt)) } else { const GORGE: f32 = 0.5; const BANK_SCALE: f32 = 24.0; - // Weighting of this riverbank on nearby terrain (higher when closer to the river). This - // 'pulls' the riverbank toward the river's altitude to make sure that we get a smooth + // Weighting of this riverbank on nearby terrain (higher when closer to + // the river). This 'pulls' the riverbank + // toward the river's altitude to make sure that we get a smooth // transition from normal terrain to the water. let weight = Lerp::lerp( - BANK_STRENGTH / (1.0 + (river_edge_dist as f32 - 3.0).max(0.0) * BANK_STRENGTH / BANK_SCALE), + BANK_STRENGTH + / (1.0 + + (river_edge_dist as f32 - 3.0).max(0.0) * BANK_STRENGTH + / BANK_SCALE), 0.0, - // cubic((river_edge_dist / BANK_SCALE).clamped(0.0, 1.0) as f64) as f32, + // cubic((river_edge_dist / BANK_SCALE).clamped(0.0, 1.0) as f64) + // as f32, (river_edge_dist / BANK_SCALE).clamped(0.0, 1.0), ); let alt = alt.with(water_alt + GORGE, weight); @@ -586,8 +658,8 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { alt }; // let alt = if !in_river && lake_dist > 0.0 { - // alt.with_max(water_alt + GORGE - river_edge_dist.powf(2.0) / 10.0) - // } else { + // alt.with_max(water_alt + GORGE - river_edge_dist.powf(2.0) / + // 10.0) } else { // alt // }; alt @@ -758,11 +830,11 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { }; let alt_for_river = alt; - // + if overlap_count == 0.0 { - // 0.0 - // } else { - // river_overlap_distance_product / overlap_count - // } as f32; + // + if overlap_count == 0.0 { + // 0.0 + // } else { + // river_overlap_distance_product / overlap_count + // } as f32; let riverless_alt_delta = (sim.gen_ctx.small_nz.get( (wposf_turb.div(200.0 * (32.0 / TerrainChunkSize::RECT_SIZE.x as f64))).into_array(), @@ -802,261 +874,238 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { let riverless_alt_delta = riverless_alt_delta + (cliff - 0.5) * cliff_height; let river_gouge = 0.5; - let (_in_water, old_water_dist, alt_, old_water_level, _riverless_alt, warp_factor) = if let Some( - (max_border_river_pos, river_chunk, max_border_river, max_border_river_dist), - ) = - max_river - { - // This is flowing into a lake, or a lake, or is at least a non-ocean tile. - // - // If we are <= water_alt, we are in the lake; otherwise, we are flowing into - // it. - match max_border_river.river_kind { - Some(RiverKind::River { cross_section }) => 'block: { - if max_border_river_dist.map(|(_, dist, _, _)| dist) - != Some(Vec2::zero()) - { - let (_, _, river_width, (_, (river_pos, _), _)) = - max_border_river_dist.unwrap(); - let river_dist = wposf.distance(river_pos); + let (_in_water, old_water_dist, alt_, old_water_level, _riverless_alt, warp_factor) = + if let Some(( + max_border_river_pos, + river_chunk, + max_border_river, + max_border_river_dist, + )) = max_river + { + // This is flowing into a lake, or a lake, or is at least a non-ocean tile. + // + // If we are <= water_alt, we are in the lake; otherwise, we are flowing into + // it. + match max_border_river.river_kind { + Some(RiverKind::River { cross_section }) => 'block: { + if max_border_river_dist.map(|(_, dist, _, _)| dist) != Some(Vec2::zero()) { + let (_, _, river_width, (_, (river_pos, _), _)) = + max_border_river_dist.unwrap(); + let river_dist = wposf.distance(river_pos); - // FIXME: Make water altitude accurate. - break 'block ( - river_scale_factor <= 1.0, - Some((river_dist - river_width * 0.5) as f32), - alt_for_river - max_dip,//alt_for_river, - downhill_water_alt, - alt, //alt_for_river, - river_scale_factor as f32, - ); - } - let ( - _, - _, - river_width, - (river_t, (river_pos, _), downhill_river_chunk), - ) = max_border_river_dist.unwrap(); - let river_alt = Lerp::lerp( - river_chunk.alt.max(river_chunk.water_alt), - downhill_river_chunk.alt.max(downhill_river_chunk.water_alt), - river_t as f32, - ); - let new_alt = river_alt - river_gouge; - let river_dist = wposf.distance(river_pos); - let river_height_factor = river_dist / (river_width * 0.5); - - // This is not a good way to determine river depth, but we do it - // anyway. TODO: Make the erosion - // sim output better river depths through `cross_section`. - let river_depth = river_width as f32 * 0.5; - - let valley_alt = Lerp::lerp( - new_alt - (cross_section.y * river_depth).max(1.0), - new_alt - 1.0, - (river_height_factor * river_height_factor) as f32, - ); - - ( - true, - Some((river_dist - river_width * 0.5) as f32), - alt_for_river - max_dip,//valley_alt.min(alt), - new_alt.max(valley_alt.min(alt) + 1.0), - alt, //river_alt + cross_section.y.max(1.0), - river_scale_factor as f32,//0.0, - ) - }, - Some(RiverKind::Ocean) => 'block: { - let ( - _, - dist, - river_width, - (river_t, (river_pos, _), downhill_river_chunk), - ) = if let Some(dist) = max_border_river_dist { - dist - } else { - error!( - ?max_border_river, - ?chunk_pos, - ?max_border_river_pos, - "downhill error details" - ); - panic!( - "Oceans should definitely have a downhill! \ - ...Right?" - ); - }; - let lake_water_alt = Lerp::lerp( - river_chunk.alt.max(river_chunk.water_alt), - downhill_river_chunk - .alt - .max(downhill_river_chunk.water_alt), - river_t as f32, - ) + 1.0; - - if dist == Vec2::zero() { - let river_dist = wposf.distance(river_pos); - let _river_height_factor = - river_dist / (river_width * 0.5); - break 'block ( - true, - Some((river_dist - river_width * 0.5) as f32), - alt_for_river - max_dip,//alt_for_river, - // .min(lake_water_alt - 1.0 - river_gouge), - lake_water_alt, - alt, //alt_for_river.max(lake_water_alt), - 0.0, - ); - } - - ( - river_scale_factor <= 1.0, - Some( - (wposf.distance(river_pos) - river_width * 0.5) - as f32, - ), - alt_for_river - max_dip,//alt_for_river, - downhill_water_alt + 1.0, - alt, //alt_for_river, - river_scale_factor as f32, - ) - }, - Some(RiverKind::Lake { .. }) => 'block: { - let lake_chunk = max_border_river_pos.map(|e| e as f64); - let lake_aabr = Aabr { - min: lake_chunk * neighbor_coef, - max: (lake_chunk + 1.0) * neighbor_coef, - }; - let lake_dist = lake_aabr.distance_to_point(wposf); - let downhill_river_chunk = max_border_river_pos; - let lake_id_dist = downhill_river_chunk - chunk_pos; - let in_bounds = lake_id_dist.x >= -1 - && lake_id_dist.y >= -1 - && lake_id_dist.x <= 1 - && lake_id_dist.y <= 1; - let in_bounds = in_bounds - && (lake_id_dist.x >= 0 && lake_id_dist.y >= 0); - let (_, dist, _, (river_t, _, downhill_river_chunk)) = - if let Some(dist) = max_border_river_dist { - dist - } else if lake_dist - <= TerrainChunkSize::RECT_SIZE.x as f64 * 1.0 - || in_bounds - { - let gouge_factor = 0.0; + // FIXME: Make water altitude accurate. break 'block ( - in_bounds - || downhill_water_alt - .max(river_chunk.water_alt) - > alt_for_river, - Some(lake_dist as f32), - alt_for_river - max_dip,//alt_for_river, - (downhill_water_alt.max(river_chunk.water_alt) - - river_gouge), - alt_for_river, - river_scale_factor as f32 - * (1.0 - gouge_factor), - ); - } else { - break 'block ( - false, - Some(lake_dist as f32), - alt_for_river - max_dip,//alt_for_river, + river_scale_factor <= 1.0, + Some((river_dist - river_width * 0.5) as f32), + alt_for_river - max_dip, //alt_for_river, downhill_water_alt, - alt_for_river, + alt, //alt_for_river, river_scale_factor as f32, ); - }; + } + let (_, _, river_width, (river_t, (river_pos, _), downhill_river_chunk)) = + max_border_river_dist.unwrap(); + let river_alt = Lerp::lerp( + river_chunk.alt.max(river_chunk.water_alt), + downhill_river_chunk.alt.max(downhill_river_chunk.water_alt), + river_t as f32, + ); + let new_alt = river_alt - river_gouge; + let river_dist = wposf.distance(river_pos); + let river_height_factor = river_dist / (river_width * 0.5); - let lake_dist = dist.y; - let lake_water_alt = Lerp::lerp( - river_chunk.alt.max(river_chunk.water_alt), - downhill_river_chunk - .alt - .max(downhill_river_chunk.water_alt), - river_t as f32, - ); - // if dist == Vec2::zero() { - // return ( - // true, - // Some(lake_dist as f32), - // alt_for_river, - // // .min(lake_water_alt - 1.0 - river_gouge), - // lake_water_alt - river_gouge, - // alt_for_river.max(lake_water_alt), - // 0.0, - // ); - // } - if lake_dist <= TerrainChunkSize::RECT_SIZE.x as f64 * 1.0 - || in_bounds - { - let gouge_factor = if in_bounds && lake_dist <= 1.0 { - 1.0 - } else { - 0.0 - }; - let in_bounds_ = lake_dist - <= TerrainChunkSize::RECT_SIZE.x as f64 * 0.5; - if gouge_factor == 1.0 { + // This is not a good way to determine river depth, but we do it + // anyway. TODO: Make the erosion + // sim output better river depths through `cross_section`. + let river_depth = river_width as f32 * 0.5; + + let valley_alt = Lerp::lerp( + new_alt - (cross_section.y * river_depth).max(1.0), + new_alt - 1.0, + (river_height_factor * river_height_factor) as f32, + ); + + ( + true, + Some((river_dist - river_width * 0.5) as f32), + alt_for_river - max_dip, //valley_alt.min(alt), + new_alt.max(valley_alt.min(alt) + 1.0), + alt, //river_alt + cross_section.y.max(1.0), + river_scale_factor as f32, //0.0, + ) + }, + Some(RiverKind::Ocean) => 'block: { + let (_, dist, river_width, (river_t, (river_pos, _), downhill_river_chunk)) = + if let Some(dist) = max_border_river_dist { + dist + } else { + error!( + ?max_border_river, + ?chunk_pos, + ?max_border_river_pos, + "downhill error details" + ); + panic!("Oceans should definitely have a downhill! ...Right?"); + }; + let lake_water_alt = Lerp::lerp( + river_chunk.alt.max(river_chunk.water_alt), + downhill_river_chunk.alt.max(downhill_river_chunk.water_alt), + river_t as f32, + ) + 1.0; + + if dist == Vec2::zero() { + let river_dist = wposf.distance(river_pos); + let _river_height_factor = river_dist / (river_width * 0.5); break 'block ( true, - Some(lake_dist as f32), - alt_for_river - max_dip,//alt_for_river, - // .min(lake_water_alt - 1.0 - river_gouge), - downhill_water_alt.max(lake_water_alt) - - river_gouge, - alt.max(lake_water_alt), - 0.0,// river_scale_factor as f32* (1.0 - gouge_factor), - ); - } else { - break 'block ( - true, - Some(lake_dist as f32), - alt_for_river - max_dip,//alt_for_river, - if in_bounds_ { - downhill_water_alt.max(lake_water_alt) - } else { - downhill_water_alt//.max(lake_water_alt) - }.max(river_chunk.water_alt) - river_gouge, - alt_for_river, - river_scale_factor as f32 - * (1.0 - gouge_factor), + Some((river_dist - river_width * 0.5) as f32), + alt_for_river - max_dip, //alt_for_river, + // .min(lake_water_alt - 1.0 - river_gouge), + lake_water_alt, + alt, //alt_for_river.max(lake_water_alt), + 0.0, ); } - } - ( - river_scale_factor <= 1.0, - Some(lake_dist as f32), - alt_for_river - max_dip,//alt_for_river.max(lake_water_alt).max(river_chunk.water_alt), + + ( + river_scale_factor <= 1.0, + Some((wposf.distance(river_pos) - river_width * 0.5) as f32), + alt_for_river - max_dip, //alt_for_river, + downhill_water_alt + 1.0, + alt, //alt_for_river, + river_scale_factor as f32, + ) + }, + Some(RiverKind::Lake { .. }) => 'block: { + let lake_chunk = max_border_river_pos.map(|e| e as f64); + let lake_aabr = Aabr { + min: lake_chunk * neighbor_coef, + max: (lake_chunk + 1.0) * neighbor_coef, + }; + let lake_dist = lake_aabr.distance_to_point(wposf); + let downhill_river_chunk = max_border_river_pos; + let lake_id_dist = downhill_river_chunk - chunk_pos; + let in_bounds = lake_id_dist.x >= -1 + && lake_id_dist.y >= -1 + && lake_id_dist.x <= 1 + && lake_id_dist.y <= 1; + let in_bounds = in_bounds && (lake_id_dist.x >= 0 && lake_id_dist.y >= 0); + let (_, dist, _, (river_t, _, downhill_river_chunk)) = + if let Some(dist) = max_border_river_dist { + dist + } else if lake_dist <= TerrainChunkSize::RECT_SIZE.x as f64 * 1.0 + || in_bounds + { + let gouge_factor = 0.0; + break 'block ( + in_bounds + || downhill_water_alt.max(river_chunk.water_alt) + > alt_for_river, + Some(lake_dist as f32), + alt_for_river - max_dip, //alt_for_river, + (downhill_water_alt.max(river_chunk.water_alt) - river_gouge), + alt_for_river, + river_scale_factor as f32 * (1.0 - gouge_factor), + ); + } else { + break 'block ( + false, + Some(lake_dist as f32), + alt_for_river - max_dip, //alt_for_river, + downhill_water_alt, + alt_for_river, + river_scale_factor as f32, + ); + }; + + let lake_dist = dist.y; + let lake_water_alt = Lerp::lerp( + river_chunk.alt.max(river_chunk.water_alt), + downhill_river_chunk.alt.max(downhill_river_chunk.water_alt), + river_t as f32, + ); + // if dist == Vec2::zero() { + // return ( + // true, + // Some(lake_dist as f32), + // alt_for_river, + // // .min(lake_water_alt - 1.0 - river_gouge), + // lake_water_alt - river_gouge, + // alt_for_river.max(lake_water_alt), + // 0.0, + // ); + // } + if lake_dist <= TerrainChunkSize::RECT_SIZE.x as f64 * 1.0 || in_bounds { + let gouge_factor = if in_bounds && lake_dist <= 1.0 { + 1.0 + } else { + 0.0 + }; + let in_bounds_ = + lake_dist <= TerrainChunkSize::RECT_SIZE.x as f64 * 0.5; + if gouge_factor == 1.0 { + break 'block ( + true, + Some(lake_dist as f32), + alt_for_river - max_dip, //alt_for_river, + // .min(lake_water_alt - 1.0 - river_gouge), + downhill_water_alt.max(lake_water_alt) - river_gouge, + alt.max(lake_water_alt), + 0.0, // river_scale_factor as f32* (1.0 - gouge_factor), + ); + } else { + break 'block ( + true, + Some(lake_dist as f32), + alt_for_river - max_dip, //alt_for_river, + if in_bounds_ { + downhill_water_alt.max(lake_water_alt) + } else { + downhill_water_alt //.max(lake_water_alt) + } + .max(river_chunk.water_alt) + - river_gouge, + alt_for_river, + river_scale_factor as f32 * (1.0 - gouge_factor), + ); + } + } + ( + river_scale_factor <= 1.0, + Some(lake_dist as f32), + alt_for_river - max_dip, /* alt_for_river.max(lake_water_alt). + * max(river_chunk.water_alt), */ + downhill_water_alt, + alt_for_river, + river_scale_factor as f32, + ) + }, + None => ( + false, + None, + alt_for_river - max_dip, //alt_for_river, downhill_water_alt, - alt_for_river, - river_scale_factor as f32, - ) - }, - None => ( + alt, //alt_for_river, + 1.0, + ), + } + } else { + ( false, None, - alt_for_river - max_dip,//alt_for_river, + alt_for_river - max_dip, //alt_for_river, downhill_water_alt, alt, //alt_for_river, 1.0, - ), - } - } else { - ( - false, - None, - alt_for_river - max_dip,//alt_for_river, - downhill_water_alt, - alt, //alt_for_river, - 1.0, - ) - }; + ) + }; // NOTE: To disable warp, uncomment this line. // let warp_factor = 0.0; let warp_factor = warp_factor.min(water_dist.map_or(1.0, |d| d.max(0.0) / 64.0)); let riverless_alt_delta = Lerp::lerp(0.0, riverless_alt_delta, warp_factor); - // let riverless_alt = alt + riverless_alt_delta; //riverless_alt + riverless_alt_delta; + // let riverless_alt = alt + riverless_alt_delta; //riverless_alt + + // riverless_alt_delta; let alt = alt/*alt_*/ + riverless_alt_delta; let basement = alt + sim.get_interpolated_monotone(wpos, |chunk| chunk.basement.sub(chunk.alt))?; diff --git a/world/src/layer/shrub.rs b/world/src/layer/shrub.rs index f28a28071a..da51b5ade9 100644 --- a/world/src/layer/shrub.rs +++ b/world/src/layer/shrub.rs @@ -1,4 +1,5 @@ use crate::{ + all::ForestKind, util::{seed_expan, RandomPerm, Sampler, StructureGen2d, UnitChooser}, Canvas, }; @@ -14,12 +15,13 @@ use rand_chacha::ChaChaRng; use vek::*; lazy_static! { - static ref SHRUBS: AssetHandle = Structure::load_group("shrubs"); + static ref JUNGLE_SHRUBS: AssetHandle = Structure::load_group("shrubs.jungle"); } struct Shrub { wpos: Vec3, seed: u32, + kind: ForestKind, } pub fn apply_shrubs_to(canvas: &mut Canvas, rng: &mut impl Rng) { @@ -33,7 +35,10 @@ pub fn apply_shrubs_to(canvas: &mut Canvas, rng: &mut impl Rng) { shrub_cache.entry(wpos).or_insert_with(|| { let col = info.col_or_gen(wpos)?; - if RandomPerm::new(seed).chance(37, col.tree_density * 0.3) + let mut rng = ChaChaRng::from_seed(seed_expan::rng_state(seed)); + + const BASE_SHRUB_DENSITY: f64 = 0.15; + if rng.gen_bool((BASE_SHRUB_DENSITY * col.tree_density as f64).clamped(0.0, 1.0)) && col.water_dist.map_or(true, |d| d > 8.0) && col.alt > col.water_level && col.spawn_rate > 0.9 @@ -42,6 +47,11 @@ pub fn apply_shrubs_to(canvas: &mut Canvas, rng: &mut impl Rng) { Some(Shrub { wpos: wpos.with_z(col.alt as i32), seed, + kind: *info + .chunks() + .make_forest_lottery(wpos) + .choose_seeded(seed) + .as_ref()?, }) } else { None @@ -55,7 +65,12 @@ pub fn apply_shrubs_to(canvas: &mut Canvas, rng: &mut impl Rng) { let units = UnitChooser::new(shrub.seed).get(shrub.seed).into(); - let shrubs = SHRUBS.read(); + let shrubs = match shrub.kind { + ForestKind::Mangrove => &JUNGLE_SHRUBS, + _ => continue, // TODO: Add more shrub varieties + } + .read(); + let structure = shrubs.choose(&mut rng).unwrap(); canvas.blit_structure(shrub.wpos, structure, shrub.seed, units, true); } diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index 342cd053e2..0e430315b3 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -2056,47 +2056,45 @@ impl WorldSim { self.get_nearest_way(wpos, |chunk| Some(chunk.cave)) } + /// Create a [`Lottery>`] that generates [`ForestKind`]s + /// according to the conditions at the given position. If no or fewer + /// trees are appropriate for the conditions, `None` may be generated. + pub fn make_forest_lottery(&self, wpos: Vec2) -> Lottery> { + let chunk = if let Some(chunk) = self.get_wpos(wpos) { + chunk + } else { + return Lottery::from(vec![(1.0, None)]); + }; + let env = chunk.get_environment(); + Lottery::from( + ForestKind::into_enum_iter() + .enumerate() + .map(|(i, fk)| { + const CLUSTER_SIZE: f64 = 48.0; + let nz = (FastNoise2d::new(i as u32 * 37) + .get(wpos.map(|e| e as f64) / CLUSTER_SIZE) + + 1.0) + / 2.0; + (fk.proclivity(&env) * nz, Some(fk)) + }) + .chain(std::iter::once((0.001, None))) + .collect::>(), + ) + } + /// Return an iterator over candidate tree positions (note that only some of /// these will become trees since environmental parameters may forbid /// them spawning). pub fn get_near_trees(&self, wpos: Vec2) -> impl Iterator + '_ { // Deterministic based on wpos let normal_trees = std::array::IntoIter::new(self.gen_ctx.structure_gen.get(wpos)) - .filter_map(move |(pos, seed)| { - let chunk = self.get_wpos(pos)?; - let env = Environment { - humid: chunk.humidity, - temp: chunk.temp, - near_water: if chunk.river.is_lake() - || chunk.river.near_river() - || chunk.alt < CONFIG.sea_level + 6.0 - // Close to sea in altitude - { - 1.0 - } else { - 0.0 - }, - }; + .filter_map(move |(wpos, seed)| { + let lottery = self.make_forest_lottery(wpos); Some(TreeAttr { - pos, + pos: wpos, seed, scale: 1.0, - forest_kind: *Lottery::from( - ForestKind::into_enum_iter() - .enumerate() - .map(|(i, fk)| { - const CLUSTER_SIZE: f64 = 48.0; - let nz = (FastNoise2d::new(i as u32 * 37) - .get(pos.map(|e| e as f64) / CLUSTER_SIZE) - + 1.0) - / 2.0; - (fk.proclivity(&env) * nz, Some(fk)) - }) - .chain(std::iter::once((0.001, None))) - .collect::>(), - ) - .choose_seeded(seed) - .as_ref()?, + forest_kind: *lottery.choose_seeded(seed).as_ref()?, inhabited: false, }) }); @@ -2222,8 +2220,7 @@ impl SimChunk { Some( uniform_idx_as_vec2(map_size_lg, downhill_pre as usize) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32) - + TerrainChunkSize::RECT_SIZE.map(|e| e as i32 / 2) - , + + TerrainChunkSize::RECT_SIZE.map(|e| e as i32 / 2), ) }; @@ -2424,4 +2421,20 @@ impl SimChunk { } pub fn near_cliffs(&self) -> bool { self.cliff_height > 0.0 } + + pub fn get_environment(&self) -> Environment { + Environment { + humid: self.humidity, + temp: self.temp, + near_water: if self.river.is_lake() + || self.river.near_river() + || self.alt < CONFIG.sea_level + 6.0 + // Close to sea in altitude + { + 1.0 + } else { + 0.0 + }, + } + } }