block_mesh/
simple.rs

1use crate::{
2    bounds::assert_in_bounds, IdentityVoxel, OrientedBlockFace, UnitQuadBuffer, UnorientedUnitQuad, Voxel, VoxelVisibility,
3};
4
5use ilattice::glam::UVec3;
6use ilattice::prelude::Extent;
7use ndshape::Shape;
8
9
10/// A fast and simple meshing algorithm that produces a single quad for every visible face of a block.
11///
12/// This is faster than [`greedy_quads`](crate::greedy_quads) but it produces many more quads.
13pub fn visible_block_faces<T, S>(
14    voxels: &[T],
15    voxels_shape: &S,
16    min: [u32; 3],
17    max: [u32; 3],
18    faces: &[OrientedBlockFace; 6],
19    output: &mut UnitQuadBuffer,
20) where
21    T: Voxel,
22    S: Shape<3, Coord = u32>,
23{
24    visible_block_faces_with_voxel_view::<_, IdentityVoxel<T>, _>(
25        voxels,
26        voxels_shape,
27        min,
28        max,
29        faces,
30        output,
31    )
32}
33
34/// Same as [`visible_block_faces`](visible_block_faces),
35/// with the additional ability to interpret the array as some other type.
36/// Use this if you want to mesh the same array multiple times
37/// with different sets of voxels being visible.
38pub fn visible_block_faces_with_voxel_view<'a, T, V, S>(
39    voxels: &'a [T],
40    voxels_shape: &S,
41    min: [u32; 3],
42    max: [u32; 3],
43    faces: &[OrientedBlockFace; 6],
44    output: &mut UnitQuadBuffer,
45) where
46    V: Voxel + From<&'a T>,
47    S: Shape<3, Coord = u32>,
48{
49    assert_in_bounds(voxels, voxels_shape, min, max);
50
51    let min = UVec3::from(min).as_ivec3();
52    let max = UVec3::from(max).as_ivec3();
53    let extent = Extent::from_min_and_max(min, max);
54    let interior = extent.padded(-1); // Avoid accessing out of bounds with a 3x3x3 kernel.
55    let interior =
56        Extent::from_min_and_shape(interior.minimum.as_uvec3(), interior.shape.as_uvec3());
57
58    let kernel_strides =
59        faces.map(|face| voxels_shape.linearize(face.signed_normal().as_uvec3().to_array()));
60
61    for p in interior.iter3() {
62        let p_array = p.to_array();
63        let p_index = voxels_shape.linearize(p_array);
64        let p_voxel = V::from(unsafe { voxels.get_unchecked(p_index as usize) });
65
66        if let VoxelVisibility::Empty = p_voxel.get_visibility() {
67            continue;
68        }
69
70        for (face_index, face_stride) in kernel_strides.into_iter().enumerate() {
71            let neighbor_index = p_index.wrapping_add(face_stride);
72            let neighbor_voxel = V::from(unsafe { voxels.get_unchecked(neighbor_index as usize) });
73
74            // TODO: If the face lies between two transparent voxels, we choose not to mesh it. We might need to extend the
75            // IsOpaque trait with different levels of transparency to support this.
76            let face_needs_mesh = match neighbor_voxel.get_visibility() {
77                VoxelVisibility::Empty => true,
78                VoxelVisibility::Translucent => p_voxel.get_visibility() == VoxelVisibility::Opaque,
79                VoxelVisibility::Opaque => false,
80            };
81
82            if face_needs_mesh {
83                output.groups[face_index].push(UnorientedUnitQuad { minimum: p_array });
84            }
85        }
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92    use crate::RIGHT_HANDED_Y_UP_CONFIG;
93    use ndshape::{ConstShape, ConstShape3u32};
94
95    #[test]
96    #[should_panic]
97    fn panics_with_max_out_of_bounds_access() {
98        let samples = [EMPTY; SampleShape::SIZE as usize];
99        let mut buffer = UnitQuadBuffer::new();
100        visible_block_faces(
101            &samples,
102            &SampleShape {},
103            [0; 3],
104            [34, 33, 33],
105            &RIGHT_HANDED_Y_UP_CONFIG.faces,
106            &mut buffer,
107        );
108    }
109
110    #[test]
111    #[should_panic]
112    fn panics_with_min_out_of_bounds_access() {
113        let samples = [EMPTY; SampleShape::SIZE as usize];
114        let mut buffer = UnitQuadBuffer::new();
115        visible_block_faces(
116            &samples,
117            &SampleShape {},
118            [0, 34, 0],
119            [33; 3],
120            &RIGHT_HANDED_Y_UP_CONFIG.faces,
121            &mut buffer,
122        );
123    }
124
125    type SampleShape = ConstShape3u32<34, 34, 34>;
126
127    /// Basic voxel type with one byte of texture layers
128    #[derive(Default, Clone, Copy, Eq, PartialEq)]
129    struct BoolVoxel(bool);
130
131    const EMPTY: BoolVoxel = BoolVoxel(false);
132
133    impl Voxel for BoolVoxel {
134        fn get_visibility(&self) -> VoxelVisibility {
135            if *self == EMPTY {
136                VoxelVisibility::Empty
137            } else {
138                VoxelVisibility::Opaque
139            }
140        }
141    }
142
143}