chunkgen 0.2.0

Minecraft world generation library
Documentation
use crate::util::rng::RandomSource;
use crate::util::rng::legacy_random::LegacyRandomSource;

use super::SimplexNoise;

const ISLAND_THRESHOLD: f32 = -0.9;

#[derive(Debug, Clone)]
pub struct EndIslands {
    island_noise: SimplexNoise,
}

impl EndIslands {
    pub fn new(seed: u64) -> Self {
        let mut island_random = RandomSource::Legacy(LegacyRandomSource::from_seed(seed));
        island_random.consume_count(17292);
        Self {
            island_noise: SimplexNoise::new(&mut island_random),
        }
    }

    pub fn sample(&self, block_x: f64, _block_y: f64, block_z: f64) -> f64 {
        let block_x = block_x as i32;
        let block_z = block_z as i32;
        let height = Self::height_at(&self.island_noise, block_x / 8, block_z / 8);
        (f64::from(height) - 8.0) / 128.0
    }

    fn height_at(island_noise: &SimplexNoise, section_x: i32, section_z: i32) -> f32 {
        let chunk_x = section_x / 2;
        let chunk_z = section_z / 2;
        let sub_x = section_x % 2;
        let sub_z = section_z % 2;

        let dist_sq = section_x
            .wrapping_mul(section_x)
            .wrapping_add(section_z.wrapping_mul(section_z));
        let base = 100.0_f32 - (dist_sq as f32).sqrt() * 8.0;
        let mut doffs = base.clamp(-100.0, 80.0);

        for dx in -12..=12 {
            for dz in -12..=12 {
                let island_x = i64::from(chunk_x) + i64::from(dx);
                let island_z = i64::from(chunk_z) + i64::from(dz);
                if island_x * island_x + island_z * island_z <= 4096
                    || island_noise.get_value_2d(island_x as f64, island_z as f64)
                        >= f64::from(ISLAND_THRESHOLD)
                {
                    continue;
                }

                let island_size =
                    ((island_x as f32).abs() * 3439.0 + (island_z as f32).abs() * 147.0) % 13.0
                        + 9.0;
                let dxf = sub_x as f32 - (dx * 2) as f32;
                let dzf = sub_z as f32 - (dz * 2) as f32;
                let candidate =
                    (100.0_f32 - (dxf * dxf + dzf * dzf).sqrt() * island_size).clamp(-100.0, 80.0);

                if candidate > doffs {
                    doffs = candidate;
                }
            }
        }

        doffs
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn deterministic_for_same_seed() {
        let e1 = EndIslands::new(42);
        let e2 = EndIslands::new(42);
        assert!((e1.sample(0.0, 0.0, 0.0) - e2.sample(0.0, 0.0, 0.0)).abs() < 1e-15);
    }

    #[test]
    fn sample_is_within_expected_range() {
        let e = EndIslands::new(0);
        for x in (-2000..2000).step_by(400) {
            for z in (-2000..2000).step_by(400) {
                let v = e.sample(f64::from(x), 0.0, f64::from(z));
                assert!(
                    (-0.84375..=0.5625).contains(&v),
                    "value {v} out of range at ({x}, {z})"
                );
            }
        }
    }
}