1use std::marker::PhantomData;
13use std::simd::f32x4;
14
15use glam::IVec3;
16use steel_utils::SIMD_BATCH;
17use steel_worldgen::density::{ColumnCache, DimensionNoises, NoiseSettings};
18
19use crate::noise::{Beardifier, CornerColumnStore};
20
21const MAX_INTERP: usize = 16;
23const INTERPOLATION_LANES: usize = 4;
24
25const MAX_SLICE_LEN: usize = 256;
28const MAX_CELL_HEIGHT: usize = 16;
30
31pub struct NoiseChunk<N: DimensionNoises> {
43 slices: Vec<Box<[f32; MAX_INTERP * MAX_SLICE_LEN]>>,
54 interp_count: usize,
56 corners_y: usize,
58
59 block_ys: Vec<i32>,
63
64 first_cell_x: i32,
66 first_cell_z: i32,
67 cell_min_y: i32,
69 cell_count_y: usize,
71 cell_count_xz: usize,
73
74 _phantom: PhantomData<N>,
75}
76
77impl<N: DimensionNoises> NoiseChunk<N> {
78 #[must_use]
83 #[expect(
84 clippy::missing_panics_doc,
85 reason = "panic is a compile-time constant check"
86 )]
87 pub fn new(chunk_min_block_x: i32, chunk_min_block_z: i32) -> Self {
88 let cell_width = N::Settings::CELL_WIDTH;
89 let cell_height = N::Settings::CELL_HEIGHT;
90 let min_y = N::Settings::MIN_Y;
91 let height = N::Settings::HEIGHT;
92
93 let first_cell_x = chunk_min_block_x.div_euclid(cell_width);
94 let first_cell_z = chunk_min_block_z.div_euclid(cell_width);
95 let cell_min_y = min_y.div_euclid(cell_height);
96
97 let cell_count_xz = (16 / cell_width) as usize;
98 let cell_count_y = (height / cell_height) as usize;
99 let corners_y = cell_count_y + 1;
100 let z_corners = cell_count_xz + 1;
101 let slice_len = z_corners * corners_y;
102
103 let interp_count = N::interpolated_count();
104 assert!(
105 slice_len <= MAX_SLICE_LEN,
106 "slice_len {slice_len} exceeds MAX_SLICE_LEN {MAX_SLICE_LEN}"
107 );
108 assert!(
109 interp_count <= MAX_INTERP,
110 "interp_count {interp_count} exceeds MAX_INTERP {MAX_INTERP}"
111 );
112
113 let block_ys: Vec<i32> = (0..corners_y)
114 .map(|cy| (cy as i32 + cell_min_y) * cell_height)
115 .collect();
116
117 let n_slices = cell_count_xz + 1;
118 let mut slices = Vec::with_capacity(n_slices);
119 for _ in 0..n_slices {
120 slices.push(Box::new([0.0_f32; MAX_INTERP * MAX_SLICE_LEN]));
122 }
123
124 Self {
125 slices,
126 interp_count,
127 corners_y,
128 block_ys,
129 first_cell_x,
130 first_cell_z,
131 cell_min_y,
132 cell_count_y,
133 cell_count_xz,
134 _phantom: PhantomData,
135 }
136 }
137
138 #[expect(
142 clippy::too_many_arguments,
143 reason = "slice filling needs the precomputed geometry and per-thread cache"
144 )]
145 fn fill_slice_into(
146 slice: &mut [f32; MAX_INTERP * MAX_SLICE_LEN],
147 cell_x: i32,
148 block_ys: &[i32],
149 blended_column: &mut [f32],
150 interp_count: usize,
151 corners_y: usize,
152 cell_count_xz: usize,
153 first_cell_z: i32,
154 noises: &N,
155 cache: &mut N::ColumnCache,
156 corner_columns: &CornerColumnStore,
157 ) {
158 let cell_width = N::Settings::CELL_WIDTH;
159
160 let block_x = cell_x * cell_width;
161 let chunk_boundary = |cell: i32| cell.rem_euclid(cell_count_xz as i32) == 0;
163 let x_users: u8 = if chunk_boundary(cell_x) { 2 } else { 1 };
164
165 let mut values = [0.0_f32; MAX_INTERP * SIMD_BATCH];
166
167 for cz in 0..=cell_count_xz {
168 let cell_z = first_cell_z + cz as i32;
169 let block_z = cell_z * cell_width;
170 let users = x_users * if chunk_boundary(cell_z) { 2 } else { 1 };
171 let column_base = cz * corners_y * MAX_INTERP;
172 let column_len = corners_y * MAX_INTERP;
173
174 if users > 1
175 && corner_columns.consume((cell_x, cell_z), |values| {
176 let column = &mut slice[column_base..column_base + column_len];
177 for (corner, stored) in column
178 .as_chunks_mut::<MAX_INTERP>()
179 .0
180 .iter_mut()
181 .zip(values.chunks_exact(interp_count))
182 {
183 corner[..interp_count].copy_from_slice(stored);
184 }
185 })
186 {
187 continue;
188 }
189
190 cache.ensure(block_x, block_z, noises);
192
193 noises.compute_noise_column(block_x, block_ys, block_z, blended_column);
195
196 let (y_batches, y_tail) = block_ys.as_chunks::<SIMD_BATCH>();
197 let (blended_batches, blended_tail) = blended_column.as_chunks::<SIMD_BATCH>();
198 debug_assert_eq!(y_batches.len(), blended_batches.len());
199 debug_assert_eq!(y_tail.len(), blended_tail.len());
200
201 for (batch, (ys, blended_noise_values)) in
202 y_batches.iter().zip(blended_batches.iter()).enumerate()
203 {
204 let cy = batch * SIMD_BATCH;
205 noises.fill_cell_corner_densities_y_simd::<SIMD_BATCH>(
206 cache,
207 block_x,
208 *ys,
209 block_z,
210 *blended_noise_values,
211 &mut values[..interp_count * SIMD_BATCH],
212 );
213 for lane in 0..SIMD_BATCH {
214 let corner_idx = cz * corners_y + cy + lane;
215 let base = corner_idx * MAX_INTERP;
216 let value_base = lane * interp_count;
217 slice[base..base + interp_count]
218 .copy_from_slice(&values[value_base..value_base + interp_count]);
219 }
220 }
221
222 let tail_start = y_batches.len() * SIMD_BATCH;
223 for (tail_offset, (&block_y, &blended_noise_value)) in
224 y_tail.iter().zip(blended_tail.iter()).enumerate()
225 {
226 let cy = tail_start + tail_offset;
227
228 noises.fill_cell_corner_densities(
229 cache,
230 block_x,
231 block_y,
232 block_z,
233 blended_noise_value,
234 &mut values[..interp_count],
235 );
236
237 let corner_idx = cz * corners_y + cy;
238 let base = corner_idx * MAX_INTERP;
239 slice[base..base + interp_count].copy_from_slice(&values[..interp_count]);
240 }
241
242 if users > 1 {
243 corner_columns.produce((cell_x, cell_z), users, || {
244 slice[column_base..column_base + column_len]
245 .as_chunks::<MAX_INTERP>()
246 .0
247 .iter()
248 .flat_map(|corner| &corner[..interp_count])
249 .copied()
250 .collect()
251 });
252 }
253 }
254 }
255
256 #[expect(
269 clippy::too_many_lines,
270 reason = "keeps the cell interpolation and block traversal together"
271 )]
272 pub fn fill<F>(
273 &mut self,
274 noises: &N,
275 cache: &mut N::ColumnCache,
276 beardifier: Option<&Beardifier>,
277 unsampled_air_min_y: i32,
278 corner_columns: &CornerColumnStore,
279 mut place_block: F,
280 ) where
281 F: FnMut(usize, i32, usize, f32, &[f32], &mut N::ColumnCache),
282 {
283 let cell_width = N::Settings::CELL_WIDTH;
284 let cell_height = N::Settings::CELL_HEIGHT;
285 let cell_count_xz = self.cell_count_xz;
286 let cell_count_y = self.cell_count_y;
287 let interp_count = self.interp_count;
288 let corners_y = self.corners_y;
289 let first_cell_x = self.first_cell_x;
290 let first_cell_z = self.first_cell_z;
291 let block_ys: &[i32] = &self.block_ys;
292
293 let n_slices = cell_count_xz + 1;
301 let mut local_blended = vec![0.0_f32; corners_y];
302 for (cx_off, slice) in self.slices[..n_slices].iter_mut().enumerate() {
303 let cell_x = first_cell_x + cx_off as i32;
304 Self::fill_slice_into(
305 slice,
306 cell_x,
307 block_ys,
308 &mut local_blended,
309 interp_count,
310 corners_y,
311 cell_count_xz,
312 first_cell_z,
313 noises,
314 cache,
315 corner_columns,
316 );
317 }
318
319 let air_channels = N::final_density_nonpositive_channels();
320 let mut air_cells = vec![false; cell_count_y];
321 let y_count = cell_height as usize;
322 debug_assert!(y_count <= MAX_CELL_HEIGHT);
323 let inverse_height = f32x4::splat(1.0 / cell_height as f32);
324 let channel_batches = interp_count.div_ceil(INTERPOLATION_LANES);
325 let all_batches = (1_usize << channel_batches) - 1;
326 let proving_batches = air_channels.map_or(0, |channels| {
327 channels.iter().fold(0, |mask, &channel| {
328 mask | (1 << (channel / INTERPOLATION_LANES))
329 })
330 });
331
332 let mut z_partials =
334 vec![[f32x4::splat(0.0); 2]; cell_width as usize * corners_y * channel_batches];
335
336 for cell_x_idx in 0..cell_count_xz {
337 let s0: &[f32] = &self.slices[cell_x_idx][..];
338 let s1: &[f32] = &self.slices[cell_x_idx + 1][..];
339
340 for cell_z_idx in 0..cell_count_xz {
341 let z0_base = cell_z_idx * corners_y;
342 let z1_base = (cell_z_idx + 1) * corners_y;
343
344 let cell_min_x = (self.first_cell_x + cell_x_idx as i32) * cell_width;
345 let cell_min_z = (self.first_cell_z + cell_z_idx as i32) * cell_width;
346 for (cell_y_idx, air) in air_cells.iter_mut().enumerate() {
347 let cell_min_world_y = (self.cell_min_y + cell_y_idx as i32) * cell_height;
348 *air = air_channels.is_some_and(|channels| {
349 cell_min_world_y >= unsampled_air_min_y
350 && !beardifier.is_some_and(|beard| {
351 beard.may_affect(
352 IVec3::new(cell_min_x, cell_min_world_y, cell_min_z),
353 IVec3::new(
354 cell_min_x + cell_width - 1,
355 cell_min_world_y + cell_height - 1,
356 cell_min_z + cell_width - 1,
357 ),
358 )
359 })
360 && channels.iter().all(|&ch| {
361 let i0 = (z0_base + cell_y_idx) * MAX_INTERP + ch;
362 let i1 = (z1_base + cell_y_idx) * MAX_INTERP + ch;
363 [i0, i0 + MAX_INTERP, i1, i1 + MAX_INTERP]
364 .into_iter()
365 .all(|i| s0[i] <= 0.0 && s1[i] <= 0.0)
366 })
367 });
368 }
369
370 for z_in_cell in 0..cell_width as usize {
371 let factor_z = f32x4::splat(z_in_cell as f32 / cell_width as f32);
372 for corner_y in 0..corners_y {
373 let i0 = (z0_base + corner_y) * MAX_INTERP;
374 let i1 = (z1_base + corner_y) * MAX_INTERP;
375 let row = (z_in_cell * corners_y + corner_y) * channel_batches;
376 for (batch, channel) in
377 (0..interp_count).step_by(INTERPOLATION_LANES).enumerate()
378 {
379 let corner = |slice: &[f32], base| {
380 f32x4::from_slice(unsafe {
386 slice.get_unchecked(
387 base + channel..base + channel + INTERPOLATION_LANES,
388 )
389 })
390 };
391
392 let left0 = corner(s0, i0);
393 let left1 = corner(s0, i1);
394 let right0 = corner(s1, i0);
395 let right1 = corner(s1, i1);
396 z_partials[row + batch] = [
397 left0 + factor_z * (left1 - left0),
398 right0 + factor_z * (right1 - right0),
399 ];
400 }
401 }
402 }
403
404 for x_in_cell in 0..cell_width {
405 let factor_x = x_in_cell as f32 / cell_width as f32;
406 let factor_x_v = f32x4::splat(factor_x);
407 let local_x = (cell_x_idx as i32 * cell_width + x_in_cell) as usize;
408
409 for z_in_cell in 0..cell_width {
410 let local_z = (cell_z_idx as i32 * cell_width + z_in_cell) as usize;
411
412 for cell_y_idx in (0..cell_count_y).rev() {
413 let cell_world_y = (self.cell_min_y + cell_y_idx as i32) * cell_height;
414 let row =
415 (z_in_cell as usize * corners_y + cell_y_idx) * channel_batches;
416 let mut values_by_y = [[0.0_f32; MAX_INTERP]; MAX_CELL_HEIGHT];
417
418 if N::interpolated_count() <= INTERPOLATION_LANES {
419 for (batch, channel) in
420 (0..interp_count).step_by(INTERPOLATION_LANES).enumerate()
421 {
422 let [v00, v10] = z_partials[row + batch];
423 let [v01, v11] = z_partials[row + channel_batches + batch];
424 let v0 = v00 + factor_x_v * (v10 - v00);
425 let v1 = v01 + factor_x_v * (v11 - v01);
426 let step = (v1 - v0) * inverse_height;
427 let mut value = v0 + step * f32x4::splat(0.0);
428
429 for values in values_by_y.iter_mut().take(y_count) {
430 values[channel..channel + INTERPOLATION_LANES]
431 .copy_from_slice(&value.to_array());
432 value += step;
433 }
434 }
435
436 if air_cells[cell_y_idx]
437 && air_channels.is_some_and(|channels| {
438 Self::channels_are_nonpositive(
439 &values_by_y[..y_count],
440 channels,
441 )
442 })
443 {
444 continue;
445 }
446 } else {
447 let fill_batches =
448 |batches: usize, values_by_y: &mut [[f32; MAX_INTERP]]| {
449 for (batch, channel) in (0..interp_count)
450 .step_by(INTERPOLATION_LANES)
451 .enumerate()
452 {
453 if batches & (1 << batch) == 0 {
454 continue;
455 }
456
457 let [v00, v10] = z_partials[row + batch];
458 let [v01, v11] =
459 z_partials[row + channel_batches + batch];
460 let v0 = v00 + factor_x_v * (v10 - v00);
461 let v1 = v01 + factor_x_v * (v11 - v01);
462 let step = (v1 - v0) * inverse_height;
463 let mut value = v0 + step * f32x4::splat(0.0);
464
465 for values in values_by_y.iter_mut().take(y_count) {
466 values[channel..channel + INTERPOLATION_LANES]
467 .copy_from_slice(&value.to_array());
468 value += step;
469 }
470 }
471 };
472 let first_batches = if air_cells[cell_y_idx] {
473 proving_batches
474 } else {
475 all_batches
476 };
477
478 fill_batches(first_batches, &mut values_by_y);
479
480 if air_cells[cell_y_idx]
481 && air_channels.is_some_and(|channels| {
482 Self::channels_are_nonpositive(
483 &values_by_y[..y_count],
484 channels,
485 )
486 })
487 {
488 continue;
489 }
490
491 let remaining_batches = all_batches & !first_batches;
492 if remaining_batches != 0 {
493 fill_batches(remaining_batches, &mut values_by_y);
494 }
495 }
496
497 for y_in_cell in (0..cell_height).rev() {
498 let world_y = cell_world_y + y_in_cell;
499 let interpolated = &values_by_y[y_in_cell as usize][..interp_count];
500
501 let mut density = noises.combine_interpolated(
506 cache,
507 &interpolated[..interp_count],
508 0,
509 world_y,
510 0,
511 );
512
513 let world_x = cell_x_idx as i32 * cell_width
520 + x_in_cell
521 + self.first_cell_x * cell_width;
522 let world_z = cell_z_idx as i32 * cell_width
523 + z_in_cell
524 + self.first_cell_z * cell_width;
525 if let Some(beard) = beardifier {
526 density += beard.compute(world_x, world_y, world_z) as f32;
527 }
528
529 place_block(
530 local_x,
531 world_y,
532 local_z,
533 density,
534 &interpolated[..interp_count],
535 cache,
536 );
537 }
538 }
539 }
540 }
541 }
542
543 }
546 }
547
548 fn channels_are_nonpositive(values_by_y: &[[f32; MAX_INTERP]], channels: &[usize]) -> bool {
549 values_by_y
550 .iter()
551 .all(|values| channels.iter().all(|&channel| values[channel] <= 0.0))
552 }
553
554 #[must_use]
563 pub fn prefill_material_ore_vein_values(
564 &self,
565 noises: &N,
566 cache: &mut N::ColumnCache,
567 ) -> Box<[f32]> {
568 let value_count = N::material_ore_vein_value_count();
569 if value_count == 0 {
570 return Box::default();
571 }
572
573 let cell_width = N::Settings::CELL_WIDTH;
574 let cell_height = N::Settings::CELL_HEIGHT;
575 let min_y = N::Settings::MIN_Y;
576 let value_height = N::material_ore_vein_value_height();
577 let mut values = vec![0.0; 16 * 16 * value_height * value_count];
578 let mut interpolated = [0.0_f32; MAX_INTERP];
579
580 for cell_x_idx in 0..self.cell_count_xz {
581 for cell_z_idx in 0..self.cell_count_xz {
582 let z0_base = cell_z_idx * self.corners_y;
583 let z1_base = (cell_z_idx + 1) * self.corners_y;
584 let s0 = &*self.slices[cell_x_idx];
585 let s1 = &*self.slices[cell_x_idx + 1];
586
587 for x_in_cell in 0..cell_width {
588 let factor_x = x_in_cell as f32 / cell_width as f32;
589 let local_x = (cell_x_idx as i32 * cell_width + x_in_cell) as usize;
590 let world_x =
591 self.first_cell_x * cell_width + cell_x_idx as i32 * cell_width + x_in_cell;
592
593 for z_in_cell in 0..cell_width {
594 let factor_z = z_in_cell as f32 / cell_width as f32;
595 let local_z = (cell_z_idx as i32 * cell_width + z_in_cell) as usize;
596 let world_z = self.first_cell_z * cell_width
597 + cell_z_idx as i32 * cell_width
598 + z_in_cell;
599
600 for cell_y_idx in (0..self.cell_count_y).rev() {
601 let i0_base = (z0_base + cell_y_idx) * MAX_INTERP;
602 let i1_base = (z1_base + cell_y_idx) * MAX_INTERP;
603 let i0_next = i0_base + MAX_INTERP;
604 let i1_next = i1_base + MAX_INTERP;
605 let mut values_by_y = [[0.0_f32; MAX_INTERP]; MAX_CELL_HEIGHT];
606 let y_count = cell_height as usize;
607 debug_assert!(y_count <= MAX_CELL_HEIGHT);
608
609 for channel in 0..self.interp_count {
610 let n000 = s0[i0_base + channel];
611 let n001 = s0[i1_base + channel];
612 let n100 = s1[i0_base + channel];
613 let n101 = s1[i1_base + channel];
614 let n010 = s0[i0_next + channel];
615 let n011 = s0[i1_next + channel];
616 let n110 = s1[i0_next + channel];
617 let n111 = s1[i1_next + channel];
618 let v00 = n000 + factor_z * (n001 - n000);
619 let v01 = n010 + factor_z * (n011 - n010);
620 let v10 = n100 + factor_z * (n101 - n100);
621 let v11 = n110 + factor_z * (n111 - n110);
622 let v0 = v00 + factor_x * (v10 - v00);
623 let v1 = v01 + factor_x * (v11 - v01);
624 let step = (v1 - v0) * (1.0_f32 / cell_height as f32);
625 let mut value = v0 + step * 0.0;
626 for values in values_by_y.iter_mut().take(y_count) {
627 values[channel] = value;
628 value += step;
629 }
630 }
631
632 for y_in_cell in (0..cell_height).rev() {
633 let world_y =
634 (self.cell_min_y + cell_y_idx as i32) * cell_height + y_in_cell;
635 let relative_y = (world_y - min_y) as usize;
636 if relative_y >= value_height {
637 continue;
638 }
639
640 interpolated[..self.interp_count].copy_from_slice(
641 &values_by_y[y_in_cell as usize][..self.interp_count],
642 );
643
644 let offset =
645 (relative_y * 16 * 16 + local_z * 16 + local_x) * value_count;
646 noises.fill_material_ore_vein_values(
647 cache,
648 &interpolated[..self.interp_count],
649 world_x,
650 world_y,
651 world_z,
652 &mut values[offset..offset + value_count],
653 );
654 }
655 }
656 }
657 }
658 }
659 }
660
661 values.into_boxed_slice()
662 }
663}
664
665#[cfg(test)]
666mod tests {
667 use steel_utils::random::{Random, legacy_random::LegacyRandom, xoroshiro::Xoroshiro};
668
669 use super::NoiseChunk;
670 use crate::density::{ColumnCache, DimensionNoises, NoiseSettings};
671 use crate::density_functions::{
672 end::EndNoises, nether::NetherNoises, overworld::OverworldNoises,
673 };
674 use crate::noise::CornerColumnStore;
675 use crate::noise_parameters::get_noise_parameters;
676
677 type Visit = ((usize, i32, usize), u32);
678
679 fn create_noises<N: DimensionNoises>() -> N {
680 let seed = 1;
681 let splitter = if N::Settings::LEGACY_RANDOM_SOURCE {
682 LegacyRandom::from_seed(seed).next_positional()
683 } else {
684 Xoroshiro::from_seed(seed).next_positional()
685 };
686 N::create(seed, &splitter, &get_noise_parameters())
687 }
688
689 fn fill_visits<N: DimensionNoises>(
690 noises: &N,
691 chunk: (i32, i32),
692 air_min_y: i32,
693 corner_columns: &CornerColumnStore,
694 ) -> Vec<Visit> {
695 let (min_x, min_z) = (chunk.0 * 16, chunk.1 * 16);
696 let mut noise_chunk = NoiseChunk::<N>::new(min_x, min_z);
697 let mut cache = N::ColumnCache::default();
698 cache.init_grid(min_x, min_z, noises);
699 let mut visits = Vec::new();
700 noise_chunk.fill(
701 noises,
702 &mut cache,
703 None,
704 air_min_y,
705 corner_columns,
706 |x, y, z, density, _, _| {
707 visits.push(((x, y, z), density.to_bits()));
708 },
709 );
710 visits
711 }
712
713 #[expect(
716 clippy::neg_cmp_op_on_partial_ord,
717 reason = "NaN counts as not positive, matching the aquifer's `density > 0` solid test"
718 )]
719 fn assert_air_skip_sound<N: DimensionNoises>() {
720 let noises = create_noises::<N>();
721
722 let mut skipped = 0;
723 for chunk in [(0, 0), (-7, 3), (40, -25), (-300, -120), (1000, 800)] {
724 let all = fill_visits(&noises, chunk, i32::MAX, &CornerColumnStore::default());
725 let mut kept = fill_visits(&noises, chunk, i32::MIN, &CornerColumnStore::default())
726 .into_iter()
727 .peekable();
728 for (pos, bits) in all {
729 if kept.peek().is_some_and(|&(kept_pos, _)| kept_pos == pos) {
730 let (_, kept_bits) = kept.next().expect("peeked");
731 assert_eq!(kept_bits, bits, "density changed at {pos:?} in {chunk:?}");
732 } else {
733 assert!(
734 !(f32::from_bits(bits) > 0.0),
735 "skipped solid block {pos:?} in {chunk:?}"
736 );
737 skipped += 1;
738 }
739 }
740 assert!(
741 kept.next().is_none(),
742 "skip run visited a block the full run did not"
743 );
744 }
745 assert!(skipped > 0, "no cell was skipped");
746 }
747
748 fn assert_corner_reuse_matches<N: DimensionNoises>() {
750 let noises = create_noises::<N>();
751 let shared = CornerColumnStore::default();
752 for x in -1..=1 {
753 for z in -1..=1 {
754 let chunk = (x + 30, z - 12);
755 assert_eq!(
756 fill_visits(&noises, chunk, i32::MIN, &shared),
757 fill_visits(&noises, chunk, i32::MIN, &CornerColumnStore::default()),
758 "chunk {chunk:?}"
759 );
760 }
761 }
762 }
763
764 #[test]
765 fn air_column_rejects_rounding_overshoot() {
766 let lower = -5.0 * f32::from_bits(1);
767 let upper = 0.0_f32;
768 assert!(lower <= 0.0 && upper <= 0.0);
769 let step = (upper - lower) * (1.0 / 8.0);
770 let mut value = lower + step * 0.0;
771 let mut values_by_y = [[0.0; super::MAX_INTERP]; 8];
772 for values in &mut values_by_y {
773 values[0] = value;
774 value += step;
775 }
776 assert!(values_by_y[7][0] > 0.0);
777 assert!(!NoiseChunk::<OverworldNoises>::channels_are_nonpositive(
778 &values_by_y,
779 &[0],
780 ));
781 }
782
783 #[test]
784 fn overworld_corner_reuse_matches() {
785 assert_corner_reuse_matches::<OverworldNoises>();
786 }
787
788 #[test]
789 fn nether_corner_reuse_matches() {
790 assert_corner_reuse_matches::<NetherNoises>();
791 }
792
793 #[test]
794 fn end_corner_reuse_matches() {
795 assert_corner_reuse_matches::<EndNoises>();
796 }
797
798 #[test]
799 fn overworld_air_skip_is_sound() {
800 assert_air_skip_sound::<OverworldNoises>();
801 }
802
803 #[test]
804 fn nether_air_skip_is_sound() {
805 assert_air_skip_sound::<NetherNoises>();
806 }
807
808 #[test]
809 fn end_air_skip_is_sound() {
810 assert_air_skip_sound::<EndNoises>();
811 }
812}