1mod merge_strategy;
2
3pub use merge_strategy::*;
4
5use crate::{bounds::assert_in_bounds, OrientedBlockFace, QuadBuffer, UnorientedQuad, Voxel, VoxelVisibility};
6
7use ilattice::glam::UVec3;
8use ilattice::prelude::Extent;
9use ndcopy::fill3;
10use ndshape::Shape;
11
12pub trait MergeVoxel: Voxel {
13 type MergeValue: Eq;
14
15 fn merge_value(&self) -> Self::MergeValue;
18}
19
20pub struct GreedyQuadsBuffer {
25 pub quads: QuadBuffer,
26
27 visited: Vec<bool>,
30}
31
32impl GreedyQuadsBuffer {
33 pub fn new(size: usize) -> Self {
34 Self {
35 quads: QuadBuffer::new(),
36 visited: vec![false; size],
37 }
38 }
39
40 pub fn reset(&mut self, size: usize) {
41 self.quads.reset();
42
43 if size != self.visited.len() {
44 self.visited = vec![false; size];
45 }
46 }
47}
48
49pub fn greedy_quads<T, S>(
59 voxels: &[T],
60 voxels_shape: &S,
61 min: [u32; 3],
62 max: [u32; 3],
63 faces: &[OrientedBlockFace; 6],
64 output: &mut GreedyQuadsBuffer,
65) where
66 T: MergeVoxel,
67 S: Shape<3, Coord = u32>,
68{
69 greedy_quads_with_merge_strategy::<_, _, VoxelMerger<T>>(
70 voxels,
71 voxels_shape,
72 min,
73 max,
74 faces,
75 output,
76 )
77}
78
79pub fn greedy_quads_with_merge_strategy<T, S, Merger>(
81 voxels: &[T],
82 voxels_shape: &S,
83 min: [u32; 3],
84 max: [u32; 3],
85 faces: &[OrientedBlockFace; 6],
86 output: &mut GreedyQuadsBuffer,
87) where
88 T: Voxel,
89 S: Shape<3, Coord = u32>,
90 Merger: MergeStrategy<Voxel = T>,
91{
92 assert_in_bounds(voxels, voxels_shape, min, max);
93
94 let min = UVec3::from(min).as_ivec3();
95 let max = UVec3::from(max).as_ivec3();
96 let extent = Extent::from_min_and_max(min, max);
97
98 output.reset(voxels.len());
99 let GreedyQuadsBuffer {
100 visited,
101 quads: QuadBuffer { groups },
102 } = output;
103
104 let interior = extent.padded(-1); let interior =
106 Extent::from_min_and_shape(interior.minimum.as_uvec3(), interior.shape.as_uvec3());
107
108 for (group, face) in groups.iter_mut().zip(faces.iter()) {
109 greedy_quads_for_face::<_, _, Merger>(voxels, voxels_shape, interior, face, visited, group);
110 }
111}
112
113fn greedy_quads_for_face<T, S, Merger>(
114 voxels: &[T],
115 voxels_shape: &S,
116 interior: Extent<UVec3>,
117 face: &OrientedBlockFace,
118 visited: &mut [bool],
119 quads: &mut Vec<UnorientedQuad>,
120) where
121 T: Voxel,
122 S: Shape<3, Coord = u32>,
123 Merger: MergeStrategy<Voxel = T>,
124{
125 visited.fill(false);
126
127 let OrientedBlockFace {
128 n_sign,
129 permutation,
130 n,
131 u,
132 v,
133 ..
134 } = face;
135
136 let [n_axis, u_axis, v_axis] = permutation.axes();
137 let i_n = n_axis.index();
138 let i_u = u_axis.index();
139 let i_v = v_axis.index();
140
141 let interior_shape = interior.shape.to_array();
142 let num_slices = interior_shape[i_n];
143 let mut slice_shape = [0; 3];
144 slice_shape[i_n] = 1;
145 slice_shape[i_u] = interior_shape[i_u];
146 slice_shape[i_v] = interior_shape[i_v];
147 let mut slice_extent = Extent::from_min_and_shape(interior.minimum, UVec3::from(slice_shape));
148
149 let n_stride = voxels_shape.linearize(n.to_array());
150 let u_stride = voxels_shape.linearize(u.to_array());
151 let v_stride = voxels_shape.linearize(v.to_array());
152 let face_strides = FaceStrides {
153 n_stride,
154 u_stride,
155 v_stride,
156 visibility_offset: if *n_sign > 0 {
158 n_stride
159 } else {
160 0u32.wrapping_sub(n_stride)
161 },
162 };
163
164 for _ in 0..num_slices {
165 let slice_ub = slice_extent.least_upper_bound().to_array();
166 let u_ub = slice_ub[i_u];
167 let v_ub = slice_ub[i_v];
168
169 for quad_min in slice_extent.iter3() {
170 let quad_min_array = quad_min.to_array();
171 let quad_min_index = voxels_shape.linearize(quad_min_array);
172 let quad_min_voxel = unsafe { voxels.get_unchecked(quad_min_index as usize) };
173 if unsafe {
174 !face_needs_mesh(
175 quad_min_voxel,
176 quad_min_index,
177 face_strides.visibility_offset,
178 voxels,
179 visited,
180 )
181 } {
182 continue;
183 }
184 let max_width = u_ub - quad_min_array[i_u];
188 let max_height = v_ub - quad_min_array[i_v];
189
190 let (quad_width, quad_height) = unsafe {
191 Merger::find_quad(
192 quad_min_index,
193 max_width,
194 max_height,
195 &face_strides,
196 voxels,
197 visited,
198 )
199 };
200 debug_assert!(quad_width >= 1);
201 debug_assert!(quad_width <= max_width);
202 debug_assert!(quad_height >= 1);
203 debug_assert!(quad_height <= max_height);
204
205 let mut quad_shape = [0; 3];
207 quad_shape[i_n] = 1;
208 quad_shape[i_u] = quad_width;
209 quad_shape[i_v] = quad_height;
210 fill3(quad_shape, true, visited, voxels_shape, quad_min_array);
211
212 quads.push(UnorientedQuad {
213 minimum: quad_min.to_array(),
214 width: quad_width,
215 height: quad_height,
216 });
217 }
218
219 slice_extent = slice_extent + *n;
221 }
222}
223
224pub(crate) unsafe fn face_needs_mesh<T>(
227 voxel: &T,
228 voxel_stride: u32,
229 visibility_offset: u32,
230 voxels: &[T],
231 visited: &[bool],
232) -> bool
233where
234 T: Voxel,
235{
236 if voxel.get_visibility() == VoxelVisibility::Empty || visited[voxel_stride as usize] {
237 return false;
238 }
239
240 let adjacent_voxel =
241 voxels.get_unchecked(voxel_stride.wrapping_add(visibility_offset) as usize);
242
243 match adjacent_voxel.get_visibility() {
246 VoxelVisibility::Empty => true,
247 VoxelVisibility::Translucent => voxel.get_visibility() == VoxelVisibility::Opaque,
248 VoxelVisibility::Opaque => false,
249 }
250}
251
252#[cfg(test)]
253mod tests {
254 use super::*;
255 use crate::RIGHT_HANDED_Y_UP_CONFIG;
256 use ndshape::{ConstShape, ConstShape3u32};
257
258 #[test]
259 #[should_panic]
260 fn panics_with_max_out_of_bounds_access() {
261 let samples = [EMPTY; SampleShape::SIZE as usize];
262 let mut buffer = GreedyQuadsBuffer::new(samples.len());
263 greedy_quads(
264 &samples,
265 &SampleShape {},
266 [0; 3],
267 [34, 33, 33],
268 &RIGHT_HANDED_Y_UP_CONFIG.faces,
269 &mut buffer,
270 );
271 }
272
273 #[test]
274 #[should_panic]
275 fn panics_with_min_out_of_bounds_access() {
276 let samples = [EMPTY; SampleShape::SIZE as usize];
277 let mut buffer = GreedyQuadsBuffer::new(samples.len());
278 greedy_quads(
279 &samples,
280 &SampleShape {},
281 [0, 34, 0],
282 [33; 3],
283 &RIGHT_HANDED_Y_UP_CONFIG.faces,
284 &mut buffer,
285 );
286 }
287
288 type SampleShape = ConstShape3u32<34, 34, 34>;
289
290 #[derive(Default, Clone, Copy, Eq, PartialEq)]
292 struct BoolVoxel(bool);
293
294 const EMPTY: BoolVoxel = BoolVoxel(false);
295
296 impl Voxel for BoolVoxel {
297 fn get_visibility(&self) -> VoxelVisibility {
298 if *self == EMPTY {
299 VoxelVisibility::Empty
300 } else {
301 VoxelVisibility::Opaque
302 }
303 }
304 }
305
306 impl MergeVoxel for BoolVoxel {
307 type MergeValue = Self;
308
309 fn merge_value(&self) -> Self::MergeValue {
310 *self
311 }
312 }
313}