Skip to content

Commit

Permalink
Add Experimental Plains biome
Browse files Browse the repository at this point in the history
Also add Perlin noise
  • Loading branch information
Snowiiii committed Sep 6, 2024
1 parent 91d190c commit b40c2fa
Show file tree
Hide file tree
Showing 11 changed files with 169 additions and 12 deletions.
22 changes: 22 additions & 0 deletions Cargo.lock

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

4 changes: 4 additions & 0 deletions pumpkin-world/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
12 changes: 12 additions & 0 deletions pumpkin-world/src/world_gen/generator.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use noise::Perlin;
use pumpkin_core::math::vector2::Vector2;
use static_assertions::assert_obj_safe;

Expand All @@ -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;
}
69 changes: 61 additions & 8 deletions pumpkin-world/src/world_gen/generic_generator.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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(),
Expand All @@ -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,
),
);
}
}
Expand All @@ -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,
}
}
*/
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod overworld;
pub mod superflat;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod plains;
Original file line number Diff line number Diff line change
@@ -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
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod biome;
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use pumpkin_core::math::vector2::Vector2;

use crate::{
biome::Biome,
block::BlockId,
Expand All @@ -9,6 +11,7 @@ use crate::{
},
};

#[expect(dead_code)]
pub type SuperflatGenerator = GenericGenerator<SuperflatBiomeGenerator, SuperflatTerrainGenerator>;

pub(crate) struct SuperflatBiomeGenerator {}
Expand All @@ -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 {
Expand Down
6 changes: 3 additions & 3 deletions pumpkin-world/src/world_gen/mod.rs
Original file line number Diff line number Diff line change
@@ -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))
}
1 change: 0 additions & 1 deletion pumpkin-world/src/world_gen/seed.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::hash::{DefaultHasher, Hash, Hasher};

#[expect(dead_code)]
#[derive(Clone, Copy)]
pub struct Seed(pub i64);

Expand Down

0 comments on commit b40c2fa

Please sign in to comment.