From 680b81a5e60dd3155bc43c66fc7eb133b5d35897 Mon Sep 17 00:00:00 2001 From: Joshua Yanovski Date: Sun, 18 Aug 2019 18:35:27 +0200 Subject: [PATCH] WIP for humidity. --- world/src/all.rs | 1 + world/src/block/natural.rs | 16 ++++++ world/src/config.rs | 6 +++ world/src/sim/mod.rs | 107 +++++++++++++++++++++++++++++++++---- 4 files changed, 119 insertions(+), 11 deletions(-) diff --git a/world/src/all.rs b/world/src/all.rs index f687672ccb..6226c9df44 100644 --- a/world/src/all.rs +++ b/world/src/all.rs @@ -5,4 +5,5 @@ pub enum ForestKind { Oak, Pine, SnowPine, + Mangrove, } diff --git a/world/src/block/natural.rs b/world/src/block/natural.rs index 9360e87941..f23eb40ede 100644 --- a/world/src/block/natural.rs +++ b/world/src/block/natural.rs @@ -58,6 +58,7 @@ pub fn structure_gen<'a>( ForestKind::Oak => &OAKS, ForestKind::Pine => &PINES, ForestKind::SnowPine => &SNOW_PINES, + ForestKind::Mangrove => &MANGROVE_TREES, } }; @@ -421,6 +422,19 @@ lazy_static! { ]; */ + pub static ref MANGROVE_TREES: Vec> = vec![ + // oak stumps + assets::load_map("world.tree.mangrove.1", |s: Structure| s + .with_center(Vec3::new(15, 18, 10))) + .unwrap(), + assets::load_map("world.tree.mangrove.2", |s: Structure| s + .with_center(Vec3::new(15, 18, 10))) + .unwrap(), + assets::load_map("world.tree.mangrove.3", |s: Structure| s + .with_center(Vec3::new(15, 188888888, 10))) + .unwrap(), + ]; + pub static ref QUIRKY: Vec> = vec![ st_asset("world.structure.natural.tower-ruin", (11, 14, 5)), st_asset("world.structure.natural.witch-hut", (10, 13, 9)), @@ -431,4 +445,6 @@ lazy_static! { st_asset("world.structure.natural.ribcage-large", (13, 19, 8)), st_asset("world.structure.natural.skull-large", (15, 20, 4)), ]; + + } diff --git a/world/src/config.rs b/world/src/config.rs index fc3199d0de..4c2252e1b7 100644 --- a/world/src/config.rs +++ b/world/src/config.rs @@ -4,6 +4,9 @@ pub struct Config { pub snow_temp: f32, pub tropical_temp: f32, pub desert_temp: f32, + pub desert_hum: f32, + pub forest_hum: f32, + pub jungle_hum: f32, } pub const CONFIG: Config = Config { @@ -12,4 +15,7 @@ pub const CONFIG: Config = Config { snow_temp: -0.4, tropical_temp: 0.25, desert_temp: 0.45, + desert_hum: 0.1, + forest_hum: 0.4, + jungle_hum: 0.7, }; diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index 5c22d573c4..f6f05b207f 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -14,10 +14,13 @@ use common::{ terrain::{BiomeKind, TerrainChunkSize}, vol::VolSize, }; -use noise::{BasicMulti, HybridMulti, MultiFractal, NoiseFn, RidgedMulti, Seedable, SuperSimplex}; +use noise::{BasicMulti, Billow, HybridMulti, MultiFractal, NoiseFn, RidgedMulti, Seedable, SuperSimplex}; use rand::{Rng, SeedableRng}; use rand_chacha::ChaChaRng; -use std::ops::{Add, Div, Mul, Neg, Sub}; +use std::{ + f32, + ops::{Add, Div, Mul, Neg, Sub}, +}; use vek::*; pub const WORLD_SIZE: Vec2 = Vec2 { x: 1024, y: 1024 }; @@ -29,7 +32,10 @@ pub(crate) struct GenCtx { pub alt_nz: HybridMulti, pub hill_nz: SuperSimplex, pub temp_nz: SuperSimplex, + // Fresh groundwater (currently has no effect, but should influence humidity) pub dry_nz: BasicMulti, + // Humidity noise + pub humid_nz : Billow, pub small_nz: BasicMulti, pub rock_nz: HybridMulti, pub cliff_nz: HybridMulti, @@ -72,6 +78,7 @@ impl WorldSim { .set_seed(gen_seed()), temp_nz: SuperSimplex::new().set_seed(gen_seed()), dry_nz: BasicMulti::new().set_seed(gen_seed()), + humid_nz: Billow::new().set_seed(gen_seed()), small_nz: BasicMulti::new().set_octaves(2).set_seed(gen_seed()), rock_nz: HybridMulti::new().set_persistence(0.3).set_seed(gen_seed()), cliff_nz: HybridMulti::new().set_persistence(0.3).set_seed(gen_seed()), @@ -304,6 +311,7 @@ pub struct SimChunk { pub alt: f32, pub temp: f32, pub dryness: f32, + pub humidity: f32, pub rockiness: f32, pub is_cliffs: bool, pub near_cliffs: bool, @@ -345,6 +353,9 @@ impl SimChunk { let temp = gen_ctx.temp_nz.get((wposf.div(12000.0)).into_array()) as f32; + // FIXME: Currently unused, but should represent fresh groundwater level. + // Should be correlated a little with humidity, somewhat negatively with altitude, + // and very negatively with difference in temperature from zero. let dryness = gen_ctx.dry_nz.get( (wposf .add(Vec2::new( @@ -393,8 +404,7 @@ impl SimChunk { .max(0.0) .min(1.0); - let alt = (CONFIG.sea_level - + alt_base + let alt_pre = alt_base + (0.0 + alt_main + (gen_ctx.small_nz.get((wposf.div(300.0)).into_array()) as f32) @@ -402,9 +412,49 @@ impl SimChunk { .mul(1.6)) .add(1.0) .mul(0.5) - .mul(chaos) - .mul(CONFIG.mountain_scale)) - * map_edge_factor; + .mul(chaos); + + let alt = (CONFIG.sea_level + alt_pre.mul(CONFIG.mountain_scale)) * map_edge_factor; + + // 0 to 1, hopefully. + let humid_base = + (gen_ctx.humid_nz.get(wposf.div(1024.0).into_array()) as f32) + .add(1.0) + .mul(0.5) + as f32; + + // Ideally, humidity is correlated negatively with altitude and slightly positively with + // dryness. For now we just do "negatively with altitude." We currently opt not to have + // it affected by temperature. Negative humidity is lower, positive humidity is higher. + // + // Because we want to start at 0, rise, and then saturate at 1, we use a cumulative logistic + // distribution, calculated as: + // 1/2 + 1/2 * tanh((x - μ) / (2s)) + // + // where x is the random variable (altitude relative to sea level without mountain + // scaling), μ is the altitude where humidity should be at its midpoint (currently set to 1), + // and s is the scale parameter proportional to the standard deviation σ of the humidity + // function of altitude (s = √3/π * σ). Currently we also set σ to -1, so we get ~ 68% of + // the variation due to altitude between sea level and 2 * mountain_scale (it is negative + // to make the distribution higher when the altitude is lower). + let humid_alt_sigma = -1.0; + let humid_alt_2s = 3.0f32.sqrt().mul(f32::consts::FRAC_2_PI).mul(humid_alt_sigma); + let humid_alt_mu = 1.0; + let humid_alt = alt_pre + .sub(humid_alt_mu) + .div(humid_alt_2s) + .atanh() + .mul(0.5) + .add(0.5); + // Now we just take a (currently) unweighted average of our randomly generated base humidity + // (from scaled to be from 0 to 1) and our randomly generated "base" humidity. We can + // adjust this weighting factor as desired. + let humid_weight = 1.0; + let humid_alt_weight = 1.0; + let humidity = + humid_base.mul(humid_weight) + .add(humid_alt.mul(humid_alt_weight)) + .div(humid_weight + humid_alt_weight); let cliff = gen_ctx.cliff_nz.get((wposf.div(2048.0)).into_array()) as f32 + chaos * 0.2; @@ -414,6 +464,7 @@ impl SimChunk { alt, temp, dryness, + humidity, rockiness: (gen_ctx.rock_nz.get((wposf.div(1024.0)).into_array()) as f32) .sub(0.1) .mul(1.3) @@ -429,7 +480,8 @@ impl SimChunk { .mul(0.5) .mul(1.2 - chaos * 0.95) .add(0.05) - .mul(if alt > CONFIG.sea_level + 5.0 { + // No trees in the ocean (currently), no trees in true deserts. + .mul(if alt > CONFIG.sea_level + 5.0 && humidity > CONFIG.desert_hum { 1.0 } else { 0.0 @@ -437,13 +489,46 @@ impl SimChunk { .max(0.0), forest_kind: if temp > 0.0 { if temp > CONFIG.desert_temp { - ForestKind::Palm + if humidity > CONFIG.jungle_hum { + // Forests in desert temperatures with extremely high humidity + // should probably be different from palm trees, but we use them + // for now. + ForestKind::Palm + } else if humidity > CONFIG.forest_hum { + ForestKind::Palm + } else { + // Low but not desert humidity, so we should really have some other + // terrain... + ForestKind::Savannah + } } else if temp > CONFIG.tropical_temp { - ForestKind::Savannah + if humidity > CONFIG.jungle_hum { + ForestKind::Mangrove + } else if humidity > CONFIG.forest_hum { + // NOTE: Probably the wrong kind of tree for this climtae. + ForestKind::Oak + } else { + ForestKind::Savannah + } } else { - ForestKind::Oak + if humidity > CONFIG.jungle_hum { + // Temperate climate with jungle humidity... + // https://en.wikipedia.org/wiki/Humid_subtropical_climates are often + // densely wooded and full of water. Semitropical rainforests, basically. + // For now we just treet them like otehr rainforests. + ForestKind::Mangrove + } else if humidity > CONFIG.forest_hum { + // Moderate climate, moderate humidity. + ForestKind::Oak + } else { + // With moderate temperature and low humidity, we should probably see + // something different from savannah, but oh well... + ForestKind::Savannah + } } } else { + // For now we don't take humidity into account for cold climates (but we really + // should!). if temp > CONFIG.snow_temp { ForestKind::Pine } else {