From 24a3ed683d3b244165a087876ba6ce4a57d1f2f3 Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Sat, 4 Sep 2021 15:31:14 +0100
Subject: [PATCH] Improved river and lake banks

---
 common/src/terrain/mod.rs  |  34 ++++++------
 server/src/cmd.rs          |   4 ++
 world/src/column/mod.rs    | 103 ++++++++++++++++++++-----------------
 world/src/layer/scatter.rs |   3 +-
 world/src/sim/mod.rs       |   2 +-
 5 files changed, 81 insertions(+), 65 deletions(-)

diff --git a/common/src/terrain/mod.rs b/common/src/terrain/mod.rs
index 6463eef1c4..a56aa38e8d 100644
--- a/common/src/terrain/mod.rs
+++ b/common/src/terrain/mod.rs
@@ -322,23 +322,23 @@ pub fn quadratic_nearest_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 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, 12, 0.01);
+    // 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/server/src/cmd.rs b/server/src/cmd.rs
index 375e4ccc08..7d6cf4156e 100644
--- a/server/src/cmd.rs
+++ b/server/src/cmd.rs
@@ -2704,6 +2704,8 @@ temp {:?}
 humidity {:?}
 rockiness {:?}
 tree_density {:?}
+in_river {:?}
+in_lake {:?}
 spawn_rate {:?} "#,
             wpos,
             alt,
@@ -2720,6 +2722,8 @@ spawn_rate {:?} "#,
             humidity,
             rockiness,
             tree_density,
+            col.in_river,
+            col.in_lake,
             spawn_rate
         ))
     };
diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs
index 5f2e48ebf0..3ba58351d5 100644
--- a/world/src/column/mod.rs
+++ b/world/src/column/mod.rs
@@ -139,7 +139,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
             let downhill_wpos = downhill_pos.map(|e| e as f64);
             let downhill_pos =
                 downhill_pos.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| e.div_euclid(sz as i32));
-            let neighbor_wpos = posj.map(|e| e as f64) * neighbor_coef;// + neighbor_coef * 0.5;
+            let neighbor_wpos = posj.map(|e| e as f64) * neighbor_coef + neighbor_coef * 0.5;
             let direction = neighbor_wpos - downhill_wpos;
             let river_width_min = if let RiverKind::River { cross_section } = kind {
                 cross_section.x as f64
@@ -150,7 +150,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
             let coeffs =
                 river_spline_coeffs(neighbor_wpos, chunkj.river.spline_derivative, downhill_wpos);
             let (direction, coeffs, downhill_chunk, river_t, river_pos, river_dist) = match kind {
-                RiverKind::River { .. } => {
+                RiverKind::River { .. } /*| RiverKind::Lake { .. }*/ => {
                     if let Some((t, pt, dist)) = quadratic_nearest_point(&coeffs, wposf, Vec2::new(neighbor_wpos, downhill_wpos)) /*{
                         let curve = CubicBezier2 {
                             start: neighbor_wpos,
@@ -211,7 +211,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
                     if pass_dist > 1 {
                         return (posj, chunkj, river, None);
                     }
-                    let neighbor_pass_wpos = neighbor_pass_pos.map(|e| e as f64);
+                    let neighbor_pass_wpos = neighbor_pass_pos.map(|e| e as f64) + neighbor_coef * 0.5;
                     let neighbor_pass_pos = neighbor_pass_pos
                         .map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| e / sz as i32);
                     let coeffs =
@@ -262,9 +262,11 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
             };
             let river_width_max =
                 if let Some(RiverKind::River { cross_section }) = downhill_chunk.river.river_kind {
+                    (cross_section.x as f64).min(river_width_min * 1.75) // Hack
+                } else if let Some(RiverKind::River { cross_section }) = chunkj.river.river_kind {
                     cross_section.x as f64
                 } else {
-                    lake_width
+                    lake_width * 0.5
                 };
             let river_width_noise = (sim.gen_ctx.small_nz.get((river_pos.div(16.0)).into_array()))
                 .max(-1.0)
@@ -274,10 +276,14 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
             let river_width = Lerp::lerp(
                 river_width_min,
                 river_width_max,
-                cubic(river_t.clamped(0.0, 1.0)),//.sqrt(),
+                // if matches!(chunkj.river.river_kind, Some(RiverKind::Lake { .. })) {
+                //     (1.0 - (river_t.clamped(0.0, 1.0) * 2.0 - 1.0).powi(2)).sqrt() * 0.5
+                // } else {
+                    cubic(river_t.clamped(0.0, 1.0))
+                // },
             );
 
-            let river_width = river_width;// * (1.0 + river_width_noise * 0.3);
+            let river_width = river_width.max(2.0f64.sqrt() + 0.1);// * (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
@@ -352,13 +358,17 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
             }
         }
 
-        let (river_water_level, lake_water_level, water_dist) = neighbor_river_data.clone().fold(
+        let actual_sea_level = CONFIG.sea_level + 2.0; // TODO: Don't add 2.0, why is this required?
+
+        let (river_water_level, in_river, lake_water_level, lake_dist, 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
+                WeightedSum::default().with_max(actual_sea_level), // TODO: Don't add 1.0
+                false,
+                WeightedSum::default().with_max(actual_sea_level), // TODO: Don't add 1.0
+                10000.0f32,
                 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) {
+            |(mut river_water_level, mut in_river, mut lake_water_level, mut lake_dist, 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))),
@@ -372,12 +382,17 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
                     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 { .. } => {
+                        RiverKind::River { .. } /*| RiverKind::Lake { .. }*/ => {
                             // 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);
+
                             river_water_level = river_water_level
-                                .with(river_water_alt, near_center * 10.0);
                                 // .with_max(river_water_alt)
+                                .with(river_water_alt, near_center);
+
+                            if river_edge_dist <= 0.0 {
+                                in_river = true;
+                            }
                         },
                         // Slightly wider threshold is chosen in case the lake bounds are a bit wrong
                         RiverKind::Lake { .. } => {
@@ -388,30 +403,37 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
                             );
 
                             river_water_level = river_water_level
-                                .with(lake_water_alt, near_center * 10.0);
+                                .with(lake_water_alt, near_center);
 
-                            if river_edge_dist <= 0.0 {
+                            lake_dist = lake_dist.min(river_edge_dist);
+                            // if river_edge_dist <= 0.0 {
+                            //     in_lake = true;
+                            // }
+
+                            // Lake border prevents a lake failing to propagate its altitude to nearby rivers
+                            let border = if river_width >= lake_width * 0.9 { 5.0 } else { 0.0 };
+                            if river_edge_dist <= border {
                                 lake_water_level = lake_water_level
-                                    .with(lake_water_alt, near_center * 10.0);
+                                    // Make sure the closest lake is prioritised
+                                    .with(lake_water_alt, near_center + 0.1 / (1.0 + river_edge_dist));
                                     // .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);
                             }
                         },
                         RiverKind::Ocean => {},
                     };
 
-                    let water_dist = Some(water_dist.unwrap_or(river_edge_dist).min(river_edge_dist));
+                    let river_edge_dist_unclamped = (river_dist - river_width * 0.5) as f32;
+                    let water_dist = Some(water_dist.unwrap_or(river_edge_dist_unclamped).min(river_edge_dist_unclamped));
 
-                    (river_water_level, lake_water_level, water_dist)
+                    (river_water_level, in_river, lake_water_level, lake_dist, water_dist)
                 },
-                (_, _) => (river_water_level, lake_water_level, water_dist),
+                (_, _) => (river_water_level, in_river, lake_water_level, lake_dist, water_dist),
             },
         );
-        let water_level = match (river_water_level.eval(), lake_water_level.eval()) {
+        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(CONFIG.sea_level),
-        };
+            (r, l) => r.or(l).unwrap_or(actual_sea_level),
+        } - 0.1;
 
         let riverless_alt = alt;
         let alt = neighbor_river_data.clone().fold(
@@ -429,7 +451,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
                     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 { .. } => {
+                        RiverKind::River { .. } /*| RiverKind::Lake { .. }*/ => {
                             // 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);
                             Some((river_water_alt, None))
@@ -443,7 +465,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
 
                             Some((lake_water_alt, Some(riverless_alt)))
                         },
-                        RiverKind::Ocean => Some((riverless_alt, Some(riverless_alt))),
+                        RiverKind::Ocean => None,
                     };
 
                     if let Some((water_alt, min_alt)) = water_alt {
@@ -452,7 +474,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
                             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.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));
