From bdd04e221b48f0dd587aaf8a751964734e244e38 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 3 Sep 2021 12:32:30 +0100 Subject: [PATCH] Better lake entrances and distance --- common/src/terrain/mod.rs | 36 ++++++++- world/src/column/mod.rs | 152 +++++++++++++++++++++----------------- 2 files changed, 117 insertions(+), 71 deletions(-) diff --git a/common/src/terrain/mod.rs b/common/src/terrain/mod.rs index 553c688ce5..6463eef1c4 100644 --- a/common/src/terrain/mod.rs +++ b/common/src/terrain/mod.rs @@ -295,6 +295,10 @@ pub fn quadratic_nearest_point( point: Vec2, line: Vec2>, ) -> Option<(f64, Vec2, f64)> { + let eval_at = |t: f64| spline.x * t * t + spline.y * t + spline.z; + + // Linear + // let line = LineSegment2 { // start: line.x, // end: line.y, @@ -304,13 +308,37 @@ pub fn quadratic_nearest_point( // let pos = line.start + (line.end - line.start) * t; // return Some((t, pos, pos.distance_squared(point))); + // Quadratic + // let curve = QuadraticBezier2 { - // start: spline.x, - // ctrl: spline.y, - // end: spline.z, + // start: line.x, + // ctrl: eval_at(0.5), + // end: line.y, // }; // let (t, pos) = curve.binary_search_point_by_steps(point, 16, 0.001); - // return Some((t, pos, curve.evaluate(t).distance_squared(point))); + // let t = t.clamped(0.0, 1.0); + // let pos = curve.evaluate(t); + // return Some((t, pos, pos.distance_squared(point))); + + // Cubic + + let ctrl_at = |t: f64, end: f64| { + let a = eval_at(end); + let b = eval_at(Lerp::lerp(end, t, 0.1)); + let dir = (b - a).normalized(); + let exact = eval_at(t); + a + dir * exact.distance(a) + }; + let curve = CubicBezier2 { + start: line.x, + ctrl0: ctrl_at(0.33, 0.0), + ctrl1: ctrl_at(0.66, 1.0), + end: line.y, + }; + let (t, pos) = curve.binary_search_point_by_steps(point, 16, 0.001); + let t = t.clamped(0.0, 1.0); + let pos = curve.evaluate(t); + return Some((t, pos, pos.distance_squared(point))); let a = spline.z.x; let b = spline.y.x; diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index b75655b015..5f2e48ebf0 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -51,6 +51,22 @@ pub struct Colors { pub tropical_high: (f32, f32, f32), } +fn cubic(x: f64) -> f64 { + if x < 0.5 { + 4.0 * x.powf(3.0) + } else { + 1.0 - (-2.0 * x + 2.0).powf(3.0) / 2.0 + } +} + +fn revcubic(x: f64) -> f64 { + if x < 0.5 { + (x / 4.0).powf(1.0 / 3.0) + } else { + 1.0 - (2.0 - 2.0 * x).powf(1.0 / 3.0) / 2.0 + } +} + impl<'a> ColumnGen<'a> { pub fn new(sim: &'a WorldSim) -> Self { Self { sim } } } @@ -258,7 +274,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { let river_width = Lerp::lerp( river_width_min, river_width_max, - river_t.max(0.0).min(1.0).sqrt(), + cubic(river_t.clamped(0.0, 1.0)),//.sqrt(), ); let river_width = river_width;// * (1.0 + river_width_noise * 0.3); @@ -313,27 +329,36 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { fn with_max(self, max: f32) -> 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; + let res = self.min.map_or(res, |m| m.min(res)); + let res = self.max.map_or(res, |m| m.max(res)); + Some(res) + } else { + None + } + + } fn eval_or(&self, default: f32) -> f32 { let res = if self.weight > 0.0 { self.sum / self.weight } else { default }; - let res = match self.min { - Some(min) => res.min(min), - None => res, - }; - let res = match self.max { - Some(max) => res.max(max), - None => res, - }; + let res = self.min.map_or(res, |m| m.min(res)); + let res = self.max.map_or(res, |m| m.max(res)); res } } - let water_level = neighbor_river_data.clone().fold( - WeightedSum::default().with_max(CONFIG.sea_level + 2.0), // TODO: Don't add 2.0 - |water_level, (river_chunk_idx, river_chunk, river, dist_info)| match (river.river_kind, dist_info) { + let (river_water_level, lake_water_level, water_dist) = neighbor_river_data.clone().fold( + ( + WeightedSum::default().with_max(CONFIG.sea_level + 2.0), // TODO: Don't add 2.0 + WeightedSum::default().with_max(CONFIG.sea_level + 2.0), // TODO: Don't add 2.0 + None, + ), + |(mut river_water_level, mut lake_water_level, water_dist), (river_chunk_idx, river_chunk, river, dist_info)| match (river.river_kind, dist_info) { ( Some(kind/*RiverKind::River { cross_section }*/), Some((_, dist, river_width, (river_t, (river_pos, _), downhill_chunk))), @@ -344,29 +369,49 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { 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_center = ((river_dist / (river_width * 0.5)) as f32).min(1.0).mul(f32::consts::PI).cos().add(1.0).mul(0.5); match kind { - RiverKind::River { .. } if river_edge_dist <= 0.0 => { + RiverKind::River { .. } => { // Alt of river water *is* the alt of land let river_water_alt = Lerp::lerp(river_chunk.alt, downhill_chunk.alt, river_t as f32); - water_level.with(river_water_alt, near_river) + river_water_level = river_water_level + .with(river_water_alt, near_center * 10.0); + // .with_max(river_water_alt) }, // Slightly wider threshold is chosen in case the lake bounds are a bit wrong - RiverKind::Lake { .. } if river_edge_dist <= 8.0 => { + RiverKind::Lake { .. } => { let lake_water_alt = Lerp::lerp( river_chunk.alt.max(river_chunk.water_alt), downhill_chunk.alt.max(downhill_chunk.water_alt), river_t as f32, ); - water_level.with_max(lake_water_alt) + + river_water_level = river_water_level + .with(lake_water_alt, near_center * 10.0); + + if river_edge_dist <= 0.0 { + lake_water_level = lake_water_level + .with(lake_water_alt, near_center * 10.0); + // .with_max(lake_water_alt); + } else if river_edge_dist <= 12.0 { + // lake_water_level = lake_water_level.with(lake_water_alt, near_river * 10.0); + } }, - _ => water_level, - } + RiverKind::Ocean => {}, + }; + + let water_dist = Some(water_dist.unwrap_or(river_edge_dist).min(river_edge_dist)); + + (river_water_level, lake_water_level, water_dist) }, - (_, _) => water_level, + (_, _) => (river_water_level, lake_water_level, water_dist), }, ); - let water_level = water_level.eval_or(CONFIG.sea_level); + let water_level = match (river_water_level.eval(), lake_water_level.eval()) { + (Some(r), Some(l)) => r.max(l), + (r, l) => r.or(l).unwrap_or(CONFIG.sea_level), + }; let riverless_alt = alt; let alt = neighbor_river_data.clone().fold( @@ -383,32 +428,32 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { // 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); - // Maximum depth of the river basin (including banks) - let river_basin = (river_width as f32 + 1.0).ln() * 1.5; - // River basin depth at the current column - let basin_depth = near_river * river_basin; - let water_alt = match kind { RiverKind::River { .. } => { // Alt of river water *is* the alt of land - Some(Lerp::lerp(river_chunk.alt, downhill_chunk.alt, river_t as f32)) + let river_water_alt = Lerp::lerp(river_chunk.alt, downhill_chunk.alt, river_t as f32); + Some((river_water_alt, None)) }, - RiverKind::Lake { .. } => Some(Lerp::lerp( - river_chunk.alt.max(river_chunk.water_alt), - downhill_chunk.alt.max(downhill_chunk.water_alt), - river_t as f32, - )), - _ => None, + RiverKind::Lake { .. } => { + let lake_water_alt = Lerp::lerp( + river_chunk.alt.max(river_chunk.water_alt), + downhill_chunk.alt.max(downhill_chunk.water_alt), + river_t as f32, + ); + + Some((lake_water_alt, Some(riverless_alt))) + }, + RiverKind::Ocean => Some((riverless_alt, Some(riverless_alt))), }; - if let Some(water_alt) = water_alt { + if let Some((water_alt, min_alt)) = water_alt { if river_edge_dist <= 0.0 { - const MIN_DEPTH: f32 = 0.5; + 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 riverbed_depth = near_centre * river_width as f32 * 0.35 + MIN_DEPTH; + let riverbed_depth = near_centre * river_width as f32 * 0.15 + MIN_DEPTH; // 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)); - alt.with_min(riverbed_alt) + alt.with_min(min_alt.unwrap_or(riverbed_alt).min(riverbed_alt)) } else { const GORGE: f32 = 0.5; const BANK_SCALE: f32 = 24.0; @@ -417,7 +462,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { // '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 - 1.0).max(0.0) * BANK_STRENGTH / BANK_SCALE), + BANK_STRENGTH / (1.0 + (river_edge_dist as f32 - 2.0).max(0.0) * BANK_STRENGTH / BANK_SCALE), 0.0, (river_edge_dist / BANK_SCALE).clamped(0.0, 1.0), ); @@ -433,33 +478,6 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { } else { alt } - - // match kind { - // RiverKind::River { .. } => { - // // Alt of river water *is* the alt of land - // let river_water_alt = Lerp::lerp(river_chunk.alt, downhill_chunk.alt, river_t as f32); - - // if river_edge_dist <= 0.0 { - // const GORGE: 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 riverbed_depth = near_centre * river_width as f32 * 0.35 + GORGE; - // alt.with_min(river_water_alt - riverbed_depth) - // } else { - // const BANK_SCALE: f32 = 8.0; - // const BANK_STRENGTH: f32 = 100.0; - // alt.with(river_water_alt, BANK_STRENGTH / (1.0 + river_edge_dist as f32 * BANK_STRENGTH / BANK_SCALE)) - // } - // }, - // RiverKind::Lake { .. } => { - // let lake_water_alt = Lerp::lerp( - // river_chunk.alt.max(river_chunk.water_alt), - // downhill_chunk.alt.max(downhill_chunk.water_alt), - // river_t as f32, - // ); - // alt - // }, - // _ => alt, - // } }, (_, _) => alt, }, @@ -667,7 +685,7 @@ 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, water_dist, alt_, old_water_level, _riverless_alt, warp_factor) = if let Some( + 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 @@ -920,8 +938,8 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { let warp_factor = 0.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 alt = 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))?;