mc173/gen/
noise.rs

1//! Perlin and octaves noise generators at parity with Minecraft world generation.
2//! 
3//! Note that in this module, every usage of the primitive cast operator `as` that
4//! are narrowing the values are documented and justified to be at parity with the
5//! Java conversion.
6//! 
7//! Reference for the narrowing casts in Java: 
8//! <https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html>
9
10use std::mem;
11use std::fmt;
12
13use glam::{DVec3, DVec2, IVec3};
14
15use crate::rand::JavaRandom;
16
17
18/// A cube of given size for storing noise values.
19#[repr(transparent)]
20#[derive(Clone, PartialEq)]
21pub struct NoiseCube<const X: usize, const Y: usize, const Z: usize> {
22    inner: [[[f64; Y]; Z]; X],
23}
24
25impl<const X: usize, const Y: usize, const Z: usize> NoiseCube<X, Y, Z> {
26
27    #[inline]
28    pub fn new() -> Self {
29        Self {
30            inner: [[[0.0; Y]; Z]; X],
31        }
32    }
33
34    #[inline]
35    pub fn fill(&mut self, value: f64) {
36        self.inner = [[[value; Y]; Z]; X];
37    }
38
39    #[inline]
40    pub fn get(&self, x: usize, y: usize, z: usize) -> f64 {
41        self.inner[x][z][y]
42    }
43
44    #[inline]
45    pub fn set(&mut self, x: usize, y: usize, z: usize, value: f64) {
46        self.inner[x][z][y] = value;
47    }
48
49    #[inline]
50    pub fn add(&mut self, x: usize, y: usize, z: usize, value: f64) {
51        self.inner[x][z][y] += value;
52    }
53
54}
55
56impl<const X: usize, const Y: usize, const Z: usize> Default for NoiseCube<X, Y, Z> {
57    #[inline]
58    fn default() -> Self {
59        Self::new()
60    }
61}
62
63impl<const X: usize, const Y: usize, const Z: usize> fmt::Debug for NoiseCube<X, Y, Z> {
64    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65        f.debug_tuple("NoiseCube").field(&self.inner).finish()
66    }
67}
68
69
70impl NoiseCube<1, 1, 1> {
71
72    /// Create a reference to a 1x1x1 noise cube from a reference to a single f64.
73    #[inline(always)]
74    pub fn from_ref(value: &f64) -> &Self {
75        // We use this assert to ensure that size is correct, just to be sure.
76        const __ASSERT: () = assert!(mem::size_of::<NoiseCube<1, 1, 1>>() == mem::size_of::<f64>());
77        // SAFETY: both f64 and NoiseCube<1, 1, 1> have are guaranteed to have the same
78        // layout because NoiseCube is a transparent struct around an array, and arrays
79        // have a known layout where first element is at byte offset 0.
80        // This is almost identical to std::array::from_ref
81        unsafe { &*(value as *const f64 as *const Self) }
82    }
83    
84    /// Create a mutable reference to a 1x1x1 noise cube from a mutable reference to a 
85    /// single f64.
86    #[inline(always)]
87    pub fn from_mut(value: &mut f64) -> &mut Self {
88        // SAFETY: Same as above.
89        unsafe { &mut *(value as *mut f64 as *mut Self) }
90    }
91
92}
93
94
95/// A 3D/2D Perlin noise generator.
96#[derive(Debug, Clone)]
97pub struct PerlinNoise {
98    /// Offset applied to all position given to the generator.
99    offset: DVec3,
100    /// All permutations used by Perlin noise algorithm.
101    permutations: Box<[u16; 512]>,
102}
103
104impl PerlinNoise {
105
106    /// Create a new perlin noise initialized with the given RNG.
107    pub fn new(rand: &mut JavaRandom) -> Self {
108
109        // NOTE: The narrowing casts in this function are safe because with statically
110        // know that we are in range (512 or 256) and i32 or u16 is enough.
111
112        let offset = rand.next_double_vec() * 256.0;
113
114        let mut permutations = Box::new(std::array::from_fn::<u16, 512, _>(|i| {
115            if i <= 256 {
116                i as u16
117            } else {
118                0
119            }
120        }));
121
122        for index in 0usize..256 {
123            let permutation_index = rand.next_int_bounded(256 - index as i32) as usize + index;
124            permutations.swap(index, permutation_index);
125            permutations[index + 256] = permutations[index];
126        }
127
128        Self {
129            offset,
130            permutations,
131        }
132
133    }
134
135    /// Get the noise value at given 3D coordinates.
136    pub fn gen_3d_point(&self, pos: DVec3) -> f64 {
137
138        let mut pos = pos + self.offset;
139        let pos_floor = pos.floor();
140        pos -= pos_floor;
141        let factor = pos * pos * pos * (pos * (pos * 6.0 - 15.0) + 10.0);
142        
143        let pos_int = pos_floor.as_ivec3();
144        let x_index = (pos_int.x & 255) as usize;
145        let y_index = (pos_int.y & 255) as usize;
146        let z_index = (pos_int.z & 255) as usize;
147
148        let a = self.permutations[x_index] as usize + y_index;
149        let a0 = self.permutations[a] as usize + z_index;
150        let a1 = self.permutations[a + 1] as usize + z_index;
151        let b = self.permutations[x_index + 1] as usize + y_index;
152        let b0 = self.permutations[b] as usize + z_index;
153        let b1 = self.permutations[b + 1] as usize + z_index;
154
155        let DVec3 { x, y, z } = pos;
156
157        lerp(factor.z,
158            lerp(factor.y, 
159                lerp(factor.x, 
160                    grad3(self.permutations[a0], x      , y, z), 
161                    grad3(self.permutations[b0], x - 1.0, y, z)), 
162                lerp(factor.x,
163                    grad3(self.permutations[a1], x      , y - 1.0, z),
164                    grad3(self.permutations[b1], x - 1.0, y - 1.0, z))),
165            lerp(factor.y,
166                lerp(factor.x,
167                    grad3(self.permutations[a0 + 1], x      , y, z - 1.0),
168                    grad3(self.permutations[b0 + 1], x - 1.0, y, z - 1.0)),
169                lerp(factor.x,
170                    grad3(self.permutations[a1 + 1], x      , y - 1.0, z - 1.0),
171                    grad3(self.permutations[b1 + 1], x - 1.0, y - 1.0, z - 1.0))))
172
173    }
174
175    /// Get the noise value at given 2D coordinates.
176    pub fn gen_2d_point(&self, pos: DVec2) -> f64 {
177        self.gen_3d_point(pos.extend(0.0))
178    }
179
180    /// Generate a 3D noise cube at a given offset with the given scale and frequency.
181    pub fn gen_3d<const X: usize, const Y: usize, const Z: usize>(&self, 
182        cube: &mut NoiseCube<X, Y, Z>,
183        offset: DVec3,
184        scale: DVec3,
185        amplitude: f64
186    ) {
187        
188        let mut last_y_index = usize::MAX;
189
190        let mut x0 = 0.0;
191        let mut x1 = 0.0;
192        let mut x2 = 0.0;
193        let mut x3 = 0.0;
194
195        for x_cube in 0..X {
196            let (x, x_factor, x_index) = calc_pos((offset.x + x_cube as f64) * scale.x + self.offset.x);
197            for z_cube in 0..Z {
198                let (z, z_factor, z_index) = calc_pos((offset.z + z_cube as f64) * scale.z + self.offset.z);
199                for y_cube in 0..Y {
200                    let (y, y_factor, y_index) = calc_pos((offset.y + y_cube as f64) * scale.y + self.offset.y);
201
202                    if y_cube == 0 || y_index != last_y_index {
203                        
204                        last_y_index = y_index;
205
206                        let a = self.permutations[x_index] as usize + y_index;
207                        let a0 = self.permutations[a] as usize + z_index;
208                        let a1 = self.permutations[a + 1] as usize + z_index;
209                        let b = self.permutations[x_index + 1] as usize + y_index;
210                        let b0 = self.permutations[b] as usize + z_index;
211                        let b1 = self.permutations[b + 1] as usize + z_index;
212
213                        x0 = lerp(x_factor, 
214                            grad3(self.permutations[a0], x      , y, z), 
215                            grad3(self.permutations[b0], x - 1.0, y, z));
216                        x1 = lerp(x_factor,
217                            grad3(self.permutations[a1], x      , y - 1.0, z),
218                            grad3(self.permutations[b1], x - 1.0, y - 1.0, z));
219                        x2 = lerp(x_factor,
220                            grad3(self.permutations[a0 + 1], x      , y, z - 1.0),
221                            grad3(self.permutations[b0 + 1], x - 1.0, y, z - 1.0));
222                        x3 = lerp(x_factor,
223                            grad3(self.permutations[a1 + 1], x      , y - 1.0, z - 1.0),
224                            grad3(self.permutations[b1 + 1], x - 1.0, y - 1.0, z - 1.0));
225
226                    }
227
228                    let noise = lerp(z_factor, lerp(y_factor, x0, x1), lerp(y_factor, x2, x3));
229                    cube.add(x_cube, y_cube, z_cube, noise * amplitude);
230
231                }
232            }
233        }
234
235    }
236
237    /// Generate a 2D noise cube at a given offset with the given scale and frequency.
238    pub fn gen_2d<const X: usize, const Z: usize>(&self,
239        cube: &mut NoiseCube<X, 1, Z>,
240        offset: DVec2,
241        scale: DVec2,
242        amplitude: f64
243    ) {
244
245        for x_cube in 0..X {
246            let (x, x_factor, x_index) = calc_pos((offset.x + x_cube as f64) * scale.x + self.offset.x);
247            for z_cube in 0..Z {
248                let (z, z_factor, z_index) = calc_pos((offset.y + z_cube as f64) * scale.y + self.offset.z);
249                
250                let a = self.permutations[x_index] as usize + 0;
251                let a0 = self.permutations[a] as usize + z_index;
252                let b = self.permutations[x_index + 1] as usize + 0;
253                let b0 = self.permutations[b] as usize + z_index;
254
255                let noise = lerp(z_factor,
256                    lerp(x_factor,
257                        grad2(self.permutations[a0], x, z),
258                        grad3(self.permutations[b0], x - 1.0, 0.0, z)),
259                    lerp(x_factor,
260                        grad3(self.permutations[a0 + 1], x, 0.0, z - 1.0),
261                        grad3(self.permutations[b0 + 1], x - 1.0, 0.0, z - 1.0)));
262                
263                cube.add(x_cube, 0, z_cube, noise * amplitude);
264
265            }
266        }
267
268    }
269
270    /// Weird noise generation (a handcrafted noise generator used by Notchian server 
271    /// that uses the same type of permutations table and offset as the perlin noise, so
272    /// we use the same structure).
273    /// 
274    /// The function is to be renamed if the algorithm name is found.
275    pub fn gen_weird_2d<const X: usize, const Z: usize>(&self,
276        cube: &mut NoiseCube<X, 1, Z>,
277        offset: DVec2,
278        scale: DVec2,
279        amplitude: f64
280    ) {
281
282        let const_a: f64 = 0.5 * (f64::sqrt(3.0) - 1.0);
283        let const_b: f64 = (3.0 - f64::sqrt(3.0)) / 6.0;
284        
285        for x_noise in 0..X {
286            let x = (offset.x + x_noise as f64) * scale.x + self.offset.x;
287            for z_noise in 0..Z {
288                // NOTE: Using Y component of everything but this is interpreted as Z.
289                let z = (offset.y + z_noise as f64) * scale.y + self.offset.y;
290
291                let a = (x + z) * const_a;
292                let x_wrap = wrap(x + a);
293                let z_wrap = wrap(z + a);
294
295                let b = i32::wrapping_add(x_wrap, z_wrap) as f64 * const_b;
296                let x_wrap_b = x_wrap as f64 - b;
297                let z_wrap_b = z_wrap as f64 - b;
298
299                let x_delta = x - x_wrap_b;
300                let z_delta = z - z_wrap_b;
301
302                let (
303                    x_offset, 
304                    z_offset
305                ) = if x_delta > z_delta { (1, 0) } else { (0, 1) };
306
307                let x_delta0 = x_delta - x_offset as f64 + const_b;
308                let z_delta0 = z_delta - z_offset as f64 + const_b;
309                let x_delta1 = x_delta - 1.0 + 2.0 * const_b;
310                let z_delta1 = z_delta - 1.0 + 2.0 * const_b;
311
312                let x_index = (x_wrap & 255) as usize;
313                let z_index = (z_wrap & 255) as usize;
314
315                let v0_index = self.permutations[x_index + self.permutations[z_index] as usize] % 12;
316                let v1_index = self.permutations[x_index + x_offset + self.permutations[z_index + z_offset] as usize] % 12;
317                let v2_index = self.permutations[x_index + 1 + self.permutations[z_index + 1] as usize] % 12;
318
319                let v0 = calc_weird_noise(x_delta, z_delta, v0_index as usize);
320                let v1 = calc_weird_noise(x_delta0, z_delta0, v1_index as usize);
321                let v2 = calc_weird_noise(x_delta1, z_delta1, v2_index as usize);
322
323                cube.add(x_noise, 0, z_noise, 70.0 * (v0 + v1 + v2) * amplitude);
324
325            }
326        }
327
328    }
329
330}
331
332
333/// A Perlin-based octave noise generator.
334#[derive(Debug, Clone)]
335pub struct PerlinOctaveNoise {
336    /// Collection of generators for the different octaves.
337    generators: Box<[PerlinNoise]>
338}
339
340impl PerlinOctaveNoise {
341
342    /// Create a new Perlin-based octaves noise generator.
343    pub fn new(rand: &mut JavaRandom, octaves: usize) -> Self {
344        Self {
345            generators: (0..octaves)
346                .map(move |_| PerlinNoise::new(rand))
347                .collect::<Vec<_>>()
348                .into_boxed_slice(),
349        }
350    }
351
352    /// Get the noise value at given 3D coordinates.
353    pub fn gen_3d_point(&self, pos: DVec3) -> f64 {
354        let mut ret = 0.0;
355        let mut freq = 1.0;
356        for gen in &self.generators[..] {
357            ret += gen.gen_3d_point(pos * freq) / freq;
358            freq /= 2.0;
359        }
360        ret
361    }
362
363    /// Get the noise value at given 3D coordinates.
364    pub fn gen_2d_point(&self, pos: DVec2) -> f64 {
365        let mut ret = 0.0;
366        let mut freq = 1.0;
367        for gen in &self.generators[..] {
368            ret += gen.gen_2d_point(pos * freq) / freq;
369            freq /= 2.0;
370        }
371        ret
372    }
373
374    /// Generate a 3D noise cube at a given offset with the given scale and frequency.
375    pub fn gen_3d<const X: usize, const Y: usize, const Z: usize>(&self, 
376        cube: &mut NoiseCube<X, Y, Z>,
377        offset: DVec3,
378        scale: DVec3
379    ) {
380        cube.fill(0.0);
381        let mut freq = 1.0;
382        for gen in &self.generators[..] {
383            gen.gen_3d(cube, offset, scale * freq, 1.0 / freq);
384            freq /= 2.0;
385        }
386    }
387
388    /// Generate a 2D noise cube at a given offset with the given scale and frequency.
389    pub fn gen_2d<const X: usize, const Z: usize>(&self,
390        cube: &mut NoiseCube<X, 1, Z>,
391        offset: DVec2,
392        scale: DVec2,
393    ) {
394        cube.fill(0.0);
395        let mut freq = 1.0;
396        for gen in &self.generators[..] {
397            gen.gen_2d(cube, offset, scale * freq, 1.0 / freq);
398            freq /= 2.0;
399        }
400    }
401
402    /// Weird noise generation (a handcrafted noise generator used by Notchian server 
403    /// that uses the same type of permutations table and offset as the Perlin noise, so
404    /// we use the same structure).
405    /// 
406    /// The function is to be renamed if the algorithm name is found.
407    pub fn gen_weird_2d<const X: usize, const Z: usize>(&self,
408        cube: &mut NoiseCube<X, 1, Z>,
409        offset: DVec2,
410        scale: DVec2,
411        freq_factor: f64,
412    ) {
413        cube.fill(0.0);
414        let scale = scale / 1.5;
415        let mut freq = 1.0;
416        let mut amplitude = 0.55;
417        for gen in &self.generators[..] {
418            gen.gen_weird_2d(cube, offset, scale * freq, amplitude);
419            freq *= freq_factor;
420            amplitude *= 2.0;
421        }
422    }
423
424}
425
426
427#[inline]
428fn lerp(factor: f64, from: f64, to: f64) -> f64 {
429    from + factor * (to - from)
430}
431
432#[inline]
433fn grad3(value: u16, x: f64, y: f64, z: f64) -> f64 {
434    let value = value & 15;
435    let a = if value < 8 { x } else { y };
436    let b = if value < 4 { y } else if value != 12 && value != 14 { z } else { x };
437    (if value & 1 == 0 { a } else { -a }) + (if value & 2 == 0 { b } else { -b })
438}
439
440#[inline]
441fn grad2(value: u16, x: f64, z: f64) -> f64 {
442    let value = value & 15;
443    let a = (1 - ((value & 8) >> 3)) as f64 * x;
444    let b = if value < 4 { 0.0 } else if value != 12 && value != 14 { z } else { x };
445    (if value & 1 == 0 { a } else { -a }) + (if value & 2 == 0 { b } else { -b })
446}
447
448#[inline]
449fn calc_pos(mut pos: f64) -> (f64, f64, usize) {
450
451    // PARITY: This function is really important to reproduce the infamous "Far Lands",
452    // the manual floor implementation is really important to keep like this, and avoid
453    // using the `.floor` function that just ignore the integer overflow rule that 
454    // saturate the integer value if the floating point value is too high.
455
456    let mut floor = pos as i32;
457    if pos < floor as f64 {
458        floor = floor.wrapping_sub(1);
459    }
460
461    pos -= floor as f64;
462    let factor = pos * pos * pos * (pos * (pos * 6.0 - 15.0) + 10.0);
463    let index = (floor & 255) as usize;
464
465    (pos, factor, index)
466
467}
468
469#[inline]
470fn wrap(value: f64) -> i32 {
471    let ret = value as i32;
472    if value > 0.0 { ret } else { ret.wrapping_sub(1) }
473}
474
475#[inline]
476fn calc_weird_noise(x_delta: f64, z_delta: f64, index: usize) -> f64 {
477
478    static WEIRD_TABLE: [IVec3; 12] = [
479        IVec3::new(1, 1, 0),
480        IVec3::new(-1, 1, 0),
481        IVec3::new(1, -1, 0),
482        IVec3::new(-1, -1, 0),
483        IVec3::new(1, 0, 1),
484        IVec3::new(-1, 0, 1),
485        IVec3::new(1, 0, -1),
486        IVec3::new(-1, 0, -1),
487        IVec3::new(0, 1, 1),
488        IVec3::new(0, -1, 1),
489        IVec3::new(0, 1, -1),
490        IVec3::new(0, -1, -1),
491    ];
492
493    let tmp = 0.5 - x_delta * x_delta - z_delta * z_delta;
494    if tmp < 0.0 {
495        0.0
496    } else {
497        let tmp = tmp * tmp;
498        let weird = WEIRD_TABLE[index];
499        tmp * tmp * (weird.x as f64 * x_delta + weird.y as f64 * z_delta)
500    }
501
502}