funutd/
map3gen.rs

1//! Texture generators.
2
3use super::color::*;
4use super::distance::*;
5use super::dna::*;
6use super::ease::*;
7use super::map3::*;
8use super::map3base::*;
9use super::math::*;
10use super::noise::*;
11use super::voronoi::*;
12use super::*;
13extern crate alloc;
14use alloc::boxed::Box;
15
16/// Generate a distance metric.
17pub fn gen_metric(dna: &mut Dna, name: &str) -> Distance {
18    dna.choice(
19        name,
20        [
21            (1.0, "1-norm", Distance::Norm1),
22            (4.0, "2-norm", Distance::Norm2),
23            (1.0, "4-norm", Distance::Norm4),
24            (1.0, "8-norm", Distance::Norm8),
25            (1.0, "max norm", Distance::NormMax),
26        ],
27    )
28}
29
30/// Generate an ease that is smooth near zero.
31pub fn gen_ease_smooth(dna: &mut Dna, name: &str) -> Ease {
32    dna.choice(
33        name,
34        [
35            (1.0, "smooth3", Ease::Smooth3),
36            (2.0, "smooth5", Ease::Smooth5),
37            (1.0, "smooth7", Ease::Smooth7),
38            (1.0, "smooth9", Ease::Smooth9),
39            (1.0, "squared", Ease::Squared),
40            (1.0, "cubed", Ease::Cubed),
41            (1.0, "up arc", Ease::UpArc),
42        ],
43    )
44}
45
46/// Generate an ease suitable for the Voronoi basis.
47pub fn gen_ease_voronoi(dna: &mut Dna, name: &str) -> Ease {
48    dna.choice(
49        name,
50        [
51            (1.0, "id", Ease::Id),
52            (1.0, "smooth3", Ease::Smooth3),
53            (1.0, "smooth5", Ease::Smooth5),
54            (1.0, "smooth7", Ease::Smooth7),
55            (1.0, "smooth9", Ease::Smooth9),
56            (1.0, "squared", Ease::Squared),
57        ],
58    )
59}
60
61/// Generate an ease.
62pub fn gen_ease(dna: &mut Dna, name: &str) -> Ease {
63    dna.choice(
64        name,
65        [
66            (1.0, "id", Ease::Id),
67            (1.0, "smooth3", Ease::Smooth3),
68            (1.0, "smooth5", Ease::Smooth5),
69            (1.0, "smooth7", Ease::Smooth7),
70            (1.0, "smooth9", Ease::Smooth9),
71            (1.0, "sqrt", Ease::Sqrt),
72            (1.0, "squared", Ease::Squared),
73            (1.0, "cubed", Ease::Cubed),
74            (1.0, "down arc", Ease::DownArc),
75            (1.0, "up arc", Ease::UpArc),
76        ],
77    )
78}
79
80/// Generate a texture with a palette.
81pub fn genmap3palette(complexity: f32, tiling: TilingMode, dna: &mut Dna) -> Box<dyn Texture> {
82    match tiling {
83        TilingMode::None => genmap3palette_hasher(complexity, tile_none(), dna),
84        TilingMode::Z => genmap3palette_hasher(complexity, tile_z(), dna),
85        TilingMode::XY => genmap3palette_hasher(complexity, tile_xy(), dna),
86        TilingMode::All => genmap3palette_hasher(complexity, tile_all(), dna),
87    }
88}
89
90/// Generate a texture with a palette.
91pub fn genmap3palette_hasher<H: 'static + Hasher>(
92    complexity: f32,
93    hasher: H,
94    dna: &mut Dna,
95) -> Box<dyn Texture> {
96    let h1 = dna.f32("hue 1");
97    let s1 = dna.f32("saturation 1");
98    let l1 = dna.f32_xform("lightness 1", sqrt);
99    let h2 = dna.f32("hue 2");
100    let s2 = dna.f32("saturation 2");
101    let l2 = dna.f32_xform("lightness 2", sqrt);
102    let h3 = dna.f32("hue 3");
103    let s3 = dna.f32("saturation 3");
104    let l3 = dna.f32_xform("lightness 3", sqrt);
105    let map = genmap3_hasher(complexity, false, hasher, dna);
106
107    palette(h1, s1, l1, h2, s2, l2, h3, s3, l3, map)
108}
109
110/// Generate a texture.
111pub fn genmap3(complexity: f32, tiling: TilingMode, dna: &mut Dna) -> Box<dyn Texture> {
112    match tiling {
113        TilingMode::None => genmap3_hasher(complexity, false, tile_none(), dna),
114        TilingMode::Z => genmap3_hasher(complexity, false, tile_z(), dna),
115        TilingMode::XY => genmap3_hasher(complexity, false, tile_xy(), dna),
116        TilingMode::All => genmap3_hasher(complexity, false, tile_all(), dna),
117    }
118}
119
120/// Generate a texture.
121pub fn genmap3_hasher<H: 'static + Hasher>(
122    complexity: f32,
123    is_fractal: bool,
124    hasher: H,
125    dna: &mut Dna,
126) -> Box<dyn Texture> {
127    let basis_weight = if complexity <= 10.0 {
128        1.5
129    } else if complexity <= 40.0 {
130        0.8
131    } else if complexity <= 80.0 {
132        0.4
133    } else {
134        0.2
135    };
136    let unary_weight = if complexity >= 20.0 {
137        1.5
138    } else if complexity >= 5.0 {
139        1.0
140    } else {
141        0.01
142    };
143    let binary_weight = if complexity >= 8.0 { 1.0 } else { 0.01 };
144    let fractal_weight: f32 = if is_fractal {
145        // If we are a child of a fractalizer, we cannot start a new one.
146        0.0
147    } else if complexity >= 9.0 {
148        0.8
149    } else {
150        // A small weight is enough to allow the user to edit the value in the editor.
151        0.01
152    };
153
154    let choice = dna.index(
155        "node type",
156        [
157            (basis_weight, "basis"),
158            (unary_weight, "unary"),
159            (binary_weight, "binary"),
160            (fractal_weight, "fractal"),
161        ],
162    );
163
164    if choice == 0 {
165        dna.group();
166        // Generate 1 octave of something.
167        let seed = dna.u32("seed") as u64;
168        let frequency = if is_fractal {
169            // The frequency comes from the fractalizer so we can choose any value.
170            2.0
171        } else {
172            dna.f32_xform("frequency", |x| xerp(2.0, 32.0, x))
173        };
174        let texture: Box<dyn Texture> = match dna.index(
175            "basis",
176            [
177                (1.0, "gradient noise"),
178                (1.0, "value noise"),
179                (1.0, "Voronoi"),
180                (0.5, "camo"),
181            ],
182        ) {
183            0 => noise(seed, frequency, hasher.clone()),
184            1 => {
185                dna.group();
186                let ease = gen_ease_smooth(dna, "noise ease");
187                dna.ungroup();
188                vnoise(seed, frequency, ease, hasher.clone())
189            }
190            2 => {
191                dna.group();
192                let pattern_x = dna.u32_in("Voronoi X pattern", 0, 25);
193                let pattern_y = dna.u32_in("Voronoi Y pattern", 0, 25);
194                let pattern_z = dna.u32_in("Voronoi Z pattern", 0, 25);
195                let ease = gen_ease_voronoi(dna, "Voronoi ease");
196                let metric = gen_metric(dna, "distance metric");
197                dna.ungroup();
198                voronoi(
199                    seed,
200                    frequency,
201                    ease,
202                    metric,
203                    hasher.clone(),
204                    pattern_x as usize,
205                    pattern_y as usize,
206                    pattern_z as usize,
207                )
208            }
209            _ => {
210                dna.group();
211                let border = dna.generate(|dna| {
212                    if dna.index("border", [(0.5, "on"), (0.5, "off")]) == 0 {
213                        dna.f32_in("border width", 0.01, 0.10)
214                    } else {
215                        0.0
216                    }
217                });
218                let sharpness = dna.f32_in("camo sharpness", 0.0, 1.0);
219                let gradient = dna.f32_in("camo gradient", 0.0, 1.0);
220                let ease = gen_ease_smooth(dna, "camo ease");
221                let metric = gen_metric(dna, "distance metric");
222                dna.ungroup();
223                camo(
224                    seed,
225                    frequency,
226                    ease,
227                    metric,
228                    hasher.clone(),
229                    border,
230                    sharpness,
231                    gradient,
232                )
233            }
234        };
235        dna.ungroup();
236        texture
237    } else if choice == 1 {
238        // Shape a map with a unary operator.
239        dna.group();
240        let child_complexity = complexity * 0.5 - 1.0;
241        let unary_node = match dna.index(
242            "unary node",
243            [
244                (1.0, "saturate"),
245                (1.0, "posterize"),
246                (1.0, "overdrive"),
247                (1.0, "vreflect"),
248                (2.0, "reflect"),
249                (3.0, "shift"),
250            ],
251        ) {
252            0 => {
253                dna.group();
254                let amount = dna.f32_in("amount", 1.0, 5.0);
255                let child = dna.generate(|dna| {
256                    genmap3_hasher(child_complexity, is_fractal, hasher.clone(), dna)
257                });
258                dna.ungroup();
259                saturate(amount * amount, child)
260            }
261            1 => {
262                dna.group();
263                let levels = dna.f32_in("levels", 2.0, 10.0);
264                let sharpness: f32 = dna.f32("sharpness");
265                let child = dna.generate(|dna| {
266                    genmap3_hasher(child_complexity, is_fractal, hasher.clone(), dna)
267                });
268                dna.ungroup();
269                posterize(levels, sharpness, child)
270            }
271            2 => {
272                dna.group();
273                let amount = dna.f32_in("amount", 1.0, 5.0);
274                let child = dna.generate(|dna| {
275                    genmap3_hasher(child_complexity, is_fractal, hasher.clone(), dna)
276                });
277                dna.ungroup();
278                overdrive(amount * amount, child)
279            }
280            3 => {
281                dna.group();
282                let amount = dna.f32_in("amount", 1.0, 10.0);
283                let child = dna.generate(|dna| {
284                    genmap3_hasher(child_complexity, is_fractal, hasher.clone(), dna)
285                });
286                dna.ungroup();
287                vreflect(amount, child)
288            }
289            4 => {
290                dna.group();
291                let amount = dna.f32_in("amount", 1.0, 2.0);
292                let x_offset = dna.f32_in("X offset", -1.0, 1.0);
293                let y_offset = dna.f32_in("Y offset", -1.0, 1.0);
294                let z_offset = dna.f32_in("Z offset", -1.0, 1.0);
295                let child = dna.generate(|dna| {
296                    genmap3_hasher(child_complexity, is_fractal, hasher.clone(), dna)
297                });
298                dna.ungroup();
299                reflect(amount, vec3(x_offset, y_offset, z_offset), child)
300            }
301            _ => {
302                dna.group();
303                let seed = dna.u32("seed");
304                let child = dna.generate(|dna| {
305                    genmap3_hasher(child_complexity, is_fractal, hasher.clone(), dna)
306                });
307                dna.ungroup();
308                shift(seed, child)
309            }
310        };
311        dna.ungroup();
312        unary_node
313    } else if choice == 2 {
314        // Combine two maps with a binary operator.
315        dna.group();
316        let child_complexity = complexity * 0.5 - 1.0;
317        let binary_node = match dna.index(
318            "binary node",
319            [
320                (1.0, "rotate"),
321                (1.0, "softmix"),
322                (1.0, "layer"),
323                (2.0, "displace"),
324            ],
325        ) {
326            0 => {
327                dna.group();
328                let amount = dna.f32_in("amount", 1.0, 3.0);
329                let child_a = dna.generate(|dna| {
330                    genmap3_hasher(child_complexity, is_fractal, hasher.clone(), dna)
331                });
332                let child_b = dna.generate(|dna| {
333                    genmap3_hasher(child_complexity, is_fractal, hasher.clone(), dna)
334                });
335                dna.ungroup();
336                rotate(amount, child_a, child_b)
337            }
338            1 => {
339                dna.group();
340                let amount = dna.f32_in("amount", 1.0, 5.0);
341                let displacement = dna.f32_in("displacement", 0.0, 0.5);
342                let child_a = dna.generate(|dna| {
343                    genmap3_hasher(child_complexity, is_fractal, hasher.clone(), dna)
344                });
345                let child_b = dna.generate(|dna| {
346                    genmap3_hasher(child_complexity, is_fractal, hasher.clone(), dna)
347                });
348                dna.ungroup();
349                softmix3(amount * amount, displacement, child_a, child_b)
350            }
351            2 => {
352                dna.group();
353                let width = dna.f32_in("width", 1.0, 3.0);
354                let ease = gen_ease(dna, "layer ease");
355                let child_a = dna.generate(|dna| {
356                    genmap3_hasher(child_complexity, is_fractal, hasher.clone(), dna)
357                });
358                let child_b = dna.generate(|dna| {
359                    genmap3_hasher(child_complexity, is_fractal, hasher.clone(), dna)
360                });
361                dna.ungroup();
362                layer(width, ease, child_a, child_b)
363            }
364            _ => {
365                dna.group();
366                let amount = dna.f32_in("amount", 0.0, 0.5);
367                let child_a = dna.generate(|dna| {
368                    genmap3_hasher(child_complexity, is_fractal, hasher.clone(), dna)
369                });
370                let child_b = dna.generate(|dna| {
371                    genmap3_hasher(child_complexity, is_fractal, hasher.clone(), dna)
372                });
373                dna.ungroup();
374                displace(amount, child_a, child_b)
375            }
376        };
377        dna.ungroup();
378        binary_node
379    } else {
380        // Fractalize map by sampling many octaves.
381        dna.group();
382        let child_complexity = min(20.0, complexity * 0.5 - 1.0);
383        let base_f = dna.f32_in("base frequency", 1.5, 9.0);
384        let roughness = dna.f32_xform("roughness", |x| xerp(0.4, 0.9, x));
385        let octaves = dna.u32_in("octaves", 2, 10) as usize;
386        let first_octave = dna.u32_in("first octave", 0, octaves as u32 - 1) as usize;
387        let lacunarity = dna.f32_xform("lacunarity", |x| xerp(1.5, 3.0, x));
388        let displace = dna.generate(|dna| {
389            if dna.index("displace", [(0.333, "on"), (0.666, "off")]) == 0 {
390                dna.f32_in("amount", 0.0, 0.5)
391            } else {
392                0.0
393            }
394        });
395        let layer = dna.generate(|dna| {
396            if dna.index("layer", [(0.333, "on"), (0.666, "off")]) == 0 {
397                dna.f32_in("width", 1.0, 4.0)
398            } else {
399                0.0
400            }
401        });
402        let child_basis =
403            dna.generate(|dna| genmap3_hasher(child_complexity, true, hasher.clone(), dna));
404        dna.ungroup();
405        fractal(
406            base_f,
407            octaves,
408            first_octave,
409            roughness,
410            lacunarity,
411            displace,
412            layer,
413            child_basis,
414        )
415    }
416}