Added staircase to giant trees

This commit is contained in:
Joshua Barretto 2021-02-08 17:57:55 +00:00
parent ba4e979825
commit 9d7a647153
3 changed files with 41 additions and 30 deletions

View File

@ -87,5 +87,5 @@ pub struct TreeAttr {
pub seed: u32, pub seed: u32,
pub scale: f32, pub scale: f32,
pub forest_kind: ForestKind, pub forest_kind: ForestKind,
pub lanterns: bool, pub inhabited: bool,
} }

View File

@ -60,7 +60,7 @@ pub fn apply_trees_to(canvas: &mut Canvas, dynamic_rng: &mut impl Rng) {
canvas.foreach_col(|canvas, wpos2d, col| { canvas.foreach_col(|canvas, wpos2d, col| {
let trees = info.land().get_near_trees(wpos2d); let trees = info.land().get_near_trees(wpos2d);
for TreeAttr { pos, seed, scale, forest_kind, lanterns } in trees { for TreeAttr { pos, seed, scale, forest_kind, inhabited } in trees {
let tree = if let Some(tree) = tree_cache.entry(pos).or_insert_with(|| { let tree = if let Some(tree) = tree_cache.entry(pos).or_insert_with(|| {
let col = ColumnGen::new(info.land()).get((pos, info.index()))?; let col = ColumnGen::new(info.land()).get((pos, info.index()))?;
@ -127,7 +127,7 @@ pub fn apply_trees_to(canvas: &mut Canvas, dynamic_rng: &mut impl Rng) {
ForestKind::Giant => { ForestKind::Giant => {
break 'model TreeModel::Procedural( break 'model TreeModel::Procedural(
ProceduralTree::generate( ProceduralTree::generate(
TreeConfig::giant(&mut RandomPerm::new(seed), scale), TreeConfig::giant(&mut RandomPerm::new(seed), scale, inhabited),
&mut RandomPerm::new(seed), &mut RandomPerm::new(seed),
), ),
StructureBlock::TemperateLeaves, StructureBlock::TemperateLeaves,
@ -184,9 +184,10 @@ pub fn apply_trees_to(canvas: &mut Canvas, dynamic_rng: &mut impl Rng) {
TreeModel::Structure(s) => s.get(model_pos).ok().copied(), TreeModel::Structure(s) => s.get(model_pos).ok().copied(),
TreeModel::Procedural(t, leaf_block) => Some( TreeModel::Procedural(t, leaf_block) => Some(
match t.is_branch_or_leaves_at(model_pos.map(|e| e as f32 + 0.5)) { match t.is_branch_or_leaves_at(model_pos.map(|e| e as f32 + 0.5)) {
(true, _) => StructureBlock::Log, (_, _, true) => StructureBlock::Hollow,
(_, true) => *leaf_block, (true, _, _) => StructureBlock::Log,
(_, _) => StructureBlock::None, (_, true, _) => *leaf_block,
(_, _, _) => StructureBlock::None,
}, },
), ),
} { } {
@ -201,8 +202,8 @@ pub fn apply_trees_to(canvas: &mut Canvas, dynamic_rng: &mut impl Rng) {
Block::air, Block::air,
) )
.map(|block| { .map(|block| {
// Add mushrooms to the tree // Add lights to the tree
if lanterns && last_block.is_air() && block.kind() == BlockKind::Wood && dynamic_rng.gen_range(0..48) == 0 { if inhabited && last_block.is_air() && block.kind() == BlockKind::Wood && dynamic_rng.gen_range(0..256) == 0 {
canvas.set(wpos + Vec3::unit_z(), Block::air(SpriteKind::Lantern)); canvas.set(wpos + Vec3::unit_z(), Block::air(SpriteKind::Lantern));
// Add a snow covering to the block above under certain circumstances // Add a snow covering to the block above under certain circumstances
} else if col.snow_cover } else if col.snow_cover
@ -266,6 +267,8 @@ pub struct TreeConfig {
pub leaf_vertical_scale: f32, pub leaf_vertical_scale: f32,
/// How evenly spaced (vs random) sub-branches are along their parent. /// How evenly spaced (vs random) sub-branches are along their parent.
pub proportionality: f32, pub proportionality: f32,
/// Whether the tree is inhabited (adds various features and effects)
pub inhabited: bool,
} }
impl TreeConfig { impl TreeConfig {
@ -286,6 +289,7 @@ impl TreeConfig {
branch_len_bias: 0.0, branch_len_bias: 0.0,
leaf_vertical_scale: 1.0, leaf_vertical_scale: 1.0,
proportionality: 0.0, proportionality: 0.0,
inhabited: false,
} }
} }
@ -306,10 +310,11 @@ impl TreeConfig {
branch_len_bias: 0.75, branch_len_bias: 0.75,
leaf_vertical_scale: 0.3, leaf_vertical_scale: 0.3,
proportionality: 1.0, proportionality: 1.0,
inhabited: false,
} }
} }
pub fn giant(rng: &mut impl Rng, scale: f32) -> Self { pub fn giant(rng: &mut impl Rng, scale: f32, inhabited: bool) -> Self {
let log_scale = 1.0 + scale.log2().max(0.0); let log_scale = 1.0 + scale.log2().max(0.0);
Self { Self {
@ -325,6 +330,7 @@ impl TreeConfig {
branch_len_bias: 0.0, branch_len_bias: 0.0,
leaf_vertical_scale: 0.6, leaf_vertical_scale: 0.6,
proportionality: 0.0, proportionality: 0.0,
inhabited,
} }
} }
} }
@ -385,7 +391,7 @@ impl ProceduralTree {
0.0 0.0
}; };
let has_stairs = branch_radius > 3.0 && start.xy().distance(end.xy()) < (start.z - end.z).abs(); let has_stairs = config.inhabited && branch_radius > 3.0 && start.xy().distance(end.xy()) < (start.z - end.z).abs();
let bark_radius = if has_stairs { 8.0 } else { 0.0 }; let bark_radius = if has_stairs { 8.0 } else { 0.0 };
// The AABB that covers this branch, along with wood and leaves that eminate // The AABB that covers this branch, along with wood and leaves that eminate
@ -478,13 +484,13 @@ impl ProceduralTree {
pub fn get_bounds(&self) -> Aabb<f32> { self.branches[self.trunk_idx].aabb } pub fn get_bounds(&self) -> Aabb<f32> { self.branches[self.trunk_idx].aabb }
// Recursively search for branches or leaves by walking the tree's branch graph. // Recursively search for branches or leaves by walking the tree's branch graph.
fn is_branch_or_leaves_at_inner(&self, pos: Vec3<f32>, branch_idx: usize) -> (bool, bool) { fn is_branch_or_leaves_at_inner(&self, pos: Vec3<f32>, branch_idx: usize) -> (bool, bool, bool) {
let branch = &self.branches[branch_idx]; let branch = &self.branches[branch_idx];
// Always probe the sibling branch, since our AABB doesn't include its bounds // Always probe the sibling branch, since our AABB doesn't include its bounds
// (it's not one of our children) // (it's not one of our children)
let branch_or_leaves = branch let branch_or_leaves = branch
.sibling_idx .sibling_idx
.map(|idx| Vec2::from(self.is_branch_or_leaves_at_inner(pos, idx))) .map(|idx| Vec3::from(self.is_branch_or_leaves_at_inner(pos, idx)))
.unwrap_or_default(); .unwrap_or_default();
// Only continue probing this sub-graph of the tree if the sample position falls // Only continue probing this sub-graph of the tree if the sample position falls
@ -492,10 +498,10 @@ impl ProceduralTree {
if branch.aabb.contains_point(pos) { if branch.aabb.contains_point(pos) {
(branch_or_leaves (branch_or_leaves
// Probe this branch // Probe this branch
| Vec2::from(branch.is_branch_or_leaves_at(pos)) | Vec3::from(branch.is_branch_or_leaves_at(pos))
// Probe the children of this branch // Probe the children of this branch
| branch.child_idx | branch.child_idx
.map(|idx| Vec2::from(self.is_branch_or_leaves_at_inner(pos, idx))) .map(|idx| Vec3::from(self.is_branch_or_leaves_at_inner(pos, idx)))
.unwrap_or_default()) .unwrap_or_default())
.into_tuple() .into_tuple()
} else { } else {
@ -506,7 +512,7 @@ impl ProceduralTree {
/// Determine whether there are either branches or leaves at the given /// Determine whether there are either branches or leaves at the given
/// position in the tree. /// position in the tree.
#[inline(always)] #[inline(always)]
pub fn is_branch_or_leaves_at(&self, pos: Vec3<f32>) -> (bool, bool) { pub fn is_branch_or_leaves_at(&self, pos: Vec3<f32>) -> (bool, bool, bool) {
self.is_branch_or_leaves_at_inner(pos, self.trunk_idx) self.is_branch_or_leaves_at_inner(pos, self.trunk_idx)
} }
} }
@ -532,7 +538,7 @@ struct Branch {
impl Branch { impl Branch {
/// Determine whether there are either branches or leaves at the given /// Determine whether there are either branches or leaves at the given
/// position in the branch. /// position in the branch.
pub fn is_branch_or_leaves_at(&self, pos: Vec3<f32>) -> (bool, bool) { pub fn is_branch_or_leaves_at(&self, pos: Vec3<f32>) -> (bool, bool, bool) {
// fn finvsqrt(x: f32) -> f32 { // fn finvsqrt(x: f32) -> f32 {
// let y = f32::from_bits(0x5f375a86 - (x.to_bits() >> 1)); // let y = f32::from_bits(0x5f375a86 - (x.to_bits() >> 1));
// y * (1.5 - ( x * 0.5 * y * y )) // y * (1.5 - ( x * 0.5 * y * y ))
@ -542,19 +548,24 @@ impl Branch {
let p_d2 = p.distance_squared(pos); let p_d2 = p.distance_squared(pos);
if p_d2 < self.wood_radius.powi(2) { if p_d2 < self.wood_radius.powi(2) {
(true, false) (true, false, false) // Wood
} else if self.has_stairs && { } else if {
let horizontal_projected = Lerp::lerp_unclamped(self.line.start, self.line.end, (pos.z - self.line.start.z) / (self.line.end.z - self.line.start.z)); let diff = (p - pos) / Vec3::new(1.0, 1.0, self.leaf_vertical_scale);
let rpos = pos.xy() - horizontal_projected.xy(); diff.magnitude_squared() < self.leaf_radius.powi(2)
} {
(false, true, false) // Leaves
} else {
let stair_width = 5.0;
let stair_thickness = 1.5;
let stair_space = 6.0;
if self.has_stairs && p_d2 < (self.wood_radius + stair_width).powi(2) {
let rpos = pos.xy() - p.xy();
let stretch = 32.0; let stretch = 32.0;
let stair_section = ((rpos.x as f32).atan2(rpos.y as f32) / (f32::consts::PI * 2.0) * stretch + pos.z).rem_euclid(stretch); let stair_section = ((rpos.x as f32).atan2(rpos.y as f32) / (f32::consts::PI * 2.0) * stretch + pos.z).rem_euclid(stretch);
stair_section < 2.0 && p_d2 < (self.wood_radius + 8.0).powi(2) (stair_section < stair_thickness, false, stair_section >= stair_thickness && stair_section < stair_thickness + stair_space) // Stairs
} {
(true, false)
} else { } else {
let diff = (p - pos) / Vec3::new(1.0, 1.0, self.leaf_vertical_scale); (false, false, false)
}
(false, diff.magnitude_squared() < self.leaf_radius.powi(2))
} }
} }
} }

View File

@ -2022,7 +2022,7 @@ impl WorldSim {
}) })
.collect::<Vec<_>>()) .collect::<Vec<_>>())
.choose_seeded(seed), .choose_seeded(seed),
lanterns: false, inhabited: false,
}) })
}); });
@ -2034,7 +2034,7 @@ impl WorldSim {
seed, seed,
scale: 4.0, scale: 4.0,
forest_kind: ForestKind::Giant, forest_kind: ForestKind::Giant,
lanterns: true, inhabited: (seed / 13) % 2 == 0,
}); });
normal_trees.chain(giant_trees) normal_trees.chain(giant_trees)