mirror of
https://github.com/Cactus-minecraft-server/World.git
synced 2025-12-07 10:40:37 +00:00
Big changes to the function that give the height of a chunk . The goal was to make it similar to minecraft generation
This commit is contained in:
156
src/perlin.rs
156
src/perlin.rs
@@ -7,11 +7,8 @@ use rand_chacha::ChaCha8Rng;
|
|||||||
const CHUNK_SIZE: usize = 16;
|
const CHUNK_SIZE: usize = 16;
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct Noise {
|
pub struct Noise {
|
||||||
// World-to-lattice scaling factor. Larger => smoother/lower-frequency noise.
|
|
||||||
scale: f32,
|
scale: f32,
|
||||||
// Output amplitude (scalar multiplier on the Perlin value).
|
|
||||||
amplitude: f32,
|
amplitude: f32,
|
||||||
// Global seed for determinism across calls.
|
|
||||||
seed: u64,
|
seed: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,16 +18,66 @@ pub struct Vector {
|
|||||||
y: f32,
|
y: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_height_chunk(noise: &Noise, cx: i32, cz: i32) -> [[f32; CHUNK_SIZE]; CHUNK_SIZE] {
|
const MIN_Y: i32 = -64;
|
||||||
let mut h = [[0.0; CHUNK_SIZE]; CHUNK_SIZE];
|
const MAX_Y: i32 = 320;
|
||||||
for lx in 0..CHUNK_SIZE {
|
const SEA_LEVEL: i32 = 63;
|
||||||
for lz in 0..CHUNK_SIZE {
|
|
||||||
let wx = (cx * CHUNK_SIZE as i32 + lx as i32) as f32;
|
#[inline]
|
||||||
let wz = (cz * CHUNK_SIZE as i32 + lz as i32) as f32;
|
fn fbm_seeded(
|
||||||
h[lx][lz] = noise.get(wx, wz);
|
seed: u64,
|
||||||
|
scale: f32,
|
||||||
|
x: f32,
|
||||||
|
z: f32,
|
||||||
|
octaves: u32,
|
||||||
|
persistence: f32,
|
||||||
|
lacunarity: f32,
|
||||||
|
) -> f32 {
|
||||||
|
let base = Noise::new(scale, 1.0, seed);
|
||||||
|
let (mut amp, mut freq, mut sum, mut norm) = (1.0f32, 1.0f32, 0.0f32, 0.0f32);
|
||||||
|
for _ in 0..octaves {
|
||||||
|
sum += amp * base.get(x * freq, z * freq);
|
||||||
|
norm += amp;
|
||||||
|
amp *= persistence;
|
||||||
|
freq *= lacunarity;
|
||||||
|
}
|
||||||
|
(sum / norm).clamp(-1.0, 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_height_chunk(seed: u64, cx: i32, cz: i32) -> [[i32; CHUNK_SIZE]; CHUNK_SIZE] {
|
||||||
|
const SALT_CONT: u64 = 0xC0DEC0DEu64;
|
||||||
|
const SALT_MNT: u64 = 0xBEEF1234u64;
|
||||||
|
const SALT_DET: u64 = 0xDE7ACAFEu64;
|
||||||
|
const SALT_ERO: u64 = 0xE20DFACEu64;
|
||||||
|
|
||||||
|
const GAIN: f32 = 220.0;
|
||||||
|
|
||||||
|
let mut out = [[SEA_LEVEL; CHUNK_SIZE]; CHUNK_SIZE];
|
||||||
|
|
||||||
|
for lz in 0..CHUNK_SIZE {
|
||||||
|
for lx in 0..CHUNK_SIZE {
|
||||||
|
let xw = (cx * CHUNK_SIZE as i32 + lx as i32) as f32;
|
||||||
|
let zw = (cz * CHUNK_SIZE as i32 + lz as i32) as f32;
|
||||||
|
|
||||||
|
let cont = fbm_seeded(seed ^ SALT_CONT, 1500.0, xw, zw, 6, 0.5, 2.0); // [-1,1]
|
||||||
|
let mnt = fbm_seeded(seed ^ SALT_MNT, 400.0, xw, zw, 6, 0.5, 2.0);
|
||||||
|
let det = fbm_seeded(seed ^ SALT_DET, 48.0, xw, zw, 5, 0.5, 2.0);
|
||||||
|
let eros = fbm_seeded(seed ^ SALT_ERO, 1200.0, xw, zw, 5, 0.5, 2.0);
|
||||||
|
|
||||||
|
let ridge = (1.0 - mnt.abs()).powf(1.5); // [0,1]
|
||||||
|
let c_cont = cont * 0.5; // [-0.5,0.5]
|
||||||
|
let c_ridge = (ridge - 0.5); // [-0.5,0.5]
|
||||||
|
let c_det = det * 0.25; // [-0.25,0.25]
|
||||||
|
let c_eros = eros * 0.35; // [-0.35,0.35]
|
||||||
|
|
||||||
|
let s = 0.9 * c_cont + 0.8 * c_ridge + 0.25 * c_det - 0.35 * c_eros;
|
||||||
|
|
||||||
|
let mut y = SEA_LEVEL as f32 + s * GAIN;
|
||||||
|
y = y.clamp(MIN_Y as f32, MAX_Y as f32);
|
||||||
|
out[lx][lz] = y.round() as i32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
h
|
|
||||||
|
out
|
||||||
}
|
}
|
||||||
impl Noise {
|
impl Noise {
|
||||||
/// Construct a noise generator.
|
/// Construct a noise generator.
|
||||||
@@ -391,100 +438,47 @@ mod perlin_tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod viz {
|
mod viz_chunk2 {
|
||||||
use super::*;
|
use super::*;
|
||||||
use image::{GrayImage, Luma};
|
use image::{ImageBuffer, Luma};
|
||||||
|
|
||||||
// Render a preview image. Run with: `cargo test -- --ignored`
|
|
||||||
#[test]
|
|
||||||
#[ignore]
|
|
||||||
fn dump_perlin_png() {
|
|
||||||
let noise = Noise::new(64.0, 10.0, 42);
|
|
||||||
|
|
||||||
let (w, h) = (512u32, 512u32);
|
|
||||||
let mut img: GrayImage = GrayImage::new(w, h);
|
|
||||||
|
|
||||||
for y in 0..h {
|
|
||||||
for x in 0..w {
|
|
||||||
let fx = x as f32;
|
|
||||||
let fy = y as f32;
|
|
||||||
|
|
||||||
// Multi-octave (fractal Brownian motion) for richer structure.
|
|
||||||
let mut v = 0.0f32;
|
|
||||||
let mut amp = 1.0f32;
|
|
||||||
let mut freq = 1.0f32;
|
|
||||||
for _ in 0..5 {
|
|
||||||
v += amp * noise.perlin(fx / noise.scale * freq, fy / noise.scale * freq);
|
|
||||||
amp *= 0.5;
|
|
||||||
freq *= 2.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map from roughly [-1,1] to [0,255]
|
|
||||||
let n01 = (v * 0.5 + 0.5).clamp(0.0, 1.0);
|
|
||||||
let p = (n01 * 255.0).round() as u8;
|
|
||||||
|
|
||||||
img.put_pixel(x, y, Luma([p]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::fs::create_dir_all("target").ok();
|
|
||||||
img.save("perlin.png").expect("save png");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[cfg(test)]
|
|
||||||
mod viz_chunk {
|
|
||||||
use super::*;
|
|
||||||
use image::{GrayImage, Luma};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
fn dump_chunk_png() {
|
fn dump_chunk_png() {
|
||||||
let noise = Noise::new(64.0, 10.0, 42);
|
let seed: u64 = 42;
|
||||||
let chunks_x: usize = 32;
|
let chunks_x: usize = 32;
|
||||||
let chunks_z: usize = 32;
|
let chunks_z: usize = 32;
|
||||||
|
|
||||||
let w = (chunks_x * CHUNK_SIZE) as u32;
|
let w: u32 = (chunks_x * CHUNK_SIZE) as u32;
|
||||||
let h = (chunks_z * CHUNK_SIZE) as u32;
|
let h: u32 = (chunks_z * CHUNK_SIZE) as u32;
|
||||||
|
|
||||||
let mut field = vec![0.0f32; (w * h) as usize];
|
let mut field = vec![0i32; (w as usize) * (h as usize)];
|
||||||
let mut vmin = f32::INFINITY;
|
|
||||||
let mut vmax = f32::NEG_INFINITY;
|
|
||||||
|
|
||||||
for cz in 0..chunks_z {
|
for cz in 0..chunks_z {
|
||||||
for cx in 0..chunks_x {
|
for cx in 0..chunks_x {
|
||||||
let tile = generate_height_chunk(&noise, cx as i32, cz as i32);
|
let tile = generate_height_chunk(seed, cx as i32, cz as i32);
|
||||||
|
|
||||||
for lz in 0..CHUNK_SIZE {
|
for lz in 0..CHUNK_SIZE {
|
||||||
for lx in 0..CHUNK_SIZE {
|
for lx in 0..CHUNK_SIZE {
|
||||||
let x = cx * CHUNK_SIZE + lx;
|
let x = cx * CHUNK_SIZE + lx;
|
||||||
let z = cz * CHUNK_SIZE + lz;
|
let z = cz * CHUNK_SIZE + lz;
|
||||||
let idx = z * (w as usize) + x;
|
field[z * (w as usize) + x] = tile[lx][lz];
|
||||||
|
|
||||||
let v = tile[lx][lz];
|
|
||||||
field[idx] = v;
|
|
||||||
if v < vmin {
|
|
||||||
vmin = v;
|
|
||||||
}
|
|
||||||
if v > vmax {
|
|
||||||
vmax = v;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut img: GrayImage = GrayImage::new(w, h);
|
let mut img: ImageBuffer<Luma<u16>, Vec<u16>> = ImageBuffer::new(w, h);
|
||||||
let span = (vmax - vmin).max(std::f32::EPSILON);
|
let denom = (MAX_Y - MIN_Y) as f32;
|
||||||
for z in 0..(h as usize) {
|
for z in 0..h {
|
||||||
for x in 0..(w as usize) {
|
for x in 0..w {
|
||||||
let v = field[z * (w as usize) + x];
|
let v = field[(z as usize) * (w as usize) + (x as usize)];
|
||||||
let n01 = ((v - vmin) / span).clamp(0.0, 1.0);
|
let n01 = ((v - MIN_Y) as f32 / denom).clamp(0.0, 1.0);
|
||||||
let p = (n01 * 255.0).round() as u8;
|
let p = (n01 * u16::MAX as f32).round() as u16;
|
||||||
img.put_pixel(x as u32, z as u32, Luma([p]));
|
img.put_pixel(x, z, Luma([p]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::fs::create_dir_all("target").ok();
|
std::fs::create_dir_all("target").ok();
|
||||||
img.save("chunk_heightmap.png").expect("save png");
|
img.save("target/heightmap16.png").unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user