Add Experimental Plains biome

Also add Perlin noise
This commit is contained in:
Alexander Medvedev
2024-09-06 17:54:14 +01:00
parent 91d190c212
commit b40c2fa988
11 changed files with 169 additions and 12 deletions

22
Cargo.lock generated
View File

@@ -1543,6 +1543,17 @@ dependencies = [
"libc",
]
[[package]]
name = "noise"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6da45c8333f2e152fc665d78a380be060eb84fad8ca4c9f7ac8ca29216cff0cc"
dependencies = [
"num-traits",
"rand",
"rand_xorshift",
]
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
@@ -1977,9 +1988,11 @@ dependencies = [
"futures",
"itertools 0.13.0",
"log",
"noise",
"num-derive",
"num-traits",
"pumpkin-core",
"rand",
"rayon",
"serde",
"serde_json",
@@ -2075,6 +2088,15 @@ dependencies = [
"getrandom",
]
[[package]]
name = "rand_xorshift"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
dependencies = [
"rand_core",
]
[[package]]
name = "rayon"
version = "1.10.0"

View File

@@ -19,5 +19,9 @@ serde_json = "1.0"
static_assertions = "1.1.0"
log.workspace = true
noise = "0.9.0"
rand = "0.8.5"
num-traits = "0.2"
num-derive = "0.4"

View File

@@ -1,3 +1,4 @@
use noise::Perlin;
use pumpkin_core::math::vector2::Vector2;
use static_assertions::assert_obj_safe;
@@ -20,6 +21,17 @@ pub(crate) trait BiomeGenerator: Sync + Send {
fn generate_biome(&self, at: XZBlockCoordinates) -> Biome;
}
#[expect(dead_code)]
pub(crate) trait TerrainGenerator: Sync + Send {
fn prepare_chunk(&self, at: &Vector2<i32>);
/// Is static
fn generate_block(&self, at: BlockCoordinates, biome: Biome) -> BlockId;
}
pub(crate) trait PerlinTerrainGenerator: Sync + Send {
fn prepare_chunk(&self, at: &Vector2<i32>, perlin: &Perlin);
/// Dependens on the perlin noise height
fn generate_block(&self, at: BlockCoordinates, chunk_height: i16, biome: Biome) -> BlockId;
}

View File

@@ -1,35 +1,46 @@
use noise::{NoiseFn, Perlin};
use pumpkin_core::math::vector2::Vector2;
use crate::{
chunk::{ChunkBlocks, ChunkData},
coordinates::{ChunkRelativeBlockCoordinates, ChunkRelativeXZBlockCoordinates},
WORLD_LOWEST_Y, WORLD_MAX_Y,
WORLD_LOWEST_Y,
};
use super::{
generator::{BiomeGenerator, GeneratorInit, TerrainGenerator, WorldGenerator},
generator::{BiomeGenerator, GeneratorInit, PerlinTerrainGenerator, WorldGenerator},
Seed,
};
pub struct GenericGenerator<B: BiomeGenerator, T: TerrainGenerator> {
pub struct GenericGenerator<B: BiomeGenerator, T: PerlinTerrainGenerator> {
biome_generator: B,
terrain_generator: T,
// TODO: May make this optional?. But would be pain to use in most biomes then. Maybe make a new trait like
// PerlinTerrainGenerator
perlin: Perlin,
}
impl<B: BiomeGenerator + GeneratorInit, T: TerrainGenerator + GeneratorInit> GeneratorInit
impl<B: BiomeGenerator + GeneratorInit, T: PerlinTerrainGenerator + GeneratorInit> GeneratorInit
for GenericGenerator<B, T>
{
fn new(seed: Seed) -> Self {
Self {
biome_generator: B::new(seed),
terrain_generator: T::new(seed),
perlin: Perlin::new(seed.0 as u32),
}
}
}
impl<B: BiomeGenerator, T: TerrainGenerator> WorldGenerator for GenericGenerator<B, T> {
impl<B: BiomeGenerator, T: PerlinTerrainGenerator> WorldGenerator for GenericGenerator<B, T> {
fn generate_chunk(&self, at: Vector2<i32>) -> ChunkData {
let mut blocks = ChunkBlocks::default();
self.terrain_generator.prepare_chunk(&at, &self.perlin);
let noise_value = self.perlin.get([at.x as f64 / 16.0, at.z as f64 / 16.0]);
let base_height = 64.0;
let height_variation = 16.0;
let chunk_height = (noise_value * height_variation + base_height) as i32;
for x in 0..16u8 {
for z in 0..16u8 {
@@ -42,7 +53,7 @@ impl<B: BiomeGenerator, T: TerrainGenerator> WorldGenerator for GenericGenerator
);
// Iterate from the highest block to the lowest, in order to minimize the heightmap updates
for y in (WORLD_LOWEST_Y..WORLD_MAX_Y).rev() {
for y in (WORLD_LOWEST_Y..chunk_height as i16).rev() {
let coordinates = ChunkRelativeBlockCoordinates {
x: x.into(),
y: y.into(),
@@ -51,8 +62,11 @@ impl<B: BiomeGenerator, T: TerrainGenerator> WorldGenerator for GenericGenerator
blocks.set_block(
coordinates,
self.terrain_generator
.generate_block(coordinates.with_chunk_coordinates(at), biome),
self.terrain_generator.generate_block(
coordinates.with_chunk_coordinates(at),
chunk_height as i16,
biome,
),
);
}
}
@@ -64,3 +78,42 @@ impl<B: BiomeGenerator, T: TerrainGenerator> WorldGenerator for GenericGenerator
}
}
}
// TODO: implement static terrain generator
/*
fn generate_chunk(&mut self, at: Vector2<i32>) -> ChunkData {
let mut blocks = ChunkBlocks::default();
self.terrain_generator.prepare_chunk(&at, &self.perlin);
for x in 0..16u8 {
for z in 0..16u8 {
let biome = self.biome_generator.generate_biome(
ChunkRelativeXZBlockCoordinates {
x: x.into(),
z: z.into(),
}
.with_chunk_coordinates(at),
);
// Iterate from the highest block to the lowest, in order to minimize the heightmap updates
for y in (WORLD_LOWEST_Y..WORLD_MAX_Y).rev() {
let coordinates = ChunkRelativeBlockCoordinates {
x: x.into(),
y: y.into(),
z: z.into(),
};
blocks.set_block(
coordinates,
self.terrain_generator
.generate_block(coordinates.with_chunk_coordinates(at), biome),
);
}
}
}
ChunkData {
blocks,
position: at,
}
}
*/

View File

@@ -0,0 +1 @@
pub mod plains;

View File

@@ -0,0 +1,60 @@
use noise::Perlin;
use pumpkin_core::math::vector2::Vector2;
use crate::{
biome::Biome,
block::BlockId,
coordinates::{BlockCoordinates, XZBlockCoordinates},
world_gen::{
generator::{BiomeGenerator, GeneratorInit, PerlinTerrainGenerator},
generic_generator::GenericGenerator,
Seed,
},
};
pub type PlainsGenerator = GenericGenerator<PlainsBiomeGenerator, PlainsTerrainGenerator>;
pub(crate) struct PlainsBiomeGenerator {}
impl GeneratorInit for PlainsBiomeGenerator {
fn new(_: Seed) -> Self {
Self {}
}
}
impl BiomeGenerator for PlainsBiomeGenerator {
// TODO make generic over Biome and allow changing the Biome in the config.
fn generate_biome(&self, _: XZBlockCoordinates) -> Biome {
Biome::Plains
}
}
pub(crate) struct PlainsTerrainGenerator {}
impl GeneratorInit for PlainsTerrainGenerator {
fn new(_: Seed) -> Self {
Self {}
}
}
impl PerlinTerrainGenerator for PlainsTerrainGenerator {
fn prepare_chunk(&self, _at: &Vector2<i32>, _perlin: &Perlin) {}
// TODO allow specifying which blocks should be at which height in the config.
fn generate_block(&self, at: BlockCoordinates, chunk_height: i16, _: Biome) -> BlockId {
let begin_stone_height = chunk_height - 5;
let begin_dirt_height = chunk_height - 1;
let y = *at.y;
if y == -64 {
BlockId::from_id(79) // BEDROCK
} else if y >= -63 && y <= begin_stone_height {
return BlockId::from_id(1); // STONE
} else if y >= begin_stone_height && y < begin_dirt_height {
return BlockId::from_id(10); // DIRT;
} else if y == chunk_height - 1 {
return BlockId::from_id(9); // GRASS BLOCK
} else {
BlockId::AIR
}
}
}

View File

@@ -0,0 +1 @@
pub mod biome;

View File

@@ -1,3 +1,5 @@
use pumpkin_core::math::vector2::Vector2;
use crate::{
biome::Biome,
block::BlockId,
@@ -9,6 +11,7 @@ use crate::{
},
};
#[expect(dead_code)]
pub type SuperflatGenerator = GenericGenerator<SuperflatBiomeGenerator, SuperflatTerrainGenerator>;
pub(crate) struct SuperflatBiomeGenerator {}
@@ -35,6 +38,7 @@ impl GeneratorInit for SuperflatTerrainGenerator {
}
impl TerrainGenerator for SuperflatTerrainGenerator {
fn prepare_chunk(&self, _at: &Vector2<i32>) {}
// TODO allow specifying which blocks should be at which height in the config.
fn generate_block(&self, at: BlockCoordinates, _: Biome) -> BlockId {
match *at.y {

View File

@@ -1,15 +1,15 @@
mod generator;
mod generic_generator;
mod implementations;
mod implementation;
mod seed;
pub use generator::WorldGenerator;
use implementation::overworld::biome::plains::PlainsGenerator;
pub use seed::Seed;
use generator::GeneratorInit;
use implementations::superflat::SuperflatGenerator;
pub fn get_world_gen(seed: Seed) -> Box<dyn WorldGenerator> {
// TODO decide which WorldGenerator to pick based on config.
Box::new(SuperflatGenerator::new(seed))
Box::new(PlainsGenerator::new(seed))
}

View File

@@ -1,6 +1,5 @@
use std::hash::{DefaultHasher, Hash, Hasher};
#[expect(dead_code)]
#[derive(Clone, Copy)]
pub struct Seed(pub i64);