vox_engine 0.3.3

voxel game engine
use super::{Generator, helpers, noise::FractalNoise};
use getset::CloneGetters;
use rapier3d::glamx::{IVec3, Vec3};
use std::collections::HashMap;

#[derive(CloneGetters)]
pub struct World {
    #[getset(get_clone = "pub with_prefix")]
    pub(super) world: Vec<u128>,
    #[getset(get_clone = "pub with_prefix")]
    pub(super) dimensions: Vec3,
    pub(super) chunked_coords: HashMap<IVec3, Vec<IVec3>>,
    #[getset(get_clone = "pub with_prefix")]
    pub(super) biomes: Vec<u8>,
    pub(super) collider_chunk_size: i32,
}

impl World {
    pub fn new(
        seed: u64,
        dimensions: Vec3,
        collider_chunk_size: i32,
        _generator: Option<Box<dyn Generator>>,
    ) -> Self {
        let height_map = FractalNoise::new(seed, 5, 2.0, 0.5);
        let temperature_map = FractalNoise::new((seed as i64 - 1).abs() as u64, 5, 2.0, 0.5);
        let moisture_map = FractalNoise::new((seed as i64 + 1).abs() as u64, 5, 2.0, 0.5);

        let texel_x = dimensions.x as usize / 4;
        let texel_y = dimensions.y as usize / 4;
        let texel_z = dimensions.z as usize / 8;

        let total = texel_x * texel_y * texel_z;

        let mut world = vec![0u128; total];
        let mut biomes = vec![0u8; ((dimensions.x * dimensions.z) * 4.0) as usize];

        let mut set_voxel = |x: usize, y: usize, z: usize| {
            let (texel, bit) = helpers::voxel_texel_and_bit(
                x as i32,
                y as i32,
                z as i32,
                texel_x as i32,
                texel_y as i32,
            );

            world[texel] |= 1u128 << bit;
        };

        let mut set_biome =
            |x: usize, y: usize, x_len: usize, step: usize, offset: usize, value: u8| {
                let index = (x * x_len) + y;

                biomes[(index * step) + offset] = value;
            };

        let noise_frequency = 1.0 / 450.0;
        let temperature_moisture_freq = 1.0 / 800.0;
        for x in 0..(dimensions.x as usize) {
            for z in 0..(dimensions.z as usize) {
                let height_factor =
                    height_map.sample(x as f64 * noise_frequency, z as f64 * noise_frequency);
                let temperature_raw = temperature_map.sample(
                    x as f64 * temperature_moisture_freq,
                    z as f64 * temperature_moisture_freq,
                );
                let moisture_raw = moisture_map.sample(
                    x as f64 * temperature_moisture_freq,
                    z as f64 * temperature_moisture_freq,
                );

                let temperature = ((temperature_raw * 0.5 + 0.5) * 255.0) as u8;
                let moisture = ((moisture_raw * 0.5 + 0.5) * 255.0) as u8;
                let height_factor = height_factor * 0.5 + 0.5;

                set_biome(x, z, dimensions.x as usize, 4, 0, temperature as u8);
                set_biome(
                    x,
                    z,
                    dimensions.x as usize,
                    4,
                    1,
                    (height_factor * 255.0) as u8,
                );
                set_biome(x, z, dimensions.x as usize, 4, 2, moisture as u8);
                set_biome(x, z, dimensions.x as usize, 4, 3, 255);

                let height = (dimensions.y as f64 * height_factor) as usize;
                let height = height.clamp(1, dimensions.y as usize);
                for y in 0..height {
                    set_voxel(x, y, z);
                }
            }
        }

        let mut world_struct = World {
            world,
            dimensions,
            chunked_coords: HashMap::new(),
            biomes,
            collider_chunk_size,
        };
        world_struct.chunked_coords = world_struct.compute_chunked_surface_coords();
        world_struct
    }

    pub fn get_dimensions_as_arr(&self) -> [u32; 3] {
        [
            self.dimensions.x as u32,
            self.dimensions.y as u32,
            self.dimensions.z as u32,
        ]
    }

    pub fn get_world_as_u32(&self) -> Vec<u32> {
        self.world
            .iter()
            .flat_map(|v| {
                let bytes = v.to_le_bytes();
                [
                    u32::from_le_bytes(bytes[0..4].try_into().unwrap()),
                    u32::from_le_bytes(bytes[4..8].try_into().unwrap()),
                    u32::from_le_bytes(bytes[8..12].try_into().unwrap()),
                    u32::from_le_bytes(bytes[12..16].try_into().unwrap()),
                ]
            })
            .collect()
    }
}