1#[derive(Debug, Clone, Copy, PartialEq)]
19pub struct PerlinNoise {
21 pub persistence: f32,
23 pub octaves: f32,
25 pub seed_x: i32,
27 pub seed_y: i32,
29}
30
31const PI: f32 = std::f32::consts::PI;
32
33fn cosf(x: f32) -> f32 {
34 crate::crt::cosf(x)
35}
36
37fn powf(base: f32, exp: f32) -> f32 {
39 (base as f64).powf(exp as f64) as f32
40}
41
42impl PerlinNoise {
43 pub fn new(persistence: f32, octaves: f32, seed_x: i32, seed_y: i32) -> Self {
47 PerlinNoise {
48 persistence,
49 octaves,
50 seed_x,
51 seed_y,
52 }
53 }
54
55 #[inline]
56 fn hash(&self, x: i32, y: i32) -> u32 {
57 (self.seed_y.wrapping_add(y) as u32)
58 .wrapping_mul(0x39)
59 .wrapping_add(self.seed_x as u32)
60 .wrapping_add(x as u32)
61 }
62
63 #[inline]
64 fn value(n: u32) -> f32 {
65 let n = n ^ n.wrapping_mul(0x2000);
66 let m = n
67 .wrapping_mul(n)
68 .wrapping_mul(0x3d73)
69 .wrapping_add(0xc0ae5)
70 .wrapping_mul(n)
71 .wrapping_add(0xd208dd0d)
72 & 0x7fff_ffff;
73 1.0 - m as f32 * 9.313_226e-10
74 }
75
76 pub fn random(&self, x: i32, y: i32) -> f32 {
80 Self::value(self.hash(x, y))
81 }
82
83 pub fn smooth_noise(&self, x: i32, y: i32) -> f32 {
88 let c = self.hash(x, y);
89 let v = |d: i32| Self::value(c.wrapping_add(d as u32));
90 let corners = v(-0x38) + v(-0x3a) + v(0x38) + v(0x3a);
91 let sides = v(1) + v(-1) + v(-0x39) + v(0x39);
92 corners * 0.0625 + sides * 0.125 + v(0) * 0.25
93 }
94
95 pub fn generate(&self, x: f32, y: f32) -> f32 {
99 let mut total = 0.0f32;
100 let mut i = 0i32;
101 while (i as f32) < self.octaves {
102 let freq = powf(2.0, i as f32);
103 let (fx, fy) = (freq * x, freq * y);
104 let (ix, iy) = (fx as i32, fy as i32);
105 let a = (1.0 - cosf((fx - ix as f32) * PI)) * 0.5;
106 let b = (1.0 - cosf((fy - iy as f32) * PI)) * 0.5;
107 let top = self.smooth_noise(ix, iy) * (1.0 - a) + self.smooth_noise(ix + 1, iy) * a;
108 let bottom =
109 self.smooth_noise(ix, iy + 1) * (1.0 - a) + self.smooth_noise(ix + 1, iy + 1) * a;
110 let v = top * (1.0 - b) + bottom * b;
111 total += v * powf(self.persistence, i as f32);
112 i += 1;
113 }
114 total
115 }
116}
117
118#[cfg(test)]
119mod golden {
120 use super::*;
122
123 #[test]
124 fn matches_original_engine() {
125 let n = PerlinNoise::new(0.5, 5.0, 12345, -6789);
126 for (x, y, expected) in [
127 (0.0f32, 0.0f32, 0.6994447112083435f32),
128 (1.25, 7.5, -0.048602547496557236),
129 (100.75, 33.1, 0.27891477942466736),
130 (511.5, 255.25, 0.1838667392730713),
131 ] {
132 assert_eq!(n.generate(x, y).to_bits(), expected.to_bits(), "({x}, {y})");
133 }
134 }
135}