Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions server/src/worldgen/finishers/clumped.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::worldgen::util::shuffle_seed_for_chunk;
use crate::worldgen::{ChunkBiomes, FinishingGenerator, TopBlocks};
use crate::worldgen::{FinishingGenerator, NearbyBiomes, TopBlocks};
use feather_blocks::Block;
use feather_core::{Biome, Chunk};
use rand::{Rng, SeedableRng};
Expand All @@ -14,7 +14,7 @@ impl FinishingGenerator for ClumpedFoliageFinisher {
fn generate_for_chunk(
&self,
chunk: &mut Chunk,
biomes: &ChunkBiomes,
biomes: &NearbyBiomes,
top_blocks: &TopBlocks,
seed: u64,
) {
Expand Down
2 changes: 2 additions & 0 deletions server/src/worldgen/finishers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
mod clumped;
mod single;
mod snow;
mod tree;

pub use clumped::ClumpedFoliageFinisher;
pub use single::SingleFoliageFinisher;
pub use snow::SnowFinisher;
pub use tree::TreeFinisher;
4 changes: 2 additions & 2 deletions server/src/worldgen/finishers/single.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::worldgen::util::shuffle_seed_for_chunk;
use crate::worldgen::{ChunkBiomes, FinishingGenerator, TopBlocks};
use crate::worldgen::{FinishingGenerator, NearbyBiomes, TopBlocks};
use feather_blocks::{Block, WaterData};
use feather_core::{Biome, Chunk};
use rand::{Rng, SeedableRng};
Expand All @@ -13,7 +13,7 @@ impl FinishingGenerator for SingleFoliageFinisher {
fn generate_for_chunk(
&self,
chunk: &mut Chunk,
biomes: &ChunkBiomes,
biomes: &NearbyBiomes,
top_blocks: &TopBlocks,
seed: u64,
) {
Expand Down
4 changes: 2 additions & 2 deletions server/src/worldgen/finishers/snow.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::worldgen::{ChunkBiomes, FinishingGenerator, TopBlocks};
use crate::worldgen::{FinishingGenerator, NearbyBiomes, TopBlocks};
use feather_blocks::{Block, SnowData};
use feather_core::{Biome, Chunk};

Expand All @@ -10,7 +10,7 @@ impl FinishingGenerator for SnowFinisher {
fn generate_for_chunk(
&self,
chunk: &mut Chunk,
biomes: &ChunkBiomes,
biomes: &NearbyBiomes,
top_blocks: &TopBlocks,
_seed: u64,
) {
Expand Down
157 changes: 157 additions & 0 deletions server/src/worldgen/finishers/tree/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
//! Tree generation.

mod schematic;
mod ty;

use crate::worldgen::presence::PresenceGrid;
use crate::worldgen::util::shuffle_seed_for_column;
use crate::worldgen::{FinishingGenerator, NearbyBiomes, TopBlocks};
use feather_core::{Biome, Chunk, ChunkPosition};
use rand::distributions::{Distribution, WeightedIndex};
use rand::{Rng, RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;
use std::cmp;
use std::convert::TryFrom;
pub use ty::TreeType;

/// Finisher for generating trees, depending on biome.
#[derive(Default)]
pub struct TreeFinisher;

impl FinishingGenerator for TreeFinisher {
fn generate_for_chunk(
&self,
chunk: &mut Chunk,
biomes: &NearbyBiomes,
top_blocks: &TopBlocks,
seed: u64,
) {
// Presence grid for trees.
// We set values to `true` for
// any column in which a tree will be generated.
let mut tree_presences = PresenceGrid::new();

// Compute presence of trees for each column within this chunk
// and the 6 columns bordering it on each side. For columns with
// trees generated, do the following:
// * First, check if a tree is already generated within params.spread.
// If so, remove the tree.
// * Generate a tree schematic for the tree and apply it to the chunk.
for x in -6..16 + 6 {
for z in -6..16 + 6 {
let biome = biomes.biome_at(x, z);
let params = params_for_biome(biome);

if compute_presence_for_column(chunk.position(), &params, x, z, seed) {
if tree_presences.is_presence_within(x, z, params.spread) {
continue;
}

tree_presences.set_presence_at(x, z, true);

let column_seed = column_seed(seed, chunk.position(), x, z);
let mut rng = XorShiftRng::seed_from_u64(column_seed);

// Choose tree type based on weights.
let possible_trees = params.possible_trees;
let index = possible_trees.weights.sample(&mut rng);
let tree = possible_trees.trees[index];

// Generate tree schematic and write it to the chunk.
let schematic = schematic::generate_tree(tree, seed);
let y = top_blocks.top_block_at(
cmp::min(15, usize::try_from(x).unwrap_or(0)),
cmp::min(15, usize::try_from(z).unwrap_or(0)),
); // TODO
schematic.write_to_chunk(
chunk,
(chunk.position().x * 16) as isize + x,
y as isize + 1,
(chunk.position().z * 16) as isize + z,
);
}
}
}
}
}

fn compute_presence_for_column(
center_chunk: ChunkPosition,
params: &TreeParams,
column_x: isize,
column_z: isize,
seed: u64,
) -> bool {
let seed = column_seed(seed, center_chunk, column_x, column_z);
let mut rng = XorShiftRng::seed_from_u64(seed);

rng.gen_bool(params.frequency / 256.0)
}

fn column_seed(seed: u64, center_chunk: ChunkPosition, column_x: isize, column_z: isize) -> u64 {
let abs_x = center_chunk.x * 16 + column_x as i32;
let abs_z = center_chunk.z * 16 + column_z as i32;

seed.wrapping_mul(((abs_x as u64) << 32) | abs_z as u64)
}

/// WeightedIndex for a biome's tree weights, with
/// the array of possible tree types corresponding
/// to samples from the weighted index.
#[derive(Debug)]
struct TreeWeights {
weights: WeightedIndex<f64>,
trees: &'static [TreeType],
}

impl TreeWeights {
fn new(weights: &[f64], trees: &'static [TreeType]) -> Self {
Self {
weights: WeightedIndex::new(weights).unwrap(),
trees,
}
}
}

/// Settings of a biome for tree generation.
#[derive(Debug)]
struct TreeParams {
/// Frequency of trees. Higher values mean more trees.
frequency: f64,
/// Minimum distance between two trees.
spread: u32,
/// Table of tree types possible for this biome
/// and their corresponding weights. The table
/// for each tree is in a `lazy_static` variable,
/// since initializing it requires an expensive
/// allocation.
possible_trees: &'static TreeWeights,
}

lazy_static! {
static ref WEIGHTS_DEFAULT: TreeWeights = TreeWeights::new(&[1.0], &[TreeType::Oak]);
static ref WEIGHTS_FOREST: TreeWeights = TreeWeights::new(&[1.0], &[TreeType::Oak]);
}

impl Default for TreeParams {
/// Tree parameters which generate zero trees.
fn default() -> Self {
Self {
frequency: 0.0,
spread: 0,
possible_trees: &WEIGHTS_DEFAULT,
}
}
}

/// Returns the tree parameters for the given biome.
fn params_for_biome(biome: Biome) -> TreeParams {
match biome {
Biome::Forest | Biome::DarkForest => TreeParams {
frequency: 4.0,
spread: 2,
possible_trees: &WEIGHTS_FOREST,
},
_ => TreeParams::default(), // TODO
}
}
84 changes: 84 additions & 0 deletions server/src/worldgen/finishers/tree/schematic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//! Generation of tree schematics.

use super::TreeType;
use crate::worldgen::schematic::{Schematic, SchematicBuilder};
use rand::{Rng, SeedableRng};
use rand_xorshift::XorShiftRng;

/// Generates a tree with the given type and seed.
pub fn generate_tree(ty: TreeType, seed: u64) -> Schematic {
let mut rng = XorShiftRng::seed_from_u64(seed);
match ty {
TreeType::Oak => generate_oak(&mut rng),
TreeType::DarkOak => generate_dark_oak(&mut rng),
TreeType::Birch => generate_birch(&mut rng),
TreeType::Spruce => generate_spruce(&mut rng),
TreeType::Acacia => generate_acacia(&mut rng),
TreeType::Jungle => generate_jungle(&mut rng),
}
}

fn generate_oak<R: Rng>(rng: &mut R) -> Schematic {
let ty = TreeType::Oak;
// TODO: tall oak variant
let mut tree = SchematicBuilder::new()
.with_dimensions(5, 7, 5)
.with_center(2, 0, 2)
.build();

// Logs
for y in 0..6 {
tree.set_block_at(0, y, 0, TreeType::Oak.log_block());
}

// Leaves

// Bottom part (two 5x5 squares with random corners)
for x in -2isize..=2 {
for z in -2isize..=2 {
for y in 0..2 {
let y = y + 3;

if x.abs() == 2 && z.abs() == 2 && rng.gen_bool(0.3) || x == 0 && z == 0 {
continue; // Variance in corner leaves
}

tree.set_block_at(x, y, z, ty.leaf_block(manhattan(0, y, 0, x, y, z) as i32));
}
}
}
// Top part
let positions = [[1, 0], [-1, 0], [0, 1], [0, -1]];
for y in 5..=6 {
positions
.iter()
.for_each(|pos| tree.set_block_at(pos[0], y, pos[1], ty.leaf_block(1)));
}
tree.set_block_at(0, 6, 0, ty.leaf_block(1));

tree
}

fn generate_dark_oak<R: Rng>(_rng: &mut R) -> Schematic {
unimplemented!()
}

fn generate_birch<R: Rng>(_rng: &mut R) -> Schematic {
unimplemented!()
}

fn generate_spruce<R: Rng>(_rng: &mut R) -> Schematic {
unimplemented!()
}

fn generate_acacia<R: Rng>(_rng: &mut R) -> Schematic {
unimplemented!()
}

fn generate_jungle<R: Rng>(_rng: &mut R) -> Schematic {
unimplemented!()
}

fn manhattan(x1: isize, y1: isize, z1: isize, x2: isize, y2: isize, z2: isize) -> isize {
(x2 - x1).abs() + (y2 - y1).abs() + (z2 - z1).abs()
}
74 changes: 74 additions & 0 deletions server/src/worldgen/finishers/tree/ty.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use feather_blocks::{
AcaciaLeavesData, AcaciaLogAxis, AcaciaLogData, BirchLeavesData, BirchLogAxis, BirchLogData,
Block, DarkOakLeavesData, DarkOakLogAxis, DarkOakLogData, JungleLeavesData, JungleLogAxis,
JungleLogData, OakLeavesData, OakLogAxis, OakLogData, SpruceLeavesData, SpruceLogAxis,
SpruceLogData,
};

/// Type of a tree.
#[derive(Debug, Clone, Copy)]
pub enum TreeType {
Oak,
DarkOak,
Birch,
Spruce,
Acacia,
Jungle,
}

impl TreeType {
/// Returns the block corresponding to a log of this tree.
pub fn log_block(self) -> Block {
match self {
TreeType::Oak => Block::OakLog(OakLogData {
axis: OakLogAxis::Y,
}),
TreeType::DarkOak => Block::DarkOakLog(DarkOakLogData {
axis: DarkOakLogAxis::Y,
}),
TreeType::Birch => Block::BirchLog(BirchLogData {
axis: BirchLogAxis::Y,
}),
TreeType::Spruce => Block::SpruceLog(SpruceLogData {
axis: SpruceLogAxis::Y,
}),
TreeType::Acacia => Block::AcaciaLog(AcaciaLogData {
axis: AcaciaLogAxis::Y,
}),
TreeType::Jungle => Block::JungleLog(JungleLogData {
axis: JungleLogAxis::Y,
}),
}
}

/// Returns the block corresponding to a leaf of this tree,
/// at the given distance away from the nearest log.
pub fn leaf_block(self, distance: i32) -> Block {
match self {
TreeType::Oak => Block::OakLeaves(OakLeavesData {
distance,
persistent: true,
}),
TreeType::DarkOak => Block::DarkOakLeaves(DarkOakLeavesData {
distance,
persistent: true,
}),
TreeType::Birch => Block::BirchLeaves(BirchLeavesData {
distance,
persistent: true,
}),
TreeType::Spruce => Block::SpruceLeaves(SpruceLeavesData {
distance,
persistent: true,
}),
TreeType::Acacia => Block::AcaciaLeaves(AcaciaLeavesData {
distance,
persistent: true,
}),
TreeType::Jungle => Block::JungleLeaves(JungleLeavesData {
distance,
persistent: true,
}),
}
}
}
Loading