alex/beta-minecraft-rust
Minecraft Beta 1.7.3 Rewrite
BRANCH main
SNAPSHOT Draw terrain GPU-driven from packed per-section buffers with GPU occlusion culling, add a render distance slider (2-128) with a quad/disk shape, cut chunk bookkeeping at high render distances, and fix a light underflow at y=0
DOWNLOAD
root crates server src populate big_tree.rs
//! Port of `WorldGenBigTree`.
use common::JavaRandom;
use common::math::floor_f64;
use common::world::Block;
use crate::level::Level;
/// The two axes other than the major one, indexed by `major` and `major + 3`.
const OTHER_AXES: [usize; 6] = [2, 0, 0, 1, 2, 1];
const HEIGHT_LIMIT_LIMIT: i32 = 12;
const HEIGHT_ATTENUATION: f64 = 0.618;
const BRANCH_SLOPE: f64 = 0.381;
const SCALE_WIDTH: f64 = 1.0;
const LEAF_DENSITY: f64 = 1.0;
/// The original's truncated pi, which changes branch angles slightly.
#[allow(clippy::approx_constant)]
const PI: f64 = 3.14159;
struct BigTree<'a> {
level: &'a mut Level,
rand: JavaRandom,
base: [i32; 3],
height_limit: i32,
height: i32,
/// `leafDistanceLimit`: the height of a leaf cluster.
leaf_distance: i32,
/// Leaf cluster positions and the height their branch starts at.
nodes: Vec<[i32; 4]>,
}
/// Population raises the leaf clusters from four to five layers with `setScale(1, 1, 1)`.
pub fn generate(
level: &mut Level,
[x, y, z]: [i32; 3],
random: &mut JavaRandom,
leaf_distance: i32,
) -> bool {
let mut rand = JavaRandom::new(random.next_long());
let height_limit = 5 + rand.next_int(HEIGHT_LIMIT_LIMIT);
let mut tree = BigTree {
level,
rand,
base: [x, y, z],
height_limit,
height: 0,
leaf_distance,
nodes: Vec::new(),
};
if !tree.valid_location() {
return false;
}
tree.generate_leaf_nodes();
for i in 0..tree.nodes.len() {
let [x, y, z, _] = tree.nodes[i];
for layer in 0..tree.leaf_distance {
tree.leaf_layer(x, y + layer, z, tree.leaf_size(layer));
}
}
tree.place_line([x, y, z], [x, y + tree.height, z]);
// `generateLeafNodeBases`: branches from the trunk to the higher leaf clusters
for i in 0..tree.nodes.len() {
let [nx, ny, nz, branch_y] = tree.nodes[i];
if f64::from(branch_y - y) >= f64::from(tree.height_limit) * 0.2 {
tree.place_line([x, branch_y, z], [nx, ny, nz]);
}
}
true
}
impl BigTree<'_> {
fn block(&mut self, pos: [i32; 3]) -> Block {
self.level.block(pos[0], pos[1], pos[2])
}
/// `generateLeafNodeList`.
fn generate_leaf_nodes(&mut self) {
self.height =
((f64::from(self.height_limit) * HEIGHT_ATTENUATION) as i32).min(self.height_limit - 1);
let per_layer =
((1.382 + (LEAF_DENSITY * f64::from(self.height_limit) / 13.0).powi(2)) as i32).max(1);
let mut y = self.base[1] + self.height_limit - self.leaf_distance;
let trunk_top = self.base[1] + self.height;
let mut layer = y - self.base[1];
self.nodes.push([self.base[0], y, self.base[2], trunk_top]);
y -= 1;
while layer >= 0 {
let size = self.layer_size(layer);
if size >= 0.0 {
for _ in 0..per_layer {
let distance =
SCALE_WIDTH * f64::from(size) * (f64::from(self.rand.next_float()) + 0.328);
let angle = f64::from(self.rand.next_float()) * 2.0 * PI;
let nx = floor_f64(distance * angle.sin() + f64::from(self.base[0]) + 0.5);
let nz = floor_f64(distance * angle.cos() + f64::from(self.base[2]) + 0.5);
let node = [nx, y, nz];
if self.check_line(node, [nx, y + self.leaf_distance, nz]) != -1 {
continue;
}
let dx = f64::from((self.base[0] - nx).abs());
let dz = f64::from((self.base[2] - nz).abs());
let rise = (dx.powi(2) + dz.powi(2)).sqrt() * BRANCH_SLOPE;
let branch_y = if f64::from(y) - rise > f64::from(trunk_top) {
trunk_top
} else {
(f64::from(y) - rise) as i32
};
if self.check_line([self.base[0], branch_y, self.base[2]], node) == -1 {
self.nodes.push([nx, y, nz, branch_y]);
}
}
}
y -= 1;
layer -= 1;
}
}
/// `layerSize`: the crown radius at a height above the base, negative below the crown.
fn layer_size(&self, height: i32) -> f32 {
if f64::from(height) < f64::from(self.height_limit as f32) * 0.3 {
return -1.618;
}
let half = self.height_limit as f32 / 2.0;
let offset = self.height_limit as f32 / 2.0 - height as f32;
let radius = if offset == 0.0 {
half
} else if offset.abs() >= half {
0.0
} else {
(f64::from(half.abs()).powi(2) - f64::from(offset.abs()).powi(2)).sqrt() as f32
};
radius * 0.5
}
/// `leafSize`.
fn leaf_size(&self, layer: i32) -> f32 {
if !(0..self.leaf_distance).contains(&layer) {
-1.0
} else if layer == 0 || layer == self.leaf_distance - 1 {
2.0
} else {
3.0
}
}
/// `genTreeLayer` with the y axis as the major axis: a disc of leaves.
fn leaf_layer(&mut self, x: i32, y: i32, z: i32, radius: f32) {
let extent = (f64::from(radius) + 0.618) as i32;
for dx in -extent..=extent {
for dz in -extent..=extent {
let distance = ((f64::from(dx.abs()) + 0.5).powi(2)
+ (f64::from(dz.abs()) + 0.5).powi(2))
.sqrt();
if distance > f64::from(radius) {
continue;
}
let pos = [x + dx, y, z + dz];
if matches!(self.block(pos), Block::AIR | Block::LEAVES) {
self.level.set_block(pos[0], pos[1], pos[2], Block::LEAVES);
}
}
}
}
/// `placeBlockLine` with logs.
fn place_line(&mut self, from: [i32; 3], to: [i32; 3]) {
for (_, [x, y, z]) in line(from, to, 0.5) {
self.level.set_block(x, y, z, Block::LOG);
}
}
/// `checkBlockLine`: -1 if the line is clear of everything but air and leaves,
/// otherwise the distance to the first obstruction.
fn check_line(&mut self, from: [i32; 3], to: [i32; 3]) -> i32 {
line(from, to, 0.0)
.find(|&(_, pos)| !matches!(self.block(pos), Block::AIR | Block::LEAVES))
.map_or(-1, |(i, _)| i.abs())
}
/// `validTreeLocation`: needs grass or dirt below and at least six free blocks above.
fn valid_location(&mut self) -> bool {
let [x, y, z] = self.base;
if !matches!(self.block([x, y - 1, z]), Block::GRASS | Block::DIRT) {
return false;
}
match self.check_line(self.base, [x, y + self.height_limit - 1, z]) {
-1 => true,
free if free < 6 => false,
free => {
self.height_limit = free;
true
}
}
}
}
/// The blocks of a line, stepping along its dominant axis, with the signed step count.
fn line(from: [i32; 3], to: [i32; 3], bias: f64) -> impl Iterator<Item = (i32, [i32; 3])> {
let delta = [0, 1, 2].map(|axis| to[axis] - from[axis]);
let mut major = 0;
for axis in 0..3 {
if delta[axis].abs() > delta[major].abs() {
major = axis;
}
}
let (a, b) = (OTHER_AXES[major], OTHER_AXES[major + 3]);
let step = delta[major].signum();
let ratio_a = f64::from(delta[a]) / f64::from(delta[major]);
let ratio_b = f64::from(delta[b]) / f64::from(delta[major]);
let steps = if step == 0 { 0 } else { delta[major].abs() + 1 };
(0..steps).map(move |n| {
let i = n * step;
let mut pos = [0; 3];
pos[major] = floor_f64(f64::from(from[major] + i) + bias);
pos[a] = floor_f64(f64::from(from[a]) + f64::from(i) * ratio_a + bias);
pos[b] = floor_f64(f64::from(from[b]) + f64::from(i) * ratio_b + bias);
(i, pos)
})
}