veloren/world/src/layer/tree.rs

289 lines
11 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();
fn add_branches(branches: &mut Vec<Branch>, rng: &mut impl Rng, start: Vec3<f32>, dir: Vec3<f32>, depth: usize) {
let branch_dir = (dir + Vec3::<f32>::zero().map(|_| rng.gen_range(-1.0, 1.0)).cross(dir).normalized() * 0.45 * (depth as f32 + 0.5)).normalized(); // I wish `vek` had a `Vec3::from_fn`
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 {
line: LineSegment3 { start, end },
radius: 0.3 + 2.5 / (depth + 1) as f32,
health: if depth == 4 {
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
},
});
if depth < 4 {
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 {
2021-01-03 20:43:07 +00:00
add_branches(branches, rng, end, branch_dir, depth + 1);
}
}
}
add_branches(&mut branches, rng, Vec3::zero(), Vec3::unit_z(), 0);
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) {
let mut is_branch = false;
2021-01-04 01:27:24 +00:00
let mut health = 0.0f32;
for branch in &self.branches {
let p_d2 = branch.line.projected_point(pos).distance_squared(pos);
2021-01-04 01:27:24 +00:00
#[allow(unsafe_code)]
fn finvsqrt(x: f32) -> f32 {
// Magic number based on Chris Lomont work:
// const MAGIC_U32: u32 = 0x5f375a86;
// The Original Magic Number:
// const MAGIC_32: u32 = 0x5f3759df;
const threehalfs: f32 = 1.5f32;
let x2: f32 = x * 0.5f32;
let mut i: u32 = unsafe { std::mem::transmute(x) };// evil floating point bit level hacking
i = 0x5f375a86 - (i >> 1); // what the fuck?
let y: f32 = unsafe { std::mem::transmute(i) };
let y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
// let y = y * (threehalfs - (x2 * y * y)); // 2nd iteration, this can be removed
y
}
is_branch |= p_d2 < branch.radius.powi(2);
2021-01-04 01:27:24 +00:00
health = health.max(branch.health * finvsqrt(p_d2));
}
(is_branch, health > 1.0)
2021-01-03 20:43:07 +00:00
}
}
struct Branch {
line: LineSegment3<f32>,
radius: f32,
health: f32,
}