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
10pub 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
34pub 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); 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 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 #[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}