1use crate::math::{fade, fract, lerp};
2
3pub fn pcg(v: u32) -> u32 {
4 let s = v.wrapping_mul(747796405).wrapping_add(2891336453);
5 let w = ((s >> ((s >> 28) + 4)) ^ s).wrapping_mul(277803737);
6 (w >> 22) ^ w
7}
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
10pub enum Hash {
11 #[default]
12 Xor,
13 Nested,
14}
15
16pub fn hash3(p: [i32; 3], hash: Hash) -> f32 {
17 match hash {
18 Hash::Xor => hash3_xor(p),
19 Hash::Nested => hash3_nested(p),
20 }
21}
22
23fn hash3_xor(p: [i32; 3]) -> f32 {
24 let h = (p[0] as u32).wrapping_mul(73856093)
25 ^ (p[1] as u32).wrapping_mul(19349663)
26 ^ (p[2] as u32).wrapping_mul(83492791);
27 (pcg(h) >> 8) as f32 / 16777216.0
28}
29
30fn hash3_nested(p: [i32; 3]) -> f32 {
31 let h = pcg((p[0] as u32).wrapping_mul(73856093)
32 ^ pcg((p[1] as u32).wrapping_mul(19349663) ^ pcg((p[2] as u32).wrapping_mul(83492791))));
33 (h >> 8) as f32 / 16777216.0
34}
35
36pub fn hash2(p: [i32; 2], seed: u32) -> f32 {
37 let mut h = (p[0] as u32).wrapping_mul(0x8da6_b343) ^ (p[1] as u32).wrapping_mul(0xd816_3841);
38 h ^= seed;
39 h ^= h >> 15;
40 h = h.wrapping_mul(0x2c1b_3c6d);
41 h ^= h >> 12;
42 h = h.wrapping_mul(0x297a_2d39);
43 h ^= h >> 15;
44 (h >> 8) as f32 / 16777216.0
45}
46
47pub fn value_noise3(p: [f32; 3], hash: Hash) -> f32 {
48 match hash {
49 Hash::Xor => lattice_noise(p, hash3_xor),
50 Hash::Nested => lattice_noise(p, hash3_nested),
51 }
52}
53
54fn lattice_noise(p: [f32; 3], hash: fn([i32; 3]) -> f32) -> f32 {
55 let i = [
56 p[0].floor() as i32,
57 p[1].floor() as i32,
58 p[2].floor() as i32,
59 ];
60 let f = [p[0] - i[0] as f32, p[1] - i[1] as f32, p[2] - i[2] as f32];
61 let u = [fade(f[0]), fade(f[1]), fade(f[2])];
62 let h = |x, y, z| hash([i[0] + x, i[1] + y, i[2] + z]);
63 let a = lerp(
64 lerp(h(0, 0, 0), h(1, 0, 0), u[0]),
65 lerp(h(0, 1, 0), h(1, 1, 0), u[0]),
66 u[1],
67 );
68 let b = lerp(
69 lerp(h(0, 0, 1), h(1, 0, 1), u[0]),
70 lerp(h(0, 1, 1), h(1, 1, 1), u[0]),
71 u[1],
72 );
73 lerp(a, b, u[2])
74}
75
76pub fn fbm3(p: [f32; 3], octaves: i32, hash: Hash) -> f32 {
77 fbm3_filtered(p, octaves, 0.0, hash)
78}
79
80pub fn octave_weight(footprint: f32) -> f32 {
81 let t = ((footprint - 0.5) * 2.0).clamp(0.0, 1.0);
82 1.0 - t * t * (3.0 - 2.0 * t)
83}
84
85pub fn fbm3_filtered(p: [f32; 3], octaves: i32, footprint: f32, hash: Hash) -> f32 {
86 let mut sum = 0.0;
87 let mut amp = 0.5;
88 let mut q = p;
89 let mut width = footprint;
90 for _ in 0..octaves.max(0) {
91 let weight = octave_weight(width);
92 if weight == 1.0 {
93 sum += amp * value_noise3(q, hash);
94 } else if weight == 0.0 {
95 sum += amp * 0.5;
96 } else {
97 sum += amp * (weight * value_noise3(q, hash) + (1.0 - weight) * 0.5);
98 }
99 q = [q[0] * 2.03 + 1.7, q[1] * 2.03 + 9.2, q[2] * 2.03 + 3.1];
100 amp *= 0.5;
101 width *= 2.03;
102 }
103 sum
104}
105
106pub fn noise2(p: [f32; 2], seed: u32) -> f32 {
107 let i = [p[0].floor() as i32, p[1].floor() as i32];
108 let f = [p[0] - i[0] as f32, p[1] - i[1] as f32];
109 let u = [fade(f[0]), fade(f[1])];
110 let a = lerp(hash2(i, seed), hash2([i[0] + 1, i[1]], seed), u[0]);
111 let b = lerp(
112 hash2([i[0], i[1] + 1], seed),
113 hash2([i[0] + 1, i[1] + 1], seed),
114 u[0],
115 );
116 lerp(a, b, u[1])
117}
118
119pub fn hash21(p: [f32; 2]) -> f32 {
120 let mut q = [fract(p[0] * 123.34), fract(p[1] * 456.21)];
121 let d = q[0] * (q[0] + 45.32) + q[1] * (q[1] + 45.32);
122 q[0] += d;
123 q[1] += d;
124 fract(q[0] * q[1])
125}
126
127pub fn value_noise2_signed(p: [f32; 2]) -> f32 {
128 let i = [p[0].floor(), p[1].floor()];
129 let f = [p[0] - i[0], p[1] - i[1]];
130 let u = [fade(f[0]), fade(f[1])];
131 let h = |x: f32, y: f32| hash21([i[0] + x, i[1] + y]);
132 let a = lerp(h(0.0, 0.0), h(1.0, 0.0), u[0]);
133 let b = lerp(h(0.0, 1.0), h(1.0, 1.0), u[0]);
134 lerp(a, b, u[1]) * 2.0 - 1.0
135}
136
137pub fn flow(p: [f32; 2], time: f32) -> f32 {
138 let warp = [
139 value_noise2_signed([p[0] * 0.006 + time * 0.010, p[1] * 0.006 + 3.1]),
140 value_noise2_signed([p[0] * 0.006 + 7.7, p[1] * 0.006 - time * 0.008]),
141 ];
142 value_noise2_signed([
143 p[0] * 0.009 + warp[0] * 1.6 + time * 0.006,
144 p[1] * 0.009 + warp[1] * 1.6,
145 ]) * 0.65
146 + value_noise2_signed([
147 p[0] * 0.021 - warp[0],
148 p[1] * 0.021 - warp[1] + time * 0.009,
149 ]) * 0.35
150}
151
152fn lattice_plane_x(i: [i32; 3]) -> [f32; 4] {
153 [
154 hash3_xor(i),
155 hash3_xor([i[0], i[1] + 1, i[2]]),
156 hash3_xor([i[0], i[1], i[2] + 1]),
157 hash3_xor([i[0], i[1] + 1, i[2] + 1]),
158 ]
159}
160
161fn lattice_plane_y(i: [i32; 3]) -> [f32; 4] {
162 [
163 hash3_xor(i),
164 hash3_xor([i[0] + 1, i[1], i[2]]),
165 hash3_xor([i[0], i[1], i[2] + 1]),
166 hash3_xor([i[0] + 1, i[1], i[2] + 1]),
167 ]
168}
169
170fn lattice_plane_z(i: [i32; 3]) -> [f32; 4] {
171 [
172 hash3_xor(i),
173 hash3_xor([i[0] + 1, i[1], i[2]]),
174 hash3_xor([i[0], i[1] + 1, i[2]]),
175 hash3_xor([i[0] + 1, i[1] + 1, i[2]]),
176 ]
177}
178
179fn bilerp4(p: [f32; 4], u: f32, v: f32) -> f32 {
180 lerp(lerp(p[0], p[1], u), lerp(p[2], p[3], u), v)
181}
182
183pub fn value_noise3_gradient(p: [f32; 3], scale: f32) -> [f32; 3] {
184 let q = p.map(|v| v * scale);
185 let i = q.map(|v| v.floor() as i32);
186 let f = q.map(|v| v - v.floor());
187 let [u, v, w] = f.map(fade);
188 let left = q.map(|v| (v - 0.35).floor() != v.floor());
189 let right = q.map(|v| (v + 0.35).floor() != v.floor());
190 let x0 = lattice_plane_x(i);
191 let x1 = lattice_plane_x([i[0] + 1, i[1], i[2]]);
192 let xlo = if left[0] {
193 lattice_plane_x([i[0] - 1, i[1], i[2]])
194 } else {
195 x0
196 };
197 let xhi = if right[0] {
198 lattice_plane_x([i[0] + 2, i[1], i[2]])
199 } else {
200 x1
201 };
202 let xminus = lerp(
203 bilerp4(xlo, v, w),
204 bilerp4(if left[0] { x0 } else { x1 }, v, w),
205 fade(fract(q[0] - 0.35)),
206 );
207 let xplus = lerp(
208 bilerp4(if right[0] { x1 } else { x0 }, v, w),
209 bilerp4(xhi, v, w),
210 fade(fract(q[0] + 0.35)),
211 );
212 let y0 = [x0[0], x1[0], x0[2], x1[2]];
213 let y1 = [x0[1], x1[1], x0[3], x1[3]];
214 let ylo = if left[1] {
215 lattice_plane_y([i[0], i[1] - 1, i[2]])
216 } else {
217 y0
218 };
219 let yhi = if right[1] {
220 lattice_plane_y([i[0], i[1] + 2, i[2]])
221 } else {
222 y1
223 };
224 let yminus = lerp(
225 bilerp4(ylo, u, w),
226 bilerp4(if left[1] { y0 } else { y1 }, u, w),
227 fade(fract(q[1] - 0.35)),
228 );
229 let yplus = lerp(
230 bilerp4(if right[1] { y1 } else { y0 }, u, w),
231 bilerp4(yhi, u, w),
232 fade(fract(q[1] + 0.35)),
233 );
234 let z0 = [x0[0], x1[0], x0[1], x1[1]];
235 let z1 = [x0[2], x1[2], x0[3], x1[3]];
236 let zlo = if left[2] {
237 lattice_plane_z([i[0], i[1], i[2] - 1])
238 } else {
239 z0
240 };
241 let zhi = if right[2] {
242 lattice_plane_z([i[0], i[1], i[2] + 2])
243 } else {
244 z1
245 };
246 let zminus = lerp(
247 bilerp4(zlo, u, v),
248 bilerp4(if left[2] { z0 } else { z1 }, u, v),
249 fade(fract(q[2] - 0.35)),
250 );
251 let zplus = lerp(
252 bilerp4(if right[2] { z1 } else { z0 }, u, v),
253 bilerp4(zhi, u, v),
254 fade(fract(q[2] + 0.35)),
255 );
256 [
257 (xplus - xminus) / 0.7,
258 (yplus - yminus) / 0.7,
259 (zplus - zminus) / 0.7,
260 ]
261}
262
263#[cfg(test)]
264mod tests {
265 use super::{Hash, fbm3, fbm3_filtered, octave_weight, value_noise3, value_noise3_gradient};
266
267 #[test]
268 fn wobble_reuses_hashes_without_changing_the_noise() {
269 for sample in 0..1000 {
270 let p = [
271 sample as f32 * 0.013 - 6.0,
272 sample as f32 * 0.007 - 2.0,
273 sample as f32 * 0.031 - 9.0,
274 ];
275 let q = p.map(|v| v * 640.0);
276 let n =
277 |x: f32, y: f32, z: f32| value_noise3([q[0] + x, q[1] + y, q[2] + z], Hash::Xor);
278 let expected = [
279 (n(0.35, 0.0, 0.0) - n(-0.35, 0.0, 0.0)) / 0.7,
280 (n(0.0, 0.35, 0.0) - n(0.0, -0.35, 0.0)) / 0.7,
281 (n(0.0, 0.0, 0.35) - n(0.0, 0.0, -0.35)) / 0.7,
282 ];
283 let actual = value_noise3_gradient(p, 640.0);
284 for axis in 0..3 {
285 assert!(
286 (actual[axis] - expected[axis]).abs() < 0.0001,
287 "sample {sample} axis {axis}: actual {} expected {} q {q:?}",
288 actual[axis],
289 expected[axis]
290 );
291 }
292 }
293 }
294
295 #[test]
296 fn filtered_octaves_are_continuous_and_deterministic_across_zoom() {
297 let p = [0.212, -0.354, 0.477];
298 assert_eq!(
299 fbm3_filtered(p, 4, 0.0, Hash::Nested),
300 fbm3(p, 4, Hash::Nested)
301 );
302 let mut previous = 1.0;
303 for step in 0..=1000 {
304 let footprint = step as f32 * 0.002;
305 let weight = octave_weight(footprint);
306 assert!(weight <= previous);
307 previous = weight;
308 let left = fbm3_filtered(p, 4, footprint, Hash::Nested);
309 let right = fbm3_filtered(p, 4, footprint + 0.00001, Hash::Nested);
310 assert!((left - right).abs() < 0.0001);
311 assert_eq!(left, fbm3_filtered(p, 4, footprint, Hash::Nested));
312 }
313 assert_eq!(octave_weight(0.5), 1.0);
314 assert_eq!(octave_weight(1.0), 0.0);
315 }
316}