vox_engine 0.3.0

voxel game engine
use super::{World, helpers};
use rapier3d::{
    glamx::{IVec3, Vec3, ivec3},
    prelude::*,
};
use rayon::prelude::*;
use std::collections::HashMap;

impl World {
    pub fn chunk_keys(&self) -> impl Iterator<Item = &IVec3> {
        self.chunked_coords.keys()
    }

    pub fn build_chunk_collider(&self, chunk_key: IVec3) -> Option<Collider> {
        let coords = self.chunked_coords.get(&chunk_key)?;
        if coords.is_empty() {
            return None;
        }
        Some(ColliderBuilder::voxels(Vec3::ONE, &coords.as_slice()).build())
    }

    pub(super) fn is_solid_fast(&self, x: i32, y: i32, z: i32, texel_x: i32, texel_y: i32) -> bool {
        let (dx, dy, dz) = (
            self.dimensions.x as i32,
            self.dimensions.y as i32,
            self.dimensions.z as i32,
        );

        if x < 0 || y < 0 || z < 0 || x >= dx || y >= dy || z >= dz {
            return false;
        }

        let (texel, bit) = helpers::voxel_texel_and_bit(x, y, z, texel_x, texel_y);

        (self.world[texel] >> bit) & 1 != 0
    }

    pub(super) fn compute_chunked_surface_coords(&self) -> HashMap<IVec3, Vec<IVec3>> {
        let texel_x = self.dimensions.x as i32 / 4;
        let texel_y = self.dimensions.y as i32 / 4;

        self.world
            .par_iter()
            .enumerate()
            .filter(|(_, word)| **word != 0)
            .fold(
                HashMap::<IVec3, Vec<IVec3>>::new,
                |mut local, (i, &word)| {
                    let i = i as i32;
                    let tx = i % texel_x;
                    let ty = (i / texel_x) % texel_y;
                    let tz = i / (texel_x * texel_y);

                    let mut w = word;
                    while w != 0 {
                        let bit = w.trailing_zeros();
                        w &= w - 1;

                        let (channel, local_y, local_z) = helpers::bit_to_local(bit);

                        let x = tx * 4 + channel;
                        let y = ty * 4 + local_y;
                        let z = tz * 8 + local_z;

                        let neighbor_solid = |dchan: i32,
                                              dy: i32,
                                              dz: i32,
                                              dx: i32,
                                              dz_global: i32|
                         -> bool {
                            let (nchan, nly, nlz) = (channel + dchan, local_y + dy, local_z + dz);
                            if nchan >= 0 && nchan < 4 && nly >= 0 && nly < 4 && nlz >= 0 && nlz < 8
                            {
                                let nbit = helpers::local_to_bit(nchan, nly, nlz);
                                (word >> nbit) & 1 != 0
                            } else {
                                self.is_solid_fast(x + dx, y + dy, z + dz_global, texel_x, texel_y)
                            }
                        };

                        let exposed = !neighbor_solid(1, 0, 0, 1, 0)
                            || !neighbor_solid(-1, 0, 0, -1, 0)
                            || !neighbor_solid(0, 1, 0, 0, 0)
                            || !neighbor_solid(0, -1, 0, 0, 0)
                            || !neighbor_solid(0, 0, 1, 0, 1)
                            || !neighbor_solid(0, 0, -1, 0, -1);

                        if exposed {
                            let chunk_key = ivec3(
                                x.div_euclid(self.collider_chunk_size),
                                y.div_euclid(self.collider_chunk_size),
                                z.div_euclid(self.collider_chunk_size),
                            );
                            local.entry(chunk_key).or_default().push(ivec3(x, y, z));
                        }
                    }
                    local
                },
            )
            .reduce(HashMap::new, |mut a, b| {
                for (key, mut coords) in b {
                    a.entry(key).or_default().append(&mut coords);
                }
                a
            })
    }
}