veloren_world/layer/
shrub.rs1use crate::{
2 Canvas,
3 all::ForestKind,
4 layer::cave::tunnel_bounds_at,
5 util::{Sampler, StructureGen2d, UnitChooser, gen_cache::StructureGenCache, seed_expan},
6};
7use common::{
8 assets::AssetHandle,
9 terrain::structure::{Structure, StructuresGroup},
10};
11use lazy_static::lazy_static;
12use rand::prelude::*;
13use rand_chacha::ChaChaRng;
14use vek::*;
15
16lazy_static! {
17 static ref JUNGLE_SHRUBS: AssetHandle<StructuresGroup> = Structure::load_group("shrubs.jungle");
18 static ref SAVANNAH_SHRUBS: AssetHandle<StructuresGroup> =
19 Structure::load_group("shrubs.savannah");
20 static ref TEMPERATE_SHRUBS: AssetHandle<StructuresGroup> =
21 Structure::load_group("shrubs.temperate");
22 static ref TAIGA_SHRUBS: AssetHandle<StructuresGroup> = Structure::load_group("shrubs.taiga");
23}
24
25struct Shrub {
26 wpos: Vec3<i32>,
27 seed: u32,
28 kind: ForestKind,
29}
30
31pub fn apply_shrubs_to(canvas: &mut Canvas, _dynamic_rng: &mut impl Rng) {
32 let mut shrub_gen = StructureGenCache::new(StructureGen2d::new(canvas.index().seed, 8, 4));
33
34 let info = canvas.info();
35
36 canvas.foreach_col(|_, wpos2d, _| {
37 shrub_gen.get(wpos2d, |wpos, seed| {
38 let col = info.col_or_gen(wpos)?;
39
40 let mut rng = ChaChaRng::from_seed(seed_expan::rng_state(seed));
41
42 const BASE_SHRUB_DENSITY: f64 = 0.15;
43 if rng.gen_bool((BASE_SHRUB_DENSITY * col.tree_density as f64).clamped(0.0, 1.0))
44 && col.water_dist.is_none_or(|d| d > 8.0)
45 && col.alt > col.water_level
46 && col.spawn_rate > 0.9
47 && col.path.is_none_or(|(d, _, _, _)| d > 6.0)
48 && !tunnel_bounds_at(wpos, &info, &info.land())
49 .any(|(_, z_range, _, _, _, _)| z_range.contains(&(col.alt as i32 - 1)))
50 {
51 let kind = *info
52 .chunks()
53 .make_forest_lottery(wpos)
54 .choose_seeded(seed)
55 .as_ref()?;
56 if rng.gen_bool(kind.shrub_density_factor() as f64) {
57 Some(Shrub {
58 wpos: wpos.with_z(col.alt as i32),
59 seed,
60 kind,
61 })
62 } else {
63 None
64 }
65 } else {
66 None
67 }
68 });
69 });
70
71 for shrub in shrub_gen.generated() {
72 let mut rng = ChaChaRng::from_seed(seed_expan::rng_state(shrub.seed));
73
74 let units = UnitChooser::new(shrub.seed).get(shrub.seed).into();
75
76 let shrubs = match shrub.kind {
77 ForestKind::Mangrove => *JUNGLE_SHRUBS,
78 ForestKind::Acacia | ForestKind::Baobab => *SAVANNAH_SHRUBS,
79 ForestKind::Oak | ForestKind::Chestnut => *TEMPERATE_SHRUBS,
80 ForestKind::Pine => *TAIGA_SHRUBS,
81 _ => continue, }
83 .read();
84
85 let structure = shrubs.choose(&mut rng).unwrap();
86 canvas.blit_structure(shrub.wpos, structure, shrub.seed, units, true);
87 }
88}