mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Add a default world map.
Also adds map versioning, proper scaling, and updates sediment rendering. It also tones down warp.
This commit is contained in:
parent
7f2573c561
commit
d54f22c9fa
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -4,3 +4,4 @@
|
||||
*.wav filter=lfs diff=lfs merge=lfs -text
|
||||
*.ogg filter=lfs diff=lfs merge=lfs -text
|
||||
*.ico filter=lfs diff=lfs merge=lfs -text
|
||||
assets/world/map/*.bin filter=lfs diff=lfs merge=lfs -text
|
||||
|
BIN
assets/world/map/veloren_0_5_0_0.bin
(Stored with Git LFS)
Normal file
BIN
assets/world/map/veloren_0_5_0_0.bin
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -112,10 +112,15 @@ impl Server {
|
||||
settings.world_seed,
|
||||
WorldOpts {
|
||||
seed_elements: true,
|
||||
world_file: if let Some(ref file) = settings.map_file {
|
||||
FileOpts::Load(file.clone())
|
||||
world_file: if let Some(ref opts) = settings.map_file {
|
||||
opts.clone()
|
||||
} else {
|
||||
FileOpts::Generate
|
||||
// Load default map from assets.
|
||||
//
|
||||
// TODO: Consider using some naming convention to automatically change this
|
||||
// with changing versions, or at least keep it in a constant somewhere that's
|
||||
// easy to change.
|
||||
FileOpts::LoadAsset("world.map.veloren_0_5_0_0".into())
|
||||
},
|
||||
..WorldOpts::default()
|
||||
},
|
||||
|
@ -1,6 +1,9 @@
|
||||
use portpicker::pick_unused_port;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::{fs, io::prelude::*, net::SocketAddr, path::PathBuf};
|
||||
use world::sim::FileOpts;
|
||||
|
||||
const DEFAULT_WORLD_SEED: u32 = 5284;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
@ -15,7 +18,9 @@ pub struct ServerSettings {
|
||||
//pub login_server: whatever
|
||||
pub start_time: f64,
|
||||
pub admins: Vec<String>,
|
||||
pub map_file: Option<PathBuf>,
|
||||
/// When set to None, loads the default map file (if available); otherwise, uses the value of
|
||||
/// the file options to decide how to proceed.
|
||||
pub map_file: Option<FileOpts>,
|
||||
}
|
||||
|
||||
impl Default for ServerSettings {
|
||||
@ -23,7 +28,7 @@ impl Default for ServerSettings {
|
||||
Self {
|
||||
gameserver_address: SocketAddr::from(([0; 4], 14004)),
|
||||
metrics_address: SocketAddr::from(([0; 4], 14005)),
|
||||
world_seed: 5284,
|
||||
world_seed: DEFAULT_WORLD_SEED,
|
||||
server_name: "Veloren Alpha".to_owned(),
|
||||
server_description: "This is the best Veloren server.".to_owned(),
|
||||
max_players: 100,
|
||||
@ -89,6 +94,11 @@ impl ServerSettings {
|
||||
}
|
||||
|
||||
pub fn singleplayer() -> Self {
|
||||
let mut load = Self::load();
|
||||
if let None = load.map_file {
|
||||
// If lodaing the default map file, make sure the seed is also default.
|
||||
load.world_seed = DEFAULT_WORLD_SEED;
|
||||
};
|
||||
Self {
|
||||
//BUG: theoretically another process can grab the port between here and server creation, however the timewindow is quite small
|
||||
gameserver_address: SocketAddr::from((
|
||||
|
@ -70,7 +70,7 @@ impl<'a> BlockGen<'a> {
|
||||
// (height max) [12.70, 46.33] + 3 = [15.70, 49.33]
|
||||
/ (1.0 + 3.0 * cliff_sample.chaos)
|
||||
+ 3.0;
|
||||
// COnservative range of radius: [8, 47]
|
||||
// Conservative range of radius: [8, 47]
|
||||
let radius = RandomField::new(seed + 2).get(cliff_pos3d) % 48 + 8;
|
||||
|
||||
max_height.max(
|
||||
@ -191,8 +191,8 @@ impl<'a> BlockGen<'a> {
|
||||
.gen_ctx
|
||||
.warp_nz
|
||||
.get(wposf.div(24.0))
|
||||
.mul((chaos - 0.1).max(0.0).powf(2.0))
|
||||
.mul(48.0);
|
||||
.mul((chaos - 0.1).max(0.0).min(1.0).powf(2.0))
|
||||
.mul(/*48.0*/ 16.0);
|
||||
let warp = Lerp::lerp(0.0, warp, warp_factor);
|
||||
|
||||
let surface_height = alt + warp;
|
||||
@ -227,7 +227,7 @@ impl<'a> BlockGen<'a> {
|
||||
false,
|
||||
height,
|
||||
on_cliff,
|
||||
basement.min(basement + warp),
|
||||
basement + height - alt,
|
||||
(if water_level <= alt {
|
||||
water_level + warp
|
||||
} else {
|
||||
@ -430,7 +430,7 @@ impl<'a> ZCache<'a> {
|
||||
0.0
|
||||
};
|
||||
|
||||
let min = self.sample.alt - (self.sample.chaos * 48.0 + cave_depth);
|
||||
let min = self.sample.alt - (self.sample.chaos.min(1.0) * 16.0 + cave_depth);
|
||||
let min = min - 4.0;
|
||||
|
||||
let cliff = BlockGen::get_cliff_height(
|
||||
|
@ -829,12 +829,12 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
||||
/* .mul((1.0 - chaos).min(1.0).max(0.3))
|
||||
.mul(1.0 - humidity) */
|
||||
/* .mul(32.0) */;
|
||||
let basement = alt_
|
||||
+ sim./*get_interpolated*/get_interpolated_monotone(wpos, |chunk| chunk.basement.sub(chunk.alt))?;
|
||||
|
||||
let riverless_alt_delta = Lerp::lerp(0.0, riverless_alt_delta, warp_factor);
|
||||
let alt = alt_ + riverless_alt_delta;
|
||||
let basement = basement.min(alt);
|
||||
let basement = alt
|
||||
+ sim./*get_interpolated*/get_interpolated_monotone(wpos, |chunk| chunk.basement.sub(chunk.alt))?;
|
||||
// let basement = basement.min(alt);
|
||||
|
||||
let rock = (sim.gen_ctx.small_nz.get(
|
||||
Vec3::new(wposf.x, wposf.y, alt as f64)
|
||||
|
@ -1,6 +1,6 @@
|
||||
#![deny(unsafe_code)]
|
||||
#![allow(incomplete_features)]
|
||||
#![feature(const_generics, label_break_value)]
|
||||
#![feature(arbitrary_enum_discriminant, const_generics, label_break_value)]
|
||||
|
||||
mod all;
|
||||
mod block;
|
||||
|
@ -232,7 +232,7 @@ pub fn get_rivers<F: fmt::Debug + Float + Into<f64>, G: Float + Into<f64>>(
|
||||
let mut rivers = vec![RiverData::default(); WORLD_SIZE.x * WORLD_SIZE.y].into_boxed_slice();
|
||||
let neighbor_coef = TerrainChunkSize::RECT_SIZE.map(|e| e as f64);
|
||||
// (Roughly) area of a chunk, times minutes per second.
|
||||
let mins_per_sec = /*64.0*/1.0; //1.0 / 64.0;
|
||||
let mins_per_sec = /*64.0*/1.0/*1.0 / 16.0*/;
|
||||
let chunk_area_factor = neighbor_coef.x * neighbor_coef.y / mins_per_sec;
|
||||
// NOTE: This technically makes us discontinuous, so we should be cautious about using this.
|
||||
let derivative_divisor = 1.0;
|
||||
@ -567,16 +567,21 @@ pub fn get_rivers<F: fmt::Debug + Float + Into<f64>, G: Float + Into<f64>>(
|
||||
/// Precompute the maximum slope at all points.
|
||||
///
|
||||
/// TODO: See if allocating in advance is worthwhile.
|
||||
fn get_max_slope(h: &[Alt], rock_strength_nz: &(impl NoiseFn<Point3<f64>> + Sync)) -> Box<[f64]> {
|
||||
fn get_max_slope(
|
||||
h: &[Alt],
|
||||
rock_strength_nz: &(impl NoiseFn<Point3<f64>> + Sync),
|
||||
height_scale: impl Fn(usize) -> Alt + Sync,
|
||||
) -> Box<[f64]> {
|
||||
let min_max_angle = (15.0/*6.0*//*30.0*//*6.0*//*15.0*/ / 360.0 * 2.0 * f64::consts::PI).tan();
|
||||
let max_max_angle =
|
||||
(60.0/*54.0*//*50.0*//*54.0*//*45.0*/ / 360.0 * 2.0 * f64::consts::PI).tan();
|
||||
let max_angle_range = max_max_angle - min_max_angle;
|
||||
let height_scale = 1.0 / 4.0; // 1.0; // 1.0 / CONFIG.mountain_scale as f64;
|
||||
// let height_scale = 1.0 / 4.0; // 1.0; // 1.0 / CONFIG.mountain_scale as f64;
|
||||
h.par_iter()
|
||||
.enumerate()
|
||||
.map(|(posi, &z)| {
|
||||
let wposf = uniform_idx_as_vec2(posi).map(|e| e as f64) * TerrainChunkSize::RECT_SIZE.map(|e| e as f64);
|
||||
let height_scale = height_scale(posi);
|
||||
let wposz = z as f64 / height_scale;// * CONFIG.mountain_scale as f64;
|
||||
// Normalized to be between 6 and and 54 degrees.
|
||||
let div_factor = /*32.0*//*16.0*//*64.0*//*256.0*//*8.0 / 4.0*//*8.0*/(2.0 * TerrainChunkSize::RECT_SIZE.x as f64) / 8.0/* * 8.0*//*1.0*//*4.0*//* * /*1.0*/16.0/* TerrainChunkSize::RECT_SIZE.x as f64 / 8.0 */*/;
|
||||
@ -723,12 +728,16 @@ fn erode(
|
||||
epsilon_0: impl Fn(usize) -> f32 + Sync,
|
||||
alpha: impl Fn(usize) -> f32 + Sync,
|
||||
is_ocean: impl Fn(usize) -> bool + Sync,
|
||||
// scaling factors
|
||||
height_scale: impl Fn(f32) -> Alt + Sync,
|
||||
k_da_scale: impl Fn(f64) -> f64,
|
||||
) {
|
||||
let compute_stats = true;
|
||||
log::debug!("Done draining...");
|
||||
let height_scale = 1.0; // 1.0 / CONFIG.mountain_scale as f64;
|
||||
// let height_scale = 1.0 / 4.0; // 1.0 / CONFIG.mountain_scale as f64;
|
||||
let min_erosion_height = 0.0; // -<Alt as Float>::infinity();
|
||||
let mmaxh = CONFIG.mountain_scale as f64 * height_scale;
|
||||
|
||||
// let mmaxh = CONFIG.mountain_scale as f64 * height_scale;
|
||||
// Since maximum uplift rate is expected to be 5.010e-4 m * y^-1, and
|
||||
// 1.0 height units is 1.0 / height_scale m, whatever the
|
||||
// max uplift rate is (in units / y), we can find dt by multiplying by
|
||||
@ -749,20 +758,21 @@ fn erode(
|
||||
let dt = max_uplift as f64/* / height_scale*/ /* * CONFIG.mountain_scale as f64*/ / /*5.010e-4*/1e-3/*0.2e-3*/;
|
||||
log::debug!("dt={:?}", dt);
|
||||
// Minimum sediment thickness before we treat erosion as sediment based.
|
||||
let sediment_thickness = 1.0e-4 * dt;
|
||||
let sediment_thickness = |_n| /*6.25e-5*/1.0e-4 * dt;
|
||||
let neighbor_coef = TerrainChunkSize::RECT_SIZE.map(|e| e as f64);
|
||||
let chunk_area = neighbor_coef.x * neighbor_coef.y;
|
||||
let min_length = neighbor_coef.reduce_partial_min();
|
||||
let max_stable = /*max_slope * */min_length * min_length / (dt/* / 2.0*/); //1.0/* + /*max_uplift as f64 / dt*/sed / dt*/;
|
||||
|
||||
// Landslide constant: ideally scaled to 10e-2 m / y^-1
|
||||
let l = /*200.0 * max_uplift as f64;*/(1.0e-2 /*/ CONFIG.mountain_scale as f64*/ * height_scale);
|
||||
let l_tot = l * dt;
|
||||
// let l = /*200.0 * max_uplift as f64;*/(1.0e-2 /*/ CONFIG.mountain_scale as f64*/ * height_scale);
|
||||
// let l_tot = l * dt;
|
||||
// Debris flow coefficient (m / year).
|
||||
let k_df = 1.0e-4/*0.0*/;
|
||||
// Debris flow area coefficient (m^(-2q)).
|
||||
let q = 0.2;
|
||||
let q_ = /*1.0*/1.5/*1.0*/;
|
||||
let k_da = /*5.0*//*2.5*//*5.0*//*2.5*//*5.0*/2.5 * 4.0.powf(2.0 * q);
|
||||
let k_da = /*5.0*//*2.5*//*5.0*//*2.5*//*5.0*/2.5 * k_da_scale(q);
|
||||
let nx = WORLD_SIZE.x;
|
||||
let ny = WORLD_SIZE.y;
|
||||
let dx = TerrainChunkSize::RECT_SIZE.x as f64/* * height_scale*//* / CONFIG.mountain_scale as f64*/;
|
||||
@ -887,7 +897,8 @@ fn erode(
|
||||
|| {
|
||||
rayon::join(
|
||||
|| {
|
||||
let max_slope = get_max_slope(h, rock_strength_nz);
|
||||
let max_slope =
|
||||
get_max_slope(h, rock_strength_nz, |posi| height_scale(n_f(posi)));
|
||||
log::debug!("Got max slopes...");
|
||||
max_slope
|
||||
},
|
||||
@ -932,7 +943,8 @@ fn erode(
|
||||
} else {
|
||||
let old_b_i = b[posi];
|
||||
let sed = (h_t[posi] - old_b_i) as f64;
|
||||
let k = if sed > sediment_thickness {
|
||||
let n = n_f(posi);
|
||||
let k = if sed > sediment_thickness(n) {
|
||||
// Sediment
|
||||
// k_fs
|
||||
k_fs_mult_sed * kf(posi)
|
||||
@ -941,7 +953,7 @@ fn erode(
|
||||
// k_fb
|
||||
kf(posi)
|
||||
} * dt;
|
||||
let n = n_f(posi) as f64;
|
||||
let n = n as f64;
|
||||
let m = m_f(posi) as f64;
|
||||
|
||||
let mwrec_i = &mwrec[posi];
|
||||
@ -985,7 +997,8 @@ fn erode(
|
||||
kf(posi)
|
||||
} * dt; */
|
||||
// let g_i = g(posi) as f64;
|
||||
let g_i = if sed > sediment_thickness {
|
||||
let n = n_f(posi);
|
||||
let g_i = if sed > sediment_thickness(n) {
|
||||
g_fs_mult_sed * g(posi) as f64
|
||||
} else {
|
||||
g(posi) as f64
|
||||
@ -1381,7 +1394,8 @@ fn erode(
|
||||
// let g_i = g(posi) as Compute;
|
||||
let old_b_i = b_i;
|
||||
let sed = (h_t_i - old_b_i) as f64;
|
||||
let g_i = if sed > sediment_thickness {
|
||||
let n = n_f(posi);
|
||||
let g_i = if sed > sediment_thickness(n) {
|
||||
g_fs_mult_sed * g(posi) as Compute
|
||||
} else {
|
||||
g(posi) as Compute
|
||||
@ -1463,6 +1477,7 @@ fn erode(
|
||||
} else {
|
||||
// *is_done.at(posi) = done_val;
|
||||
let posj = posj as usize;
|
||||
let posj_stack = mstack_inv[posj];
|
||||
// let dxy = (uniform_idx_as_vec2(posi) - uniform_idx_as_vec2(posj)).map(|e| e as f64);
|
||||
|
||||
// Has an outgoing flow edge (posi, posj).
|
||||
@ -1478,7 +1493,7 @@ fn erode(
|
||||
// h[i](t + dt) = (h[i](t) + δt * (uplift[i] + flux(i) * h[j](t + δt))) / (1 + flux(i) * δt).
|
||||
// NOTE: posj has already been computed since it's downhill from us.
|
||||
// Therefore, we can rely on wh being set to the water height for that node.
|
||||
// let h_j = h[posj] as f64;
|
||||
let h_j = h[posj_stack] as f64;
|
||||
// let a_j = a[posj] as f64;
|
||||
let wh_j = wh[posj] as f64;
|
||||
// let old_a_i = a[posi] as f64;
|
||||
@ -1503,7 +1518,7 @@ fn erode(
|
||||
}; */
|
||||
|
||||
// Only perform erosion if we are above the water level of the previous node.
|
||||
if old_elev_i > wh_j/*h_j*//*h[posj]*/ {
|
||||
if old_elev_i > wh_j/*h_j*//*h_j*//*h[posj]*/ {
|
||||
let mut dtherm = 0.0f64;
|
||||
/* {
|
||||
// Thermal erosion (landslide)
|
||||
@ -1565,8 +1580,9 @@ fn erode(
|
||||
assert!(posj_stack > stacki); */
|
||||
// This can happen in cases where receiver kk is neither uphill of
|
||||
// nor downhill from posi's direct receiver.
|
||||
if /*new_ht_i/*old_ht_i*/ >= (h_t[posj]/* + uplift(posj) as f64*/)*/old_elev_i /*>=*/> wh[posj] as f64/*h[posj]*//*h_j*/ {
|
||||
let h_j = h_stack[posj_stack] as f64;
|
||||
if /*new_ht_i/*old_ht_i*/ >= (h_t[posj]/* + uplift(posj) as f64*/)*/old_elev_i /*>=*/> /*wh[posj] as f64*//*h[posj]*/h_j {
|
||||
// let h_j = h_stack[posj_stack] as f64;
|
||||
let elev_j = h_j/* + a_j.max(0.0)*/;
|
||||
/* let flux = /*k * (p * chunk_area * area[posi] as f64).powf(m) / neighbor_distance;*/k_fs_fact[kk] + k_df_fact[kk];
|
||||
assert!(flux.is_normal() && flux.is_positive() || flux == 0.0);
|
||||
@ -1601,7 +1617,9 @@ fn erode(
|
||||
let mut mask = [MaskType::new(false); 8];
|
||||
mrec_downhill(&mrec, posi).for_each(|(kk, posj)| {
|
||||
let posj_stack = mstack_inv[posj];
|
||||
if old_elev_i > wh[posj] as f64 {
|
||||
let h_j = h_stack[posj_stack];
|
||||
if /*new_h_t_i > (h_t[posj] + uplift(posj)) as f64 */old_elev_i > /*wh[posj]*/h_j as f64 {
|
||||
// let h_j = h_stack[posj_stack];
|
||||
// k_fs_weights[kk] = k_fs_fact[kk] as SimdType;
|
||||
// k_fs_weights[max] = k_fs_fact[kk] as SimdType;
|
||||
// /*k_fs_weights = */k_fs_weights.replace(max, k_fs_fact[kk]/*.extract(kk)*/ as f64);
|
||||
@ -1609,7 +1627,7 @@ fn erode(
|
||||
// k_df_weights[max] = k_df_fact[kk] as SimdType;
|
||||
// /*k_df_weights = */k_df_weights.replace(max, k_df_fact[kk]/*.extract(kk)*/ as f64);
|
||||
mask[kk] = MaskType::new(true);
|
||||
rec_heights[kk] = h_stack[posj_stack] as SimdType;
|
||||
rec_heights[kk] = h_j as SimdType;
|
||||
// rec_heights[max] = h[posj] as SimdType;
|
||||
// /*rec_heights = */rec_heights.replace(max, h[posj] as f64);
|
||||
// max += 1;
|
||||
@ -1928,7 +1946,7 @@ fn erode(
|
||||
|
||||
// If we dipped below the receiver's water level, set our height to the receiver's
|
||||
// water level.
|
||||
if new_h_i <= wh_j/*elev_j*//*h[posj]*/ {
|
||||
if new_h_i <= wh_j/*h_j*//*elev_j*//*h[posj]*/ {
|
||||
if compute_stats {
|
||||
ncorr += 1;
|
||||
}
|
||||
@ -1941,7 +1959,7 @@ fn erode(
|
||||
// will be set precisely equal to the estimated height, meaning it
|
||||
// effectively "vanishes" and just deposits sediment to its reciever.
|
||||
// (This is probably related to criteria for block Gauss-Seidel, etc.).
|
||||
new_h_i = /*h[posj];*/wh_j;// - df_part.max(0.0);
|
||||
new_h_i = /*h[posj];*/wh_j/*h_j*/;// - df_part.max(0.0);
|
||||
// df_part = 0.0;
|
||||
/* let lposj = lake_sill[posj];
|
||||
lake_sill[posi] = lposj;
|
||||
@ -2076,7 +2094,7 @@ fn erode(
|
||||
sums += mag_slope;
|
||||
// Just use the computed rate.
|
||||
} */
|
||||
h_stack[/*(posi*/stacki] = new_h_i as Alt;
|
||||
h_stack[/*posi*/stacki] = new_h_i as Alt;
|
||||
// println!("Delta: {:?} - {:?} = {:?}", new_h_i, h_p_i, new_h_i - h_p_i);
|
||||
// a[posi] = df_part as Alt;
|
||||
// Make sure to update the basement as well!
|
||||
@ -2327,7 +2345,8 @@ fn erode(
|
||||
let kd_factor =
|
||||
// 1.0;
|
||||
(1.0 / (max_slope / mid_slope/*.sqrt()*//*.powf(0.03125)*/).powf(/*2.0*/2.0))/*.min(kdsed)*/;
|
||||
max_slopes[posi] = if sed > sediment_thickness && kdsed > 0.0 {
|
||||
let n = n_f(posi);
|
||||
max_slopes[posi] = if sed > sediment_thickness(n) && kdsed > 0.0 {
|
||||
// Sediment
|
||||
kdsed /* * kd_factor*/
|
||||
} else {
|
||||
@ -3386,6 +3405,10 @@ pub fn do_erosion(
|
||||
g: impl Fn(usize) -> f32 + Sync,
|
||||
epsilon_0: impl Fn(usize) -> f32 + Sync,
|
||||
alpha: impl Fn(usize) -> f32 + Sync,
|
||||
// scaling factors
|
||||
height_scale: impl Fn(f32) -> Alt + Sync,
|
||||
k_d_scale: f64,
|
||||
k_da_scale: impl Fn(f64) -> f64,
|
||||
) -> (Box<[Alt]>, Box<[Alt]> /*, Box<[Alt]>*/) {
|
||||
log::debug!("Initializing erosion arrays...");
|
||||
let oldh_ = (0..WORLD_SIZE.x * WORLD_SIZE.y)
|
||||
@ -3479,7 +3502,7 @@ pub fn do_erosion(
|
||||
// Bedrock transport coefficients (diffusivity) in m^2 / year. For now, we set them all to be equal
|
||||
// on land, but in theory we probably want to at least differentiate between soil, bedrock, and
|
||||
// sediment.
|
||||
let height_scale = 1.0 / 4.0; // 1.0 / CONFIG.mountain_scale as f64;
|
||||
/* let height_scale = 1.0 / 4.0; // 1.0 / CONFIG.mountain_scale as f64;
|
||||
let time_scale = 1.0; //1.0 / 4.0; // 4.0.powf(-n)
|
||||
let mmaxh = CONFIG.mountain_scale as f64 * height_scale;
|
||||
let dt = max_uplift as f64 / height_scale /* * CONFIG.mountain_scale as f64*/ / 5.010e-4;
|
||||
@ -3487,10 +3510,12 @@ pub fn do_erosion(
|
||||
2.0e-5 * dt;
|
||||
let kd_bedrock =
|
||||
/*1e-2*//*0.25e-2*/1e-2 / 1.0 * height_scale * height_scale/* / (CONFIG.mountain_scale as f64 * CONFIG.mountain_scale as f64) */
|
||||
/* * k_fb / 2e-5 */;
|
||||
/* * k_fb / 2e-5 */; */
|
||||
let kdsed =
|
||||
/*1.5e-2*//*1e-4*//*1.25e-2*//*1.5e-2 */1.5e-2 / 1.0 * height_scale * height_scale / time_scale/* / (CONFIG.mountain_scale as f64 * CONFIG.mountain_scale as f64) */
|
||||
/*1.5e-2*//*1e-4*//*1.25e-2*//*1.5e-2 *//*1.5e-2 / 1.0*//* * height_scale * height_scale / time_scale*//* / (CONFIG.mountain_scale as f64 * CONFIG.mountain_scale as f64) */
|
||||
1.5e-2 / 4.0
|
||||
/* * k_fb / 2e-5 */;
|
||||
let kdsed = kdsed * k_d_scale;
|
||||
// let kd = |posi: usize| kd_bedrock; // if is_ocean(posi) { /*0.0*/kd_bedrock } else { kd_bedrock };
|
||||
let n = |posi: usize| n[posi];
|
||||
let m = |posi: usize| m[posi];
|
||||
@ -3499,6 +3524,8 @@ pub fn do_erosion(
|
||||
let g = |posi: usize| g[posi];
|
||||
let epsilon_0 = |posi: usize| epsilon_0[posi];
|
||||
let alpha = |posi: usize| alpha[posi];
|
||||
let height_scale = |n| height_scale(n);
|
||||
let k_da_scale = |q| k_da_scale(q);
|
||||
// Hillslope diffusion coefficient for sediment.
|
||||
let mut is_done = bitbox![0; WORLD_SIZE.x * WORLD_SIZE.y];
|
||||
(0..n_steps).for_each(|i| {
|
||||
@ -3529,6 +3556,8 @@ pub fn do_erosion(
|
||||
epsilon_0,
|
||||
alpha,
|
||||
|posi| is_ocean(posi),
|
||||
height_scale,
|
||||
k_da_scale,
|
||||
);
|
||||
});
|
||||
(h, b /*, a*/)
|
||||
|
@ -30,6 +30,7 @@ use crate::{
|
||||
CONFIG,
|
||||
};
|
||||
use common::{
|
||||
assets,
|
||||
terrain::{BiomeKind, TerrainChunkSize},
|
||||
vol::RectVolSize,
|
||||
};
|
||||
@ -113,6 +114,7 @@ pub(crate) struct GenCtx {
|
||||
pub uplift_nz: Worley,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub enum FileOpts {
|
||||
/// If set, generate the world map and do not try to save to or load from file
|
||||
/// (default).
|
||||
@ -120,8 +122,17 @@ pub enum FileOpts {
|
||||
/// If set, generate the world map and save the world file (path is created
|
||||
/// the same way screenshot paths are).
|
||||
Save,
|
||||
/// If set, load the world file from this path in legacy format (errors if
|
||||
/// path not found). This option may be removed at some point, since it only applies to maps
|
||||
/// generated before map saving was merged into master.
|
||||
LoadLegacy(PathBuf),
|
||||
/// If set, load the world file from this path (errors if path not found).
|
||||
Load(PathBuf),
|
||||
/// If set, look for the world file at this asset specifier (errors if asset is not found).
|
||||
///
|
||||
/// NOTE: Could stand to merge this with `Load` and construct an enum that can handle either a
|
||||
/// PathBuf or an asset specifier, at some point.
|
||||
LoadAsset(String),
|
||||
}
|
||||
|
||||
impl Default for FileOpts {
|
||||
@ -145,17 +156,131 @@ impl Default for WorldOpts {
|
||||
}
|
||||
}
|
||||
|
||||
/// A way to store certain components between runs of map generation. Only intended for
|
||||
/// development purposes--no attempt is made to detect map invalidation or make sure that the map
|
||||
/// is synchronized with updates to noise-rs, changes to other parameters, etc.
|
||||
/// LEGACY: Remove when people stop caring.
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct WorldFile {
|
||||
#[repr(C)]
|
||||
pub struct WorldFileLegacy {
|
||||
/// Saved altitude height map.
|
||||
pub alt: Box<[Alt]>,
|
||||
/// Saved basement height map.
|
||||
pub basement: Box<[Alt]>,
|
||||
}
|
||||
|
||||
/// Version of the world map intended for use in Veloren 0.5.0.
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[repr(C)]
|
||||
pub struct WorldMap_0_5_0 {
|
||||
/// Saved altitude height map.
|
||||
pub alt: Box<[Alt]>,
|
||||
/// Saved basement height map.
|
||||
pub basement: Box<[Alt]>,
|
||||
}
|
||||
|
||||
/// Errors when converting a map to the most recent type (currently,
|
||||
/// shared by the various map types, but at some point we might switch to
|
||||
/// version-specific errors if it feels worthwhile).
|
||||
#[derive(Debug)]
|
||||
pub enum WorldFileError {
|
||||
/// Map size was invalid, and it can't be converted to a valid one.
|
||||
WorldSizeInvalid,
|
||||
}
|
||||
|
||||
/// WORLD MAP.
|
||||
///
|
||||
/// A way to store certain components between runs of map generation. Only intended for
|
||||
/// development purposes--no attempt is made to detect map invalidation or make sure that the map
|
||||
/// is synchronized with updates to noise-rs, changes to other parameters, etc.
|
||||
///
|
||||
/// The map is verisoned to enable format detection between versions of Veloren, so that when we
|
||||
/// update the map format we don't break existing maps (or at least, we will try hard not to break
|
||||
/// maps between versions; if we can't avoid it, we can at least give a reasonable error message).
|
||||
///
|
||||
/// NOTE: We rely somemwhat heavily on the implementation specifics of bincode to make sure this is
|
||||
/// backwards compatible. When adding new variants here, Be very careful to make sure tha the old
|
||||
/// variants are preserved in the correct order and with the correct names and indices, and make
|
||||
/// sure to keep the #[repr(u32)]!
|
||||
///
|
||||
/// All non-legacy versions of world files should (ideally) fit in this format. Since the format
|
||||
/// contains a version and is designed to be extensible backwards-compatibly, the only
|
||||
/// reason not to use this forever would be if we decided to move away from BinCode, or
|
||||
/// store data across multiple files (or something else weird I guess).
|
||||
///
|
||||
/// Update this when you add a new map version.
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[repr(u32)]
|
||||
pub enum WorldFile {
|
||||
Veloren_0_5_0(WorldMap_0_5_0) = 0,
|
||||
}
|
||||
|
||||
/// Data for the most recent map type. Update this when you add a new map verson.
|
||||
pub type ModernMap = WorldMap_0_5_0;
|
||||
|
||||
impl WorldFileLegacy {
|
||||
#[inline]
|
||||
/// Idea: each map type except the latest knows how to transform
|
||||
/// into the the subsequent map version, and each map type including the
|
||||
/// latest exposes an "into_modern()" method that converts this map type
|
||||
/// to the modern map type. Thus, to migrate a map from an old format to a new
|
||||
/// format, we just need to transform the old format to the subsequent map
|
||||
/// version, and then call .into_modern() on that--this should construct a call chain that
|
||||
/// ultimately ends up with a modern version.
|
||||
pub fn into_modern(self) -> Result<ModernMap, WorldFileError> {
|
||||
if self.alt.len() != self.basement.len()
|
||||
|| self.alt.len() != WORLD_SIZE.x as usize * WORLD_SIZE.y as usize
|
||||
{
|
||||
return Err(WorldFileError::WorldSizeInvalid);
|
||||
}
|
||||
|
||||
/* let f = |h| h;// / 4.0;
|
||||
let mut map = map;
|
||||
map.alt.par_iter_mut()
|
||||
.zip(map.basement.par_iter_mut())
|
||||
.for_each(|(mut h, mut b)| {
|
||||
*h = f(*h);
|
||||
*b = f(*b);
|
||||
}); */
|
||||
|
||||
let map = WorldMap_0_5_0 {
|
||||
alt: self.alt,
|
||||
basement: self.basement,
|
||||
};
|
||||
|
||||
map.into_modern()
|
||||
}
|
||||
}
|
||||
|
||||
impl WorldMap_0_5_0 {
|
||||
#[inline]
|
||||
pub fn into_modern(self) -> Result<ModernMap, WorldFileError> {
|
||||
if self.alt.len() != self.basement.len()
|
||||
|| self.alt.len() != WORLD_SIZE.x as usize * WORLD_SIZE.y as usize
|
||||
{
|
||||
return Err(WorldFileError::WorldSizeInvalid);
|
||||
}
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl WorldFile {
|
||||
/// Turns map data from the latest version into a versioned WorldFile ready for serialization.
|
||||
/// Whenever a new map is updated, just change the variant we construct here to make sure we're
|
||||
/// using the latest map version.
|
||||
|
||||
pub fn new(map: ModernMap) -> Self {
|
||||
WorldFile::Veloren_0_5_0(map)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Turns a WorldFile into the latest version. Whenever a new map version is added, just add
|
||||
/// it to this match statement.
|
||||
pub fn into_modern(self) -> Result<ModernMap, (WorldFileError)> {
|
||||
match self {
|
||||
WorldFile::Veloren_0_5_0(map) => map.into_modern(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WorldSim {
|
||||
pub seed: u32,
|
||||
pub(crate) chunks: Vec<SimChunk>,
|
||||
@ -291,8 +416,48 @@ impl WorldSim {
|
||||
let rock_strength_nz = ScaleBias::new(&rock_strength_nz)
|
||||
.set_scale(1.0 / rock_strength_scale); */
|
||||
|
||||
let height_scale = 1.0f64; // 1.0 / CONFIG.mountain_scale as f64;
|
||||
let max_erosion_per_delta_t = /*8.0*//*32.0*//*1.0*//*32.0*//*32.0*//*16.0*//*64.0*//*32.0*/16.0/*128.0*//*1.0*//*0.2 * /*100.0*/250.0*//*128.0*//*16.0*//*128.0*//*32.0*/ * height_scale;
|
||||
// Suppose the old world has grid spacing Δx' = Δy', new Δx = Δy.
|
||||
// We define grid_scale such that Δx = height_scale * Δx' ⇒
|
||||
// grid_scale = Δx / Δx'.
|
||||
let grid_scale = 1.0f64 / 4.0/*1.0*/;
|
||||
// Now, suppose we want to generate a world with "similar" topography, defined in this case
|
||||
// as having roughly equal slopes at steady state, with the simulation taking roughly as
|
||||
// many steps to get to the point the previous world was at when it finished being
|
||||
// simulated.
|
||||
//
|
||||
// Some computations with our coupled SPL/debris flow give us (for slope S constant) the following
|
||||
// suggested scaling parameters to make this work:
|
||||
// k_fs_scale ≡ (K𝑓 / K𝑓') = grid_scale^(-2m) = grid_scale^(-2θn)
|
||||
let k_fs_scale = |theta, n| grid_scale.powf(-2.0 * (theta * n) as f64);
|
||||
|
||||
// k_da_scale ≡ (K_da / K_da') = grid_scale^(-2q)
|
||||
let k_da_scale = |q| grid_scale.powf(-2.0 * q);
|
||||
//
|
||||
// Some other estimated parameters are harder to come by and *much* more dubious, not being accurate
|
||||
// for the coupled equation. But for the SPL only one we roughly find, for h the height at steady
|
||||
// state and time τ = time to steady state, with Hack's Law estimated b = 2.0 and various other
|
||||
// simplifying assumptions, the estimate:
|
||||
// height_scale ≡ (h / h') = grid_scale^(n)
|
||||
let height_scale = |n: f32| grid_scale.powf(n as f64) as Alt;
|
||||
// time_scale ≡ (τ / τ') = grid_scale^(n)
|
||||
let time_scale = |n: f32| grid_scale.powf(n as f64);
|
||||
//
|
||||
// Based on this estimate, we have:
|
||||
// delta_t_scale ≡ (Δt / Δt') = time_scale
|
||||
let delta_t_scale = |n: f32| time_scale(n);
|
||||
// alpha_scale ≡ (α / α') = height_scale^(-1)
|
||||
let alpha_scale = |n: f32| height_scale(n).recip() as f32;
|
||||
//
|
||||
// Slightly more dubiously (need to work out the math better) we find:
|
||||
// k_d_scale ≡ (K_d / K_d') = grid_scale^2 / (height_scale * time_scale)
|
||||
let k_d_scale = |n: f32| /*grid_scale.powi(2) / time_scale(n)*//*height_scale(n)*/grid_scale.powi(2) / (/*height_scale(n) * */time_scale(n))/* * (1.0 / 16.0)*/;
|
||||
// epsilon_0_scale ≡ (ε₀ / ε₀') = height_scale(n) / time_scale(n)
|
||||
let epsilon_0_scale = |n| /*height_scale(n) as f32*//*1.0*/(height_scale(n) / time_scale(n)) as f32/* * 1.0 / 4.0*/;
|
||||
|
||||
// Approximate n for purposes of computation of parameters above over the whole grid (when
|
||||
// a chunk isn't available).
|
||||
let n_approx = 1.0;
|
||||
let max_erosion_per_delta_t = /*8.0*//*32.0*//*1.0*//*32.0*//*32.0*//*16.0*//*64.0*//*32.0*/64.0/*128.0*//*1.0*//*0.2 * /*100.0*/250.0*//*128.0*//*16.0*//*128.0*//*32.0*/ * delta_t_scale(n_approx);
|
||||
let erosion_pow_low = /*0.25*//*1.5*//*2.0*//*0.5*//*4.0*//*0.25*//*1.0*//*2.0*//*1.5*//*1.5*//*0.35*//*0.43*//*0.5*//*0.45*//*0.37*/1.002;
|
||||
let erosion_pow_high = /*1.5*//*1.0*//*0.55*//*0.51*//*2.0*/1.002;
|
||||
let erosion_center = /*0.45*//*0.75*//*0.75*//*0.5*//*0.75*/0.5;
|
||||
@ -531,15 +696,56 @@ impl WorldSim {
|
||||
});
|
||||
|
||||
// Calculate oceans.
|
||||
let old_height =
|
||||
|posi: usize| alt_old[posi].1 * CONFIG.mountain_scale * height_scale as f32;
|
||||
let is_ocean = get_oceans(|posi: usize| alt_old[posi].1);
|
||||
let is_ocean_fn = |posi: usize| is_ocean[posi];
|
||||
/* let is_ocean = (0..WORLD_SIZE.x * WORLD_SIZE.y)
|
||||
.into_par_iter()
|
||||
.map(|i| map_edge_factor(i) == 0.0)
|
||||
.collect::<Vec<_>>(); */
|
||||
let is_ocean = get_oceans(old_height);
|
||||
let is_ocean_fn = |posi: usize| is_ocean[posi];
|
||||
|
||||
let turb_wposf_div = 8.0/*64.0*/;
|
||||
let n_func = |posi| {
|
||||
if is_ocean_fn(posi) {
|
||||
return 1.0;
|
||||
}
|
||||
let wposf = (uniform_idx_as_vec2(posi) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32))
|
||||
.map(|e| e as f64);
|
||||
let turb_wposf = wposf
|
||||
.div(TerrainChunkSize::RECT_SIZE.map(|e| e as f64))
|
||||
.div(turb_wposf_div);
|
||||
let turb = Vec2::new(
|
||||
gen_ctx.turb_x_nz.get(turb_wposf.into_array()),
|
||||
gen_ctx.turb_y_nz.get(turb_wposf.into_array()),
|
||||
) * uplift_turb_scale
|
||||
* TerrainChunkSize::RECT_SIZE.map(|e| e as f64);
|
||||
// let turb = Vec2::zero();
|
||||
let turb_wposf = wposf + turb;
|
||||
let turb_wposi = turb_wposf
|
||||
.map2(TerrainChunkSize::RECT_SIZE, |e, f| e / f as f64)
|
||||
.map2(WORLD_SIZE, |e, f| (e as i32).max(f as i32 - 1).min(0));
|
||||
let turb_posi = vec2_as_uniform_idx(turb_wposi);
|
||||
let uheight = gen_ctx
|
||||
.uplift_nz
|
||||
.get(turb_wposf.into_array())
|
||||
/* .min(0.5)
|
||||
.max(-0.5)*/
|
||||
.min(1.0)
|
||||
.max(-1.0)
|
||||
.mul(0.5)
|
||||
.add(0.5);
|
||||
/* if uheight > 0.8 {
|
||||
1.5
|
||||
} else {
|
||||
1.0
|
||||
} */
|
||||
// ((1.5 - 0.6) * uheight + 0.6) as f32
|
||||
// ((1.5 - 1.0) * uheight + 1.0) as f32
|
||||
// ((3.5 - 1.5) * (1.0 - uheight) + 1.5) as f32
|
||||
1.0
|
||||
};
|
||||
let old_height = |posi: usize| {
|
||||
alt_old[posi].1 * CONFIG.mountain_scale * height_scale(n_func(posi)) as f32
|
||||
};
|
||||
|
||||
let uplift_nz_dist = gen_ctx.uplift_nz.clone().enable_range(true);
|
||||
// Recalculate altitudes without oceans.
|
||||
@ -694,54 +900,15 @@ impl WorldSim {
|
||||
let erosion_factor = |x: f64| (/*if x <= /*erosion_center*/alt_old_center_uniform/*alt_old_center*/ { erosion_pow_low.ln() } else { erosion_pow_high.ln() } * */(/*exp_inverse_cdf*//*logit*/inv_func(x) - alt_exp_min_uniform) / (alt_exp_max_uniform - alt_exp_min_uniform))/*0.5 + (x - 0.5).signum() * ((x - 0.5).mul(2.0).abs(
|
||||
).powf(erosion_pow).mul(0.5))*//*.powf(0.5)*//*.powf(1.5)*//*.powf(2.0)*/;
|
||||
let rock_strength_div_factor = /*8.0*/(2.0 * TerrainChunkSize::RECT_SIZE.x as f64) / 8.0;
|
||||
let time_scale = 1.0; //4.0/*4.0*/;
|
||||
let n_func = |posi| {
|
||||
if is_ocean_fn(posi) {
|
||||
return 1.0;
|
||||
}
|
||||
let wposf = (uniform_idx_as_vec2(posi) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32))
|
||||
.map(|e| e as f64);
|
||||
let turb_wposf = wposf
|
||||
.div(TerrainChunkSize::RECT_SIZE.map(|e| e as f64))
|
||||
.div(turb_wposf_div);
|
||||
let turb = Vec2::new(
|
||||
gen_ctx.turb_x_nz.get(turb_wposf.into_array()),
|
||||
gen_ctx.turb_y_nz.get(turb_wposf.into_array()),
|
||||
) * uplift_turb_scale
|
||||
* TerrainChunkSize::RECT_SIZE.map(|e| e as f64);
|
||||
// let turb = Vec2::zero();
|
||||
let turb_wposf = wposf + turb;
|
||||
let turb_wposi = turb_wposf
|
||||
.map2(TerrainChunkSize::RECT_SIZE, |e, f| e / f as f64)
|
||||
.map2(WORLD_SIZE, |e, f| (e as i32).max(f as i32 - 1).min(0));
|
||||
let turb_posi = vec2_as_uniform_idx(turb_wposi);
|
||||
let uheight = gen_ctx
|
||||
.uplift_nz
|
||||
.get(turb_wposf.into_array())
|
||||
/* .min(0.5)
|
||||
.max(-0.5)*/
|
||||
.min(1.0)
|
||||
.max(-1.0)
|
||||
.mul(0.5)
|
||||
.add(0.5);
|
||||
/* if uheight > 0.8 {
|
||||
1.5
|
||||
} else {
|
||||
1.0
|
||||
} */
|
||||
// ((1.5 - 0.6) * uheight + 0.6) as f32
|
||||
// ((1.5 - 1.0) * uheight + 1.0) as f32
|
||||
// ((3.5 - 1.5) * (1.0 - uheight) + 1.5) as f32
|
||||
1.0
|
||||
};
|
||||
// let time_scale = 1.0; //4.0/*4.0*/;
|
||||
let theta_func = |posi| 0.4;
|
||||
let kf_func = {
|
||||
|posi| {
|
||||
let m_i = (theta_func(posi) * n_func(posi)) as f64;
|
||||
let kf_scale_i = k_fs_scale(theta_func(posi), n_func(posi)) as f64;
|
||||
// let precip_mul = (0.25).powf(m);
|
||||
if is_ocean_fn(posi) {
|
||||
// multiplied by height_scale^(2m) to account for change in area.
|
||||
return 1.0e-4 * 4.0.powf(2.0 * m_i)/* / time_scale*/; // .powf(-(1.0 - 2.0 * m_i))/* * 4.0*/;
|
||||
return 1.0e-4 * kf_scale_i/* / time_scale*/; // .powf(-(1.0 - 2.0 * m_i))/* * 4.0*/;
|
||||
// return 2.0e-5;
|
||||
// return 2.0e-6;
|
||||
// return 2.0e-10;
|
||||
@ -784,7 +951,7 @@ impl WorldSim {
|
||||
let oheight = /*alt_old*//*alt_base*/alt_old_no_ocean[/*(turb_posi / 64) * 64*/posi].0 as f64;
|
||||
let oheight_2 = /*alt_old*//*alt_base*/(alt_old_no_ocean[/*(turb_posi / 64) * 64*/posi].1 as f64 / CONFIG.mountain_scale as f64);
|
||||
|
||||
// kf = 1.5e-4: high-high (plateau [fan sediment])
|
||||
let kf_i = // kf = 1.5e-4: high-high (plateau [fan sediment])
|
||||
// kf = 1e-4: high (plateau)
|
||||
// kf = 2e-5: normal (dike [unexposed])
|
||||
// kf = 1e-6: normal-low (dike [exposed])
|
||||
@ -808,17 +975,26 @@ impl WorldSim {
|
||||
// ((1.0 - uheight) * (0.5 - 0.5 * /*((1.32 - uchaos as f64) / 1.32)*/oheight) * (1.5e-4 - 2.0e-6) + 2.0e-6)
|
||||
// 2e-5
|
||||
// multiplied by height_scale^(2m) to account for change in area.
|
||||
2.5e-6 * 4.0.powf(/*-(1.0 - 2.0 * m_i)*/ 2.0 * m_i) /* / time_scale*//* / 4.0 * 0.25 *//* * 4.0*/
|
||||
// 2.5e-6/* / time_scale*//* / 4.0 * 0.25 *//* * 4.0*/
|
||||
1.0e-6
|
||||
// 2.0e-6
|
||||
// 2.9e-10
|
||||
// ((1.0 - uheight) * (5e-5 - 2.9e-10) + 2.9e-10)
|
||||
// ((1.0 - uheight) * (5e-5 - 2.9e-14) + 2.9e-14)
|
||||
;
|
||||
kf_i * kf_scale_i
|
||||
}
|
||||
};
|
||||
let kd_func = {
|
||||
|posi| {
|
||||
// let height_scale = 1.0 / 4.0.powf(-n_i);
|
||||
let n = n_func(posi);
|
||||
let kd_scale_i = k_d_scale(n);
|
||||
if is_ocean_fn(posi) {
|
||||
return 1.0e-2 * 4.0.powf(-2.0) * time_scale;
|
||||
let kd_i =
|
||||
/*1.0e-2*/
|
||||
1.0e-2 / 4.0
|
||||
;
|
||||
kd_i * kd_scale_i;
|
||||
}
|
||||
let wposf = (uniform_idx_as_vec2(posi)
|
||||
* TerrainChunkSize::RECT_SIZE.map(|e| e as i32))
|
||||
@ -846,13 +1022,25 @@ impl WorldSim {
|
||||
.max(-1.0)
|
||||
.mul(0.5)
|
||||
.add(0.5);
|
||||
let uchaos = /* gen_ctx.chaos_nz.get((wposf.div(3_000.0)).into_array())
|
||||
.min(1.0)
|
||||
.max(-1.0)
|
||||
.mul(0.5)
|
||||
.add(0.5); */
|
||||
chaos[posi].1;
|
||||
|
||||
// kd = 1e-1: high (mountain, dike)
|
||||
// kd = 1.5e-2: normal-high (plateau [fan sediment])
|
||||
// kd = 1e-2: normal (plateau)
|
||||
// multiplied by height_scale² to account for change in area, then divided by
|
||||
// time_scale to account for lower dt.
|
||||
1.0e-2 * 4.0.powf(-2.0) * time_scale // m_old^2 / y * (1 m_new / 4 m_old)^2
|
||||
let kd_i = // 1.0e-2 * kd_scale_i;// m_old^2 / y * (1 m_new / 4 m_old)^2
|
||||
1.10e-2 / 4.0
|
||||
// (uheight * (1.0e-1 - 1.0e-2) + 1.0e-2)
|
||||
// ((1.0 - uheight) * (0.5 + 0.5 * ((1.32 - uchaos as f64) / 1.32)) * (1.0e-2 - 1.0e-3) + 1.0e-3)
|
||||
// (uheight * (1.0e-2 - 1.0e-3) + 1.0e-3) / 2.0
|
||||
;
|
||||
kd_i * kd_scale_i
|
||||
}
|
||||
};
|
||||
let g_func = |posi| {
|
||||
@ -922,10 +1110,9 @@ impl WorldSim {
|
||||
// 1.5
|
||||
};
|
||||
let epsilon_0_func = |posi| {
|
||||
let n_i = n_func(posi);
|
||||
// height_scale is roughly [using Hack's Law with b = 2 and SPL without debris flow or
|
||||
// epsilon_0_scale is roughly [using Hack's Law with b = 2 and SPL without debris flow or
|
||||
// hillslopes] equal to the ratio of the old to new area, to the power of -n_i.
|
||||
let height_scale = 4.0.powf(-n_i);
|
||||
let epsilon_0_scale_i = epsilon_0_scale(n_func(posi));
|
||||
if is_ocean_fn(posi) {
|
||||
// marine: ε₀ = 2.078e-3
|
||||
// divide by height scale, multiplied by time_scale, cancels out to 1; idea is that
|
||||
@ -952,7 +1139,11 @@ impl WorldSim {
|
||||
//
|
||||
// ε₀ e^(-α' H') (Δt' * height_scale) / height_scale = ε₀' e^(-α' H') Δt'
|
||||
// ε₀ = ε₀'
|
||||
return 2.078e-3 * height_scale/* * time_scale*/;
|
||||
let epsilon_0_i =
|
||||
//2.078e-3
|
||||
2.078e-3 / 4.0
|
||||
;
|
||||
return epsilon_0_i * epsilon_0_scale_i/* * time_scale*/;
|
||||
// return 5.0;
|
||||
}
|
||||
let wposf = (uniform_idx_as_vec2(posi) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32))
|
||||
@ -980,6 +1171,9 @@ impl WorldSim {
|
||||
.max(-1.0)
|
||||
.mul(0.5)
|
||||
.add(0.5);
|
||||
/* let n_i = n_func(posi);
|
||||
let height_scale = height_scale(n_i);
|
||||
let uheight = uheight / height_scale; */
|
||||
let wposf3 = Vec3::new(
|
||||
wposf.x,
|
||||
wposf.y,
|
||||
@ -994,9 +1188,9 @@ impl WorldSim {
|
||||
.max(-1.0)
|
||||
.mul(0.5)
|
||||
.add(0.5);
|
||||
let center = /*0.25*/0.4 / 4.0;
|
||||
let dmin = center - /*0.15;//0.05*/0.05 / 4.0;
|
||||
let dmax = center + /*0.05*//*0.10*/0.05 / 4.0; //0.05;
|
||||
let center = /*0.25*/0.4;
|
||||
let dmin = center - /*0.15;//0.05*/0.05;
|
||||
let dmax = center + /*0.05*//*0.10*/0.05; //0.05;
|
||||
let log_odds = |x: f64| logit(x) - logit(center);
|
||||
let ustrength = logistic_cdf(
|
||||
1.0 * logit(rock_strength.min(1.0f64 - 1e-7).max(1e-7))
|
||||
@ -1010,25 +1204,28 @@ impl WorldSim {
|
||||
// Nunnock River (fractured granite, least weathered?): ε₀ = 5.3e-5
|
||||
// The stronger the rock, the lower the production rate of exposed bedrock.
|
||||
// divide by height scale, then multiplied by time_scale, cancels out.
|
||||
((1.0 - ustrength) * (/*3.18e-4*/2.078e-3 - 5.3e-5) + 5.3e-5) as f32 * height_scale
|
||||
let epsilon_0_i =
|
||||
// ((1.0 - ustrength) * (/*3.18e-4*/2.078e-3 - 5.3e-5) + 5.3e-5) as f32
|
||||
((1.0 - ustrength) * (/*3.18e-4*/2.078e-3 - 5.3e-5) + 5.3e-5) as f32 / 4.0
|
||||
;
|
||||
/* * time_scale*/
|
||||
// 0.0
|
||||
;
|
||||
epsilon_0_i * epsilon_0_scale_i
|
||||
};
|
||||
let alpha_func = |posi| {
|
||||
let n_i = n_func(posi);
|
||||
// height_scale is roughly [using Hack's Law with b = 2 and SPL without debris flow or
|
||||
// hillslopes] equal to the ratio of the old to new area, to the power of -n_i.
|
||||
let height_scale = 4.0.powf(-n_i);
|
||||
if is_ocean_fn(posi) {
|
||||
// marine: α = 3.7e-2
|
||||
// divided by height_scale; idea is that since the final height itself will be
|
||||
// the old height * height scale, and we take the rate as ε₀ * e^(-αH), to keep
|
||||
// the rate of rate of change in soil production consistent we must divide H by
|
||||
// height_scale.
|
||||
//
|
||||
// αH = α(H' * height_scale) = α'H'
|
||||
// α = α' / height_scale
|
||||
return 3.7e-2 / height_scale;
|
||||
let alpha_scale_i = alpha_scale(n_func(posi));
|
||||
if is_ocean_fn(posi) {
|
||||
// marine: α = 3.7e-2
|
||||
return 3.7e-2 * alpha_scale_i;
|
||||
}
|
||||
let wposf = (uniform_idx_as_vec2(posi) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32))
|
||||
.map(|e| e as f64);
|
||||
@ -1055,6 +1252,9 @@ impl WorldSim {
|
||||
.max(-1.0)
|
||||
.mul(0.5)
|
||||
.add(0.5);
|
||||
/* let n_i = n_func(posi);
|
||||
let height_scale = height_scale(n_i);
|
||||
let uheight = uheight / height_scale; */
|
||||
let wposf3 = Vec3::new(
|
||||
wposf.x,
|
||||
wposf.y,
|
||||
@ -1069,9 +1269,9 @@ impl WorldSim {
|
||||
.max(-1.0)
|
||||
.mul(0.5)
|
||||
.add(0.5);
|
||||
let center = /*0.25*/0.4 / 4.0;
|
||||
let dmin = center - /*0.15;//0.05*/0.05 / 4.0;
|
||||
let dmax = center + /*0.05*//*0.10*/0.05 / 4.0; //0.05;
|
||||
let center = /*0.25*/0.4;
|
||||
let dmin = center - /*0.15;//0.05*/0.05;
|
||||
let dmax = center + /*0.05*//*0.10*/0.05; //0.05;
|
||||
let log_odds = |x: f64| logit(x) - logit(center);
|
||||
let ustrength = logistic_cdf(
|
||||
1.0 * logit(rock_strength.min(1.0f64 - 1e-7).max(1e-7))
|
||||
@ -1084,8 +1284,8 @@ impl WorldSim {
|
||||
// Nunnock river (fractured granite, least weathered?): α = 2e-3
|
||||
// Point Reyes: α = 1.6e-2
|
||||
// The stronger the rock, the faster the decline in soil production.
|
||||
// divided by height_scale.
|
||||
(ustrength * (4.2e-2 - 1.6e-2) + 1.6e-2) as f32 / height_scale
|
||||
let alpha_i = (ustrength * (4.2e-2 - 1.6e-2) + 1.6e-2) as f32;
|
||||
alpha_i * alpha_scale_i
|
||||
};
|
||||
let uplift_fn = |posi| {
|
||||
if is_ocean_fn(posi) {
|
||||
@ -1304,9 +1504,30 @@ impl WorldSim {
|
||||
.collect::<Vec<_>>();
|
||||
let is_ocean_fn = |posi: usize| is_ocean[posi]; */
|
||||
|
||||
// Load map, if necessary.
|
||||
// Parse out the contents of various map formats into the values we need.
|
||||
let parsed_world_file = (|| {
|
||||
if let FileOpts::Load(ref path) = opts.world_file {
|
||||
let map = match opts.world_file {
|
||||
FileOpts::LoadLegacy(ref path) => {
|
||||
let file = match File::open(path) {
|
||||
Ok(file) => file,
|
||||
Err(err) => {
|
||||
log::warn!("Couldn't read path for maps: {:?}", err);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let reader = BufReader::new(file);
|
||||
let map: WorldFileLegacy = match bincode::deserialize_from(reader) {
|
||||
Ok(map) => map,
|
||||
Err(err) => {
|
||||
log::warn!("Couldn't parse legacy map: {:?}). Maybe you meant to try a regular load?", err);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
map.into_modern()
|
||||
}
|
||||
FileOpts::Load(ref path) => {
|
||||
let file = match File::open(path) {
|
||||
Ok(file) => file,
|
||||
Err(err) => {
|
||||
@ -1319,31 +1540,50 @@ impl WorldSim {
|
||||
let map: WorldFile = match bincode::deserialize_from(reader) {
|
||||
Ok(map) => map,
|
||||
Err(err) => {
|
||||
log::warn!("Couldn't parse map: {:?})", err);
|
||||
log::warn!("Couldn't parse modern map: {:?}). Maybe you meant to try a legacy load?", err);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
if map.alt.len() != map.basement.len()
|
||||
|| map.alt.len() != WORLD_SIZE.x as usize * WORLD_SIZE.y as usize
|
||||
{
|
||||
log::warn!("World size of map is invalid.");
|
||||
map.into_modern()
|
||||
}
|
||||
FileOpts::LoadAsset(ref specifier) => {
|
||||
let reader = match assets::load_file(specifier, &["bin"]) {
|
||||
Ok(reader) => reader,
|
||||
Err(err) => {
|
||||
log::warn!(
|
||||
"Couldn't read asset specifier {:?} for maps: {:?}",
|
||||
specifier,
|
||||
err
|
||||
);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
/* let f = |h| h;// / 4.0;
|
||||
let mut map = map;
|
||||
map.alt.par_iter_mut()
|
||||
.zip(map.basement.par_iter_mut())
|
||||
.for_each(|(mut h, mut b)| {
|
||||
*h = f(*h);
|
||||
*b = f(*b);
|
||||
}); */
|
||||
let map: WorldFile = match bincode::deserialize_from(reader) {
|
||||
Ok(map) => map,
|
||||
Err(err) => {
|
||||
log::warn!("Couldn't parse modern map: {:?}). Maybe you meant to try a legacy load?", err);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
Some(map)
|
||||
} else {
|
||||
map.into_modern()
|
||||
}
|
||||
FileOpts::Generate | FileOpts::Save => return None,
|
||||
};
|
||||
|
||||
match map {
|
||||
Ok(map) => Some(map),
|
||||
Err(e) => {
|
||||
match e {
|
||||
WorldFileError::WorldSizeInvalid => {
|
||||
log::warn!("World size of map is invalid.");
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
let (alt, basement /*, alluvium*/) = if let Some(map) = parsed_world_file {
|
||||
@ -1358,7 +1598,9 @@ impl WorldSim {
|
||||
max_erosion_per_delta_t as f32,
|
||||
n_steps,
|
||||
&river_seed,
|
||||
// varying conditions
|
||||
&rock_strength_nz,
|
||||
// initial conditions
|
||||
|posi| alt_func(posi), // + if is_ocean_fn(posi) { 0.0 } else { 128.0 },
|
||||
|posi| {
|
||||
alt_func(posi)
|
||||
@ -1371,6 +1613,7 @@ impl WorldSim {
|
||||
}, // if is_ocean_fn(posi) { old_height(posi) } else { 0.0 },
|
||||
// |posi| 0.0,
|
||||
is_ocean_fn,
|
||||
// empirical constants
|
||||
uplift_fn,
|
||||
|posi| n_func(posi),
|
||||
|posi| theta_func(posi),
|
||||
@ -1379,12 +1622,16 @@ impl WorldSim {
|
||||
|posi| g_func(posi),
|
||||
|posi| epsilon_0_func(posi),
|
||||
|posi| alpha_func(posi),
|
||||
// scaling factors
|
||||
|n| height_scale(n),
|
||||
k_d_scale(n_approx),
|
||||
|q| k_da_scale(q),
|
||||
);
|
||||
|
||||
// Quick "small scale" erosion cycle in order to lower extreme angles.
|
||||
do_erosion(
|
||||
0.0,
|
||||
(1.0 * height_scale) as f32,
|
||||
(1.0/* * height_scale*/) as f32,
|
||||
n_small_steps,
|
||||
&river_seed,
|
||||
&rock_strength_nz,
|
||||
@ -1392,7 +1639,7 @@ impl WorldSim {
|
||||
|posi| basement[posi] as f32,
|
||||
// |posi| /*alluvium[posi] as f32*/0.0f32,
|
||||
is_ocean_fn,
|
||||
|posi| uplift_fn(posi) * (1.0 * height_scale / max_erosion_per_delta_t),
|
||||
|posi| uplift_fn(posi) * (1.0/* * height_scale*/ / max_erosion_per_delta_t),
|
||||
|posi| n_func(posi),
|
||||
|posi| theta_func(posi),
|
||||
|posi| kf_func(posi),
|
||||
@ -1400,11 +1647,15 @@ impl WorldSim {
|
||||
|posi| g_func(posi),
|
||||
|posi| epsilon_0_func(posi),
|
||||
|posi| alpha_func(posi),
|
||||
|n| height_scale(n),
|
||||
k_d_scale(n_approx),
|
||||
|q| k_da_scale(q),
|
||||
)
|
||||
};
|
||||
|
||||
// Save map, if necessary.
|
||||
let map = WorldFile { alt, basement };
|
||||
// NOTE: We wll always save a map with latest version.
|
||||
let map = WorldFile::new(ModernMap { alt, basement });
|
||||
(|| {
|
||||
if let FileOpts::Save = opts.world_file {
|
||||
use std::time::SystemTime;
|
||||
@ -1438,7 +1689,10 @@ impl WorldSim {
|
||||
}
|
||||
}
|
||||
})();
|
||||
let (alt, basement) = (map.alt, map.basement);
|
||||
|
||||
// Skip validation--we just performed a no-op conversion for this map, so it had better be
|
||||
// valid!
|
||||
let ModernMap { alt, basement } = map.into_modern().unwrap();
|
||||
|
||||
// Additional small-scale eroson after map load, only used during testing.
|
||||
let (alt, basement /*, alluvium*/) = if n_post_load_steps == 0 {
|
||||
@ -1446,7 +1700,7 @@ impl WorldSim {
|
||||
} else {
|
||||
do_erosion(
|
||||
0.0,
|
||||
(1.0 * height_scale) as f32,
|
||||
(1.0/* * height_scale*/) as f32,
|
||||
n_post_load_steps,
|
||||
&river_seed,
|
||||
&rock_strength_nz,
|
||||
@ -1454,7 +1708,7 @@ impl WorldSim {
|
||||
|posi| basement[posi] as f32,
|
||||
// |posi| alluvium[posi] as f32,
|
||||
is_ocean_fn,
|
||||
|posi| uplift_fn(posi) * (1.0 * height_scale / max_erosion_per_delta_t),
|
||||
|posi| uplift_fn(posi) * (1.0/* * height_scale*/ / max_erosion_per_delta_t),
|
||||
|posi| n_func(posi),
|
||||
|posi| theta_func(posi),
|
||||
|posi| kf_func(posi),
|
||||
@ -1462,6 +1716,9 @@ impl WorldSim {
|
||||
|posi| g_func(posi),
|
||||
|posi| epsilon_0_func(posi),
|
||||
|posi| alpha_func(posi),
|
||||
|n| height_scale(n),
|
||||
k_d_scale(n_approx),
|
||||
|q| k_da_scale(q),
|
||||
)
|
||||
};
|
||||
|
||||
@ -2207,10 +2464,10 @@ impl SimChunk {
|
||||
panic!("Halp!");
|
||||
} */
|
||||
|
||||
let height_scale = 1.0; // 1.0 / CONFIG.mountain_scale;
|
||||
let mut alt = CONFIG.sea_level.add(alt_pre.div(height_scale));
|
||||
let mut basement = CONFIG.sea_level.add(basement_pre.div(height_scale));
|
||||
let water_alt = CONFIG.sea_level.add(water_alt_pre.div(height_scale));
|
||||
// let height_scale = 1.0; // 1.0 / CONFIG.mountain_scale;
|
||||
let mut alt = CONFIG.sea_level.add(alt_pre /*.div(height_scale)*/);
|
||||
let mut basement = CONFIG.sea_level.add(basement_pre /*.div(height_scale)*/);
|
||||
let water_alt = CONFIG.sea_level.add(water_alt_pre /*.div(height_scale)*/);
|
||||
let downhill = if downhill_pre == -2 {
|
||||
None
|
||||
} else if downhill_pre < 0 {
|
||||
|
Loading…
Reference in New Issue
Block a user