veloren_world/site/
tree.rs

1use crate::{
2    Canvas, Land,
3    layer::tree::{ProceduralTree, TreeConfig},
4    site::SpawnRules,
5    util::{FastNoise, Sampler},
6};
7use common::{
8    generation::EntityInfo,
9    terrain::{Block, BlockKind, SpriteKind},
10};
11use rand::prelude::*;
12use vek::*;
13
14// Temporary, do trees through the new site system later
15pub struct Tree {
16    pub origin: Vec2<i32>,
17    alt: i32,
18    seed: u32,
19    tree: ProceduralTree,
20}
21
22impl Tree {
23    pub fn generate(origin: Vec2<i32>, land: &Land, rng: &mut impl Rng) -> Self {
24        Self {
25            origin,
26            alt: land.get_alt_approx(origin) as i32,
27            seed: rng.gen(),
28            tree: {
29                let config = TreeConfig::giant(rng, 4.0, true);
30                ProceduralTree::generate(config, rng)
31            },
32        }
33    }
34
35    pub fn radius(&self) -> f32 { 512.0 }
36
37    pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
38        let trunk_radius = 48i32;
39        SpawnRules {
40            trees: wpos.distance_squared(self.origin) > trunk_radius.pow(2),
41            ..SpawnRules::default()
42        }
43    }
44
45    pub fn render(&self, canvas: &mut Canvas, dynamic_rng: &mut impl Rng) {
46        let nz = FastNoise::new(self.seed);
47        let calendar = None;
48
49        canvas.foreach_col(|canvas, wpos2d, col| {
50            let rpos2d = wpos2d - self.origin;
51            let bounds = self.tree.get_bounds().map(|e| e as i32);
52
53            if !Aabr::from(bounds).contains_point(rpos2d) {
54                // Skip this column
55                return;
56            }
57
58            let mut above = true;
59            let mut last = None;
60            for z in (bounds.min.z..bounds.max.z + 1).rev() {
61                let wpos = wpos2d.with_z(self.alt + z);
62                let rposf = (wpos - self.origin.with_z(self.alt)).map(|e| e as f32 + 0.5);
63
64                let (branch, leaves, platform, air) = self.tree.is_branch_or_leaves_at(rposf);
65
66                if (branch || leaves) && above && col.snow_cover {
67                    canvas.set(
68                        wpos + Vec3::unit_z(),
69                        Block::new(BlockKind::Snow, Rgb::new(255, 255, 255)),
70                    );
71                }
72
73                let block = if air {
74                    Some(Block::empty())
75                } else if leaves {
76                    if above && dynamic_rng.gen_bool(0.0005) {
77                        canvas.spawn(
78                            EntityInfo::at(wpos.map(|e| e as f32) + Vec3::unit_z())
79                                .with_asset_expect(
80                                    match dynamic_rng.gen_range(0..2) {
81                                        0 => "common.entity.wild.aggressive.deadwood",
82                                        _ => "common.entity.wild.aggressive.maneater",
83                                    },
84                                    dynamic_rng,
85                                    calendar,
86                                ),
87                        );
88                    } else if above && dynamic_rng.gen_bool(0.0001) {
89                        canvas.spawn(
90                            EntityInfo::at(wpos.map(|e| e as f32) + Vec3::unit_z())
91                                .with_asset_expect(
92                                    "common.entity.wild.aggressive.swamp_troll",
93                                    dynamic_rng,
94                                    calendar,
95                                ),
96                        );
97                    }
98
99                    let dark = Rgb::new(10, 70, 50).map(|e| e as f32);
100                    let light = Rgb::new(80, 140, 10).map(|e| e as f32);
101                    let leaf_col = Lerp::lerp(
102                        dark,
103                        light,
104                        nz.get(rposf.map(|e| e as f64) * 0.05) * 0.5 + 0.5,
105                    );
106
107                    Some(Block::new(BlockKind::Leaves, leaf_col.map(|e| e as u8)))
108                } else if branch {
109                    Some(Block::new(BlockKind::Wood, Rgb::new(80, 32, 0)))
110                } else if platform {
111                    Some(Block::new(BlockKind::Wood, Rgb::new(180, 130, 50)))
112                } else {
113                    None
114                };
115
116                // Chests in trees
117                if last.is_none() && block.is_some() && dynamic_rng.gen_bool(0.00025) {
118                    canvas.set(wpos + Vec3::unit_z(), Block::air(SpriteKind::Chest));
119                }
120
121                if let Some(block) = block {
122                    above = false;
123                    canvas.set(wpos, block);
124                } else if last
125                    .is_some_and(|b: Block| matches!(b.kind(), BlockKind::Leaves | BlockKind::Wood))
126                    && dynamic_rng.gen_bool(0.0005)
127                {
128                    canvas.set(wpos, Block::air(SpriteKind::Beehive));
129                }
130
131                last = block;
132            }
133        });
134    }
135}