mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Distribution "fun."
This commit is contained in:
parent
405f55d725
commit
8c644e2bb1
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -2814,6 +2814,14 @@ name = "static_assertions"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "statrs"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stb_truetype"
|
||||
version = "0.2.6"
|
||||
@ -3172,6 +3180,7 @@ dependencies = [
|
||||
"noise 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"statrs 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"vek 0.9.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"veloren-common 0.3.0",
|
||||
"zerocopy 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -3695,6 +3704,7 @@ dependencies = [
|
||||
"checksum sphynx 0.1.0 (git+https://gitlab.com/veloren/sphynx.git?rev=11cdc7422568aaabd376c87242a60f636e68b40d)" = "<none>"
|
||||
"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"
|
||||
"checksum static_assertions 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c19be23126415861cb3a23e501d34a708f7f9b2183c5252d690941c2e69199d5"
|
||||
"checksum statrs 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "835a1669368b3524d9214cfcb274a1f5e3b0820b5f275c1509bfb0cc67664636"
|
||||
"checksum stb_truetype 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "69b7df505db8e81d54ff8be4693421e5b543e08214bd8d99eb761fcb4d5668ba"
|
||||
"checksum stdweb 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ef5430c8e36b713e13b48a9f709cc21e046723fe44ce34587b73a830203b533e"
|
||||
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
@ -13,6 +13,7 @@ lazy_static = "1.3.0"
|
||||
rand = "0.7.0"
|
||||
rand_chacha = "0.2.1"
|
||||
zerocopy = "0.2.8"
|
||||
statrs = "0.11.0"
|
||||
|
||||
[dev-dependencies]
|
||||
minifb = { git = "https://github.com/emoon/rust_minifb.git" }
|
||||
|
@ -259,7 +259,8 @@ impl<'a> Sampler for ColumnGen<'a> {
|
||||
let cliff = Rgb::lerp(cold_stone, warm_stone, marble);
|
||||
|
||||
let grass = Rgb::lerp(cold_grass, warm_grass, marble.powf(1.5).powf(1.0.sub(humidity)));
|
||||
let moss = Rgb::lerp(cold_grass, dark_grass, marble.powf(1.5).powf(1.0.sub(humidity)));
|
||||
let snow_moss = Rgb::lerp(cold_grass, dark_grass, marble.powf(1.5).powf(temp));
|
||||
let moss = Rgb::lerp(dark_grass, cold_grass, marble.powf(1.5).powf(1.0.sub(humidity)));
|
||||
let rainforest = Rgb::lerp(wet_grass, warm_grass, marble.powf(1.5).powf(1.0.sub(humidity)));
|
||||
let sand = Rgb::lerp(beach_sand, desert_sand, marble);
|
||||
|
||||
@ -269,9 +270,18 @@ impl<'a> Sampler for ColumnGen<'a> {
|
||||
marble_small.sub(0.5).mul(0.2).add(0.75).powf(0.667).powf(1.0.sub(humidity)),
|
||||
);
|
||||
|
||||
// For below desert humidity, we are always sand or rock, depending on altitude.
|
||||
let ground = Rgb::lerp(sand, cliff, alt.sub(CONFIG.mountain_scale * 0.25).div(CONFIG.mountain_scale * 0.125));
|
||||
// From desert to forest humidity, we go from tundra to moss to grass to moss to sand,
|
||||
// For below desert humidity, we are always sand or rock, depending on altitude and
|
||||
// temperature.
|
||||
let ground = Rgb::lerp(
|
||||
Rgb::lerp(
|
||||
sand,
|
||||
dirt,
|
||||
temp.sub(CONFIG.desert_temp).mul(0.5)
|
||||
),
|
||||
cliff,
|
||||
alt.sub(CONFIG.mountain_scale * 0.25).div(CONFIG.mountain_scale * 0.125)
|
||||
);
|
||||
// From desert to forest humidity, we go from tundra to dirt to grass to moss to sand,
|
||||
// depending on temperature.
|
||||
let ground = Rgb::lerp(
|
||||
ground,
|
||||
@ -282,10 +292,12 @@ impl<'a> Sampler for ColumnGen<'a> {
|
||||
// below snow_temp
|
||||
tundra,
|
||||
// snow_temp to 0
|
||||
moss,
|
||||
temp.sub(CONFIG.snow_temp)/*.div(CONFIG.snow_temp.neg())*/
|
||||
.sub((marble - 0.5) * 0.05)
|
||||
.mul(256.0)
|
||||
dirt,
|
||||
temp.sub(CONFIG.snow_temp)
|
||||
.div(CONFIG.snow_temp.neg())
|
||||
/*.sub((marble - 0.5) * 0.05)
|
||||
.mul(256.0)*/
|
||||
.mul(1.0)
|
||||
),
|
||||
// 0 to tropical_temp
|
||||
grass,
|
||||
@ -305,7 +317,7 @@ impl<'a> Sampler for ColumnGen<'a> {
|
||||
.div(CONFIG.forest_hum.sub(CONFIG.desert_hum))
|
||||
.mul(1.0)
|
||||
);
|
||||
// From forest to jungle humidity, we go from snow to moss to grass to tropics to sand
|
||||
// From forest to jungle humidity, we go from snow to dark grass to grass to tropics to sand
|
||||
// depending on temperature.
|
||||
let ground = Rgb::lerp(
|
||||
ground,
|
||||
@ -316,7 +328,7 @@ impl<'a> Sampler for ColumnGen<'a> {
|
||||
// below snow_temp
|
||||
snow,
|
||||
// snow_temp to 0
|
||||
moss,
|
||||
snow_moss,
|
||||
temp.sub(CONFIG.snow_temp)/*.div(CONFIG.snow_temp.neg())*/
|
||||
.sub((marble - 0.5) * 0.05)
|
||||
.mul(256.0)
|
||||
|
@ -13,9 +13,9 @@ pub const CONFIG: Config = Config {
|
||||
sea_level: 140.0,
|
||||
mountain_scale: 1000.0,
|
||||
snow_temp: -0.4,
|
||||
tropical_temp: 0.25,
|
||||
tropical_temp: 0.2,
|
||||
desert_temp: 0.45,
|
||||
desert_hum: 0.35,
|
||||
desert_hum: 0.2,
|
||||
forest_hum: 0.5,
|
||||
jungle_hum: 0.6,
|
||||
jungle_hum: 0.8,
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![deny(unsafe_code)]
|
||||
#![feature(euclidean_division, bind_by_move_pattern_guards, option_flattening)]
|
||||
#![feature(const_generics, euclidean_division, bind_by_move_pattern_guards, option_flattening)]
|
||||
|
||||
mod all;
|
||||
mod block;
|
||||
|
@ -17,6 +17,13 @@ use common::{
|
||||
use noise::{BasicMulti, Billow, HybridMulti, MultiFractal, NoiseFn, RidgedMulti, Seedable, SuperSimplex};
|
||||
use rand::{Rng, SeedableRng};
|
||||
use rand_chacha::ChaChaRng;
|
||||
use statrs::distribution::{
|
||||
InverseGamma,
|
||||
LogNormal,
|
||||
Gamma,
|
||||
Normal,
|
||||
Univariate,
|
||||
};
|
||||
use std::{
|
||||
f32,
|
||||
ops::{Add, Div, Mul, Neg, Sub},
|
||||
@ -25,6 +32,34 @@ use vek::*;
|
||||
|
||||
pub const WORLD_SIZE: Vec2<usize> = Vec2 { x: 1024, y: 1024 };
|
||||
|
||||
/// Computes the cumulative distribution function of the weighted sum of k independent,
|
||||
/// uniformly distributed random variables between 0 and 1. For each variable i, we use weights[i]
|
||||
/// as the weight to give samples[i] (the weights should all be positive).
|
||||
///
|
||||
/// If the precondition is met, the distribution of the result of calling this function will be
|
||||
/// uniformly distributed while preserving the same information that was in the original average.
|
||||
///
|
||||
/// NOTE: For N > 33 the function will no longer return correct results since we will overflow u32.
|
||||
fn cdf_irwin_hall<const N : usize>(weights: &[f32; N], samples: [f32; N]) -> f32 {
|
||||
// Take the average of the weights
|
||||
// (to scale the weights down so their sum is in the (0..=N) range).
|
||||
let avg = weights.iter().sum::<f32>() / N as f32;
|
||||
// Take the sum.
|
||||
let x : f32 =
|
||||
weights.iter().zip(samples.iter()).map(|(weight, sample)| weight / avg * sample).sum();
|
||||
// CDF = 1 / N! * Σ{k = 0 to floor(x)} ((-1)^k (N choose k) (x - k) ^ N)
|
||||
let mut binom = 1; // (-1)^0 * (n choose 0) = 1 * 1 = 1
|
||||
let mut y = x.powi(N as i32); // 1 * (x - 0)^N = x ^N
|
||||
// 1..floor(x)
|
||||
for k in (1..=x.floor() as i32) {
|
||||
// (-1)^k (N choose k) = ((-1)^(k-1) (N choose (k - 1))) * -(N + 1 - k) / k for k ≥ 1.
|
||||
binom *= -(N as i32 + 1 - k) / k;
|
||||
y += binom as f32 * (x - k as f32).powi(N as i32);
|
||||
}
|
||||
// Remember to multiply by 1 / N! at the end.
|
||||
y / (1..=N as i32).product::<i32>() as f32
|
||||
}
|
||||
|
||||
pub(crate) struct GenCtx {
|
||||
pub turb_x_nz: SuperSimplex,
|
||||
pub turb_y_nz: SuperSimplex,
|
||||
@ -379,13 +414,16 @@ impl SimChunk {
|
||||
// "Base" of the chunk, to be multiplied by CONFIG.mountain_scale (multiplied value is
|
||||
// from -0.25 * (CONFIG.mountain_scale * 1.1) to 0.25 * (CONFIG.mountain_scale * 0.9),
|
||||
// but value here is from -0.275 to 0.225).
|
||||
let alt_base = (gen_ctx.alt_nz.get((wposf.div(12_000.0)).into_array()) as f32)
|
||||
let alt_base_pre = (gen_ctx.alt_nz.get((wposf.div(12_000.0)).into_array()) as f32);
|
||||
|
||||
let alt_base = alt_base_pre
|
||||
.sub(0.1)
|
||||
.mul(0.25);
|
||||
|
||||
// Extension upwards from the base. A positive number from 0 to 1 curved to be maximal at
|
||||
// 0.
|
||||
let alt_main = (gen_ctx.alt_nz.get((wposf.div(2_000.0)).into_array()) as f32)
|
||||
let alt_main_pre = (gen_ctx.alt_nz.get((wposf.div(2_000.0)).into_array()) as f32);
|
||||
let alt_main = alt_main_pre
|
||||
.abs()
|
||||
.powf(1.35);
|
||||
|
||||
@ -519,19 +557,33 @@ impl SimChunk {
|
||||
// (2)^(-1)
|
||||
//
|
||||
|
||||
// Now we just take a (currently) unweighted average of our randomly generated base humidity
|
||||
// Take the weighted average of our randomly generated base humidity, the scaled
|
||||
// negative altitude, and other random variable (to add some noise) to yield the
|
||||
// final humidity.
|
||||
const WEIGHTS : [f32; 4] = [3.0, 1.0, 1.0, 1.0];
|
||||
let humidity = cdf_irwin_hall(
|
||||
&WEIGHTS,
|
||||
[humid_base,
|
||||
alt_main_pre.mul(0.5).add(0.5),
|
||||
alt_base_pre.mul(0.5).add(0.5),
|
||||
(gen_ctx.small_nz.get((wposf.div(500.0)).into_array()) as f32)
|
||||
.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 = 3.0;
|
||||
let humid_alt_weight = 1.0;
|
||||
let humidity =
|
||||
humid_base.mul(humid_weight)
|
||||
.add(humid_alt.mul(humid_alt_weight)
|
||||
.add(humid_alt
|
||||
.mul(humid_alt_weight)
|
||||
// Adds some noise to the humidity effect of altitude to dampen it.
|
||||
.mul(gen_ctx.small_nz.get((wposf.div(10240.0)).into_array()) as f32)
|
||||
.mul((gen_ctx.small_nz.get((wposf.div(500.0)).into_array()) as f32)
|
||||
.mul(0.5)
|
||||
.add(0.5))
|
||||
.div(humid_weight + humid_alt_weight);
|
||||
.add(0.5)))
|
||||
.div(humid_weight + humid_alt_weight); */
|
||||
|
||||
let temp_base =
|
||||
gen_ctx.temp_nz.get((wposf.div(12000.0)).into_array()) as f32;
|
||||
@ -540,13 +592,13 @@ impl SimChunk {
|
||||
// distribution different for temperature as well.
|
||||
let temp_alt_sigma = -0.0625;
|
||||
let temp_alt_2s = 3.0f32.sqrt().mul(f32::consts::FRAC_2_PI).mul(temp_alt_sigma);
|
||||
let temp_alt_mu = 0.0;
|
||||
let temp_alt_mu = 0.0625;
|
||||
// Scaled to [-1, 1] already.
|
||||
let temp_alt = humid_alt_pre
|
||||
.sub(temp_alt_mu)
|
||||
.div(temp_alt_2s)
|
||||
.tanh();
|
||||
let temp_weight = 4.0;
|
||||
let temp_weight = 2.0;
|
||||
let temp_alt_weight = 1.0;
|
||||
let temp =
|
||||
temp_base.mul(temp_weight)
|
||||
@ -591,9 +643,9 @@ impl SimChunk {
|
||||
.add(0.05)
|
||||
.max(0.0)
|
||||
.min(1.0)
|
||||
.mul(0.5)
|
||||
.mul(0.4)
|
||||
// Tree density should go (by a lot) with humidity.
|
||||
.add(humidity.mul(0.5))
|
||||
.add(humidity.mul(0.6))
|
||||
// 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
|
||||
@ -602,6 +654,30 @@ impl SimChunk {
|
||||
})
|
||||
.max(0.0);
|
||||
|
||||
// let humid_normal = InverseGamma::new(4.0, 0.1).unwrap();
|
||||
// let humid_normal = LogNormal::new(0.0, 0.1).unwrap();
|
||||
let humid_normal = Gamma::new(1.0, 0.5).unwrap();
|
||||
// let humid_normal = Gamma::new(0.1, 1.0).unwrap();
|
||||
// let humid_normal = Normal::new(0.5, 0.05).unwrap();
|
||||
/*if humid_normal.cdf(humid_base as f64) > 0.9 *//* {
|
||||
println!("HIGH HUMIDITY: {:?}", humid_base);
|
||||
} */
|
||||
if pos == Vec2::new(1023, 1023) {
|
||||
let mut noise = (0..1024*1024).map( |i| {
|
||||
let wposf = Vec2::new(i as f64 / 1024.0, i as f64 % 1024.0);
|
||||
gen_ctx.humid_nz.get(wposf.div(1024.0).into_array()) as f32
|
||||
} ).collect::<Vec<_>>();
|
||||
noise.sort_unstable_by( |f, g| f.partial_cmp(g).unwrap() );
|
||||
for (k, f) in noise.iter().enumerate().step_by(1024 * 1024 / 100) {
|
||||
println!("{:?}%: {:?}, ", k / (1024 * 1024 / 100), f);
|
||||
}
|
||||
}
|
||||
/* if alt_main_pre.mul(0.5).add(0.5) > 0.7 {
|
||||
println!("HIGH: {:?}", alt_main_pre);
|
||||
} */
|
||||
/* if humidity > CONFIG.jungle_hum {
|
||||
println!("JUNGLE");
|
||||
} */
|
||||
|
||||
Self {
|
||||
chaos,
|
||||
@ -627,14 +703,22 @@ impl SimChunk {
|
||||
// Forests in desert temperatures with extremely high humidity
|
||||
// should probably be different from palm trees, but we use them
|
||||
// for now.
|
||||
/* /*if tree_density > 0.0 */{
|
||||
println!("Palm trees (jungle): {:?}, altitude: {:?}, humidity: {:?}, temperature: {:?}, density: {:?}", wposf, alt, humidity, temp, tree_density);
|
||||
} */
|
||||
ForestKind::Palm
|
||||
} else if humidity > CONFIG.forest_hum {
|
||||
/* /*if tree_density > 0.0 */{
|
||||
println!("Palm trees (forest): {:?}, altitude: {:?}, humidity: {:?}, temperature: {:?}, density: {:?}", wposf, alt, humidity, temp, tree_density);
|
||||
} */
|
||||
ForestKind::Palm
|
||||
} else {
|
||||
// Low but not desert humidity, so we should really have some other
|
||||
// terrain...
|
||||
/* if humidity < CONFIG.desert_hum {
|
||||
println!("True desert: {:?}, altitude: {:?}, humidity: {:?}, temperature: {:?}, density: {:?}", wposf, alt, humidity, temp, tree_density);
|
||||
} else {
|
||||
println!("Savannah (desert): {:?}, altitude: {:?}, humidity: {:?}, temperature: {:?}, density: {:?}", wposf, alt, humidity, temp, tree_density);
|
||||
} */
|
||||
ForestKind::Savannah
|
||||
}
|
||||
@ -665,6 +749,9 @@ impl SimChunk {
|
||||
// Moderate climate, moderate humidity.
|
||||
ForestKind::Oak
|
||||
} else {
|
||||
/* if humidity < CONFIG.desert_hum {
|
||||
println!("True desert: {:?}, altitude: {:?}, humidity: {:?}, temperature: {:?}, density: {:?}", wposf, alt, humidity, temp, tree_density);
|
||||
} */
|
||||
// With moderate temperature and low humidity, we should probably see
|
||||
// something different from savannah, but oh well...
|
||||
ForestKind::Savannah
|
||||
@ -679,6 +766,9 @@ impl SimChunk {
|
||||
} */
|
||||
ForestKind::SnowPine
|
||||
} else {
|
||||
/* if humidity < CONFIG.desert_hum {
|
||||
println!("True desert: {:?}, altitude: {:?}, humidity: {:?}, temperature: {:?}, density: {:?}", wposf, alt, humidity, temp, tree_density);
|
||||
} */
|
||||
ForestKind::Pine
|
||||
}
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user