veloren_world/layer/
tree.rs

1use crate::{
2    Canvas, CanvasInfo, ColumnSample,
3    all::*,
4    block::block_from_structure,
5    column::ColumnGen,
6    layer::cave::tunnel_bounds_at,
7    util::{RandomPerm, Sampler, UnitChooser, gen_cache::StructureGenCache},
8};
9use common::{
10    assets::AssetHandle,
11    calendar::{Calendar, CalendarEvent},
12    terrain::{
13        Block, BlockKind, SpriteKind,
14        structure::{Structure, StructureBlock, StructuresGroup},
15    },
16    vol::ReadVol,
17};
18use lazy_static::lazy_static;
19use rand::prelude::*;
20use std::{f32, ops::Range};
21use vek::*;
22
23lazy_static! {
24    static ref OAK_STUMPS: AssetHandle<StructuresGroup> = Structure::load_group("trees.oak_stumps");
25    static ref PALMS: AssetHandle<StructuresGroup> = Structure::load_group("trees.palms");
26    static ref FRUIT_TREES: AssetHandle<StructuresGroup> =
27        Structure::load_group("trees.fruit_trees");
28    static ref BIRCHES: AssetHandle<StructuresGroup> = Structure::load_group("trees.birch");
29    static ref SWAMP_TREES: AssetHandle<StructuresGroup> =
30        Structure::load_group("trees.swamp_trees");
31}
32
33static MODEL_RAND: RandomPerm = RandomPerm::new(0xDB21C052);
34static UNIT_CHOOSER: UnitChooser = UnitChooser::new(0x700F4EC7);
35static QUIRKY_RAND: RandomPerm = RandomPerm::new(0xA634460F);
36
37// Ensure that it's valid to place a tree here
38pub fn tree_valid_at(
39    wpos: Vec2<i32>,
40    col: &ColumnSample,
41    info: Option<CanvasInfo<'_>>,
42    seed: u32,
43) -> bool {
44    if col.alt < col.water_level
45        || col.spawn_rate < 0.9
46        || col.water_dist.map(|d| d < 8.0).unwrap_or(false)
47        || col.path.map(|(d, _, _, _)| d < 12.0).unwrap_or(false)
48        || info.is_some_and(|info| {
49            tunnel_bounds_at(wpos, &info, &info.land())
50                .any(|(_, z_range, _, _, _, _)| z_range.contains(&(col.alt as i32 - 2)))
51        })
52    {
53        return false;
54    }
55
56    if ((seed.wrapping_mul(13)) & 0xFF) as f32 / 256.0 > col.tree_density {
57        return false;
58    }
59
60    true
61}
62
63pub fn apply_trees_to(
64    canvas: &mut Canvas,
65    dynamic_rng: &mut impl Rng,
66    calendar: Option<&Calendar>,
67) {
68    // TODO: Get rid of this
69    #[expect(clippy::large_enum_variant)]
70    enum TreeModel {
71        Structure(Structure),
72        Procedural(ProceduralTree),
73    }
74
75    struct Tree {
76        pos: Vec3<i32>,
77        model: TreeModel,
78        leaf_block: StructureBlock,
79        seed: u32,
80        units: (Vec2<i32>, Vec2<i32>),
81        lights: bool,
82    }
83
84    let info = canvas.info();
85    let mut tree_cache = StructureGenCache::new(info.chunks().gen_ctx.structure_gen.clone());
86
87    canvas.foreach_col(|canvas, wpos2d, col| {
88        let trees = tree_cache.get(wpos2d, |wpos, seed| {
89            let scale = 1.0;
90            let inhabited = false;
91            let forest_kind = *info
92                .chunks()
93                .make_forest_lottery(wpos)
94                .choose_seeded(seed)
95                .as_ref()?;
96
97            let col = ColumnGen::new(info.chunks()).get((wpos, info.index(), calendar))?;
98
99            let crowding = col.tree_density;
100
101            if !tree_valid_at(wpos, &col, Some(info), seed) {
102                return None;
103            }
104
105            Some(Tree {
106                pos: Vec3::new(wpos.x, wpos.y, col.alt as i32),
107                model: 'model: {
108                    let models: AssetHandle<_> = match forest_kind {
109                        ForestKind::Oak if QUIRKY_RAND.chance(seed + 1, 1.0 / 16.0) => *OAK_STUMPS,
110                        ForestKind::Oak if QUIRKY_RAND.chance(seed + 2, 1.0 / 20.0) => {
111                            break 'model TreeModel::Procedural(ProceduralTree::generate(
112                                TreeConfig::apple(&mut RandomPerm::new(seed), scale),
113                                &mut RandomPerm::new(seed),
114                            ));
115                        },
116                        ForestKind::Palm => *PALMS,
117                        ForestKind::Acacia => {
118                            break 'model TreeModel::Procedural(ProceduralTree::generate(
119                                TreeConfig::acacia(&mut RandomPerm::new(seed), scale),
120                                &mut RandomPerm::new(seed),
121                            ));
122                        },
123                        ForestKind::Baobab => {
124                            break 'model TreeModel::Procedural(ProceduralTree::generate(
125                                TreeConfig::baobab(&mut RandomPerm::new(seed), scale),
126                                &mut RandomPerm::new(seed),
127                            ));
128                        },
129                        ForestKind::Oak => {
130                            break 'model TreeModel::Procedural(ProceduralTree::generate(
131                                TreeConfig::oak(&mut RandomPerm::new(seed), scale, crowding),
132                                &mut RandomPerm::new(seed),
133                            ));
134                        },
135                        ForestKind::Dead => {
136                            break 'model TreeModel::Procedural(ProceduralTree::generate(
137                                TreeConfig::dead(&mut RandomPerm::new(seed), scale),
138                                &mut RandomPerm::new(seed),
139                            ));
140                        },
141                        ForestKind::Chestnut => {
142                            break 'model TreeModel::Procedural(ProceduralTree::generate(
143                                TreeConfig::chestnut(&mut RandomPerm::new(seed), scale, crowding),
144                                &mut RandomPerm::new(seed),
145                            ));
146                        },
147                        ForestKind::Pine => {
148                            break 'model TreeModel::Procedural(ProceduralTree::generate(
149                                TreeConfig::pine(&mut RandomPerm::new(seed), scale, calendar),
150                                &mut RandomPerm::new(seed),
151                            ));
152                        },
153                        ForestKind::Cedar => {
154                            break 'model TreeModel::Procedural(ProceduralTree::generate(
155                                TreeConfig::cedar(&mut RandomPerm::new(seed), scale),
156                                &mut RandomPerm::new(seed),
157                            ));
158                        },
159                        ForestKind::Redwood => {
160                            break 'model TreeModel::Procedural(ProceduralTree::generate(
161                                TreeConfig::redwood(&mut RandomPerm::new(seed), scale),
162                                &mut RandomPerm::new(seed),
163                            ));
164                        },
165                        ForestKind::Birch => {
166                            break 'model TreeModel::Procedural(ProceduralTree::generate(
167                                TreeConfig::birch(&mut RandomPerm::new(seed), scale),
168                                &mut RandomPerm::new(seed),
169                            ));
170                        },
171                        ForestKind::Frostpine => {
172                            break 'model TreeModel::Procedural(ProceduralTree::generate(
173                                TreeConfig::frostpine(&mut RandomPerm::new(seed), scale),
174                                &mut RandomPerm::new(seed),
175                            ));
176                        },
177
178                        ForestKind::Mangrove => {
179                            break 'model TreeModel::Procedural(ProceduralTree::generate(
180                                TreeConfig::jungle(&mut RandomPerm::new(seed), scale),
181                                &mut RandomPerm::new(seed),
182                            ));
183                        },
184                        ForestKind::Swamp => *SWAMP_TREES,
185                        ForestKind::Giant => {
186                            break 'model TreeModel::Procedural(ProceduralTree::generate(
187                                TreeConfig::giant(&mut RandomPerm::new(seed), scale, inhabited),
188                                &mut RandomPerm::new(seed),
189                            ));
190                        },
191                        ForestKind::Mapletree => {
192                            break 'model TreeModel::Procedural(ProceduralTree::generate(
193                                TreeConfig::oak(&mut RandomPerm::new(seed), scale, crowding),
194                                &mut RandomPerm::new(seed),
195                            ));
196                        },
197                        ForestKind::Cherry => {
198                            break 'model TreeModel::Procedural(ProceduralTree::generate(
199                                TreeConfig::cherry(&mut RandomPerm::new(seed), scale),
200                                &mut RandomPerm::new(seed),
201                            ));
202                        },
203                        ForestKind::AutumnTree => {
204                            break 'model TreeModel::Procedural(ProceduralTree::generate(
205                                TreeConfig::oak(&mut RandomPerm::new(seed), scale, crowding),
206                                &mut RandomPerm::new(seed),
207                            ));
208                        },
209                    };
210
211                    let models = models.read();
212                    TreeModel::Structure(
213                        models
214                            [(MODEL_RAND.get(seed.wrapping_mul(17)) / 13) as usize % models.len()]
215                        .clone(),
216                    )
217                },
218                leaf_block: forest_kind.leaf_block(),
219                seed,
220                units: UNIT_CHOOSER.get(seed),
221                lights: inhabited,
222            })
223        });
224
225        for tree in trees {
226            let bounds = match &tree.model {
227                TreeModel::Structure(s) => s.get_bounds(),
228                TreeModel::Procedural(t) => t.get_bounds().map(|e| e as i32),
229            };
230
231            let rpos2d = (wpos2d - tree.pos.xy())
232                .map2(Vec2::new(tree.units.0, tree.units.1), |p, unit| unit * p)
233                .sum();
234            if !Aabr::from(bounds).contains_point(rpos2d) {
235                // Skip this column
236                continue;
237            }
238
239            let hanging_sprites = match &tree.model {
240                TreeModel::Structure(_) => [(0.0004, SpriteKind::Beehive)].as_ref(),
241                TreeModel::Procedural(t) => t.config.hanging_sprites,
242            };
243
244            let mut is_top = true;
245            let mut is_leaf_top = true;
246            let mut last_block = Block::empty();
247            for z in (bounds.min.z..bounds.max.z).rev() {
248                let wpos = Vec3::new(wpos2d.x, wpos2d.y, tree.pos.z + z);
249                let model_pos = Vec3::from(
250                    (wpos - tree.pos)
251                        .xy()
252                        .map2(Vec2::new(tree.units.0, tree.units.1), |rpos, unit| {
253                            unit * rpos
254                        })
255                        .sum(),
256                ) + Vec3::unit_z() * (wpos.z - tree.pos.z);
257                let sblock;
258                block_from_structure(
259                    info.index(),
260                    if let Some(block) = match &tree.model {
261                        TreeModel::Structure(s) => s.get(model_pos).ok(),
262                        TreeModel::Procedural(t) => Some(
263                            match t.is_branch_or_leaves_at(model_pos.map(|e| e as f32 + 0.5)) {
264                                (_, _, true, _) => {
265                                    sblock = StructureBlock::Filled(
266                                        BlockKind::Wood,
267                                        Rgb::new(150, 98, 41),
268                                    );
269                                    &sblock
270                                },
271                                (_, _, _, true) => &StructureBlock::None,
272                                (true, _, _, _) => &t.config.trunk_block,
273                                (_, true, _, _) => &tree.leaf_block,
274                                _ => &StructureBlock::None,
275                            },
276                        ),
277                    } {
278                        block
279                    } else {
280                        break;
281                    },
282                    wpos,
283                    tree.pos.xy(),
284                    tree.seed,
285                    col,
286                    Block::air,
287                    calendar,
288                    &Vec2::new(tree.units.0, tree.units.1),
289                )
290                .map(|(block, sprite_cfg)| {
291                    // Add lights to the tree
292                    if tree.lights
293                        && last_block.is_air()
294                        && block.kind() == BlockKind::Wood
295                        && dynamic_rng.gen_range(0..256) == 0
296                    {
297                        canvas.set(wpos + Vec3::unit_z(), Block::air(SpriteKind::Lantern));
298                        // Add a snow covering to the block above under certain
299                        // circumstances
300                    } else if col.snow_cover
301                        && ((block.kind() == BlockKind::Leaves && is_leaf_top)
302                            || (is_top && block.is_filled()))
303                    {
304                        canvas.set(
305                            wpos + Vec3::unit_z(),
306                            Block::new(BlockKind::Snow, Rgb::new(210, 210, 255)),
307                        );
308                    }
309                    canvas.set(wpos, block);
310                    if let Some(sprite_cfg) = sprite_cfg {
311                        canvas.set_sprite_cfg(wpos, sprite_cfg);
312                    }
313                    is_leaf_top = false;
314                    is_top = false;
315                    last_block = block;
316                })
317                .unwrap_or_else(|| {
318                    // Hanging sprites
319                    if last_block.is_filled() {
320                        for (chance, sprite) in hanging_sprites {
321                            if dynamic_rng.gen_bool(*chance as f64) {
322                                canvas.map_resource(wpos, |block| block.with_sprite(*sprite));
323                            }
324                        }
325                    }
326
327                    is_leaf_top = true;
328                    last_block = Block::empty();
329                });
330            }
331        }
332    });
333}
334
335/// A type that specifies the generation properties of a tree.
336#[derive(Clone)]
337pub struct TreeConfig {
338    /// Length of trunk, also scales other branches.
339    pub trunk_len: f32,
340    /// Radius of trunk, also scales other branches.
341    pub trunk_radius: f32,
342    /// The scale that child branch lengths should be compared to their parents.
343    pub branch_child_len: f32,
344    /// The scale that child branch radii should be compared to their parents.
345    pub branch_child_radius: f32,
346    /// Whether the child of a branch has its radius lerped to its parent.
347    pub branch_child_radius_lerp: bool,
348    /// The range of radii that leaf-emitting branches might have.
349    pub leaf_radius: Range<f32>,
350    /// An additional leaf radius that may be scaled with proportion along the
351    /// parent and `branch_len_bias`.
352    pub leaf_radius_scaled: f32,
353    /// 0 - 1 (0 = chaotic, 1 = straight).
354    pub straightness: f32,
355    /// Maximum number of branch layers (not including trunk).
356    pub max_depth: usize,
357    /// The number of branches that form from each branch.
358    pub splits: Range<f32>,
359    /// The range of proportions along a branch at which a split into another
360    /// branch might occur. This value is clamped between 0 and 1, but a
361    /// wider range may bias the results towards branch ends.
362    pub split_range: Range<f32>,
363    /// The bias applied to the length of branches based on the proportion along
364    /// their parent that they eminate from. -1.0 = negative bias (branches
365    /// at ends are longer, branches at the start are shorter) 0.0 = no bias
366    /// (branches do not change their length with regard to parent branch
367    /// proportion) 1.0 = positive bias (branches at ends are shorter,
368    /// branches at the start are longer)
369    pub branch_len_bias: f32,
370    /// The scale of leaves in the vertical plane. Less than 1.0 implies a
371    /// flattening of the leaves.
372    pub leaf_vertical_scale: f32,
373    /// How evenly spaced (vs random) sub-branches are along their parent.
374    pub proportionality: f32,
375    /// Whether the tree is inhabited (adds various features and effects)
376    pub inhabited: bool,
377    pub hanging_sprites: &'static [(f32, SpriteKind)],
378    /// The colour of branches and the trunk.
379    pub trunk_block: StructureBlock,
380}
381
382impl TreeConfig {
383    pub fn oak(rng: &mut impl Rng, scale: f32, crowding: f32) -> Self {
384        let scale = scale * (0.8 + rng.gen::<f32>().powi(2) * 0.5);
385        let log_scale = 1.0 + scale.log2().max(0.0);
386
387        Self {
388            trunk_len: 11.0 * scale,
389            trunk_radius: 1.5 * scale,
390            branch_child_len: 0.75,
391            branch_child_radius: 0.75,
392            branch_child_radius_lerp: true,
393            leaf_radius: 2.0 * log_scale..2.5 * log_scale,
394            leaf_radius_scaled: 0.0,
395            straightness: 0.3 + crowding * 0.3,
396            max_depth: 4,
397            splits: 4.25..6.25,
398            split_range: 0.75..1.5,
399            branch_len_bias: 0.0,
400            leaf_vertical_scale: 1.0,
401            proportionality: 0.0,
402            inhabited: false,
403            hanging_sprites: &[(0.0002, SpriteKind::Apple), (0.00007, SpriteKind::Beehive)],
404            trunk_block: StructureBlock::Filled(BlockKind::Wood, Rgb::new(90, 45, 15)),
405        }
406    }
407
408    pub fn dead(rng: &mut impl Rng, scale: f32) -> Self {
409        let scale = scale * (0.8 + rng.gen::<f32>().powi(2) * 0.5);
410
411        Self {
412            trunk_len: 9.0 * scale,
413            trunk_radius: 2.0 * scale,
414            branch_child_len: 0.9,
415            branch_child_radius: 0.7,
416            branch_child_radius_lerp: true,
417            leaf_radius: 0.0..0.1,
418            leaf_radius_scaled: 0.0,
419            straightness: 0.35,
420            max_depth: 3,
421            splits: 2.25..3.25,
422            split_range: 0.75..1.5,
423            branch_len_bias: 0.0,
424            leaf_vertical_scale: 1.0,
425            proportionality: 0.0,
426            inhabited: false,
427            hanging_sprites: &[(0.0002, SpriteKind::Apple), (0.00007, SpriteKind::Beehive)],
428            trunk_block: StructureBlock::Filled(BlockKind::Wood, Rgb::new(55, 34, 32)),
429        }
430    }
431
432    pub fn apple(rng: &mut impl Rng, scale: f32) -> Self {
433        let scale = scale * (0.8 + rng.gen::<f32>().powi(2) * 0.5);
434        let log_scale = 1.0 + scale.log2().max(0.0);
435
436        Self {
437            trunk_len: 3.0 * scale,
438            trunk_radius: 1.5 * scale,
439            branch_child_len: 0.9,
440            branch_child_radius: 0.9,
441            branch_child_radius_lerp: true,
442            leaf_radius: 2.0 * log_scale..3.0 * log_scale,
443            leaf_radius_scaled: 0.0,
444            straightness: 0.4,
445            max_depth: 6,
446            splits: 1.0..3.0,
447            split_range: 0.5..2.0,
448            branch_len_bias: 0.0,
449            leaf_vertical_scale: 0.7,
450            proportionality: 0.0,
451            inhabited: false,
452            hanging_sprites: &[(0.03, SpriteKind::Apple), (0.007, SpriteKind::Beehive)],
453            trunk_block: StructureBlock::Filled(BlockKind::Wood, Rgb::new(90, 45, 15)),
454        }
455    }
456
457    pub fn frostpine(rng: &mut impl Rng, scale: f32) -> Self {
458        let scale = scale * (0.8 + rng.gen::<f32>().powi(2) * 0.5);
459        let log_scale = 1.0 + scale.log2().max(0.0);
460
461        Self {
462            trunk_len: 36.0 * scale,
463            trunk_radius: 2.3 * scale,
464            branch_child_len: 0.25 / scale,
465            branch_child_radius: 0.0,
466            branch_child_radius_lerp: false,
467            leaf_radius: 1.3..2.2,
468            leaf_radius_scaled: 0.4 * log_scale,
469            straightness: 0.3,
470            max_depth: 1,
471            splits: 34.0 * scale..35.0 * scale,
472            split_range: 0.1..1.2,
473            branch_len_bias: 0.75,
474            leaf_vertical_scale: 0.6,
475            proportionality: 1.0,
476            inhabited: false,
477            hanging_sprites: &[(0.0001, SpriteKind::Beehive)],
478            trunk_block: StructureBlock::Filled(BlockKind::Wood, Rgb::new(79, 102, 105)),
479        }
480    }
481
482    pub fn jungle(rng: &mut impl Rng, scale: f32) -> Self {
483        let scale = scale * (0.8 + rng.gen::<f32>() * 0.5);
484        let log_scale = 1.0 + scale.log2().max(0.0);
485
486        Self {
487            trunk_len: 44.0 * scale,
488            trunk_radius: 2.25 * scale,
489            branch_child_len: 0.35,
490            branch_child_radius: 0.5,
491            branch_child_radius_lerp: true,
492            leaf_radius: 10.0 * log_scale..11.5 * log_scale,
493            leaf_radius_scaled: -8.0 * log_scale,
494            straightness: 0.2,
495            max_depth: 2,
496            splits: 7.5..8.5,
497            split_range: 0.2..1.25,
498            branch_len_bias: 0.5,
499            leaf_vertical_scale: 0.35,
500            proportionality: 0.8,
501            inhabited: false,
502            hanging_sprites: &[(0.00007, SpriteKind::Beehive), (0.015, SpriteKind::Liana)],
503            trunk_block: StructureBlock::Filled(BlockKind::Wood, Rgb::new(118, 67, 42)),
504        }
505    }
506
507    pub fn baobab(rng: &mut impl Rng, scale: f32) -> Self {
508        let scale = scale * (0.5 + rng.gen::<f32>().powi(4) * 1.0);
509        let log_scale = 1.0 + scale.log2().max(0.0);
510
511        Self {
512            trunk_len: 24.0 * scale,
513            trunk_radius: 7.0 * scale,
514            branch_child_len: 0.55,
515            branch_child_radius: 0.3,
516            branch_child_radius_lerp: true,
517            leaf_radius: 2.5 * log_scale..3.0 * log_scale,
518            leaf_radius_scaled: 0.0,
519            straightness: 0.5,
520            max_depth: 4,
521            splits: 3.0..3.5,
522            split_range: 0.95..1.0,
523            branch_len_bias: 0.0,
524            leaf_vertical_scale: 0.2,
525            proportionality: 1.0,
526            inhabited: false,
527            hanging_sprites: &[(0.00007, SpriteKind::Beehive)],
528            trunk_block: StructureBlock::Filled(BlockKind::Wood, Rgb::new(125, 60, 6)),
529        }
530    }
531
532    pub fn cedar(rng: &mut impl Rng, scale: f32) -> Self {
533        let scale = scale * (0.8 + rng.gen::<f32>().powi(2) * 0.5);
534        let log_scale = 1.0 + scale.log2().max(0.0);
535
536        Self {
537            trunk_len: 45.0 * scale,
538            trunk_radius: 1.3 * scale,
539            branch_child_len: 0.4,
540            branch_child_radius: 0.6,
541            branch_child_radius_lerp: true,
542            leaf_radius: 2.0 * log_scale..2.5 * log_scale,
543            leaf_radius_scaled: 0.0,
544            straightness: 0.3,
545            max_depth: 2,
546            splits: 16.0..18.0,
547            split_range: 0.2..1.2,
548            branch_len_bias: 0.7,
549            leaf_vertical_scale: 0.3,
550            proportionality: 0.7,
551            inhabited: false,
552            hanging_sprites: &[(0.00007, SpriteKind::Beehive)],
553            trunk_block: StructureBlock::Filled(BlockKind::Wood, Rgb::new(110, 68, 65)),
554        }
555    }
556
557    pub fn birch(rng: &mut impl Rng, scale: f32) -> Self {
558        let scale = scale * (0.8 + rng.gen::<f32>().powi(2) * 0.5);
559        let log_scale = 1.0 + scale.log2().max(0.0);
560
561        Self {
562            trunk_len: 24.0 * scale,
563            trunk_radius: 1.2 * scale,
564            branch_child_len: 0.4,
565            branch_child_radius: 0.75,
566            branch_child_radius_lerp: true,
567            leaf_radius: 4.0 * log_scale..5.0 * log_scale,
568            leaf_radius_scaled: 0.0,
569            straightness: 0.6,
570            max_depth: 4,
571            splits: 1.75..2.5,
572            split_range: 0.6..1.2,
573            branch_len_bias: 0.0,
574            leaf_vertical_scale: 0.5,
575            proportionality: 0.0,
576            inhabited: false,
577            hanging_sprites: &[(0.00007, SpriteKind::Beehive)],
578            trunk_block: StructureBlock::BirchWood,
579        }
580    }
581
582    pub fn acacia(rng: &mut impl Rng, scale: f32) -> Self {
583        let scale = scale * (0.9 + rng.gen::<f32>().powi(4) * 0.75);
584        let log_scale = 1.0 + scale.log2().max(0.0);
585
586        Self {
587            trunk_len: 7.5 * scale,
588            trunk_radius: 1.5 * scale,
589            branch_child_len: 0.75,
590            branch_child_radius: 0.75,
591            branch_child_radius_lerp: true,
592            leaf_radius: 4.5 * log_scale..5.5 * log_scale,
593            leaf_radius_scaled: 0.0,
594            straightness: 0.4,
595            max_depth: 5,
596            splits: 1.75..2.25,
597            split_range: 1.0..1.25,
598            branch_len_bias: 0.0,
599            leaf_vertical_scale: 0.2,
600            proportionality: 1.0,
601            inhabited: false,
602            hanging_sprites: &[(0.00005, SpriteKind::Beehive)],
603            trunk_block: StructureBlock::Filled(BlockKind::Wood, Rgb::new(150, 95, 65)),
604        }
605    }
606
607    pub fn chestnut(rng: &mut impl Rng, scale: f32, crowding: f32) -> Self {
608        let scale = scale * (0.85 + rng.gen::<f32>().powi(4) * 0.3);
609        let log_scale = 1.0 + scale.log2().max(0.0);
610
611        Self {
612            trunk_len: 13.0 * scale,
613            trunk_radius: 1.65 * scale,
614            branch_child_len: 0.75,
615            branch_child_radius: 0.6,
616            branch_child_radius_lerp: true,
617            leaf_radius: 1.5 * log_scale..2.0 * log_scale,
618            leaf_radius_scaled: 0.0,
619            straightness: 0.25 + crowding * 0.2,
620            max_depth: 5,
621            splits: 3.5..4.25,
622            split_range: 0.5..1.25,
623            branch_len_bias: 0.0,
624            leaf_vertical_scale: 0.65,
625            proportionality: 0.5,
626            inhabited: false,
627            hanging_sprites: &[(0.00007, SpriteKind::Beehive)],
628            trunk_block: StructureBlock::Filled(BlockKind::Wood, Rgb::new(110, 42, 28)),
629        }
630    }
631
632    pub fn pine(rng: &mut impl Rng, scale: f32, calendar: Option<&Calendar>) -> Self {
633        let scale = scale * (1.0 + rng.gen::<f32>().powi(4) * 0.5);
634        let log_scale = 1.0 + scale.log2().max(0.0);
635
636        Self {
637            trunk_len: 32.0 * scale,
638            trunk_radius: 1.25 * scale,
639            branch_child_len: 0.3 / scale,
640            branch_child_radius: 0.0,
641            branch_child_radius_lerp: false,
642            leaf_radius: 1.9..2.1,
643            leaf_radius_scaled: 1.5 * log_scale,
644            straightness: -0.25,
645            max_depth: 1,
646            splits: 34.0 * scale..35.0 * scale,
647            split_range: 0.2..1.2,
648            branch_len_bias: 0.75,
649            leaf_vertical_scale: 0.3,
650            proportionality: 1.0,
651            inhabited: false,
652            hanging_sprites: if calendar.is_some_and(|c| c.is_event(CalendarEvent::Christmas)) {
653                &[(0.0001, SpriteKind::Beehive), (0.01, SpriteKind::Orb)]
654            } else {
655                &[(0.0001, SpriteKind::Beehive)]
656            },
657            trunk_block: StructureBlock::Filled(BlockKind::Wood, Rgb::new(90, 35, 15)),
658        }
659    }
660
661    pub fn redwood(rng: &mut impl Rng, scale: f32) -> Self {
662        let scale = scale * (1.0 + rng.gen::<f32>().powi(4) * 0.5);
663
664        Self {
665            trunk_len: 80.0 * scale,
666            trunk_radius: 2.75 * scale,
667            branch_child_len: 0.25,
668            branch_child_radius: 0.3,
669            branch_child_radius_lerp: false,
670            leaf_radius: 1.3..1.5,
671            leaf_radius_scaled: 0.0,
672            straightness: -0.3,
673            max_depth: 2,
674            splits: 45.0 * scale..50.0 * scale,
675            split_range: 0.4..1.2,
676            branch_len_bias: 0.75,
677            leaf_vertical_scale: 0.6,
678            proportionality: 1.0,
679            inhabited: false,
680            hanging_sprites: &[(0.001, SpriteKind::Beehive)],
681            trunk_block: StructureBlock::RedwoodWood,
682        }
683    }
684
685    pub fn giant(_rng: &mut impl Rng, scale: f32, inhabited: bool) -> Self {
686        let log_scale = 1.0 + scale.log2().max(0.0);
687
688        Self {
689            trunk_len: 11.0 * scale,
690            trunk_radius: 6.0 * scale,
691            branch_child_len: 0.9,
692            branch_child_radius: 0.75,
693            branch_child_radius_lerp: true,
694            leaf_radius: 2.5 * scale..3.75 * scale,
695            leaf_radius_scaled: 0.0,
696            straightness: 0.36,
697            max_depth: (7.0 + log_scale) as usize,
698            splits: 1.5..2.5,
699            split_range: 1.0..1.1,
700            branch_len_bias: 0.0,
701            leaf_vertical_scale: 0.6,
702            proportionality: 0.0,
703            inhabited,
704            hanging_sprites: &[(0.00025, SpriteKind::Apple), (0.00025, SpriteKind::Beehive)],
705            trunk_block: StructureBlock::Filled(BlockKind::Wood, Rgb::new(110, 68, 22)),
706        }
707    }
708
709    pub fn cherry(rng: &mut impl Rng, scale: f32) -> Self {
710        let scale = scale * (0.8 + rng.gen::<f32>().powi(2) * 0.5);
711        let log_scale = 1.0 + scale.log2().max(0.0);
712
713        Self {
714            trunk_len: 7.0 * scale,
715            trunk_radius: 1.27 * scale,
716            branch_child_len: 0.9,
717            branch_child_radius: 0.70,
718            branch_child_radius_lerp: true,
719            leaf_radius: 2.5 * log_scale..3.0 * log_scale,
720            leaf_radius_scaled: 0.0,
721            straightness: 0.55,
722            max_depth: 4,
723            splits: 2.0..3.0,
724            split_range: 0.75..1.3,
725            branch_len_bias: 0.0,
726            leaf_vertical_scale: 1.0,
727            proportionality: 0.0,
728            inhabited: false,
729            hanging_sprites: &[],
730            trunk_block: StructureBlock::Filled(BlockKind::Wood, Rgb::new(69, 37, 17)),
731        }
732    }
733}
734
735// TODO: Rename this to `Tree` when the name conflict is gone
736pub struct ProceduralTree {
737    branches: Vec<Branch>,
738    trunk_idx: usize,
739    config: TreeConfig,
740    roots: Vec<Root>,
741    root_aabb: Aabb<f32>,
742}
743
744impl ProceduralTree {
745    /// Generate a new tree using the given configuration and seed.
746    pub fn generate(config: TreeConfig, rng: &mut impl Rng) -> Self {
747        let mut this = Self {
748            branches: Vec::new(),
749            trunk_idx: 0, // Gets replaced later
750            config: config.clone(),
751            roots: Vec::new(),
752            root_aabb: Aabb::new_empty(Vec3::zero()),
753        };
754
755        // Make the roots visible a little
756        let trunk_origin = Vec3::unit_z() * (config.trunk_radius * 0.25 + 3.0);
757
758        // Add the tree trunk (and sub-branches) recursively
759        let (trunk_idx, _) = this.add_branch(
760            &config,
761            // Our trunk starts at the origin...
762            trunk_origin,
763            // ...and has a roughly upward direction
764            Vec3::new(rng.gen_range(-1.0..1.0), rng.gen_range(-1.0..1.0), 10.0).normalized(),
765            config.trunk_len,
766            config.trunk_radius,
767            0,
768            None,
769            1.0,
770            rng,
771        );
772        this.trunk_idx = trunk_idx;
773
774        // Add roots
775        let mut root_aabb = Aabb::new_empty(Vec3::zero());
776        for _ in 0..4 {
777            let dir =
778                Vec3::new(rng.gen_range(-1.0..1.0), rng.gen_range(-1.0..1.0), -1.0).normalized();
779            let len = config.trunk_len * 0.75;
780            let radius = config.trunk_radius;
781            let mut aabb = Aabb {
782                min: trunk_origin,
783                max: trunk_origin + dir * len,
784            }
785            .made_valid();
786            aabb.min -= radius;
787            aabb.max += radius;
788
789            root_aabb.expand_to_contain(aabb);
790
791            this.roots.push(Root {
792                line: LineSegment3 {
793                    start: trunk_origin,
794                    end: trunk_origin + dir * 10.0,
795                },
796                radius,
797            });
798        }
799
800        this.root_aabb = root_aabb;
801
802        this
803    }
804
805    // Recursively add a branch (with sub-branches) to the tree's branch graph,
806    // returning the index and AABB of the branch. This AABB gets propagated
807    // down to the parent and is used later during sampling to cull the branches to
808    // be sampled.
809    fn add_branch(
810        &mut self,
811        config: &TreeConfig,
812        start: Vec3<f32>,
813        dir: Vec3<f32>,
814        branch_len: f32,
815        branch_radius: f32,
816        depth: usize,
817        sibling_idx: Option<usize>,
818        proportion: f32,
819        rng: &mut impl Rng,
820    ) -> (usize, Aabb<f32>) {
821        let end = start + dir * branch_len;
822        let line = LineSegment3 { start, end };
823        let wood_radius = branch_radius;
824        let leaf_radius = if depth == config.max_depth {
825            rng.gen_range(config.leaf_radius.clone())
826                + config.leaf_radius_scaled
827                    * Lerp::lerp(1.0, 1.0 - proportion, config.branch_len_bias.abs())
828        } else {
829            0.0
830        };
831
832        let has_stairs = config.inhabited
833            && depth < config.max_depth
834            && branch_radius > 6.5
835            && start.xy().distance(end.xy()) < (start.z - end.z).abs() * 1.5;
836        let bark_radius = if has_stairs { 5.0 } else { 0.0 } + wood_radius * 0.25;
837
838        // The AABB that covers this branch, along with wood and leaves that eminate
839        // from it
840        let mut aabb = Aabb {
841            min: Vec3::partial_min(start, end) - (wood_radius + bark_radius).max(leaf_radius),
842            max: Vec3::partial_max(start, end) + (wood_radius + bark_radius).max(leaf_radius),
843        };
844
845        let mut child_idx = None;
846        // Don't add child branches if we're already enough layers into the tree
847        if depth < config.max_depth {
848            let x_axis = dir
849                .cross(Vec3::<f32>::zero().map(|_| rng.gen_range(-1.0..1.0)))
850                .normalized();
851            let y_axis = dir.cross(x_axis).normalized();
852            let screw_shift = rng.gen_range(0.0..f32::consts::TAU);
853
854            let splits = rng.gen_range(config.splits.clone()).round() as usize;
855            for i in 0..splits {
856                let proportion = i as f32 / (splits - 1) as f32;
857                let dist = Lerp::lerp(rng.gen_range(0.0..1.0), proportion, config.proportionality);
858
859                const PHI: f32 = 0.618;
860                const RAD_PER_BRANCH: f32 = f32::consts::TAU * PHI;
861                let screw = (screw_shift + i as f32 * RAD_PER_BRANCH).sin() * x_axis
862                    + (screw_shift + i as f32 * RAD_PER_BRANCH).cos() * y_axis;
863
864                // Choose a point close to the branch to act as the target direction for the
865                // branch to grow in let split_factor =
866                // rng.gen_range(config.split_range.start, config.split_range.end).clamped(0.0,
867                // 1.0);
868                let split_factor =
869                    Lerp::lerp(config.split_range.start, config.split_range.end, dist);
870                let tgt = Lerp::lerp_unclamped(start, end, split_factor)
871                    + Lerp::lerp(
872                        Vec3::<f32>::zero().map(|_| rng.gen_range(-1.0..1.0)),
873                        screw,
874                        config.proportionality,
875                    );
876                // Start the branch at the closest point to the target
877                let branch_start = line.projected_point(tgt);
878                // Now, interpolate between the target direction and the parent branch's
879                // direction to find a direction
880                let branch_dir =
881                    Lerp::lerp_unclamped(tgt - branch_start, dir, config.straightness).normalized();
882
883                let (branch_idx, branch_aabb) = self.add_branch(
884                    config,
885                    branch_start,
886                    branch_dir,
887                    branch_len
888                        * config.branch_child_len
889                        * (1.0
890                            - (split_factor - 0.5)
891                                * 2.0
892                                * config.branch_len_bias.clamped(-1.0, 1.0)),
893                    branch_radius * config.branch_child_radius,
894                    depth + 1,
895                    child_idx,
896                    proportion,
897                    rng,
898                );
899                child_idx = Some(branch_idx);
900                // Parent branches AABBs include the AABBs of child branches to allow for
901                // culling during sampling
902                aabb.expand_to_contain(branch_aabb);
903            }
904        }
905
906        let idx = self.branches.len(); // Compute the index that this branch is going to have
907        self.branches.push(Branch {
908            line,
909            wood_radius,
910            leaf_radius,
911            leaf_vertical_scale: config.leaf_vertical_scale,
912            aabb,
913            sibling_idx,
914            child_idx,
915            has_stairs,
916        });
917
918        (idx, aabb)
919    }
920
921    /// Get the bounding box that covers the tree (all branches and leaves)
922    pub fn get_bounds(&self) -> Aabb<f32> {
923        self.branches[self.trunk_idx].aabb.union(self.root_aabb)
924    }
925
926    // Recursively search for branches or leaves by walking the tree's branch graph.
927    fn walk_inner(
928        &self,
929        descend: &mut impl FnMut(&Branch, &Branch) -> bool,
930        parent: &Branch,
931        branch_idx: usize,
932    ) {
933        let branch = &self.branches[branch_idx];
934        // Always probe the sibling branch, since it's not a child of the current
935        // branch.
936        let _branch_or_leaves = branch
937            .sibling_idx
938            .map(|idx| self.walk_inner(descend, parent, idx));
939
940        // Only continue probing this sub-graph of the tree if the branch maches a
941        // criteria (usually that it falls within the region we care about
942        // sampling)
943        if descend(branch, parent) {
944            // Probe the children of this branch
945            let _children = branch
946                .child_idx
947                .map(|idx| self.walk_inner(descend, branch, idx));
948        }
949    }
950
951    /// Recursively walk the tree's branches, calling the current closure with
952    /// the branch and its parent. If the closure returns `false`, recursion
953    /// into the child branches is skipped.
954    pub fn walk<F: FnMut(&Branch, &Branch) -> bool>(&self, mut f: F) {
955        self.walk_inner(&mut f, &self.branches[self.trunk_idx], self.trunk_idx);
956    }
957
958    /// Determine whether there are either branches or leaves at the given
959    /// position in the tree.
960    #[inline(always)]
961    pub fn is_branch_or_leaves_at(&self, pos: Vec3<f32>) -> (bool, bool, bool, bool) {
962        let mut flags = Vec4::broadcast(false);
963        self.walk(|branch, parent| {
964            if branch.aabb.contains_point(pos) {
965                flags |=
966                    Vec4::<bool>::from(branch.is_branch_or_leaves_at(&self.config, pos, parent).0);
967                true
968            } else {
969                false
970            }
971        });
972
973        let (log, leaf, platform, air) = flags.into_tuple();
974
975        let root = if self.root_aabb.contains_point(pos) {
976            self.roots.iter().any(|root| {
977                let p = root.line.projected_point(pos);
978                let d2 = p.distance_squared(pos);
979                d2 < root.radius.powi(2)
980            })
981        } else {
982            false
983        };
984        (
985            (log || root), /* & !air */
986            leaf & !air,
987            platform & !air,
988            air,
989        )
990    }
991}
992
993// Branches are arranged in a graph shape. Each branch points to both its first
994// child (if any) and also to the next branch in the list of child branches
995// associated with the parent. This means that the entire tree is laid out in a
996// walkable graph where each branch refers only to two other branches. As a
997// result, walking the tree is simply a case of performing double recursion.
998pub struct Branch {
999    line: LineSegment3<f32>,
1000    wood_radius: f32,
1001    leaf_radius: f32,
1002    leaf_vertical_scale: f32,
1003    aabb: Aabb<f32>,
1004
1005    sibling_idx: Option<usize>,
1006    child_idx: Option<usize>,
1007
1008    has_stairs: bool,
1009}
1010
1011impl Branch {
1012    /// Determine whether there are either branches or leaves at the given
1013    /// position in the branch.
1014    /// (branch, leaves, stairs, forced_air)
1015    pub fn is_branch_or_leaves_at(
1016        &self,
1017        config: &TreeConfig,
1018        pos: Vec3<f32>,
1019        parent: &Branch,
1020    ) -> ((bool, bool, bool, bool), f32) {
1021        // fn finvsqrt(x: f32) -> f32 {
1022        //     let y = f32::from_bits(0x5f375a86 - (x.to_bits() >> 1));
1023        //     y * (1.5 - ( x * 0.5 * y * y ))
1024        // }
1025
1026        fn length_factor(line: LineSegment3<f32>, p: Vec3<f32>) -> f32 {
1027            let len_sq = line.start.distance_squared(line.end);
1028            if len_sq < 0.001 {
1029                0.0
1030            } else {
1031                (p - line.start).dot(line.end - line.start) / len_sq
1032            }
1033        }
1034
1035        // fn smooth(a: f32, b: f32, k: f32) -> f32 {
1036        //     // let h = (0.5 + 0.5 * (b - a) / k).clamped(0.0, 1.0);
1037        //     // Lerp::lerp(b, a, h) - k * h * (1.0 - h)
1038
1039        //     let h = (k-(a-b).abs()).max(0.0);
1040        //     a.min(b) - h * h * 0.25 / k
1041        // }
1042
1043        let p = self.line.projected_point(pos);
1044        let d2 = p.distance_squared(pos);
1045
1046        let length_factor = length_factor(self.line, pos);
1047        let wood_radius = if config.branch_child_radius_lerp {
1048            Lerp::lerp(parent.wood_radius, self.wood_radius, length_factor)
1049        } else {
1050            self.wood_radius
1051        };
1052
1053        let mask = if d2 < wood_radius.powi(2) {
1054            (true, false, false, false) // Wood
1055        } else if {
1056            let diff = (p - pos) / Vec3::new(1.0, 1.0, self.leaf_vertical_scale);
1057            diff.magnitude_squared() < self.leaf_radius.powi(2)
1058        } {
1059            (false, true, false, false) // Leaves
1060        } else {
1061            let stair_width = 5.0;
1062            let stair_thickness = 2.0;
1063            let stair_space = 5.0;
1064            if self.has_stairs {
1065                let (platform, air) = if pos.z >= self.line.start.z.min(self.line.end.z) - 1.0
1066                    && pos.z
1067                        <= self.line.start.z.max(self.line.end.z) + stair_thickness + stair_space
1068                    && d2 < (wood_radius + stair_width).powi(2)
1069                {
1070                    let rpos = pos.xy() - p;
1071                    let stretch = 32.0;
1072                    let stair_section = (rpos.x.atan2(rpos.y) / (f32::consts::PI * 2.0) * stretch
1073                        + pos.z)
1074                        .rem_euclid(stretch);
1075                    (
1076                        stair_section < stair_thickness,
1077                        stair_section >= stair_thickness
1078                            && stair_section < stair_thickness + stair_space,
1079                    ) // Stairs
1080                } else {
1081                    (false, false)
1082                };
1083
1084                let platform = platform
1085                    || (self.has_stairs
1086                        && self.wood_radius > 4.0
1087                        && !air
1088                        && d2 < (wood_radius + 10.0).powi(2)
1089                        && pos.z % 48.0 < stair_thickness);
1090
1091                (false, false, platform, air)
1092            } else {
1093                (false, false, false, false)
1094            }
1095        };
1096
1097        (mask, d2)
1098    }
1099
1100    /// This returns an AABB of both the branch and all of the children of that
1101    /// branch
1102    pub fn get_aabb(&self) -> Aabb<f32> { self.aabb }
1103
1104    pub fn get_line(&self) -> LineSegment3<f32> { self.line }
1105
1106    pub fn get_wood_radius(&self) -> f32 { self.wood_radius }
1107
1108    pub fn get_leaf_radius(&self) -> f32 { self.leaf_radius }
1109}
1110
1111struct Root {
1112    line: LineSegment3<f32>,
1113    radius: f32,
1114}