mirror of
https://github.com/griffi-gh/kubi.git
synced 2025-02-16 06:38:19 -06:00
Compare commits
2 commits
d620ba3840
...
875f131fe1
Author | SHA1 | Date | |
---|---|---|---|
|
875f131fe1 | ||
|
91326ce2dc |
|
@ -9,9 +9,8 @@ use crate::{
|
|||
queue::QueuedBlock,
|
||||
};
|
||||
|
||||
mod _01_terrain;
|
||||
mod _02_water;
|
||||
mod _03_caves;
|
||||
pub mod steps;
|
||||
pub mod structures;
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, Default, NoUninit, CheckedBitPattern)]
|
||||
|
@ -83,11 +82,17 @@ macro_rules! run_steps {
|
|||
};
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct WorldGeneratorData {
|
||||
pub master_height_map: Option<Vec<Vec<i32>>>,
|
||||
}
|
||||
|
||||
pub struct WorldGenerator {
|
||||
seed: u64,
|
||||
chunk_position: IVec3,
|
||||
blocks: BlockData,
|
||||
queue: Vec<QueuedBlock>,
|
||||
pub data: WorldGeneratorData,
|
||||
}
|
||||
|
||||
impl WorldGenerator {
|
||||
|
@ -153,12 +158,23 @@ impl WorldGenerator {
|
|||
(0..CHUNK_SIZE as i32).contains(&position).then_some(position)
|
||||
}
|
||||
|
||||
/// crude hash of self.seed and x
|
||||
fn seeded_hash(&self, x: impl std::hash::Hash) -> u64 {
|
||||
//use std::hash to hash the seed and x
|
||||
use std::hash::{Hash, Hasher};
|
||||
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
||||
x.hash(&mut hasher);
|
||||
self.seed.hash(&mut hasher);
|
||||
hasher.finish()
|
||||
}
|
||||
|
||||
pub fn new(chunk_position: IVec3, seed: u64) -> Self {
|
||||
Self {
|
||||
seed,
|
||||
chunk_position,
|
||||
blocks: Box::new([[[Block::Air; CHUNK_SIZE]; CHUNK_SIZE]; CHUNK_SIZE]),
|
||||
queue: Vec::with_capacity(0),
|
||||
data: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -167,9 +183,12 @@ impl WorldGenerator {
|
|||
/// Will return `None` only if the generation was aborted.
|
||||
pub fn generate(mut self, abort: Option<Arc<Atomic<AbortState>>>) -> Option<(BlockData, Vec<QueuedBlock>)> {
|
||||
run_steps!(&mut self, abort, [
|
||||
_01_terrain::TerrainStep,
|
||||
_02_water::WaterStep,
|
||||
_03_caves::CaveStep,
|
||||
steps::_01_terrain::TerrainStep,
|
||||
steps::_02_water::WaterStep,
|
||||
steps::_03_caves::CaveStep,
|
||||
steps::_04_layers::LayersStep,
|
||||
steps::_05_decorate::DecorateStep,
|
||||
steps::_06_trees::TreesStep,
|
||||
]).then_some((self.blocks, self.queue))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
|
||||
use fastnoise_lite::{FastNoiseLite, FractalType};
|
||||
use glam::{ivec3, IVec3};
|
||||
use crate::{block::Block, chunk::CHUNK_SIZE};
|
||||
use super::{SeedThingy, WorldGenStep, WorldGenerator};
|
||||
|
||||
pub struct CaveStep {
|
||||
a: FastNoiseLite,
|
||||
b: FastNoiseLite,
|
||||
}
|
||||
|
||||
impl WorldGenStep for CaveStep {
|
||||
fn initialize(gen: &WorldGenerator) -> Self {
|
||||
let mut seeder = SeedThingy::new(gen.seed);
|
||||
|
||||
let mut a = FastNoiseLite::with_seed(seeder.next_seed());
|
||||
a.set_fractal_type(Some(FractalType::FBm));
|
||||
a.set_fractal_octaves(Some(2));
|
||||
|
||||
let mut b = FastNoiseLite::with_seed(seeder.next_seed());
|
||||
b.set_fractal_type(Some(FractalType::FBm));
|
||||
b.set_fractal_octaves(Some(2));
|
||||
|
||||
Self { a, b }
|
||||
}
|
||||
|
||||
fn generate(&mut self, gen: &mut WorldGenerator) {
|
||||
//TODO
|
||||
|
||||
// for x in 0..CHUNK_SIZE as i32 {
|
||||
// for y in 0..CHUNK_SIZE as i32 {
|
||||
// for z in 0..CHUNK_SIZE as i32 {
|
||||
// let pos = ivec3(x, y, z);
|
||||
// if gen.query(pos) != Block::Stone { continue }
|
||||
// let pos_global = gen.global_position(pos);
|
||||
// let noise_a = self.a.get_noise_3d(pos_global.x as f64, pos_global.y as f64, pos_global.z as f64) * 0.5 + 0.5;
|
||||
// let noise_b = self.b.get_noise_3d(pos_global.x as f64, pos_global.y as f64, pos_global.z as f64) * 0.5 + 0.5;
|
||||
// if noise_a.min(noise_b) > (1. - (-y as f32 / 400.).clamp(0., 1.)) {
|
||||
// gen.place(pos, Block::Air);
|
||||
// }
|
||||
// //TODO
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
6
kubi-shared/src/worldgen/steps.rs
Normal file
6
kubi-shared/src/worldgen/steps.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
pub mod _01_terrain;
|
||||
pub mod _02_water;
|
||||
pub mod _03_caves;
|
||||
pub mod _04_layers;
|
||||
pub mod _05_decorate;
|
||||
pub mod _06_trees;
|
|
@ -1,7 +1,7 @@
|
|||
use fastnoise_lite::{FastNoiseLite, FractalType};
|
||||
use glam::ivec3;
|
||||
use crate::{block::Block, chunk::CHUNK_SIZE};
|
||||
use super::{SeedThingy, WorldGenStep, WorldGenerator};
|
||||
use super::super::{SeedThingy, WorldGenStep, WorldGenerator};
|
||||
|
||||
pub struct TerrainStep {
|
||||
noise: FastNoiseLite,
|
||||
|
@ -18,14 +18,17 @@ impl WorldGenStep for TerrainStep {
|
|||
}
|
||||
|
||||
fn generate(&mut self, gen: &mut WorldGenerator) {
|
||||
let mut height_map = vec![vec![0; CHUNK_SIZE]; CHUNK_SIZE];
|
||||
for x in 0..CHUNK_SIZE as i32 {
|
||||
for z in 0..CHUNK_SIZE as i32 {
|
||||
let global_xz = gen.global_position(ivec3(x, 0, z));
|
||||
let height = (self.noise.get_noise_2d(global_xz.x as f64, global_xz.z as f64) * 32.0) as i32;
|
||||
height_map[x as usize][z as usize] = height;
|
||||
for y in 0..gen.local_height(height) {
|
||||
gen.place(ivec3(x, y, z), Block::Stone);
|
||||
}
|
||||
}
|
||||
}
|
||||
gen.data.master_height_map = Some(height_map);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
use glam::ivec3;
|
||||
use crate::{block::Block, chunk::CHUNK_SIZE};
|
||||
use super::{WorldGenerator, WorldGenStep};
|
||||
use super::super::{WorldGenerator, WorldGenStep};
|
||||
|
||||
pub const WATER_LEVEL: i32 = 0;
|
||||
|
||||
pub struct WaterStep;
|
||||
|
||||
|
@ -9,7 +11,7 @@ impl WorldGenStep for WaterStep {
|
|||
fn generate(&mut self, gen: &mut WorldGenerator) {
|
||||
for x in 0..CHUNK_SIZE as i32 {
|
||||
for z in 0..CHUNK_SIZE as i32 {
|
||||
for y in 0..gen.local_height(0) {
|
||||
for y in 0..gen.local_height(WATER_LEVEL) {
|
||||
gen.place_if_empty(ivec3(x, y, z), Block::Water);
|
||||
}
|
||||
}
|
49
kubi-shared/src/worldgen/steps/_03_caves.rs
Normal file
49
kubi-shared/src/worldgen/steps/_03_caves.rs
Normal file
|
@ -0,0 +1,49 @@
|
|||
use fastnoise_lite::{FastNoiseLite, FractalType};
|
||||
use glam::{ivec3, FloatExt, IVec3};
|
||||
use crate::{block::Block, chunk::CHUNK_SIZE};
|
||||
use super::super::{SeedThingy, WorldGenStep, WorldGenerator};
|
||||
|
||||
pub struct CaveStep {
|
||||
a: FastNoiseLite,
|
||||
b: FastNoiseLite,
|
||||
}
|
||||
|
||||
impl WorldGenStep for CaveStep {
|
||||
fn initialize(gen: &WorldGenerator) -> Self {
|
||||
let mut seeder = SeedThingy::new(gen.seed);
|
||||
|
||||
let mut a = FastNoiseLite::with_seed(seeder.next_seed());
|
||||
a.set_fractal_type(Some(FractalType::FBm));
|
||||
a.set_fractal_octaves(Some(2));
|
||||
|
||||
let mut b = FastNoiseLite::with_seed(seeder.next_seed());
|
||||
b.set_fractal_type(Some(FractalType::FBm));
|
||||
b.set_fractal_octaves(Some(2));
|
||||
|
||||
Self { a, b }
|
||||
}
|
||||
|
||||
fn generate(&mut self, gen: &mut WorldGenerator) {
|
||||
for x in 0..CHUNK_SIZE as i32 {
|
||||
for y in 0..CHUNK_SIZE as i32 {
|
||||
for z in 0..CHUNK_SIZE as i32 {
|
||||
let cave_size = ((gen.offset().y + y - 50) as f64 / -200.).clamp(0., 1.) as f32;
|
||||
let inv_cave_size = 1. - cave_size;
|
||||
if cave_size < 0.1 { continue }
|
||||
|
||||
let pos = ivec3(x, y, z);
|
||||
if gen.query(pos) != Block::Stone { continue }
|
||||
|
||||
let pos_global = gen.global_position(pos);
|
||||
let noise_a = self.a.get_noise_3d(pos_global.x as f64, pos_global.y as f64, pos_global.z as f64) * 0.5 + 0.5;
|
||||
let noise_b = self.b.get_noise_3d(pos_global.x as f64, pos_global.y as f64, pos_global.z as f64) * 0.5 + 0.5;
|
||||
|
||||
if noise_a.min(noise_b) > (0.6 + 0.4 * inv_cave_size) {
|
||||
gen.place(pos, Block::Air);
|
||||
}
|
||||
//TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
36
kubi-shared/src/worldgen/steps/_04_layers.rs
Normal file
36
kubi-shared/src/worldgen/steps/_04_layers.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use glam::ivec3;
|
||||
use crate::{block::Block, chunk::CHUNK_SIZE};
|
||||
use super::{
|
||||
_02_water::WATER_LEVEL,
|
||||
super::{WorldGenStep, WorldGenerator}
|
||||
};
|
||||
|
||||
pub struct LayersStep;
|
||||
|
||||
impl WorldGenStep for LayersStep {
|
||||
fn initialize(_: &WorldGenerator) -> Self { Self }
|
||||
|
||||
fn generate(&mut self, gen: &mut WorldGenerator) {
|
||||
for x in 0..CHUNK_SIZE as i32 {
|
||||
for z in 0..CHUNK_SIZE as i32 {
|
||||
let terrain_height = gen.data.master_height_map.as_ref().unwrap()[x as usize][z as usize];
|
||||
|
||||
// Dirt layer height, naturally gets thinner as height gets deeper
|
||||
let mut dirt_layer_height = (((terrain_height as f32 + 15.) / 20.).clamp(0., 1.) * 8.).round() as i32;
|
||||
dirt_layer_height -= (gen.seeded_hash((x, z, 1)) & 1) as i32; //+ (gen.seeded_hash((x, z, 0xbau8)) & 1) as i32;
|
||||
|
||||
// Place dirt layer
|
||||
for y in gen.local_height(terrain_height - dirt_layer_height)..gen.local_height(terrain_height) {
|
||||
gen.place(ivec3(x, y, z), Block::Dirt);
|
||||
}
|
||||
|
||||
// If above water level, place grass
|
||||
if terrain_height >= WATER_LEVEL {
|
||||
if let Some(local_y) = gen.local_y_position(terrain_height - 1) {
|
||||
gen.place(ivec3(x, local_y, z), Block::Grass);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
31
kubi-shared/src/worldgen/steps/_05_decorate.rs
Normal file
31
kubi-shared/src/worldgen/steps/_05_decorate.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
use glam::ivec3;
|
||||
use crate::{block::Block, chunk::CHUNK_SIZE};
|
||||
use super::{
|
||||
_02_water::WATER_LEVEL,
|
||||
super::{WorldGenStep, WorldGenerator},
|
||||
};
|
||||
|
||||
pub struct DecorateStep;
|
||||
|
||||
impl WorldGenStep for DecorateStep {
|
||||
fn initialize(_: &WorldGenerator) -> Self { Self }
|
||||
|
||||
fn generate(&mut self, gen: &mut WorldGenerator) {
|
||||
for x in 0..CHUNK_SIZE as i32 {
|
||||
for z in 0..CHUNK_SIZE as i32 {
|
||||
let global_xz = gen.global_position(ivec3(x, 0, z));
|
||||
|
||||
let terrain_height = gen.data.master_height_map.as_ref().unwrap()[x as usize][z as usize];
|
||||
|
||||
//Place tall grass
|
||||
if terrain_height >= WATER_LEVEL {
|
||||
if let Some(local_y) = gen.local_y_position(terrain_height) {
|
||||
if (gen.seeded_hash((global_xz.x, global_xz.z)) & 0xf) == 0xf {
|
||||
gen.place_if_empty(ivec3(x, local_y, z), Block::TallGrass);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
45
kubi-shared/src/worldgen/steps/_06_trees.rs
Normal file
45
kubi-shared/src/worldgen/steps/_06_trees.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
use bincode::de;
|
||||
use fastnoise_lite::{FastNoiseLite, NoiseType};
|
||||
use glam::ivec3;
|
||||
use crate::{block::Block, chunk::CHUNK_SIZE, worldgen::SeedThingy};
|
||||
use super::_02_water::WATER_LEVEL;
|
||||
use crate::worldgen::{
|
||||
WorldGenStep, WorldGenerator,
|
||||
structures::{Structure, TreeStructure},
|
||||
};
|
||||
|
||||
|
||||
pub struct TreesStep {
|
||||
density_noise: FastNoiseLite,
|
||||
}
|
||||
|
||||
impl WorldGenStep for TreesStep {
|
||||
fn initialize(gen: &WorldGenerator) -> Self {
|
||||
let mut seeder = SeedThingy::new(gen.seed.rotate_left(5));
|
||||
let mut density_noise = FastNoiseLite::with_seed(seeder.next_seed());
|
||||
density_noise.set_noise_type(Some(NoiseType::OpenSimplex2));
|
||||
density_noise.set_frequency(Some(0.008));
|
||||
Self { density_noise }
|
||||
}
|
||||
|
||||
fn generate(&mut self, gen: &mut WorldGenerator) {
|
||||
for x in 0..CHUNK_SIZE as i32 {
|
||||
for z in 0..CHUNK_SIZE as i32 {
|
||||
let terrain_height = gen.data.master_height_map.as_ref().unwrap()[x as usize][z as usize];
|
||||
if terrain_height < WATER_LEVEL { continue }
|
||||
|
||||
let global_xz = gen.global_position(ivec3(x, 0, z));
|
||||
let mut density = self.density_noise.get_noise_2d(global_xz.x as f64, global_xz.z as f64) * 0.5 + 0.5;
|
||||
density = density.powi(3);
|
||||
if gen.seeded_hash((global_xz.x, global_xz.z, 0xfef)) & 0xff >= (density * 7.).round() as u64 {
|
||||
continue
|
||||
}
|
||||
|
||||
let tree = TreeStructure::default();
|
||||
if let Some(local_y) = gen.local_y_position(terrain_height) {
|
||||
tree.place(gen, ivec3(x, local_y, z));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
9
kubi-shared/src/worldgen/structures.rs
Normal file
9
kubi-shared/src/worldgen/structures.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
use glam::IVec3;
|
||||
use super::WorldGenerator;
|
||||
|
||||
mod tree;
|
||||
pub use tree::TreeStructure;
|
||||
|
||||
pub trait Structure {
|
||||
fn place(&self, gen: &mut WorldGenerator, root_pos: IVec3);
|
||||
}
|
58
kubi-shared/src/worldgen/structures/tree.rs
Normal file
58
kubi-shared/src/worldgen/structures/tree.rs
Normal file
|
@ -0,0 +1,58 @@
|
|||
use glam::IVec3;
|
||||
use super::Structure;
|
||||
use crate::{block::Block, worldgen::WorldGenerator};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct TreeStructure {
|
||||
pub height: i32,
|
||||
}
|
||||
|
||||
impl Default for TreeStructure {
|
||||
fn default() -> Self {
|
||||
Self { height: 5 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Structure for TreeStructure {
|
||||
fn place(&self, gen: &mut WorldGenerator, root: IVec3) {
|
||||
//check the block below the tree, if it's grass, replace it with dirt
|
||||
//XXX: This won't work if root.y == 0
|
||||
if root.y != 0 && gen.query(root - IVec3::Y) == Block::Grass {
|
||||
gen.place(root - IVec3::Y, Block::Dirt);
|
||||
}
|
||||
|
||||
//Tree stem
|
||||
for y in root.y..root.y + self.height {
|
||||
gen.place_or_queue(IVec3::new(root.x, y, root.z), Block::Wood);
|
||||
}
|
||||
|
||||
//Tree leaves
|
||||
//Try to create the following shape:
|
||||
//(a 5x2x5 cube that wraps around the stem with a 3x1x3 cube on top)
|
||||
// xxx
|
||||
// xx|xx
|
||||
// xx|xx
|
||||
// |
|
||||
|
||||
for y in 0..=4_i32 {
|
||||
for x in -2..=2_i32 {
|
||||
for z in -2..=2_i32 {
|
||||
//Do not overwrite the stem
|
||||
if y < 3 && x == 0 && z == 0 {
|
||||
continue
|
||||
}
|
||||
// Cut off the corners of the top layer
|
||||
if y >= 3 && (x.abs() > 1 || z.abs() > 1) {
|
||||
continue
|
||||
}
|
||||
let position = IVec3::new(
|
||||
root.x + x,
|
||||
root.y + self.height - 3 + y,
|
||||
root.z + z
|
||||
);
|
||||
gen.place_or_queue(position, Block::Leaf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue