veloren/world/src/layer/tree.rs

331 lines
12 KiB
Rust
Raw Normal View History

use crate::{
all::ForestKind,
block::block_from_structure,
column::ColumnGen,
util::{RandomPerm, Sampler, UnitChooser},
Canvas, CONFIG,
};
use common::{
2020-12-12 22:14:24 +00:00
assets::AssetHandle,
2021-01-03 20:43:07 +00:00
terrain::{Block, BlockKind, structure::{Structure, StructureBlock, StructuresGroup}},
vol::ReadVol,
};
2020-12-12 01:45:46 +00:00
use hashbrown::HashMap;
use lazy_static::lazy_static;
2020-12-12 22:14:24 +00:00
use std::f32;
use vek::*;
2021-01-03 20:43:07 +00:00
use rand::prelude::*;
lazy_static! {
2020-12-12 22:14:24 +00:00
static ref OAKS: AssetHandle<StructuresGroup> = Structure::load_group("oaks");
static ref OAK_STUMPS: AssetHandle<StructuresGroup> = Structure::load_group("oak_stumps");
static ref PINES: AssetHandle<StructuresGroup> = Structure::load_group("pines");
static ref PALMS: AssetHandle<StructuresGroup> = Structure::load_group("palms");
static ref ACACIAS: AssetHandle<StructuresGroup> = Structure::load_group("acacias");
static ref BAOBABS: AssetHandle<StructuresGroup> = Structure::load_group("baobabs");
static ref FRUIT_TREES: AssetHandle<StructuresGroup> = Structure::load_group("fruit_trees");
static ref BIRCHES: AssetHandle<StructuresGroup> = Structure::load_group("birch");
2020-12-13 01:09:57 +00:00
static ref MANGROVE_TREES: AssetHandle<StructuresGroup> =
Structure::load_group("mangrove_trees");
2020-12-12 22:14:24 +00:00
static ref QUIRKY: AssetHandle<StructuresGroup> = Structure::load_group("quirky");
static ref QUIRKY_DRY: AssetHandle<StructuresGroup> = Structure::load_group("quirky_dry");
static ref SWAMP_TREES: AssetHandle<StructuresGroup> = Structure::load_group("swamp_trees");
}
static MODEL_RAND: RandomPerm = RandomPerm::new(0xDB21C052);
static UNIT_CHOOSER: UnitChooser = UnitChooser::new(0x700F4EC7);
static QUIRKY_RAND: RandomPerm = RandomPerm::new(0xA634460F);
2020-11-23 15:39:03 +00:00
#[allow(clippy::if_same_then_else)]
pub fn apply_trees_to(canvas: &mut Canvas) {
2021-01-03 20:43:07 +00:00
// TODO: Get rid of this
enum TreeModel {
Structure(Structure),
Procedural(ProceduralTree),
}
struct Tree {
pos: Vec3<i32>,
2021-01-03 20:43:07 +00:00
model: TreeModel,
seed: u32,
units: (Vec2<i32>, Vec2<i32>),
}
let mut tree_cache = HashMap::new();
let info = canvas.info();
canvas.foreach_col(|canvas, wpos2d, col| {
let trees = info.land().get_near_trees(wpos2d);
for (tree_wpos, seed) in trees {
let tree = if let Some(tree) = tree_cache.entry(tree_wpos).or_insert_with(|| {
let col = ColumnGen::new(info.land()).get((tree_wpos, info.index()))?;
2020-11-11 11:42:22 +00:00
let is_quirky = QUIRKY_RAND.chance(seed, 1.0 / 500.0);
2020-11-23 15:39:03 +00:00
// Ensure that it's valid to place a *thing* here
2020-11-11 11:42:22 +00:00
if col.alt < col.water_level
2020-11-22 01:37:20 +00:00
|| col.spawn_rate < 0.9
|| col.water_dist.map(|d| d < 8.0).unwrap_or(false)
|| col.path.map(|(d, _, _, _)| d < 12.0).unwrap_or(false)
{
return None;
2020-11-23 15:39:03 +00:00
}
// Ensure that it's valid to place a tree here
if !is_quirky && ((seed.wrapping_mul(13)) & 0xFF) as f32 / 256.0 > col.tree_density
2020-11-15 01:40:23 +00:00
{
2020-11-11 11:42:22 +00:00
return None;
}
Some(Tree {
pos: Vec3::new(tree_wpos.x, tree_wpos.y, col.alt as i32),
2021-01-03 20:43:07 +00:00
model: 'model: {
2020-12-12 22:14:24 +00:00
let models: AssetHandle<_> = if is_quirky {
if col.temp > CONFIG.desert_temp {
2020-12-12 22:14:24 +00:00
*QUIRKY_DRY
} else {
2020-12-12 22:14:24 +00:00
*QUIRKY
}
} else {
match col.forest_kind {
2020-11-15 01:40:23 +00:00
ForestKind::Oak if QUIRKY_RAND.chance(seed + 1, 1.0 / 16.0) => {
2020-12-12 22:14:24 +00:00
*OAK_STUMPS
2020-11-15 01:40:23 +00:00
},
ForestKind::Oak if QUIRKY_RAND.chance(seed + 2, 1.0 / 20.0) => {
2020-12-12 22:14:24 +00:00
*FRUIT_TREES
2020-11-15 01:40:23 +00:00
},
2020-12-12 22:14:24 +00:00
ForestKind::Palm => *PALMS,
ForestKind::Acacia => *ACACIAS,
ForestKind::Baobab => *BAOBABS,
2021-01-03 20:43:07 +00:00
// ForestKind::Oak => *OAKS,
ForestKind::Oak => {
let mut rng = RandomPerm::new(seed);
break 'model TreeModel::Procedural(ProceduralTree::generate(&mut rng));
},
2020-12-12 22:14:24 +00:00
ForestKind::Pine => *PINES,
ForestKind::Birch => *BIRCHES,
ForestKind::Mangrove => *MANGROVE_TREES,
ForestKind::Swamp => *SWAMP_TREES,
}
};
2020-12-12 22:14:24 +00:00
let models = models.read();
2021-01-03 20:43:07 +00:00
TreeModel::Structure(models[(MODEL_RAND.get(seed.wrapping_mul(17)) / 13) as usize % models.len()]
.clone())
},
seed,
units: UNIT_CHOOSER.get(seed),
})
}) {
tree
} else {
continue;
};
2021-01-03 20:43:07 +00:00
let bounds = match &tree.model {
TreeModel::Structure(s) => s.get_bounds(),
TreeModel::Procedural(t) => t.get_bounds().map(|e| e as i32),
};
let rpos2d = (wpos2d - tree.pos.xy())
.map2(Vec2::new(tree.units.0, tree.units.1), |p, unit| {
unit * p
})
.sum();
if !Aabr::from(bounds).contains_point(rpos2d) {
2021-01-03 20:43:07 +00:00
// Skip this column
continue;
2021-01-03 20:43:07 +00:00
}
2020-11-09 15:06:37 +00:00
let mut is_top = true;
2020-11-09 17:09:33 +00:00
let mut is_leaf_top = true;
2020-11-09 15:06:37 +00:00
for z in (bounds.min.z..bounds.max.z).rev() {
let wpos = Vec3::new(wpos2d.x, wpos2d.y, tree.pos.z + z);
let model_pos = Vec3::from(
(wpos - tree.pos)
.xy()
.map2(Vec2::new(tree.units.0, tree.units.1), |rpos, unit| {
unit * rpos
})
.sum(),
) + Vec3::unit_z() * (wpos.z - tree.pos.z);
block_from_structure(
info.index(),
2021-01-03 20:43:07 +00:00
if let Some(block) = match &tree.model {
TreeModel::Structure(s) => s.get(model_pos).ok().copied(),
TreeModel::Procedural(t) => Some(match t.is_branch_or_leaves_at(model_pos.map(|e| e as f32 + 0.5)) {
(true, _) => StructureBlock::Normal(Rgb::new(60, 30, 0)),
(_, true) => StructureBlock::TemperateLeaves,
(_, _) => StructureBlock::None,
}),
2021-01-03 20:43:07 +00:00
} {
block
} else {
break
},
wpos,
tree.pos.xy(),
tree.seed,
col,
Block::air,
)
2020-11-09 15:06:37 +00:00
.map(|block| {
2020-11-09 17:09:33 +00:00
// Add a snow covering to the block above under certain circumstances
if col.snow_cover
&& ((block.kind() == BlockKind::Leaves && is_leaf_top)
|| (is_top && block.is_filled()))
{
2020-11-09 15:06:37 +00:00
canvas.set(
wpos + Vec3::unit_z(),
Block::new(BlockKind::Snow, Rgb::new(210, 210, 255)),
);
}
canvas.set(wpos, block);
2020-11-09 17:09:33 +00:00
is_leaf_top = false;
2020-11-09 15:06:37 +00:00
is_top = false;
})
.unwrap_or_else(|| {
2020-11-09 17:09:33 +00:00
is_leaf_top = true;
2020-11-09 15:06:37 +00:00
});
}
}
});
}
2021-01-03 20:43:07 +00:00
// TODO: Rename this to `Tree` when the name conflict is gone
struct ProceduralTree {
branches: Vec<Branch>,
}
impl ProceduralTree {
pub fn generate(rng: &mut impl Rng) -> Self {
let mut branches = Vec::new();
const ITERATIONS: usize = 4;
fn add_branches(branches: &mut Vec<Branch>, start: Vec3<f32>, dir: Vec3<f32>, depth: usize, rng: &mut impl Rng) {
let mut branch_dir = (dir + Vec3::<f32>::new(rng.gen_range(-1.0, 1.0),rng.gen_range(-1.0, 1.0),rng.gen_range(0.25, 1.0)).cross(dir).normalized() * 0.45 * (depth as f32 + 0.5)).normalized(); // I wish `vek` had a `Vec3::from_fn`
if branch_dir.z < 0. {
2021-01-05 17:34:22 +00:00
branch_dir.z = (branch_dir.z) / 16. + 0.2
}
let branch_len = 12.0 / (depth as f32 * 0.25 + 1.0); // Zipf, I guess
2021-01-03 20:43:07 +00:00
let end = start + branch_dir * branch_len;
branches.push(Branch::new(LineSegment3 { start, end },0.3 + 2.5 / (depth + 1) as f32,if depth == ITERATIONS {
2021-01-04 01:27:24 +00:00
rng.gen_range(3.0, 5.0)
2021-01-03 20:43:07 +00:00
} else {
0.0
2021-01-05 16:21:15 +00:00
}));
2021-01-03 20:43:07 +00:00
if depth < ITERATIONS {
2021-01-04 01:49:26 +00:00
let sub_branches = if depth == 0 { 3 } else { rng.gen_range(2, 4) };
for _ in 0..sub_branches {
add_branches(branches, end, branch_dir, depth + 1, rng);
2021-01-03 20:43:07 +00:00
}
}
}
2021-01-04 21:19:43 +00:00
let height = rng.gen_range(13, 30) as f32;
let dx = rng.gen_range(-5, 5) as f32;
let dy = rng.gen_range(-5, 5) as f32;
// Generate the trunk
2021-01-05 16:21:15 +00:00
branches.push(Branch::new(LineSegment3 { start: Vec3::zero(), end: Vec3::new(dx, dy, height)},3.0,0.0));
2021-01-04 21:19:43 +00:00
// Generate branches
const TEN_DEGREES: f32 = f32::consts::TAU / 36.;
2021-01-04 21:19:43 +00:00
let mut current_angle = 0.;
while current_angle < f32::consts::TAU {
for i in 1..3 {
let current_angle = current_angle + rng.gen_range(-TEN_DEGREES / 2., TEN_DEGREES / 2.);
2021-01-04 21:19:43 +00:00
add_branches(
&mut branches,
Vec3::new(dx, dy, height - rng.gen_range(0.0, height / 3.0)),
Vec3::new(current_angle.cos(), current_angle.sin(), rng.gen_range(0.2 * i as f32, 0.7 * i as f32)).normalized(),
1,
rng
2021-01-04 21:19:43 +00:00
);
if rng.gen_range(0, 4) != 2 {
break;
}
}
current_angle += rng.gen_range(TEN_DEGREES, TEN_DEGREES * 5.);
2021-01-04 21:19:43 +00:00
}
2021-01-03 20:43:07 +00:00
add_branches(
&mut branches,
Vec3::new(dx, dy, height - rng.gen_range(0.0, height / 3.0)),
Vec3::new(rng.gen_range(-0.2, 0.2), rng.gen_range(-0.2, 0.2), 1.).normalized(),
2,
rng
);
2021-01-03 20:43:07 +00:00
Self {
branches,
}
}
pub fn get_bounds(&self) -> Aabb<f32> {
let bounds = self.branches
.iter()
.fold(Aabb::default(), |Aabb { min, max }, branch| Aabb {
min: Vec3::partial_min(min, Vec3::partial_min(branch.line.start, branch.line.end) - branch.radius - 8.0),
max: Vec3::partial_max(max, Vec3::partial_max(branch.line.start, branch.line.end) + branch.radius + 8.0),
});
2021-01-03 20:43:07 +00:00
self.branches
.iter()
.for_each(|branch| {
assert!(bounds.contains_point(branch.line.start));
assert!(bounds.contains_point(branch.line.end));
});
2021-01-03 20:43:07 +00:00
bounds
2021-01-03 20:43:07 +00:00
}
pub fn is_branch_or_leaves_at(&self, pos: Vec3<f32>) -> (bool, bool) {
2021-01-05 16:21:15 +00:00
let mut is_leave = false;
for branch in &self.branches {
let p_d2 = branch.line.projected_point(pos).distance_squared(pos);
2021-01-05 16:21:15 +00:00
if !is_leave {
fn finvsqrt(x: f32) -> f32 {
2021-01-05 16:50:09 +00:00
let y = f32::from_bits(0x5f375a86 - (x.to_bits() >> 1));
y * (1.5 - ( x * 0.5 * y * y ))
2021-01-05 16:21:15 +00:00
}
if branch.health * finvsqrt(p_d2) > 1.0 {
is_leave = true;
}
}
if p_d2 < branch.squared_radius {
return (true,false);
2021-01-04 01:27:24 +00:00
}
}
2021-01-05 16:21:15 +00:00
(false, is_leave)
2021-01-03 20:43:07 +00:00
}
}
struct Branch {
line: LineSegment3<f32>,
radius: f32,
2021-01-05 16:21:15 +00:00
squared_radius: f32,
2021-01-03 20:43:07 +00:00
health: f32,
}
2021-01-05 16:21:15 +00:00
impl Branch {
fn new(line: LineSegment3<f32>,radius: f32,health: f32) -> Self {
Self {
line,
squared_radius: radius.powi(2),
radius,
health,
}
}
}