Better lake entrances and distance

This commit is contained in:
Joshua Barretto 2021-09-03 12:32:30 +01:00
parent f118d15745
commit 826aff2ea4
2 changed files with 117 additions and 71 deletions

View File

@ -295,6 +295,10 @@ pub fn quadratic_nearest_point(
point: Vec2<f64>,
line: Vec2<Vec2<f64>>,
) -> Option<(f64, Vec2<f64>, 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;

View File

@ -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<f32> {
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))?;