1use std::simd::Simd;
2
3use crate::noise::ImprovedNoise;
4use crate::random::{PositionalRandom, Random, RandomSource, RandomSplitter, name_hash::NameHash};
5
6const INPUT_FACTOR: f64 = 1.018_126_888_217_522_7;
7const TARGET_DEVIATION: f64 = 1.0 / 3.0;
8const DEVIATION_COEFFICIENT: f64 = 0.270_224_783_124_521_1;
9
10#[derive(Debug, Clone)]
11struct Layer {
12 noise: ImprovedNoise,
13 frequency: f64,
14 amplitude: f32,
15}
16
17#[derive(Debug, Clone)]
19pub struct NormalNoise {
20 layers: Box<[Layer]>,
21 max_value: f64,
22}
23
24impl NormalNoise {
25 #[must_use]
26 pub fn create(
28 splitter: &RandomSplitter,
29 noise_id: &str,
30 first_octave: i32,
31 amplitudes: &[f64],
32 ) -> Self {
33 let mut random = splitter.with_hash_of(&NameHash::new(noise_id));
34 Self::create_from_random(&mut random, first_octave, amplitudes)
35 }
36
37 #[must_use]
38 pub fn create_from_random(
40 random: &mut RandomSource,
41 first_octave: i32,
42 amplitudes: &[f64],
43 ) -> Self {
44 let base_amplitude = parity_base_amplitude(first_octave, amplitudes);
45 Self::create_from_random_with_params(
46 random,
47 first_octave,
48 base_amplitude,
49 amplitudes.len() as i32,
50 true,
51 amplitudes,
52 )
53 }
54
55 #[must_use]
56 pub fn create_legacy_nether_biome(
61 random: &mut RandomSource,
62 first_octave: i32,
63 amplitudes: &[f64],
64 ) -> Self {
65 assert!(!amplitudes.is_empty(), "Need at least one octave");
66 assert!(
67 first_octave <= 0 && -first_octave >= amplitudes.len() as i32 - 1,
68 "Positive octaves are disabled for legacy Nether noise"
69 );
70 let base_amplitude = parity_base_amplitude(first_octave, amplitudes);
71 let octaves = build_octaves(
72 first_octave,
73 base_amplitude,
74 amplitudes.len() as i32,
75 true,
76 amplitudes,
77 );
78 let target_amplitude = octaves
79 .iter()
80 .map(|octave| octave.amplitude.abs())
81 .sum::<f64>();
82 let value_factor =
83 (normalization_factor(target_amplitude, &octaves) * base_amplitude) as f32;
84 let mut layers = Vec::with_capacity(octaves.len() * 2);
85 Self::append_legacy_layers(
86 random,
87 first_octave,
88 amplitudes,
89 1.0,
90 value_factor,
91 &mut layers,
92 );
93 Self::append_legacy_layers(
94 random,
95 first_octave,
96 amplitudes,
97 INPUT_FACTOR,
98 value_factor,
99 &mut layers,
100 );
101 let max_value = f64::from(
102 layers
103 .iter()
104 .fold(0.0_f32, |value, layer| value + layer.amplitude.abs() * 2.0),
105 );
106 Self {
107 layers: layers.into_boxed_slice(),
108 max_value,
109 }
110 }
111
112 fn append_legacy_layers(
113 random: &mut RandomSource,
114 first_octave: i32,
115 amplitudes: &[f64],
116 stack_frequency: f64,
117 stack_amplitude: f32,
118 layers: &mut Vec<Layer>,
119 ) {
120 let zero_index = (-first_octave) as usize;
121 let mut noises = vec![None; amplitudes.len()];
122 let zero_noise = ImprovedNoise::new(random);
124 if zero_index < noises.len() && amplitudes[zero_index] != 0.0 {
125 noises[zero_index] = Some(zero_noise);
126 }
127 for index in (0..zero_index).rev() {
128 if index < noises.len() && amplitudes[index] != 0.0 {
129 noises[index] = Some(ImprovedNoise::new(random));
130 } else {
131 random.consume_count(262);
132 }
133 }
134 let mut frequency = 2.0_f64.powi(first_octave);
135 let mut amplitude = 2.0_f64.powi(amplitudes.len() as i32 - 1)
136 / (2.0_f64.powi(amplitudes.len() as i32) - 1.0);
137 for (noise, modifier) in noises.into_iter().zip(amplitudes) {
138 if let Some(noise) = noise {
139 layers.push(Layer {
140 noise,
141 frequency: frequency * stack_frequency,
142 amplitude: (amplitude * modifier) as f32 * stack_amplitude,
143 });
144 }
145 frequency *= 2.0;
146 amplitude /= 2.0;
147 }
148 }
149
150 #[must_use]
151 pub fn create_with_params(
153 splitter: &RandomSplitter,
154 noise_id: &str,
155 base_octave: i32,
156 base_amplitude: f64,
157 octave_count: i32,
158 normalize: bool,
159 amplitude_modifiers: &[f64],
160 ) -> Self {
161 let mut random = splitter.with_hash_of(&NameHash::new(noise_id));
162 Self::create_from_random_with_params(
163 &mut random,
164 base_octave,
165 base_amplitude,
166 octave_count,
167 normalize,
168 amplitude_modifiers,
169 )
170 }
171
172 #[must_use]
173 pub fn create_from_random_with_params(
175 random: &mut RandomSource,
176 base_octave: i32,
177 base_amplitude: f64,
178 octave_count: i32,
179 normalize: bool,
180 amplitude_modifiers: &[f64],
181 ) -> Self {
182 let octaves = build_octaves(
183 base_octave,
184 base_amplitude,
185 octave_count,
186 normalize,
187 amplitude_modifiers,
188 );
189 let target_amplitude = octaves
190 .iter()
191 .map(|octave| octave.amplitude.abs())
192 .sum::<f64>();
193 let normalization_factor = normalization_factor(target_amplitude, &octaves);
194
195 let first_random = random.next_positional();
196 let second_random = random.next_positional();
197 let mut layers = Vec::with_capacity(octaves.len() * 2);
198 for octave in octaves {
199 let name = format!("octave_{}", octave.index);
200 let mut first = first_random.with_hash_of(&NameHash::new(&name));
201 let mut second = second_random.with_hash_of(&NameHash::new(&name));
202 let amplitude = (normalization_factor * octave.amplitude) as f32;
203 layers.push(Layer {
204 noise: ImprovedNoise::new(&mut first),
205 frequency: octave.frequency,
206 amplitude,
207 });
208 layers.push(Layer {
209 noise: ImprovedNoise::new(&mut second),
210 frequency: octave.frequency * INPUT_FACTOR,
211 amplitude,
212 });
213 }
214
215 Self {
216 layers: layers.into_boxed_slice(),
217 max_value: target_amplitude * TARGET_DEVIATION * 6.0,
218 }
219 }
220
221 #[inline]
222 #[must_use]
223 pub fn get_value(&self, x: f64, y: f64, z: f64) -> f32 {
225 self.layers.iter().fold(0.0_f32, |value, layer| {
226 value
227 + layer.amplitude
228 * layer.noise.noise(
229 x * layer.frequency,
230 y * layer.frequency,
231 z * layer.frequency,
232 )
233 })
234 }
235
236 #[inline]
237 #[must_use]
238 pub fn get_value_xz(&self, x: f64, z: f64) -> f32 {
240 self.layers.iter().fold(0.0_f32, |value, layer| {
241 value
242 + layer.amplitude
243 * layer
244 .noise
245 .noise_xz(x * layer.frequency, z * layer.frequency)
246 })
247 }
248
249 #[inline]
250 #[must_use]
251 pub fn get_value_xy(&self, x: f64, y: f64) -> f32 {
253 self.layers.iter().fold(0.0_f32, |value, layer| {
254 value
255 + layer.amplitude
256 * layer
257 .noise
258 .noise_xy(x * layer.frequency, y * layer.frequency)
259 })
260 }
261
262 #[inline]
263 #[must_use]
264 pub fn get_value_y_simd<const N: usize>(
266 &self,
267 x: f64,
268 ys: Simd<f64, N>,
269 z: f64,
270 ) -> Simd<f32, N> {
271 let mut value = Simd::splat(0.0_f32);
272 for layer in &self.layers {
273 let noise = layer.noise.noise_y_simd(
274 x * layer.frequency,
275 ys * Simd::splat(layer.frequency),
276 z * layer.frequency,
277 );
278 value += Simd::splat(layer.amplitude) * noise;
279 }
280 value
281 }
282
283 #[inline]
284 #[must_use]
285 pub const fn max_value(&self) -> f64 {
287 self.max_value
288 }
289}
290
291#[derive(Debug, Clone, Copy)]
292struct Octave {
293 index: i32,
294 frequency: f64,
295 amplitude: f64,
296}
297
298fn build_octaves(
299 base_octave: i32,
300 base_amplitude: f64,
301 octave_count: i32,
302 normalize: bool,
303 amplitude_modifiers: &[f64],
304) -> Vec<Octave> {
305 let mut frequency = 2.0_f64.powi(base_octave);
306 let mut amplitude = base_amplitude;
307 if normalize {
308 amplitude *= normalization_constant(octave_count);
309 }
310
311 let mut octaves = Vec::with_capacity(octave_count as usize);
312 for index in 0..octave_count {
313 let modifier = amplitude_modifiers
314 .get(index as usize)
315 .copied()
316 .unwrap_or(1.0);
317 if modifier != 0.0 {
318 octaves.push(Octave {
319 index: base_octave + index,
320 frequency,
321 amplitude: amplitude * modifier,
322 });
323 }
324 frequency *= 2.0;
325 amplitude *= 0.5;
326 }
327 octaves
328}
329
330fn normalization_constant(octave_count: i32) -> f64 {
331 2.0_f64.powi(octave_count - 1) / (2.0_f64.powi(octave_count) - 1.0)
332}
333
334fn normalization_factor(target_amplitude: f64, octaves: &[Octave]) -> f64 {
335 let variance = octaves
336 .iter()
337 .map(|octave| (DEVIATION_COEFFICIENT * octave.amplitude.abs()).powi(2))
338 .sum::<f64>();
339 if variance == 0.0 {
340 return 0.0;
341 }
342 target_amplitude * TARGET_DEVIATION / (variance.sqrt() * 2.0_f64.sqrt())
343}
344
345fn parity_normalization_factor(base_amplitude: f64, amplitudes: &[f64]) -> f64 {
346 let mut range = None;
347 for (index, amplitude) in amplitudes.iter().enumerate() {
348 if *amplitude != 0.0 {
349 range = Some(match range {
350 Some((first, _)) => (first, index as i32),
351 None => (index as i32, index as i32),
352 });
353 }
354 }
355 let Some((first, last)) = range else {
356 return 0.0;
357 };
358 base_amplitude * 0.5 * TARGET_DEVIATION / (0.1 * (1.0 + 1.0 / f64::from(last - first + 1)))
359}
360
361fn parity_base_amplitude(first_octave: i32, amplitudes: &[f64]) -> f64 {
362 if amplitudes.is_empty() {
363 return 1.0;
364 }
365 let octaves = build_octaves(first_octave, 1.0, amplitudes.len() as i32, true, amplitudes);
366 let target_amplitude = octaves
367 .iter()
368 .map(|octave| octave.amplitude.abs())
369 .sum::<f64>();
370 let new_factor = normalization_factor(target_amplitude, &octaves);
371 if new_factor == 0.0 {
372 return 1.0;
373 }
374 parity_normalization_factor(1.0, amplitudes) / new_factor
375}
376
377#[cfg(test)]
378mod tests {
379 use super::*;
380 use crate::random::{RandomSource, legacy_random::LegacyRandom};
381 use std::simd::{f64x4, f64x8};
382
383 #[test]
384 fn zero_axis_helpers_match_full_noise() {
385 let samples = [
386 (0.0, -0.0),
387 (1.25, -30.75),
388 (-1000.0, 4096.5),
389 (33_554_431.5, -33_554_432.25),
390 (-0.000_000_1, 0.000_000_1),
391 ];
392 for seed in [0, 42, 98_765] {
393 for amplitudes in [&[1.0, 0.0, 1.0, 1.0, 0.5][..], &[0.0; 5][..]] {
394 let mut random = RandomSource::Legacy(LegacyRandom::from_seed(seed));
395 let noise = NormalNoise::create_from_random(&mut random, -6, amplitudes);
396 for (a, b) in samples {
397 assert_eq!(
398 noise.get_value_xz(a, b).to_bits(),
399 noise.get_value(a, 0.0, b).to_bits(),
400 "XZ: seed={seed}, ({a}, {b})"
401 );
402 assert_eq!(
403 noise.get_value_xy(a, b).to_bits(),
404 noise.get_value(a, b, 0.0).to_bits(),
405 "XY: seed={seed}, ({a}, {b})"
406 );
407 }
408 }
409 }
410 }
411
412 #[test]
413 fn value_y_simd_matches_scalar_f32_lanes() {
414 let mut random = RandomSource::Legacy(LegacyRandom::from_seed(42));
415 let noise = NormalNoise::create_from_random(&mut random, -7, &[1.0, 1.0, 1.0]);
416 let x = 123.25;
417 let z = -456.75;
418 let ys4 = [0.0, 64.5, -12.25, 255.75];
419 let simd4 = noise.get_value_y_simd(x, f64x4::from_array(ys4), z);
420
421 for (lane, y) in ys4.into_iter().enumerate() {
422 assert_eq!(simd4[lane].to_bits(), noise.get_value(x, y, z).to_bits());
423 }
424
425 let ys8 = [0.0, 64.5, -12.25, 255.75, -64.0, 1.25, 320.0, 4096.5];
426 let simd8 = noise.get_value_y_simd(x, f64x8::from_array(ys8), z);
427 for (lane, y) in ys8.into_iter().enumerate() {
428 assert_eq!(simd8[lane].to_bits(), noise.get_value(x, y, z).to_bits());
429 }
430 }
431}