1use crate::choice_enum;
15use glam::{Vec2, Vec3};
16
17choice_enum!(
18 NoiseKind { Value, Perlin, Worley, Ridged, Billow, Cellular } default Perlin
20);
21
22fn hash(mut x: u32) -> u32 {
23 x ^= x >> 16;
24 x = x.wrapping_mul(0x7feb_352d);
25 x ^= x >> 15;
26 x = x.wrapping_mul(0x846c_a68b);
27 x ^ (x >> 16)
28}
29
30fn lattice(x: i32, y: i32, z: i32, seed: u32) -> u32 {
31 hash(hash(hash(x as u32 ^ seed.wrapping_mul(0x9e37_79b1)).wrapping_add(y as u32)).wrapping_add(z as u32))
32}
33
34fn unit(h: u32) -> f32 {
35 (h >> 8) as f32 / 16_777_216.0
36}
37
38fn fade(t: f32) -> f32 {
39 t * t * t * (t * (t * 6.0 - 15.0) + 10.0)
40}
41
42fn lerp(a: f32, b: f32, t: f32) -> f32 {
43 a + (b - a) * t
44}
45
46pub fn value3(p: Vec3, seed: u32) -> f32 {
48 if !p.is_finite() {
49 return 0.0;
50 }
51 let (i, f) = (p.floor(), p - p.floor());
52 let (x, y, z) = (i.x as i32, i.y as i32, i.z as i32);
53 let (u, v, w) = (fade(f.x), fade(f.y), fade(f.z));
54 let c = |dx: i32, dy: i32, dz: i32| unit(lattice(x.wrapping_add(dx), y.wrapping_add(dy), z.wrapping_add(dz), seed)) * 2.0 - 1.0;
55 lerp(
56 lerp(lerp(c(0, 0, 0), c(1, 0, 0), u), lerp(c(0, 1, 0), c(1, 1, 0), u), v),
57 lerp(lerp(c(0, 0, 1), c(1, 0, 1), u), lerp(c(0, 1, 1), c(1, 1, 1), u), v),
58 w,
59 )
60}
61
62fn gradient(h: u32) -> Vec3 {
63 const G: [Vec3; 12] = [
65 Vec3::new(1.0, 1.0, 0.0),
66 Vec3::new(-1.0, 1.0, 0.0),
67 Vec3::new(1.0, -1.0, 0.0),
68 Vec3::new(-1.0, -1.0, 0.0),
69 Vec3::new(1.0, 0.0, 1.0),
70 Vec3::new(-1.0, 0.0, 1.0),
71 Vec3::new(1.0, 0.0, -1.0),
72 Vec3::new(-1.0, 0.0, -1.0),
73 Vec3::new(0.0, 1.0, 1.0),
74 Vec3::new(0.0, -1.0, 1.0),
75 Vec3::new(0.0, 1.0, -1.0),
76 Vec3::new(0.0, -1.0, -1.0),
77 ];
78 G[(h % 12) as usize]
79}
80
81pub fn perlin3(p: Vec3, seed: u32) -> f32 {
83 if !p.is_finite() {
84 return 0.0;
85 }
86 let (i, f) = (p.floor(), p - p.floor());
87 let (x, y, z) = (i.x as i32, i.y as i32, i.z as i32);
88 let (u, v, w) = (fade(f.x), fade(f.y), fade(f.z));
89 let d = |dx: i32, dy: i32, dz: i32| {
90 gradient(lattice(x.wrapping_add(dx), y.wrapping_add(dy), z.wrapping_add(dz), seed))
91 .dot(f - Vec3::new(dx as f32, dy as f32, dz as f32))
92 };
93 lerp(
94 lerp(lerp(d(0, 0, 0), d(1, 0, 0), u), lerp(d(0, 1, 0), d(1, 1, 0), u), v),
95 lerp(lerp(d(0, 0, 1), d(1, 0, 1), u), lerp(d(0, 1, 1), d(1, 1, 1), u), v),
96 w,
97 ) * 0.9
98}
99
100pub fn worley3(p: Vec3, seed: u32) -> (f32, f32) {
102 if !p.is_finite() {
103 return (0.0, 0.0);
104 }
105 let (i, f) = (p.floor(), p - p.floor());
106 let (mut f1, mut f2) = (8.0f32, 8.0f32);
107 for dz in -1..=1 {
108 for dy in -1..=1 {
109 for dx in -1..=1 {
110 let (x, y, z) = ((i.x as i32).wrapping_add(dx), (i.y as i32).wrapping_add(dy), (i.z as i32).wrapping_add(dz));
111 let point =
112 Vec3::new(unit(lattice(x, y, z, seed)), unit(lattice(x, y, z, seed ^ 0x51ed)), unit(lattice(x, y, z, seed ^ 0xa2b3)));
113 let d = (Vec3::new(dx as f32, dy as f32, dz as f32) + point - f).length();
114 if d < f1 {
115 f2 = f1;
116 f1 = d;
117 } else if d < f2 {
118 f2 = d;
119 }
120 }
121 }
122 }
123 (f1, f2)
124}
125
126#[derive(Clone, Copy, Debug, PartialEq)]
128pub struct Noise {
129 pub kind: NoiseKind,
131 pub seed: u32,
133 pub frequency: f32,
135 pub octaves: u32,
137 pub lacunarity: f32,
139 pub gain: f32,
141 pub warp: f32,
143}
144
145impl Default for Noise {
146 fn default() -> Self {
147 Self { kind: NoiseKind::Perlin, seed: 0, frequency: 1.0, octaves: 4, lacunarity: 2.0, gain: 0.5, warp: 0.0 }
148 }
149}
150
151impl Noise {
152 pub fn new(kind: NoiseKind) -> Self {
154 Self { kind, ..Default::default() }
155 }
156 pub fn with_seed(mut self, seed: u32) -> Self {
158 self.seed = seed;
159 self
160 }
161 pub fn with_frequency(mut self, f: f32) -> Self {
163 self.frequency = f;
164 self
165 }
166 pub fn with_octaves(mut self, o: u32) -> Self {
168 self.octaves = o;
169 self
170 }
171 pub fn with_warp(mut self, w: f32) -> Self {
173 self.warp = w;
174 self
175 }
176
177 fn single(&self, p: Vec3, seed: u32) -> f32 {
178 match self.kind {
179 NoiseKind::Value => value3(p, seed),
180 NoiseKind::Perlin => perlin3(p, seed),
181 NoiseKind::Worley => {
182 let (f1, _) = worley3(p, seed);
183 1.0 - 2.0 * f1.min(1.0)
184 }
185 NoiseKind::Cellular => {
186 let (f1, f2) = worley3(p, seed);
187 ((f2 - f1) * 2.0).min(1.0) * 2.0 - 1.0
188 }
189 NoiseKind::Ridged => 1.0 - 2.0 * perlin3(p, seed).abs().min(1.0),
190 NoiseKind::Billow => 2.0 * perlin3(p, seed).abs().min(1.0) - 1.0,
191 }
192 }
193
194 fn fractal(&self, p: Vec3, seed: u32) -> f32 {
195 let (mut sum, mut amp, mut norm, mut q) = (0.0, 1.0, 0.0, p);
196 for o in 0..self.octaves.clamp(1, 32) {
197 sum += amp * self.single(q, seed.wrapping_add(o * 101));
198 norm += amp;
199 amp *= self.gain;
200 q *= self.lacunarity;
201 }
202 sum / norm
203 }
204
205 pub fn sample3(&self, x: f32, y: f32, z: f32) -> f32 {
208 let mut p = Vec3::new(x, y, z) * self.frequency;
209 if self.warp > 0.0 {
210 let offset = Vec3::new(
211 self.fractal(p, self.seed ^ 0x1111),
212 self.fractal(p + Vec3::splat(17.3), self.seed ^ 0x2222),
213 self.fractal(p + Vec3::splat(41.7), self.seed ^ 0x3333),
214 );
215 p += offset * self.warp;
216 }
217 let sample = self.fractal(p, self.seed);
218 if sample.is_finite() {
219 sample.clamp(-1.0, 1.0)
220 } else {
221 0.0
222 }
223 }
224
225 pub fn sample2(&self, x: f32, y: f32) -> f32 {
227 self.sample3(x, y, 0.0)
228 }
229
230 pub fn sample01(&self, x: f32, y: f32, z: f32) -> f32 {
232 self.sample3(x, y, z) * 0.5 + 0.5
233 }
234
235 pub fn curl2(&self, x: f32, y: f32) -> Vec2 {
237 let e = 0.01 / self.frequency.max(1e-3);
238 let dx = (self.sample2(x + e, y) - self.sample2(x - e, y)) / (2.0 * e);
239 let dy = (self.sample2(x, y + e) - self.sample2(x, y - e)) / (2.0 * e);
240 Vec2::new(dy, -dx)
241 }
242
243 pub fn image(&self, width: u32, height: u32, a: [f32; 3], b: [f32; 3]) -> Vec<u8> {
246 self.try_image(width, height, a, b).unwrap_or_default()
247 }
248
249 pub fn try_image(&self, width: u32, height: u32, a: [f32; 3], b: [f32; 3]) -> Result<Vec<u8>, String> {
252 let pixels = (width as usize).checked_mul(height as usize).ok_or("image dimensions overflow")?;
253 if pixels == 0 || pixels > 16_777_216 || a.iter().chain(b.iter()).any(|c| !c.is_finite()) {
254 return Err("noise image requires finite colours and 1..16777216 pixels".into());
255 }
256 let mut out = Vec::with_capacity(pixels * 4);
257 for y in 0..height {
258 for x in 0..width {
259 let t = self.sample01(x as f32 / width as f32, y as f32 / height as f32, 0.0);
260 for c in 0..3 {
261 out.push(((lerp(a[c], b[c], t)).clamp(0.0, 1.0) * 255.0) as u8);
262 }
263 out.push(255);
264 }
265 }
266 Ok(out)
267 }
268}
269
270pub const TEXTURE_PREFIX: &str = "noise:";
272
273#[derive(Clone, Copy, Debug, PartialEq)]
276pub struct NoiseTexture {
277 pub noise: Noise,
279 pub size: u32,
281 pub dark: [f32; 3],
283 pub light: [f32; 3],
285}
286
287impl NoiseTexture {
288 pub fn new(noise: Noise) -> Self {
290 Self { noise, size: 256, dark: [0.05, 0.05, 0.06], light: [0.95, 0.95, 0.97] }
291 }
292
293 pub fn colors(mut self, dark: [f32; 3], light: [f32; 3]) -> Self {
295 self.dark = dark;
296 self.light = light;
297 self
298 }
299
300 pub fn key(&self) -> String {
302 use crate::reflect::Choice;
303 let n = &self.noise;
304 format!(
305 "{TEXTURE_PREFIX}{}:{},{},{},{},{},{},{},{},{},{},{},{},{}",
306 NoiseKind::OPTIONS[n.kind.to_index()],
307 n.seed,
308 n.frequency,
309 n.octaves,
310 n.lacunarity,
311 n.gain,
312 n.warp,
313 self.size,
314 self.dark[0],
315 self.dark[1],
316 self.dark[2],
317 self.light[0],
318 self.light[1],
319 self.light[2]
320 )
321 }
322
323 pub fn parse(key: &str) -> Option<NoiseTexture> {
325 use crate::reflect::Choice;
326 let (kind, rest) = key.strip_prefix(TEXTURE_PREFIX)?.split_once(':')?;
327 let kind = NoiseKind::OPTIONS.iter().position(|o| *o == kind).map(NoiseKind::from_index)?;
328 let n: Vec<&str> = rest.split(',').collect();
329 if n.len() != 13 {
330 return None;
331 }
332 let real = |i: usize| n[i].parse::<f32>().ok().filter(|v| v.is_finite());
333 Some(NoiseTexture {
334 noise: Noise {
335 kind,
336 seed: n[0].parse().ok()?,
337 frequency: real(1)?,
338 octaves: n[2].parse().ok()?,
339 lacunarity: real(3)?,
340 gain: real(4)?,
341 warp: real(5)?,
342 },
343 size: n[6].parse::<u32>().ok()?.clamp(8, 2048),
344 dark: [real(7)?, real(8)?, real(9)?],
345 light: [real(10)?, real(11)?, real(12)?],
346 })
347 }
348
349 pub fn rgba(&self) -> Vec<u8> {
351 let size = self.size.clamp(8, 2048);
352 self.noise.image(size, size, self.dark, self.light)
353 }
354}
355
356#[cfg(test)]
357mod tests {
358 use super::*;
359
360 #[test]
361 fn deterministic_and_in_range() {
362 for kind in [NoiseKind::Value, NoiseKind::Perlin, NoiseKind::Worley, NoiseKind::Ridged, NoiseKind::Billow, NoiseKind::Cellular] {
363 let n = Noise { kind, octaves: 3, warp: 0.4, ..Default::default() }.with_seed(7);
364 let (mut lo, mut hi) = (f32::MAX, f32::MIN);
365 for i in 0..2000 {
366 let (x, y) = ((i % 50) as f32 * 0.37, (i / 50) as f32 * 0.41);
367 let v = n.sample2(x, y);
368 assert!((-1.0..=1.0).contains(&v), "{kind:?} out of range: {v}");
369 assert_eq!(v, n.sample2(x, y), "{kind:?} not deterministic");
370 lo = lo.min(v);
371 hi = hi.max(v);
372 }
373 assert!(hi - lo > 0.3, "{kind:?} is nearly constant ({lo}..{hi})");
374 }
375 }
376
377 #[test]
378 fn different_seeds_differ_and_noise_is_continuous() {
379 let a = Noise::default().with_seed(1).sample2(3.3, 4.4);
380 let b = Noise::default().with_seed(2).sample2(3.3, 4.4);
381 assert_ne!(a, b);
382 let n = Noise::default();
383 assert!((n.sample2(1.0, 1.0) - n.sample2(1.0005, 1.0)).abs() < 0.01);
384 }
385
386 #[test]
387 fn texture_key_roundtrip() {
388 let t = NoiseTexture::new(Noise::new(NoiseKind::Worley).with_seed(5).with_frequency(8.0)).colors([0.1, 0.2, 0.3], [0.9, 0.8, 0.7]);
389 assert_eq!(NoiseTexture::parse(&t.key()), Some(t));
390 assert_eq!(t.rgba().len(), (t.size * t.size * 4) as usize);
391 }
392}