noise_functions/modifiers/
fbm.rs1#[cfg(feature = "nightly-simd")]
2use core::simd::{LaneCount, Simd, SupportedLaneCount};
3
4use crate::{
5 math::{fast_min, lerp},
6 Noise, Sample,
7};
8
9#[cfg(feature = "nightly-simd")]
10use crate::math::splat;
11
12use super::{fractal_bounding, Weighted};
13
14#[derive(Debug, Clone, Copy, PartialEq)]
20pub struct Fbm<Noise> {
21 pub noise: Noise,
22 pub octaves: u32,
23 pub gain: f32,
24 pub lacunarity: f32,
25 pub fractal_bounding: f32,
26}
27
28impl<N> Noise for Fbm<N> {}
29
30impl<Noise> Fbm<Noise> {
31 #[inline(always)]
32 pub const fn new(noise: Noise, octaves: u32, gain: f32, lacunarity: f32) -> Self {
33 Self {
34 noise,
35 octaves,
36 gain,
37 lacunarity,
38 fractal_bounding: fractal_bounding(octaves, gain),
39 }
40 }
41
42 #[inline(always)]
43 pub const fn weighted(self, strength: f32) -> Weighted<Self> {
44 Weighted { fractal: self, strength }
45 }
46}
47
48impl<Noise, const DIM: usize> Sample<DIM> for Fbm<Noise>
49where
50 Noise: Sample<DIM>,
51{
52 #[inline]
53 fn sample_with_seed(&self, point: [f32; DIM], seed: i32) -> f32 {
54 let &Fbm {
55 ref noise,
56 octaves,
57 gain,
58 lacunarity,
59 fractal_bounding,
60 } = self;
61 fbm(noise, octaves, gain, lacunarity, fractal_bounding, seed, point)
62 }
63}
64
65impl<Noise, const DIM: usize> Sample<DIM> for Weighted<Fbm<Noise>>
66where
67 Noise: Sample<DIM>,
68{
69 #[inline]
70 fn sample_with_seed(&self, point: [f32; DIM], seed: i32) -> f32 {
71 let &Weighted {
72 fractal: Fbm {
73 ref noise,
74 octaves,
75 gain,
76 lacunarity,
77 fractal_bounding,
78 },
79 strength: weighted_strength,
80 } = self;
81 weighted_fbm(noise, octaves, gain, lacunarity, fractal_bounding, weighted_strength, seed, point)
82 }
83}
84
85#[cfg(feature = "nightly-simd")]
86impl<Noise, const DIM: usize, const LANES: usize> Sample<DIM, Simd<f32, LANES>> for Fbm<Noise>
87where
88 Noise: Sample<DIM, Simd<f32, LANES>>,
89 LaneCount<LANES>: SupportedLaneCount,
90{
91 #[inline]
92 fn sample_with_seed(&self, point: Simd<f32, LANES>, seed: i32) -> f32 {
93 let &Fbm {
94 ref noise,
95 octaves,
96 gain,
97 lacunarity,
98 fractal_bounding,
99 } = self;
100 fbm_a(noise, octaves, gain, lacunarity, fractal_bounding, seed, point)
101 }
102}
103
104#[cfg(feature = "nightly-simd")]
105impl<Noise, const DIM: usize, const LANES: usize> Sample<DIM, Simd<f32, LANES>> for Weighted<Fbm<Noise>>
106where
107 Noise: Sample<DIM, Simd<f32, LANES>>,
108 LaneCount<LANES>: SupportedLaneCount,
109{
110 #[inline]
111 fn sample_with_seed(&self, point: Simd<f32, LANES>, seed: i32) -> f32 {
112 let &Weighted {
113 fractal: Fbm {
114 ref noise,
115 octaves,
116 gain,
117 lacunarity,
118 fractal_bounding,
119 },
120 strength: weighted_strength,
121 } = self;
122
123 weighted_fbm_a(noise, octaves, gain, lacunarity, fractal_bounding, weighted_strength, seed, point)
124 }
125}
126
127#[inline(always)]
128fn fbm<Noise, const DIM: usize>(noise: &Noise, octaves: u32, gain: f32, lacunarity: f32, fractal_bounding: f32, mut seed: i32, mut point: [f32; DIM]) -> f32
129where
130 Noise: Sample<DIM>,
131{
132 let mut sum = 0.0;
133 let mut amp = fractal_bounding;
134
135 for _ in 0..octaves {
136 let noise = noise.sample_with_seed(point, seed);
137 seed = seed.wrapping_add(1);
138 sum += noise * amp;
139
140 for x in &mut point {
141 *x *= lacunarity;
142 }
143
144 amp *= gain;
145 }
146
147 sum
148}
149
150#[cfg(feature = "nightly-simd")]
151#[inline(always)]
152fn fbm_a<Noise, const DIM: usize, const LANES: usize>(noise: &Noise, octaves: u32, gain: f32, lacunarity: f32, fractal_bounding: f32, mut seed: i32, mut point: Simd<f32, LANES>) -> f32
153where
154 Noise: Sample<DIM, Simd<f32, LANES>>,
155 LaneCount<LANES>: SupportedLaneCount,
156{
157 let mut sum = 0.0;
158 let mut amp = fractal_bounding;
159
160 for _ in 0..octaves {
161 let noise = noise.sample_with_seed(point, seed);
162 seed = seed.wrapping_add(1);
163 sum += noise * amp;
164
165 point *= splat(lacunarity);
166 amp *= gain;
167 }
168
169 sum
170}
171
172#[inline(always)]
173fn weighted_fbm<Noise, const DIM: usize>(noise: &Noise, octaves: u32, gain: f32, lacunarity: f32, fractal_bounding: f32, weighted_strength: f32, mut seed: i32, mut point: [f32; DIM]) -> f32
174where
175 Noise: Sample<DIM>,
176{
177 let mut sum = 0.0;
178 let mut amp = fractal_bounding;
179
180 for _ in 0..octaves {
181 let noise = noise.sample_with_seed(point, seed);
182 seed = seed.wrapping_add(1);
183 sum += noise * amp;
184 amp *= lerp(1.0, fast_min(noise + 1.0, 2.0) * 0.5, weighted_strength);
185
186 for x in &mut point {
187 *x *= lacunarity;
188 }
189
190 amp *= gain;
191 }
192
193 sum
194}
195
196#[cfg(feature = "nightly-simd")]
197#[inline(always)]
198fn weighted_fbm_a<Noise, const DIM: usize, const LANES: usize>(
199 noise: &Noise,
200 octaves: u32,
201 gain: f32,
202 lacunarity: f32,
203 fractal_bounding: f32,
204 weighted_strength: f32,
205 mut seed: i32,
206 mut point: Simd<f32, LANES>,
207) -> f32
208where
209 Noise: Sample<DIM, Simd<f32, LANES>>,
210 LaneCount<LANES>: SupportedLaneCount,
211{
212 let mut sum = 0.0;
213 let mut amp = fractal_bounding;
214
215 for _ in 0..octaves {
216 let noise = noise.sample_with_seed(point, seed);
217 seed = seed.wrapping_add(1);
218 sum += noise * amp;
219 amp *= lerp(1.0, fast_min(noise + 1.0, 2.0) * 0.5, weighted_strength);
220
221 point *= splat(lacunarity);
222 amp *= gain;
223 }
224
225 sum
226}