alex/beta-minecraft-rust
Minecraft Beta 1.7.3 Rewrite
BRANCH main
SNAPSHOT Draw terrain GPU-driven from packed per-section buffers with GPU occlusion culling, add a render distance slider (2-128) with a quad/disk shape, cut chunk bookkeeping at high render distances, and fix a light underflow at y=0
DOWNLOAD
root crates common src climate.rs
use crate::JavaRandom;
use crate::biome::Biome;
use crate::noise::SimplexOctaves;
/// Port of `WorldChunkManager`: temperature, rainfall and biome sampling.
pub enum Climate {
Overworld(Noise),
/// `WorldChunkManagerHell`: hot, dry and all `Hell`.
Hell,
}
pub struct Noise {
temperature: SimplexOctaves,
rainfall: SimplexOctaves,
variation: SimplexOctaves,
}
/// Per-column climate for a rectangular area, laid out x-major.
#[derive(Clone, Default)]
pub struct ClimateMap {
pub temperature: Vec<f64>,
pub rainfall: Vec<f64>,
pub biomes: Vec<Biome>,
}
const TEMPERATURE_SCALE: [f64; 2] = [0.025_f32 as f64; 2];
const RAINFALL_SCALE: [f64; 2] = [0.05_f32 as f64; 2];
impl Noise {
/// The temperatures blended with the variation noise, and that variation.
fn temperatures(&self, x: i32, z: i32, size: [usize; 2]) -> (Vec<f64>, Vec<f64>) {
let Self {
temperature,
variation,
..
} = self;
let origin = [f64::from(x), f64::from(z)];
let mut temperature = temperature.sample(origin, size, TEMPERATURE_SCALE, 0.25, 0.5);
let mut variation = variation.sample(origin, size, [0.25; 2], 0.588_235_294_117_647_1, 0.5);
for (t, v) in temperature.iter_mut().zip(&mut variation) {
*v = *v * 1.1 + 0.5;
let mixed = (*t * 0.15 + 0.7) * (1.0 - 0.01) + *v * 0.01;
*t = (1.0 - (1.0 - mixed) * (1.0 - mixed)).clamp(0.0, 1.0);
}
(temperature, variation)
}
}
impl Climate {
pub fn new(seed: i64) -> Self {
let octaves = |salt: i64, count| {
SimplexOctaves::new(&mut JavaRandom::new(seed.wrapping_mul(salt)), count)
};
Self::Overworld(Noise {
temperature: octaves(9871, 4),
rainfall: octaves(39811, 4),
variation: octaves(543_321, 2),
})
}
/// Port of `loadBlockGeneratorData`.
pub fn sample(&self, x: i32, z: i32, width: usize, depth: usize) -> ClimateMap {
let len = width * depth;
let Self::Overworld(noise @ Noise { rainfall, .. }) = self else {
return ClimateMap {
temperature: vec![1.0; len],
rainfall: vec![0.0; len],
biomes: vec![Biome::Hell; len],
};
};
let (temperature, variation) = noise.temperatures(x, z, [width, depth]);
let origin = [f64::from(x), f64::from(z)];
let mut rainfall = rainfall.sample(origin, [width, depth], RAINFALL_SCALE, 1.0 / 3.0, 0.5);
for (r, v) in rainfall.iter_mut().zip(&variation) {
*r = ((*r * 0.15 + 0.5) * (1.0 - 0.002) + v * 0.002).clamp(0.0, 1.0);
}
let biomes = temperature
.iter()
.zip(&rainfall)
.map(|(&t, &r)| Biome::lookup(t, r))
.collect();
ClimateMap {
temperature,
rainfall,
biomes,
}
}
/// Port of `getTemperature`: the untransformed noise value that drives sky color.
pub fn raw_temperature(&self, x: i32, z: i32) -> f64 {
let Self::Overworld(noise) = self else {
return 1.0;
};
let origin = [f64::from(x), f64::from(z)];
noise
.temperature
.sample(origin, [1, 1], TEMPERATURE_SCALE, 0.5, 0.5)[0]
}
/// Port of `getTemperatures`, used by snow placement during population.
pub fn temperatures(&self, x: i32, z: i32, width: usize, depth: usize) -> Vec<f64> {
match self {
Self::Overworld(noise) => noise.temperatures(x, z, [width, depth]).0,
Self::Hell => vec![1.0; width * depth],
}
}
}