Skip to content

Commit

Permalink
Basic Overworld world generation (#120)
Browse files Browse the repository at this point in the history
This is a sad squashing of 49 laborious commits into one:

* Lay groundwork for a true world generator

* Implement basic height map and composition generators

* more work

* Initialize light values to 15

* Implement basic biome grid generator + visualization

* Initialize biome values in chunks

* Implement basic changing of topsoil blocks depending on column biome

* Load seed from world save

* Don't generate invalid biomes, such as TheEnd

* Adjust some parameters and fix biome composition for certain biomes

* Various fixes and tweaks

* Implement basic two-level biome generator

* Rename HeightMapGenerator -> DensityMapGenerator

* Add basic ocean support

* Correctly set water level below ocean surface

* Refactor biome generators into separate files

* Fix height map noise - don't use generate_scaled

* Refactor density_map into multiple files

* Begin work on linear interpolation for noise

* More work on trilinear interpolation

* Get tests to pass for trilinear interpolation

* Initiial implementation of density map generator

* Work more on linear interpolation

* Rewrite linear interpolation algorithm using different approach

* Fix bug with linear interpolation

* Fix another bug with interpolation (no, it still doesn't work)

* And another one

* Sigh... nothing works.

* Get density generation to work, after four days of painful debugging

* Remove physics debug message which was accidentally added in this branch

* Generate density based on biome

* Composition fixes

* Interpolate between biome parameter values

* Minor Voronoi fix: don't use generate_scaled

* Biome changes

* Add snow finisher

* Add shrub and lily pad generator

* Fix Voronoi implementation

* Add additional height noise to density generator

* Add grass finisher

* Fix NearbyBiomes impl, which caused cutoffs at biome boundaries

* Add reproducability test
  • Loading branch information
caelunshun authored Sep 14, 2019
1 parent 0bc8b03 commit 8187341
Show file tree
Hide file tree
Showing 24 changed files with 1,869 additions and 31 deletions.
59 changes: 59 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion core/src/biomes.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EnumCount)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EnumCount, FromPrimitive, ToPrimitive)]
pub enum Biome {
Badlands,
BadlandsPlateau,
Expand Down
2 changes: 1 addition & 1 deletion core/src/save/level.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ pub struct LevelData {
#[serde(rename = "rainTime")]
pub rain_time: i32,
#[serde(rename = "RandomSeed")]
pub random_seed: i64,
pub seed: i64,

#[serde(rename = "SpawnX")]
pub spawn_x: i32,
Expand Down
14 changes: 14 additions & 0 deletions core/src/world/chunk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,12 @@ impl Chunk {
self.sections.iter().map(|sec| sec.as_ref()).collect()
}

/// Returns a mutable slice of the 16 sections
/// in this chunk.
pub fn sections_mut(&mut self) -> Vec<Option<&mut ChunkSection>> {
self.sections.iter_mut().map(|sec| sec.as_mut()).collect()
}

/// Returns the position in chunk coordinates
/// of this chunk.
pub fn position(&self) -> ChunkPosition {
Expand Down Expand Up @@ -524,6 +530,14 @@ impl ChunkSection {
pub fn block_light(&self) -> &BitArray {
&self.block_light
}

pub fn sky_light_mut(&mut self) -> &mut BitArray {
&mut self.sky_light
}

pub fn block_light_mut(&mut self) -> &mut BitArray {
&mut self.block_light
}
}

impl Default for ChunkSection {
Expand Down
2 changes: 1 addition & 1 deletion generator/src/biome.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ pub fn generate_rust(input: &str, output: &str) -> Result<(), Error> {
}

let code = quote! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EnumCount)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EnumCount, FromPrimitive, ToPrimitive)]
pub enum Biome {
#(#enum_variants)*
}
Expand Down
4 changes: 4 additions & 0 deletions server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ rsa = "0.1.3"
num-bigint = { version = "0.4", features = ["rand", "i128", "u64_digit", "prime", "zeroize"], package = "num-bigint-dig" }
rsa-der = "0.2.1"
rand = "0.7.0"
rand_xorshift = "0.2.0"
rand-legacy = { path = "../util/rand-legacy" }
bytes = "0.4.12"
hashbrown = { version = "0.6.0", features = ["rayon"] }
Expand Down Expand Up @@ -60,6 +61,9 @@ thread_local = "0.3.6"
parking_lot = "0.9.0"
heapless = "0.5.1"
strum = "0.15.0"
simdnoise = "3.1.1"
simdeez = "0.6.4"
bitvec = "0.15.1"

[features]
nightly = ["specs/nightly", "parking_lot/nightly"]
11 changes: 8 additions & 3 deletions server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// tuples as their SystemData, and Clippy
// doesn't seem to like this.
#![allow(clippy::type_complexity)]
#![forbid(unsafe_code)]

#[macro_use]
extern crate log;
Expand All @@ -26,7 +25,8 @@ extern crate feather_codegen;
extern crate bitflags;
#[macro_use]
extern crate feather_core;
extern crate feather_blocks;
#[macro_use]
extern crate bitvec;

extern crate nalgebra_glm as glm;

Expand All @@ -46,7 +46,9 @@ use crate::network::send_packet_to_player;
use crate::player::PlayerDisconnectEvent;
use crate::systems::{BROADCASTER, ITEM_SPAWN, JOIN_HANDLER, NETWORK, PLAYER_INIT, SPAWNER};
use crate::util::Util;
use crate::worldgen::{EmptyWorldGenerator, SuperflatWorldGenerator, WorldGenerator};
use crate::worldgen::{
ComposableGenerator, EmptyWorldGenerator, SuperflatWorldGenerator, WorldGenerator,
};
use backtrace::Backtrace;
use feather_core::level;
use feather_core::level::{LevelData, LevelGeneratorType};
Expand Down Expand Up @@ -258,6 +260,9 @@ fn init_world<'a, 'b>(
LevelGeneratorType::Flat => Arc::new(SuperflatWorldGenerator {
options: level.clone().generator_options.unwrap_or_default(),
}),
LevelGeneratorType::Default => {
Arc::new(ComposableGenerator::default_with_seed(level.seed as u64))
}
_ => Arc::new(EmptyWorldGenerator {}),
};
world.insert(level);
Expand Down
1 change: 0 additions & 1 deletion server/src/physics/entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ impl<'a> System<'a> for EntityPhysicsSystem {
chunk_map,
entities,
) = data;

// Go through entities and update their positions according
// to their velocities.

Expand Down
129 changes: 129 additions & 0 deletions server/src/worldgen/biomes/distorted_voronoi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
use crate::worldgen::voronoi::VoronoiGrid;
use crate::worldgen::{BiomeGenerator, ChunkBiomes};
use feather_core::{Biome, ChunkPosition};
use num_traits::FromPrimitive;
use rand::{Rng, SeedableRng};
use rand_xorshift::XorShiftRng;
use strum::EnumCount;

/// Biome grid generator based on a distorted Voronoi
/// noise.
#[derive(Default)]
pub struct DistortedVoronoiBiomeGenerator;

impl BiomeGenerator for DistortedVoronoiBiomeGenerator {
fn generate_for_chunk(&self, chunk: ChunkPosition, seed: u64) -> ChunkBiomes {
let mut voronoi = VoronoiGrid::new(384, seed);

let mut biomes = ChunkBiomes::from_array([Biome::Plains; 16 * 16]); // Will be overridden

// Noise is used to distort each coordinate.
/*let x_noise =
NoiseBuilder::gradient_2d_offset(chunk.x as f32 * 16.0, 16, chunk.z as f32 * 16.0, 16)
.with_seed(seed as i32 + 1)
.generate_scaled(-4.0, 4.0);
let z_noise =
NoiseBuilder::gradient_2d_offset(chunk.x as f32 * 16.0, 16, chunk.z as f32 * 16.0, 16)
.with_seed(seed as i32 + 2)
.generate_scaled(-4.0, 4.0);*/

for x in 0..16 {
for z in 0..16 {
// Apply distortion to coordinate before passing to voronoi
// generator.
//let distort_x = x_noise[(z << 4) | x] as i32 * 8;
//let distort_z = z_noise[(z << 4) | x] as i32 * 8;

let distort_x = 0;
let distort_z = 0;

let (closest_x, closest_y) = voronoi.get(
(chunk.x * 16) + x as i32 + distort_x,
(chunk.z * 16) + z as i32 + distort_z,
);

// Shift around the closest_x and closest_y values
// and deterministically select a biome based on the
// computed value. Continue shifting the value until
// a valid biome is computed.
let combined = (i64::from(closest_x) << 32) | i64::from(closest_y);
let mut rng = XorShiftRng::seed_from_u64(combined as u64);

loop {
let shifted: u64 = rng.gen();

let biome = Biome::from_u64(shifted % Biome::count() as u64).unwrap();
if is_biome_allowed(biome) {
biomes.set_biome_at(x, z, biome);
break;
}
}
}
}

biomes
}
}

/// Returns whether the given biome is allowed in the overworld.
fn is_biome_allowed(biome: Biome) -> bool {
match biome {
Biome::TheEnd
| Biome::TheVoid
| Biome::Nether
| Biome::SmallEndIslands
| Biome::EndBarrens
| Biome::EndHighlands
| Biome::EndMidlands => false,
_ => true,
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_not_all_plains() {
// Check that the `ChunkBiomes` was overridden correctly.
let gen = DistortedVoronoiBiomeGenerator::default();

let chunk = ChunkPosition::new(5433, 132);

let biomes = gen.generate_for_chunk(chunk, 8344);

println!("{:?}", biomes);

let mut num_plains = 0;
for x in 0..16 {
for z in 0..16 {
if biomes.biome_at(x, z) == Biome::Plains {
num_plains += 1;
}
}
}

assert_ne!(num_plains, 16 * 16);
}

#[test]
fn test_deterministic() {
// Check that the result is always deterministic.
let gen = DistortedVoronoiBiomeGenerator::default();

let chunk = ChunkPosition::new(0, 0);

let seed = 52;
let first = gen.generate_for_chunk(chunk, seed);

for _ in 0..5 {
let next = gen.generate_for_chunk(chunk, seed);

for x in 0..16 {
for z in 0..16 {
assert_eq!(first.biome_at(x, z), next.biome_at(x, z));
}
}
}
}
}
7 changes: 7 additions & 0 deletions server/src/worldgen/biomes/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//! Biome grid creation.
mod distorted_voronoi;
mod two_level;

pub use distorted_voronoi::DistortedVoronoiBiomeGenerator;
pub use two_level::TwoLevelBiomeGenerator;
Loading

0 comments on commit 8187341

Please sign in to comment.