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
|
*.wav filter=lfs diff=lfs merge=lfs -text
|
||||||
*.ogg filter=lfs diff=lfs merge=lfs -text
|
*.ogg filter=lfs diff=lfs merge=lfs -text
|
||||||
*.ico 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,
|
settings.world_seed,
|
||||||
WorldOpts {
|
WorldOpts {
|
||||||
seed_elements: true,
|
seed_elements: true,
|
||||||
world_file: if let Some(ref file) = settings.map_file {
|
world_file: if let Some(ref opts) = settings.map_file {
|
||||||
FileOpts::Load(file.clone())
|
opts.clone()
|
||||||
} else {
|
} 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()
|
..WorldOpts::default()
|
||||||
},
|
},
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
use portpicker::pick_unused_port;
|
use portpicker::pick_unused_port;
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::{fs, io::prelude::*, net::SocketAddr, path::PathBuf};
|
use std::{fs, io::prelude::*, net::SocketAddr, path::PathBuf};
|
||||||
|
use world::sim::FileOpts;
|
||||||
|
|
||||||
|
const DEFAULT_WORLD_SEED: u32 = 5284;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@ -15,7 +18,9 @@ pub struct ServerSettings {
|
|||||||
//pub login_server: whatever
|
//pub login_server: whatever
|
||||||
pub start_time: f64,
|
pub start_time: f64,
|
||||||
pub admins: Vec<String>,
|
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 {
|
impl Default for ServerSettings {
|
||||||
@ -23,7 +28,7 @@ impl Default for ServerSettings {
|
|||||||
Self {
|
Self {
|
||||||
gameserver_address: SocketAddr::from(([0; 4], 14004)),
|
gameserver_address: SocketAddr::from(([0; 4], 14004)),
|
||||||
metrics_address: SocketAddr::from(([0; 4], 14005)),
|
metrics_address: SocketAddr::from(([0; 4], 14005)),
|
||||||
world_seed: 5284,
|
world_seed: DEFAULT_WORLD_SEED,
|
||||||
server_name: "Veloren Alpha".to_owned(),
|
server_name: "Veloren Alpha".to_owned(),
|
||||||
server_description: "This is the best Veloren server.".to_owned(),
|
server_description: "This is the best Veloren server.".to_owned(),
|
||||||
max_players: 100,
|
max_players: 100,
|
||||||
@ -89,6 +94,11 @@ impl ServerSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn singleplayer() -> Self {
|
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 {
|
Self {
|
||||||
//BUG: theoretically another process can grab the port between here and server creation, however the timewindow is quite small
|
//BUG: theoretically another process can grab the port between here and server creation, however the timewindow is quite small
|
||||||
gameserver_address: SocketAddr::from((
|
gameserver_address: SocketAddr::from((
|
||||||
|
@ -70,7 +70,7 @@ impl<'a> BlockGen<'a> {
|
|||||||
// (height max) [12.70, 46.33] + 3 = [15.70, 49.33]
|
// (height max) [12.70, 46.33] + 3 = [15.70, 49.33]
|
||||||
/ (1.0 + 3.0 * cliff_sample.chaos)
|
/ (1.0 + 3.0 * cliff_sample.chaos)
|
||||||
+ 3.0;
|
+ 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;
|
let radius = RandomField::new(seed + 2).get(cliff_pos3d) % 48 + 8;
|
||||||
|
|
||||||
max_height.max(
|
max_height.max(
|
||||||
@ -191,8 +191,8 @@ impl<'a> BlockGen<'a> {
|
|||||||
.gen_ctx
|
.gen_ctx
|
||||||
.warp_nz
|
.warp_nz
|
||||||
.get(wposf.div(24.0))
|
.get(wposf.div(24.0))
|
||||||
.mul((chaos - 0.1).max(0.0).powf(2.0))
|
.mul((chaos - 0.1).max(0.0).min(1.0).powf(2.0))
|
||||||
.mul(48.0);
|
.mul(/*48.0*/ 16.0);
|
||||||
let warp = Lerp::lerp(0.0, warp, warp_factor);
|
let warp = Lerp::lerp(0.0, warp, warp_factor);
|
||||||
|
|
||||||
let surface_height = alt + warp;
|
let surface_height = alt + warp;
|
||||||
@ -227,7 +227,7 @@ impl<'a> BlockGen<'a> {
|
|||||||
false,
|
false,
|
||||||
height,
|
height,
|
||||||
on_cliff,
|
on_cliff,
|
||||||
basement.min(basement + warp),
|
basement + height - alt,
|
||||||
(if water_level <= alt {
|
(if water_level <= alt {
|
||||||
water_level + warp
|
water_level + warp
|
||||||
} else {
|
} else {
|
||||||
@ -430,7 +430,7 @@ impl<'a> ZCache<'a> {
|
|||||||
0.0
|
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 min = min - 4.0;
|
||||||
|
|
||||||
let cliff = BlockGen::get_cliff_height(
|
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 - chaos).min(1.0).max(0.3))
|
||||||
.mul(1.0 - humidity) */
|
.mul(1.0 - humidity) */
|
||||||
/* .mul(32.0) */;
|
/* .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 riverless_alt_delta = Lerp::lerp(0.0, riverless_alt_delta, warp_factor);
|
||||||
let alt = alt_ + riverless_alt_delta;
|
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(
|
let rock = (sim.gen_ctx.small_nz.get(
|
||||||
Vec3::new(wposf.x, wposf.y, alt as f64)
|
Vec3::new(wposf.x, wposf.y, alt as f64)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#![deny(unsafe_code)]
|
#![deny(unsafe_code)]
|
||||||
#![allow(incomplete_features)]
|
#![allow(incomplete_features)]
|
||||||
#![feature(const_generics, label_break_value)]
|
#![feature(arbitrary_enum_discriminant, const_generics, label_break_value)]
|
||||||
|
|
||||||
mod all;
|
mod all;
|
||||||
mod block;
|
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 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);
|
let neighbor_coef = TerrainChunkSize::RECT_SIZE.map(|e| e as f64);
|
||||||
// (Roughly) area of a chunk, times minutes per second.
|
// (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;
|
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.
|
// NOTE: This technically makes us discontinuous, so we should be cautious about using this.
|
||||||
let derivative_divisor = 1.0;
|
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.
|
/// Precompute the maximum slope at all points.
|
||||||
///
|
///
|
||||||
/// TODO: See if allocating in advance is worthwhile.
|
/// 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 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 =
|
let max_max_angle =
|
||||||
(60.0/*54.0*//*50.0*//*54.0*//*45.0*/ / 360.0 * 2.0 * f64::consts::PI).tan();
|
(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 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()
|
h.par_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(posi, &z)| {
|
.map(|(posi, &z)| {
|
||||||
let wposf = uniform_idx_as_vec2(posi).map(|e| e as f64) * TerrainChunkSize::RECT_SIZE.map(|e| e as f64);
|
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;
|
let wposz = z as f64 / height_scale;// * CONFIG.mountain_scale as f64;
|
||||||
// Normalized to be between 6 and and 54 degrees.
|
// 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 */*/;
|
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,
|
epsilon_0: impl Fn(usize) -> f32 + Sync,
|
||||||
alpha: impl Fn(usize) -> f32 + Sync,
|
alpha: impl Fn(usize) -> f32 + Sync,
|
||||||
is_ocean: impl Fn(usize) -> bool + 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;
|
let compute_stats = true;
|
||||||
log::debug!("Done draining...");
|
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 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
|
// 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
|
// 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
|
// 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*/;
|
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);
|
log::debug!("dt={:?}", dt);
|
||||||
// Minimum sediment thickness before we treat erosion as sediment based.
|
// 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 neighbor_coef = TerrainChunkSize::RECT_SIZE.map(|e| e as f64);
|
||||||
let chunk_area = neighbor_coef.x * neighbor_coef.y;
|
let chunk_area = neighbor_coef.x * neighbor_coef.y;
|
||||||
let min_length = neighbor_coef.reduce_partial_min();
|
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*/;
|
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
|
// 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 = /*200.0 * max_uplift as f64;*/(1.0e-2 /*/ CONFIG.mountain_scale as f64*/ * height_scale);
|
||||||
let l_tot = l * dt;
|
// let l_tot = l * dt;
|
||||||
// Debris flow coefficient (m / year).
|
// Debris flow coefficient (m / year).
|
||||||
let k_df = 1.0e-4/*0.0*/;
|
let k_df = 1.0e-4/*0.0*/;
|
||||||
// Debris flow area coefficient (m^(-2q)).
|
// Debris flow area coefficient (m^(-2q)).
|
||||||
let q = 0.2;
|
let q = 0.2;
|
||||||
let q_ = /*1.0*/1.5/*1.0*/;
|
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 nx = WORLD_SIZE.x;
|
||||||
let ny = WORLD_SIZE.y;
|
let ny = WORLD_SIZE.y;
|
||||||
let dx = TerrainChunkSize::RECT_SIZE.x as f64/* * height_scale*//* / CONFIG.mountain_scale as f64*/;
|
let dx = TerrainChunkSize::RECT_SIZE.x as f64/* * height_scale*//* / CONFIG.mountain_scale as f64*/;
|
||||||
@ -887,7 +897,8 @@ fn erode(
|
|||||||
|| {
|
|| {
|
||||||
rayon::join(
|
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...");
|
log::debug!("Got max slopes...");
|
||||||
max_slope
|
max_slope
|
||||||
},
|
},
|
||||||
@ -932,7 +943,8 @@ fn erode(
|
|||||||
} else {
|
} else {
|
||||||
let old_b_i = b[posi];
|
let old_b_i = b[posi];
|
||||||
let sed = (h_t[posi] - old_b_i) as f64;
|
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
|
// Sediment
|
||||||
// k_fs
|
// k_fs
|
||||||
k_fs_mult_sed * kf(posi)
|
k_fs_mult_sed * kf(posi)
|
||||||
@ -941,7 +953,7 @@ fn erode(
|
|||||||
// k_fb
|
// k_fb
|
||||||
kf(posi)
|
kf(posi)
|
||||||
} * dt;
|
} * dt;
|
||||||
let n = n_f(posi) as f64;
|
let n = n as f64;
|
||||||
let m = m_f(posi) as f64;
|
let m = m_f(posi) as f64;
|
||||||
|
|
||||||
let mwrec_i = &mwrec[posi];
|
let mwrec_i = &mwrec[posi];
|
||||||
@ -985,7 +997,8 @@ fn erode(
|
|||||||
kf(posi)
|
kf(posi)
|
||||||
} * dt; */
|
} * dt; */
|
||||||
// let g_i = g(posi) as f64;
|
// 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
|
g_fs_mult_sed * g(posi) as f64
|
||||||
} else {
|
} else {
|
||||||
g(posi) as f64
|
g(posi) as f64
|
||||||
@ -1381,7 +1394,8 @@ fn erode(
|
|||||||
// let g_i = g(posi) as Compute;
|
// let g_i = g(posi) as Compute;
|
||||||
let old_b_i = b_i;
|
let old_b_i = b_i;
|
||||||
let sed = (h_t_i - old_b_i) as f64;
|
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
|
g_fs_mult_sed * g(posi) as Compute
|
||||||
} else {
|
} else {
|
||||||
g(posi) as Compute
|
g(posi) as Compute
|
||||||
@ -1463,6 +1477,7 @@ fn erode(
|
|||||||
} else {
|
} else {
|
||||||
// *is_done.at(posi) = done_val;
|
// *is_done.at(posi) = done_val;
|
||||||
let posj = posj as usize;
|
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);
|
// let dxy = (uniform_idx_as_vec2(posi) - uniform_idx_as_vec2(posj)).map(|e| e as f64);
|
||||||
|
|
||||||
// Has an outgoing flow edge (posi, posj).
|
// 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).
|
// 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.
|
// 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.
|
// 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 a_j = a[posj] as f64;
|
||||||
let wh_j = wh[posj] as f64;
|
let wh_j = wh[posj] as f64;
|
||||||
// let old_a_i = a[posi] 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.
|
// 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;
|
let mut dtherm = 0.0f64;
|
||||||
/* {
|
/* {
|
||||||
// Thermal erosion (landslide)
|
// Thermal erosion (landslide)
|
||||||
@ -1565,8 +1580,9 @@ fn erode(
|
|||||||
assert!(posj_stack > stacki); */
|
assert!(posj_stack > stacki); */
|
||||||
// This can happen in cases where receiver kk is neither uphill of
|
// This can happen in cases where receiver kk is neither uphill of
|
||||||
// nor downhill from posi's direct receiver.
|
// 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;
|
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 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];
|
/* 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);
|
assert!(flux.is_normal() && flux.is_positive() || flux == 0.0);
|
||||||
@ -1601,7 +1617,9 @@ fn erode(
|
|||||||
let mut mask = [MaskType::new(false); 8];
|
let mut mask = [MaskType::new(false); 8];
|
||||||
mrec_downhill(&mrec, posi).for_each(|(kk, posj)| {
|
mrec_downhill(&mrec, posi).for_each(|(kk, posj)| {
|
||||||
let posj_stack = mstack_inv[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[kk] = k_fs_fact[kk] as SimdType;
|
||||||
// k_fs_weights[max] = 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);
|
// /*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[max] = k_df_fact[kk] as SimdType;
|
||||||
// /*k_df_weights = */k_df_weights.replace(max, k_df_fact[kk]/*.extract(kk)*/ as f64);
|
// /*k_df_weights = */k_df_weights.replace(max, k_df_fact[kk]/*.extract(kk)*/ as f64);
|
||||||
mask[kk] = MaskType::new(true);
|
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[max] = h[posj] as SimdType;
|
||||||
// /*rec_heights = */rec_heights.replace(max, h[posj] as f64);
|
// /*rec_heights = */rec_heights.replace(max, h[posj] as f64);
|
||||||
// max += 1;
|
// max += 1;
|
||||||
@ -1928,7 +1946,7 @@ fn erode(
|
|||||||
|
|
||||||
// If we dipped below the receiver's water level, set our height to the receiver's
|
// If we dipped below the receiver's water level, set our height to the receiver's
|
||||||
// water level.
|
// 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 {
|
if compute_stats {
|
||||||
ncorr += 1;
|
ncorr += 1;
|
||||||
}
|
}
|
||||||
@ -1941,7 +1959,7 @@ fn erode(
|
|||||||
// will be set precisely equal to the estimated height, meaning it
|
// will be set precisely equal to the estimated height, meaning it
|
||||||
// effectively "vanishes" and just deposits sediment to its reciever.
|
// effectively "vanishes" and just deposits sediment to its reciever.
|
||||||
// (This is probably related to criteria for block Gauss-Seidel, etc.).
|
// (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;
|
// df_part = 0.0;
|
||||||
/* let lposj = lake_sill[posj];
|
/* let lposj = lake_sill[posj];
|
||||||
lake_sill[posi] = lposj;
|
lake_sill[posi] = lposj;
|
||||||
@ -2076,7 +2094,7 @@ fn erode(
|
|||||||
sums += mag_slope;
|
sums += mag_slope;
|
||||||
// Just use the computed rate.
|
// 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);
|
// println!("Delta: {:?} - {:?} = {:?}", new_h_i, h_p_i, new_h_i - h_p_i);
|
||||||
// a[posi] = df_part as Alt;
|
// a[posi] = df_part as Alt;
|
||||||
// Make sure to update the basement as well!
|
// Make sure to update the basement as well!
|
||||||
@ -2327,7 +2345,8 @@ fn erode(
|
|||||||
let kd_factor =
|
let kd_factor =
|
||||||
// 1.0;
|
// 1.0;
|
||||||
(1.0 / (max_slope / mid_slope/*.sqrt()*//*.powf(0.03125)*/).powf(/*2.0*/2.0))/*.min(kdsed)*/;
|
(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
|
// Sediment
|
||||||
kdsed /* * kd_factor*/
|
kdsed /* * kd_factor*/
|
||||||
} else {
|
} else {
|
||||||
@ -3386,6 +3405,10 @@ pub fn do_erosion(
|
|||||||
g: impl Fn(usize) -> f32 + Sync,
|
g: impl Fn(usize) -> f32 + Sync,
|
||||||
epsilon_0: impl Fn(usize) -> f32 + Sync,
|
epsilon_0: impl Fn(usize) -> f32 + Sync,
|
||||||
alpha: 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]>*/) {
|
) -> (Box<[Alt]>, Box<[Alt]> /*, Box<[Alt]>*/) {
|
||||||
log::debug!("Initializing erosion arrays...");
|
log::debug!("Initializing erosion arrays...");
|
||||||
let oldh_ = (0..WORLD_SIZE.x * WORLD_SIZE.y)
|
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
|
// 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
|
// on land, but in theory we probably want to at least differentiate between soil, bedrock, and
|
||||||
// sediment.
|
// 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 time_scale = 1.0; //1.0 / 4.0; // 4.0.powf(-n)
|
||||||
let mmaxh = CONFIG.mountain_scale as f64 * height_scale;
|
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;
|
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;
|
2.0e-5 * dt;
|
||||||
let kd_bedrock =
|
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) */
|
/*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 =
|
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 */;
|
/* * 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 kd = |posi: usize| kd_bedrock; // if is_ocean(posi) { /*0.0*/kd_bedrock } else { kd_bedrock };
|
||||||
let n = |posi: usize| n[posi];
|
let n = |posi: usize| n[posi];
|
||||||
let m = |posi: usize| m[posi];
|
let m = |posi: usize| m[posi];
|
||||||
@ -3499,6 +3524,8 @@ pub fn do_erosion(
|
|||||||
let g = |posi: usize| g[posi];
|
let g = |posi: usize| g[posi];
|
||||||
let epsilon_0 = |posi: usize| epsilon_0[posi];
|
let epsilon_0 = |posi: usize| epsilon_0[posi];
|
||||||
let alpha = |posi: usize| alpha[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.
|
// Hillslope diffusion coefficient for sediment.
|
||||||
let mut is_done = bitbox![0; WORLD_SIZE.x * WORLD_SIZE.y];
|
let mut is_done = bitbox![0; WORLD_SIZE.x * WORLD_SIZE.y];
|
||||||
(0..n_steps).for_each(|i| {
|
(0..n_steps).for_each(|i| {
|
||||||
@ -3529,6 +3556,8 @@ pub fn do_erosion(
|
|||||||
epsilon_0,
|
epsilon_0,
|
||||||
alpha,
|
alpha,
|
||||||
|posi| is_ocean(posi),
|
|posi| is_ocean(posi),
|
||||||
|
height_scale,
|
||||||
|
k_da_scale,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
(h, b /*, a*/)
|
(h, b /*, a*/)
|
||||||
|
@ -30,6 +30,7 @@ use crate::{
|
|||||||
CONFIG,
|
CONFIG,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
|
assets,
|
||||||
terrain::{BiomeKind, TerrainChunkSize},
|
terrain::{BiomeKind, TerrainChunkSize},
|
||||||
vol::RectVolSize,
|
vol::RectVolSize,
|
||||||
};
|
};
|
||||||
@ -113,6 +114,7 @@ pub(crate) struct GenCtx {
|
|||||||
pub uplift_nz: Worley,
|
pub uplift_nz: Worley,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
pub enum FileOpts {
|
pub enum FileOpts {
|
||||||
/// If set, generate the world map and do not try to save to or load from file
|
/// If set, generate the world map and do not try to save to or load from file
|
||||||
/// (default).
|
/// (default).
|
||||||
@ -120,8 +122,17 @@ pub enum FileOpts {
|
|||||||
/// If set, generate the world map and save the world file (path is created
|
/// If set, generate the world map and save the world file (path is created
|
||||||
/// the same way screenshot paths are).
|
/// the same way screenshot paths are).
|
||||||
Save,
|
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).
|
/// If set, load the world file from this path (errors if path not found).
|
||||||
Load(PathBuf),
|
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 {
|
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
|
/// LEGACY: Remove when people stop caring.
|
||||||
/// 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.
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct WorldFile {
|
#[repr(C)]
|
||||||
|
pub struct WorldFileLegacy {
|
||||||
/// Saved altitude height map.
|
/// Saved altitude height map.
|
||||||
pub alt: Box<[Alt]>,
|
pub alt: Box<[Alt]>,
|
||||||
/// Saved basement height map.
|
/// Saved basement height map.
|
||||||
pub basement: Box<[Alt]>,
|
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 struct WorldSim {
|
||||||
pub seed: u32,
|
pub seed: u32,
|
||||||
pub(crate) chunks: Vec<SimChunk>,
|
pub(crate) chunks: Vec<SimChunk>,
|
||||||
@ -291,8 +416,48 @@ impl WorldSim {
|
|||||||
let rock_strength_nz = ScaleBias::new(&rock_strength_nz)
|
let rock_strength_nz = ScaleBias::new(&rock_strength_nz)
|
||||||
.set_scale(1.0 / rock_strength_scale); */
|
.set_scale(1.0 / rock_strength_scale); */
|
||||||
|
|
||||||
let height_scale = 1.0f64; // 1.0 / CONFIG.mountain_scale as f64;
|
// Suppose the old world has grid spacing Δx' = Δy', new Δx = Δy.
|
||||||
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;
|
// 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_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_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;
|
let erosion_center = /*0.45*//*0.75*//*0.75*//*0.5*//*0.75*/0.5;
|
||||||
@ -531,15 +696,56 @@ impl WorldSim {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Calculate oceans.
|
// Calculate oceans.
|
||||||
let old_height =
|
let is_ocean = get_oceans(|posi: usize| alt_old[posi].1);
|
||||||
|posi: usize| alt_old[posi].1 * CONFIG.mountain_scale * height_scale as f32;
|
let is_ocean_fn = |posi: usize| is_ocean[posi];
|
||||||
/* let is_ocean = (0..WORLD_SIZE.x * WORLD_SIZE.y)
|
/* let is_ocean = (0..WORLD_SIZE.x * WORLD_SIZE.y)
|
||||||
.into_par_iter()
|
.into_par_iter()
|
||||||
.map(|i| map_edge_factor(i) == 0.0)
|
.map(|i| map_edge_factor(i) == 0.0)
|
||||||
.collect::<Vec<_>>(); */
|
.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 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);
|
let uplift_nz_dist = gen_ctx.uplift_nz.clone().enable_range(true);
|
||||||
// Recalculate altitudes without oceans.
|
// 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(
|
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)*/;
|
).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 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 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 theta_func = |posi| 0.4;
|
let theta_func = |posi| 0.4;
|
||||||
let kf_func = {
|
let kf_func = {
|
||||||
|posi| {
|
|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);
|
// let precip_mul = (0.25).powf(m);
|
||||||
if is_ocean_fn(posi) {
|
if is_ocean_fn(posi) {
|
||||||
// multiplied by height_scale^(2m) to account for change in area.
|
// 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-5;
|
||||||
// return 2.0e-6;
|
// return 2.0e-6;
|
||||||
// return 2.0e-10;
|
// 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 = /*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);
|
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 = 1e-4: high (plateau)
|
||||||
// kf = 2e-5: normal (dike [unexposed])
|
// kf = 2e-5: normal (dike [unexposed])
|
||||||
// kf = 1e-6: normal-low (dike [exposed])
|
// 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)
|
// ((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
|
// 2e-5
|
||||||
// multiplied by height_scale^(2m) to account for change in area.
|
// 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
|
// 2.9e-10
|
||||||
// ((1.0 - uheight) * (5e-5 - 2.9e-10) + 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)
|
// ((1.0 - uheight) * (5e-5 - 2.9e-14) + 2.9e-14)
|
||||||
|
;
|
||||||
|
kf_i * kf_scale_i
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let kd_func = {
|
let kd_func = {
|
||||||
|posi| {
|
|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) {
|
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)
|
let wposf = (uniform_idx_as_vec2(posi)
|
||||||
* TerrainChunkSize::RECT_SIZE.map(|e| e as i32))
|
* TerrainChunkSize::RECT_SIZE.map(|e| e as i32))
|
||||||
@ -846,13 +1022,25 @@ impl WorldSim {
|
|||||||
.max(-1.0)
|
.max(-1.0)
|
||||||
.mul(0.5)
|
.mul(0.5)
|
||||||
.add(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 = 1e-1: high (mountain, dike)
|
||||||
// kd = 1.5e-2: normal-high (plateau [fan sediment])
|
// kd = 1.5e-2: normal-high (plateau [fan sediment])
|
||||||
// kd = 1e-2: normal (plateau)
|
// kd = 1e-2: normal (plateau)
|
||||||
// multiplied by height_scale² to account for change in area, then divided by
|
// multiplied by height_scale² to account for change in area, then divided by
|
||||||
// time_scale to account for lower dt.
|
// 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)
|
// (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| {
|
let g_func = |posi| {
|
||||||
@ -922,10 +1110,9 @@ impl WorldSim {
|
|||||||
// 1.5
|
// 1.5
|
||||||
};
|
};
|
||||||
let epsilon_0_func = |posi| {
|
let epsilon_0_func = |posi| {
|
||||||
let n_i = n_func(posi);
|
// epsilon_0_scale is roughly [using Hack's Law with b = 2 and SPL without debris flow or
|
||||||
// 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.
|
// 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) {
|
if is_ocean_fn(posi) {
|
||||||
// marine: ε₀ = 2.078e-3
|
// marine: ε₀ = 2.078e-3
|
||||||
// divide by height scale, multiplied by time_scale, cancels out to 1; idea is that
|
// 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'
|
// ε₀ 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;
|
// return 5.0;
|
||||||
}
|
}
|
||||||
let wposf = (uniform_idx_as_vec2(posi) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32))
|
let wposf = (uniform_idx_as_vec2(posi) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32))
|
||||||
@ -980,6 +1171,9 @@ impl WorldSim {
|
|||||||
.max(-1.0)
|
.max(-1.0)
|
||||||
.mul(0.5)
|
.mul(0.5)
|
||||||
.add(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(
|
let wposf3 = Vec3::new(
|
||||||
wposf.x,
|
wposf.x,
|
||||||
wposf.y,
|
wposf.y,
|
||||||
@ -994,9 +1188,9 @@ impl WorldSim {
|
|||||||
.max(-1.0)
|
.max(-1.0)
|
||||||
.mul(0.5)
|
.mul(0.5)
|
||||||
.add(0.5);
|
.add(0.5);
|
||||||
let center = /*0.25*/0.4 / 4.0;
|
let center = /*0.25*/0.4;
|
||||||
let dmin = center - /*0.15;//0.05*/0.05 / 4.0;
|
let dmin = center - /*0.15;//0.05*/0.05;
|
||||||
let dmax = center + /*0.05*//*0.10*/0.05 / 4.0; //0.05;
|
let dmax = center + /*0.05*//*0.10*/0.05; //0.05;
|
||||||
let log_odds = |x: f64| logit(x) - logit(center);
|
let log_odds = |x: f64| logit(x) - logit(center);
|
||||||
let ustrength = logistic_cdf(
|
let ustrength = logistic_cdf(
|
||||||
1.0 * logit(rock_strength.min(1.0f64 - 1e-7).max(1e-7))
|
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
|
// Nunnock River (fractured granite, least weathered?): ε₀ = 5.3e-5
|
||||||
// The stronger the rock, the lower the production rate of exposed bedrock.
|
// The stronger the rock, the lower the production rate of exposed bedrock.
|
||||||
// divide by height scale, then multiplied by time_scale, cancels out.
|
// 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*/
|
/* * time_scale*/
|
||||||
// 0.0
|
// 0.0
|
||||||
|
;
|
||||||
|
epsilon_0_i * epsilon_0_scale_i
|
||||||
};
|
};
|
||||||
let alpha_func = |posi| {
|
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
|
// 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.
|
// 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 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
|
// the rate of rate of change in soil production consistent we must divide H by
|
||||||
// height_scale.
|
// height_scale.
|
||||||
//
|
//
|
||||||
// αH = α(H' * height_scale) = α'H'
|
// αH = α(H' * height_scale) = α'H'
|
||||||
// α = α' / height_scale
|
// α = α' / 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))
|
let wposf = (uniform_idx_as_vec2(posi) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32))
|
||||||
.map(|e| e as f64);
|
.map(|e| e as f64);
|
||||||
@ -1055,6 +1252,9 @@ impl WorldSim {
|
|||||||
.max(-1.0)
|
.max(-1.0)
|
||||||
.mul(0.5)
|
.mul(0.5)
|
||||||
.add(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(
|
let wposf3 = Vec3::new(
|
||||||
wposf.x,
|
wposf.x,
|
||||||
wposf.y,
|
wposf.y,
|
||||||
@ -1069,9 +1269,9 @@ impl WorldSim {
|
|||||||
.max(-1.0)
|
.max(-1.0)
|
||||||
.mul(0.5)
|
.mul(0.5)
|
||||||
.add(0.5);
|
.add(0.5);
|
||||||
let center = /*0.25*/0.4 / 4.0;
|
let center = /*0.25*/0.4;
|
||||||
let dmin = center - /*0.15;//0.05*/0.05 / 4.0;
|
let dmin = center - /*0.15;//0.05*/0.05;
|
||||||
let dmax = center + /*0.05*//*0.10*/0.05 / 4.0; //0.05;
|
let dmax = center + /*0.05*//*0.10*/0.05; //0.05;
|
||||||
let log_odds = |x: f64| logit(x) - logit(center);
|
let log_odds = |x: f64| logit(x) - logit(center);
|
||||||
let ustrength = logistic_cdf(
|
let ustrength = logistic_cdf(
|
||||||
1.0 * logit(rock_strength.min(1.0f64 - 1e-7).max(1e-7))
|
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
|
// Nunnock river (fractured granite, least weathered?): α = 2e-3
|
||||||
// Point Reyes: α = 1.6e-2
|
// Point Reyes: α = 1.6e-2
|
||||||
// The stronger the rock, the faster the decline in soil production.
|
// The stronger the rock, the faster the decline in soil production.
|
||||||
// divided by height_scale.
|
let alpha_i = (ustrength * (4.2e-2 - 1.6e-2) + 1.6e-2) as f32;
|
||||||
(ustrength * (4.2e-2 - 1.6e-2) + 1.6e-2) as f32 / height_scale
|
alpha_i * alpha_scale_i
|
||||||
};
|
};
|
||||||
let uplift_fn = |posi| {
|
let uplift_fn = |posi| {
|
||||||
if is_ocean_fn(posi) {
|
if is_ocean_fn(posi) {
|
||||||
@ -1304,9 +1504,30 @@ impl WorldSim {
|
|||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let is_ocean_fn = |posi: usize| is_ocean[posi]; */
|
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 = (|| {
|
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) {
|
let file = match File::open(path) {
|
||||||
Ok(file) => file,
|
Ok(file) => file,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@ -1319,31 +1540,50 @@ impl WorldSim {
|
|||||||
let map: WorldFile = match bincode::deserialize_from(reader) {
|
let map: WorldFile = match bincode::deserialize_from(reader) {
|
||||||
Ok(map) => map,
|
Ok(map) => map,
|
||||||
Err(err) => {
|
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;
|
return None;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if map.alt.len() != map.basement.len()
|
map.into_modern()
|
||||||
|| map.alt.len() != WORLD_SIZE.x as usize * WORLD_SIZE.y as usize
|
}
|
||||||
{
|
FileOpts::LoadAsset(ref specifier) => {
|
||||||
log::warn!("World size of map is invalid.");
|
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;
|
return None;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/* let f = |h| h;// / 4.0;
|
let map: WorldFile = match bincode::deserialize_from(reader) {
|
||||||
let mut map = map;
|
Ok(map) => map,
|
||||||
map.alt.par_iter_mut()
|
Err(err) => {
|
||||||
.zip(map.basement.par_iter_mut())
|
log::warn!("Couldn't parse modern map: {:?}). Maybe you meant to try a legacy load?", err);
|
||||||
.for_each(|(mut h, mut b)| {
|
return None;
|
||||||
*h = f(*h);
|
}
|
||||||
*b = f(*b);
|
};
|
||||||
}); */
|
|
||||||
|
|
||||||
Some(map)
|
map.into_modern()
|
||||||
} else {
|
}
|
||||||
|
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
|
None
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
let (alt, basement /*, alluvium*/) = if let Some(map) = parsed_world_file {
|
let (alt, basement /*, alluvium*/) = if let Some(map) = parsed_world_file {
|
||||||
@ -1358,7 +1598,9 @@ impl WorldSim {
|
|||||||
max_erosion_per_delta_t as f32,
|
max_erosion_per_delta_t as f32,
|
||||||
n_steps,
|
n_steps,
|
||||||
&river_seed,
|
&river_seed,
|
||||||
|
// varying conditions
|
||||||
&rock_strength_nz,
|
&rock_strength_nz,
|
||||||
|
// initial conditions
|
||||||
|posi| alt_func(posi), // + if is_ocean_fn(posi) { 0.0 } else { 128.0 },
|
|posi| alt_func(posi), // + if is_ocean_fn(posi) { 0.0 } else { 128.0 },
|
||||||
|posi| {
|
|posi| {
|
||||||
alt_func(posi)
|
alt_func(posi)
|
||||||
@ -1371,6 +1613,7 @@ impl WorldSim {
|
|||||||
}, // if is_ocean_fn(posi) { old_height(posi) } else { 0.0 },
|
}, // if is_ocean_fn(posi) { old_height(posi) } else { 0.0 },
|
||||||
// |posi| 0.0,
|
// |posi| 0.0,
|
||||||
is_ocean_fn,
|
is_ocean_fn,
|
||||||
|
// empirical constants
|
||||||
uplift_fn,
|
uplift_fn,
|
||||||
|posi| n_func(posi),
|
|posi| n_func(posi),
|
||||||
|posi| theta_func(posi),
|
|posi| theta_func(posi),
|
||||||
@ -1379,12 +1622,16 @@ impl WorldSim {
|
|||||||
|posi| g_func(posi),
|
|posi| g_func(posi),
|
||||||
|posi| epsilon_0_func(posi),
|
|posi| epsilon_0_func(posi),
|
||||||
|posi| alpha_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.
|
// Quick "small scale" erosion cycle in order to lower extreme angles.
|
||||||
do_erosion(
|
do_erosion(
|
||||||
0.0,
|
0.0,
|
||||||
(1.0 * height_scale) as f32,
|
(1.0/* * height_scale*/) as f32,
|
||||||
n_small_steps,
|
n_small_steps,
|
||||||
&river_seed,
|
&river_seed,
|
||||||
&rock_strength_nz,
|
&rock_strength_nz,
|
||||||
@ -1392,7 +1639,7 @@ impl WorldSim {
|
|||||||
|posi| basement[posi] as f32,
|
|posi| basement[posi] as f32,
|
||||||
// |posi| /*alluvium[posi] as f32*/0.0f32,
|
// |posi| /*alluvium[posi] as f32*/0.0f32,
|
||||||
is_ocean_fn,
|
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| n_func(posi),
|
||||||
|posi| theta_func(posi),
|
|posi| theta_func(posi),
|
||||||
|posi| kf_func(posi),
|
|posi| kf_func(posi),
|
||||||
@ -1400,11 +1647,15 @@ impl WorldSim {
|
|||||||
|posi| g_func(posi),
|
|posi| g_func(posi),
|
||||||
|posi| epsilon_0_func(posi),
|
|posi| epsilon_0_func(posi),
|
||||||
|posi| alpha_func(posi),
|
|posi| alpha_func(posi),
|
||||||
|
|n| height_scale(n),
|
||||||
|
k_d_scale(n_approx),
|
||||||
|
|q| k_da_scale(q),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Save map, if necessary.
|
// 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 {
|
if let FileOpts::Save = opts.world_file {
|
||||||
use std::time::SystemTime;
|
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.
|
// Additional small-scale eroson after map load, only used during testing.
|
||||||
let (alt, basement /*, alluvium*/) = if n_post_load_steps == 0 {
|
let (alt, basement /*, alluvium*/) = if n_post_load_steps == 0 {
|
||||||
@ -1446,7 +1700,7 @@ impl WorldSim {
|
|||||||
} else {
|
} else {
|
||||||
do_erosion(
|
do_erosion(
|
||||||
0.0,
|
0.0,
|
||||||
(1.0 * height_scale) as f32,
|
(1.0/* * height_scale*/) as f32,
|
||||||
n_post_load_steps,
|
n_post_load_steps,
|
||||||
&river_seed,
|
&river_seed,
|
||||||
&rock_strength_nz,
|
&rock_strength_nz,
|
||||||
@ -1454,7 +1708,7 @@ impl WorldSim {
|
|||||||
|posi| basement[posi] as f32,
|
|posi| basement[posi] as f32,
|
||||||
// |posi| alluvium[posi] as f32,
|
// |posi| alluvium[posi] as f32,
|
||||||
is_ocean_fn,
|
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| n_func(posi),
|
||||||
|posi| theta_func(posi),
|
|posi| theta_func(posi),
|
||||||
|posi| kf_func(posi),
|
|posi| kf_func(posi),
|
||||||
@ -1462,6 +1716,9 @@ impl WorldSim {
|
|||||||
|posi| g_func(posi),
|
|posi| g_func(posi),
|
||||||
|posi| epsilon_0_func(posi),
|
|posi| epsilon_0_func(posi),
|
||||||
|posi| alpha_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!");
|
panic!("Halp!");
|
||||||
} */
|
} */
|
||||||
|
|
||||||
let height_scale = 1.0; // 1.0 / CONFIG.mountain_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 alt = CONFIG.sea_level.add(alt_pre /*.div(height_scale)*/);
|
||||||
let mut basement = CONFIG.sea_level.add(basement_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 water_alt = CONFIG.sea_level.add(water_alt_pre /*.div(height_scale)*/);
|
||||||
let downhill = if downhill_pre == -2 {
|
let downhill = if downhill_pre == -2 {
|
||||||
None
|
None
|
||||||
} else if downhill_pre < 0 {
|
} else if downhill_pre < 0 {
|
||||||
|
Loading…
Reference in New Issue
Block a user