use crate::{
bounds::assert_in_bounds, IdentityVoxel, OrientedBlockFace, UnitQuadBuffer, UnorientedUnitQuad, Voxel, VoxelVisibility,
};
use ilattice::glam::UVec3;
use ilattice::prelude::Extent;
use ndshape::Shape;
pub fn visible_block_faces<T, S>(
voxels: &[T],
voxels_shape: &S,
min: [u32; 3],
max: [u32; 3],
faces: &[OrientedBlockFace; 6],
output: &mut UnitQuadBuffer,
) where
T: Voxel,
S: Shape<3, Coord = u32>,
{
visible_block_faces_with_voxel_view::<_, IdentityVoxel<T>, _>(
voxels,
voxels_shape,
min,
max,
faces,
output,
)
}
pub fn visible_block_faces_with_voxel_view<'a, T, V, S>(
voxels: &'a [T],
voxels_shape: &S,
min: [u32; 3],
max: [u32; 3],
faces: &[OrientedBlockFace; 6],
output: &mut UnitQuadBuffer,
) where
V: Voxel + From<&'a T>,
S: Shape<3, Coord = u32>,
{
assert_in_bounds(voxels, voxels_shape, min, max);
let min = UVec3::from(min).as_ivec3();
let max = UVec3::from(max).as_ivec3();
let extent = Extent::from_min_and_max(min, max);
let interior = extent.padded(-1); let interior =
Extent::from_min_and_shape(interior.minimum.as_uvec3(), interior.shape.as_uvec3());
let kernel_strides =
faces.map(|face| voxels_shape.linearize(face.signed_normal().as_uvec3().to_array()));
for p in interior.iter3() {
let p_array = p.to_array();
let p_index = voxels_shape.linearize(p_array);
let p_voxel = V::from(unsafe { voxels.get_unchecked(p_index as usize) });
if let VoxelVisibility::Empty = p_voxel.get_visibility() {
continue;
}
for (face_index, face_stride) in kernel_strides.into_iter().enumerate() {
let neighbor_index = p_index.wrapping_add(face_stride);
let neighbor_voxel = V::from(unsafe { voxels.get_unchecked(neighbor_index as usize) });
let face_needs_mesh = match neighbor_voxel.get_visibility() {
VoxelVisibility::Empty => true,
VoxelVisibility::Translucent => p_voxel.get_visibility() == VoxelVisibility::Opaque,
VoxelVisibility::Opaque => false,
};
if face_needs_mesh {
output.groups[face_index].push(UnorientedUnitQuad { minimum: p_array });
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::RIGHT_HANDED_Y_UP_CONFIG;
use ndshape::{ConstShape, ConstShape3u32};
#[test]
#[should_panic]
fn panics_with_max_out_of_bounds_access() {
let samples = [EMPTY; SampleShape::SIZE as usize];
let mut buffer = UnitQuadBuffer::new();
visible_block_faces(
&samples,
&SampleShape {},
[0; 3],
[34, 33, 33],
&RIGHT_HANDED_Y_UP_CONFIG.faces,
&mut buffer,
);
}
#[test]
#[should_panic]
fn panics_with_min_out_of_bounds_access() {
let samples = [EMPTY; SampleShape::SIZE as usize];
let mut buffer = UnitQuadBuffer::new();
visible_block_faces(
&samples,
&SampleShape {},
[0, 34, 0],
[33; 3],
&RIGHT_HANDED_Y_UP_CONFIG.faces,
&mut buffer,
);
}
type SampleShape = ConstShape3u32<34, 34, 34>;
#[derive(Default, Clone, Copy, Eq, PartialEq)]
struct BoolVoxel(bool);
const EMPTY: BoolVoxel = BoolVoxel(false);
impl Voxel for BoolVoxel {
fn get_visibility(&self) -> VoxelVisibility {
if *self == EMPTY {
VoxelVisibility::Empty
} else {
VoxelVisibility::Opaque
}
}
}
}