+                            let riverbed_alt = (water_alt - riverbed_depth).max(riverless_alt.min(CONFIG.sea_level - MIN_DEPTH));
                             alt.with_min(min_alt.unwrap_or(riverbed_alt).min(riverbed_alt))
                         } else {
                             const GORGE: f32 = 0.5;
@@ -464,9 +486,10 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
                             let weight = Lerp::lerp(
                                 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),
+                                cubic((river_edge_dist / BANK_SCALE).clamped(0.0, 1.0) as f64) as f32,
                             );
-                            let alt = alt.with(water_alt + GORGE, weight);
+                            let alt = alt
+                                .with(water_alt + GORGE, weight);
                             // Add "walls" around weirdly back-curving segments to prevent water walls
                             // let alt = if river_edge_dist <= 1.0 && river_t > 0.0 && river_t < 1.0 {
                             //     alt.with_max(water_alt + GORGE)
@@ -1217,22 +1240,6 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
         // dirt
         let ground = Lerp::lerp(ground, sub_surface_color, marble_mid * tree_density);
 
-        let near_ocean = max_river.and_then(|(_, _, river_data, _)| {
-            if (river_data.is_lake() || river_data.river_kind == Some(RiverKind::Ocean))
-                && alt <= water_level.max(CONFIG.sea_level + 5.0)
-            {
-                Some(water_level)
-            } else {
-                None
-            }
-        });
-
-        let ocean_level = if let Some(_sea_level) = near_ocean {
-            alt - CONFIG.sea_level
-        } else {
-            5.0
-        };
-
         let path = sim.get_nearest_path(wpos);
         let cave = sim.get_nearest_cave(wpos);
 
@@ -1246,11 +1253,11 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
             surface_color: Rgb::lerp(
                 sub_surface_color,
                 Rgb::lerp(
+                    // Beach
                     Rgb::lerp(cliff, sand, alt.sub(basement).mul(0.25)),
                     // Land
                     ground,
-                    // Beach
-                    ((ocean_level - 0.0) / 2.0).max(0.0),
+                    ((alt - CONFIG.sea_level) / 12.0).clamped(0.0, 1.0),
                 ),
                 surface_veg,
             ),
@@ -1282,6 +1289,8 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
             snow_cover,
             cliff_offset,
             cliff_height,
+            in_river,
+            in_lake: lake_dist <= 0.0,
 
             chunk: sim_chunk,
         })
@@ -1314,6 +1323,8 @@ pub struct ColumnSample<'a> {
     pub snow_cover: bool,
     pub cliff_offset: f32,
     pub cliff_height: f32,
+    pub in_river: bool,
+    pub in_lake: bool,
 
     pub chunk: &'a SimChunk,
 }
diff --git a/world/src/layer/scatter.rs b/world/src/layer/scatter.rs
index ee221edf32..c98acaa2a3 100644
--- a/world/src/layer/scatter.rs
+++ b/world/src/layer/scatter.rs
@@ -343,7 +343,8 @@ pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) {
                     * col
                         .water_dist
                         .map(|wd| Lerp::lerp(0.2, 0.0, (wd / 8.0).clamped(0.0, 1.0)))
-                        .unwrap_or(0.0),
+                        .unwrap_or(0.0)
+                    * ((col.alt - CONFIG.sea_level) / 12.0).clamped(0.0, 1.0),
                 Some((0.2, 128.0, 0.5)),
             )
         }),
diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs
index 57573fcc63..342cd053e2 100644
--- a/world/src/sim/mod.rs
+++ b/world/src/sim/mod.rs
@@ -2222,7 +2222,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)
                 ,
             )
         };