rebase master

This commit is contained in:
kralverde
2024-10-03 16:29:39 -04:00
parent 753c60a690
commit 0754a128d7
25 changed files with 475 additions and 2485 deletions

View File

@@ -2,6 +2,7 @@ pub mod boundingbox;
pub mod position;
pub mod vector2;
pub mod vector3;
pub mod voxel_shape;
pub fn wrap_degrees(var: f32) -> f32 {
let mut var1 = var % 360.0;

View File

@@ -0,0 +1,9 @@
pub struct VoxelShape {
// TODO
}
impl VoxelShape {
pub fn is_empty() -> bool {
unimplemented!()
}
}

View File

@@ -1,3 +1,4 @@
use enum_dispatch::enum_dispatch;
use serde::{Deserialize, Serialize};
// TODO make this work with the protocol
@@ -9,3 +10,26 @@ pub enum Biome {
SnowyTiga,
// TODO list all Biomes
}
#[derive(Clone)]
#[enum_dispatch(BiomeSupplierImpl)]
pub enum BiomeSupplier {
Debug(DebugBiomeSupplier),
}
#[enum_dispatch]
pub trait BiomeSupplierImpl {
fn biome(&self, x: i32, y: i32, z: i32, noise: &MultiNoiseSampler) -> Biome;
}
#[derive(Clone)]
pub struct DebugBiomeSupplier {}
impl BiomeSupplierImpl for DebugBiomeSupplier {
fn biome(&self, _x: i32, _y: i32, _z: i32, _noise: &MultiNoiseSampler) -> Biome {
Biome::Plains
}
}
// TODO: Implement
pub struct MultiNoiseSampler {}

View File

@@ -1,3 +1,4 @@
<<<<<<< HEAD
use std::collections::HashMap;
use thiserror::Error;

View File

@@ -5,7 +5,6 @@ pub mod coordinates;
pub mod cylindrical_chunk_iterator;
pub mod dimension;
pub mod global_registry;
mod height;
pub mod item;
pub mod level;
mod world_gen;

View File

@@ -1,391 +0,0 @@
use crate::{
self as pumpkin_world,
biome::Biome,
block::BlockId,
height::{HeightLimitView, HeightLimitViewImpl, StandardHeightLimitView},
world_gen::{
chunk::{BlockPos, Chunk, GenerationState, HeightMapType},
Direction,
},
};
use num_traits::PrimInt;
use pumpkin_macros::block_id;
pub const SURFACE_BLOCKS: [BlockId; 11] = [
block_id!("minecraft:podzol"),
block_id!("minecraft:gravel"),
block_id!("minecraft:grass_block"),
block_id!("minecraft:stone"),
block_id!("minecraft:coarse_dirt"),
block_id!("minecraft:sand"),
block_id!("minecraft:red_sand"),
block_id!("minecraft:mycelium"),
block_id!("minecraft:snow_block"),
block_id!("minecraft:terracotta"),
block_id!("minecraft:dirt"),
];
const BIOMES_PER_CHUNK: usize = 16 >> 2;
const LAST_CHUNK_BIOME_INDEX: usize = BIOMES_PER_CHUNK - 1;
const CHUNK_BIOME_END_INDEX: usize = BIOMES_PER_CHUNK;
const NORTH_WEST_END_INDEX: usize = 2 * LAST_CHUNK_BIOME_INDEX + 1;
const SOUTH_EAST_END_INDEX_PART: usize = 2 * CHUNK_BIOME_END_INDEX + 1;
const HORIZONTAL_BIOME_COUNT: usize = NORTH_WEST_END_INDEX + SOUTH_EAST_END_INDEX_PART;
pub struct BlendingData {
pub(crate) height_limit: HeightLimitView,
pub(crate) surface_heights: Box<[f64; HORIZONTAL_BIOME_COUNT]>,
pub(crate) biomes: Box<[Option<Vec<Biome>>; HORIZONTAL_BIOME_COUNT]>,
pub(crate) collidable_block_densities: Box<[Option<Vec<f64>>; HORIZONTAL_BIOME_COUNT]>,
initialized: bool,
}
impl BlendingData {
pub fn get_blending_data(chunk: &Chunk, _chunk_x: i32, _chunk_z: i32) -> Option<Self> {
// TODO: We currently assume all chunks have new noise. valid assumption?
if let Some(mut data) = chunk.blending_data() {
if chunk.status() >= GenerationState::Biome {
data.init_chunk_blending_data(
chunk,
&[
Direction::North,
Direction::NorthWest,
Direction::West,
Direction::SouthWest,
Direction::South,
Direction::SouthEast,
Direction::East,
Direction::NorthEast,
],
);
Some(data)
} else {
None
}
} else {
None
}
}
#[inline]
fn method_39355(i: usize) -> usize {
i & !(i.unsigned_shr(31))
}
pub(crate) fn x(index: usize) -> usize {
if index < NORTH_WEST_END_INDEX {
Self::method_39355(LAST_CHUNK_BIOME_INDEX - index)
} else {
let i = index - NORTH_WEST_END_INDEX;
CHUNK_BIOME_END_INDEX - Self::method_39355(CHUNK_BIOME_END_INDEX - i)
}
}
pub(crate) fn z(index: usize) -> usize {
if index < NORTH_WEST_END_INDEX {
Self::method_39355(index - LAST_CHUNK_BIOME_INDEX)
} else {
let i = index - NORTH_WEST_END_INDEX;
CHUNK_BIOME_END_INDEX - Self::method_39355(i - CHUNK_BIOME_END_INDEX)
}
}
pub fn height(&self, biome_x: usize, _biome_y: usize, biome_z: usize) -> f64 {
if biome_x == CHUNK_BIOME_END_INDEX || biome_z == CHUNK_BIOME_END_INDEX {
self.surface_heights[Self::south_east_index(biome_x, biome_z)]
} else if biome_x != 0 && biome_z != 0 {
f64::MAX
} else {
self.surface_heights[Self::north_west_index(biome_x, biome_z)]
}
}
fn collidable_block_density_from_column(
&self,
column: Option<&[f64]>,
half_section_y: i32,
) -> f64 {
if let Some(column) = column {
let i = self.half_section_height(half_section_y);
if (i >= 0) && ((i as usize) < column.len()) {
column[i as usize] * 0.1f64
} else {
f64::MAX
}
} else {
f64::MAX
}
}
pub fn collidable_block_density(
&self,
biome_x: usize,
half_section_y: i32,
biome_z: usize,
) -> f64 {
if half_section_y == self.bottom_half_section_y() {
0.1f64
} else if biome_x == CHUNK_BIOME_END_INDEX || biome_z == CHUNK_BIOME_END_INDEX {
self.collidable_block_density_from_column(
self.collidable_block_densities[Self::south_east_index(biome_x, biome_z)]
.as_deref(),
half_section_y,
)
} else if biome_x != 0 && biome_z != 0 {
f64::MAX
} else {
self.collidable_block_density_from_column(
self.collidable_block_densities[Self::north_west_index(biome_x, biome_z)]
.as_deref(),
half_section_y,
)
}
}
fn new(bottom_section_y: i32, top_section_y: i32, heights: Option<&[f64]>) -> Self {
let heights = match heights {
Some(heights) => {
let mut owned_heights = [0f64; HORIZONTAL_BIOME_COUNT];
assert!(
heights.len() == HORIZONTAL_BIOME_COUNT,
"Heights needs to be the right length"
);
owned_heights
.iter_mut()
.zip(heights)
.for_each(|(new, old)| {
*new = *old;
});
owned_heights
}
None => [f64::MAX; HORIZONTAL_BIOME_COUNT],
};
let collidable_block_densities: [Option<Vec<f64>>; HORIZONTAL_BIOME_COUNT] =
[const { None }; HORIZONTAL_BIOME_COUNT];
let biomes: [Option<Vec<Biome>>; HORIZONTAL_BIOME_COUNT] =
[const { None }; HORIZONTAL_BIOME_COUNT];
let i = bottom_section_y << 4;
let j = (top_section_y << 4) - i;
let height_limit = HeightLimitView::Standard(StandardHeightLimitView::new(i, j));
Self {
height_limit,
surface_heights: Box::new(heights.clone()),
biomes: Box::new(biomes),
collidable_block_densities: Box::new(collidable_block_densities),
initialized: false,
}
}
pub fn vertical_half_section_count(&self) -> i32 {
self.height_limit.vertical_section_count() * 2
}
fn collidable_and_not_tree(chunk: &Chunk, pos: &BlockPos) -> bool {
let state = chunk.get_block_state(pos);
if state.is_air()
|| state.has_tag("leaves")
|| state.has_tag("logs")
|| state.is_block(block_id!("minecraft:brown_mushroom_block"))
|| state.is_block(block_id!("minecraft:red_mushroom_block"))
{
false
} else {
state.collision_shape(chunk, pos).is_empty()
}
}
fn above_collidable_block_value(chunk: &Chunk, pos: &BlockPos) -> (f64, BlockPos) {
let pos = pos.down();
let val = if Self::collidable_and_not_tree(chunk, &pos) {
1f64
} else {
-1f64
};
(val, pos)
}
fn north_west_index(biome_x: usize, biome_z: usize) -> usize {
LAST_CHUNK_BIOME_INDEX + biome_x + biome_z
}
fn south_east_index(biome_x: usize, biome_z: usize) -> usize {
NORTH_WEST_END_INDEX + biome_x + CHUNK_BIOME_END_INDEX - biome_z
}
fn init_chunk_blending_data(&mut self, chunk: &Chunk, directions: &[Direction]) {
if !self.initialized {
if directions.contains(&Direction::North)
|| directions.contains(&Direction::West)
|| directions.contains(&Direction::NorthWest)
{
self.init_block_column(Self::north_west_index(0, 0), chunk, 0, 0);
}
if directions.contains(&Direction::North) {
for i in 1..BIOMES_PER_CHUNK {
self.init_block_column(Self::north_west_index(i, 0), chunk, (4 * i) as i32, 0);
}
}
if directions.contains(&Direction::West) {
for i in 1..BIOMES_PER_CHUNK {
self.init_block_column(Self::north_west_index(0, i), chunk, 0, (4 * i) as i32);
}
}
if directions.contains(&Direction::East) {
for i in 1..BIOMES_PER_CHUNK {
self.init_block_column(
Self::south_east_index(CHUNK_BIOME_END_INDEX, i),
chunk,
15,
(4 * i) as i32,
);
}
}
if directions.contains(&Direction::South) {
for i in 1..BIOMES_PER_CHUNK {
self.init_block_column(
Self::south_east_index(i, CHUNK_BIOME_END_INDEX),
chunk,
(4 * i) as i32,
15,
);
}
}
if directions.contains(&Direction::East) && directions.contains(&Direction::NorthEast) {
self.init_block_column(
Self::south_east_index(CHUNK_BIOME_END_INDEX, 0),
chunk,
15,
0,
);
}
if directions.contains(&Direction::East)
&& directions.contains(&Direction::South)
&& directions.contains(&Direction::SouthEast)
{
self.init_block_column(
Self::south_east_index(CHUNK_BIOME_END_INDEX, CHUNK_BIOME_END_INDEX),
chunk,
15,
15,
);
}
self.initialized = true;
}
}
fn collidable_block_density_below(chunk: &Chunk, pos: &BlockPos) -> (f64, BlockPos) {
let (val, new_pos) = Self::above_collidable_block_value(chunk, &pos);
let mut d = val;
let mut pos = new_pos;
for _ in 0..6 {
let (val, new_pos) = Self::above_collidable_block_value(chunk, &pos);
d += val;
pos = new_pos;
}
(d, pos)
}
pub fn bottom_half_section_y(&self) -> i32 {
self.height_limit.bottom_section_coord() * 2
}
fn half_section_height(&self, section_y: i32) -> i32 {
section_y - (self.bottom_half_section_y() + 1)
}
fn init_block_column(&mut self, index: usize, chunk: &Chunk, chunk_x: i32, chunk_z: i32) {
if self.surface_heights[index] == f64::MAX {
self.surface_heights[index] = self.surface_block_y(chunk, chunk_x, chunk_z) as f64;
}
self.collidable_block_densities[index] = Some(self.collidable_block_density_column(
chunk,
chunk_x,
chunk_z,
self.surface_heights[index].floor() as i32,
));
self.biomes[index] = Some(self.vertical_biome_sections(chunk, chunk_x, chunk_z));
}
fn vertical_biome_sections(&self, chunk: &Chunk, block_x: i32, block_z: i32) -> Vec<Biome> {
(0..self.vertical_biome_count())
.map(|i| {
let j = i + (self.height_limit.bottom_y() >> 2);
chunk.biome_for_noise_gen(block_x >> 2, j, block_z >> 2)
})
.collect()
}
fn vertical_biome_count(&self) -> i32 {
self.height_limit.vertical_section_count() << 2
}
fn collidable_block_density_column(
&self,
chunk: &Chunk,
chunk_x: i32,
chunk_z: i32,
height: i32,
) -> Vec<f64> {
let mut ds: Vec<f64> = (0..self.vertical_half_section_count())
.map(|_| -1f64)
.collect();
let pos = BlockPos::new(chunk_x, self.height_limit.top_y(), chunk_z);
let (mut d, mut pos) = Self::collidable_block_density_below(chunk, &pos);
for i in (0..=(ds.len() - 2)).rev() {
let (e, local_pos) = Self::above_collidable_block_value(chunk, &pos);
let (f, local_pos) = Self::collidable_block_density_below(chunk, &local_pos);
ds[i] = (d + e + f) / 15f64;
d = f;
pos = local_pos;
}
let i = self.half_section_height(height / 8);
if i >= 0 && (i as usize) < (ds.len() - 1) {
let e = (height as f64 + 0.5f64) % 8f64 / 8f64;
let f = (1f64 - e) / e;
let g = f.max(1f64) * 0.25f64;
ds[(i + 1) as usize] = -f / g;
ds[i as usize] = 1f64 / g;
}
ds
}
fn surface_block_y(&self, chunk: &Chunk, block_x: i32, block_z: i32) -> i32 {
let i = if let Some(value) =
chunk.sample_height_map(HeightMapType::WorldGenSurface, block_x, block_z)
{
value
} else {
self.height_limit.top_y()
};
let j = self.height_limit.bottom_y();
for height in (j..=i).rev() {
if SURFACE_BLOCKS.contains(
&chunk
.get_block_state(&BlockPos::new(block_x, height, block_z))
.block(),
) {
return height;
}
}
j
}
}

View File

@@ -1,351 +1,11 @@
use std::{
collections::HashMap,
sync::{Arc, LazyLock},
};
use super::noise::density::NoisePos;
use data::BlendingData;
use enum_dispatch::enum_dispatch;
use pumpkin_core::{
math::{hypot, magnitude},
random::{xoroshiro128::Xoroshiro, RandomGenerator, RandomImpl},
};
use crate::{biome::Biome, height::HeightLimitViewImpl};
use super::{
biome_coords,
chunk::{Chunk, ChunkPos},
noise::{
density::NoisePosImpl,
lerp,
perlin::{DoublePerlinNoiseParameters, DoublePerlinNoiseSampler},
},
supplier::{BiomeSupplier, StaticBiomeSupplier},
};
pub mod data;
static OFFSET_NOISE: LazyLock<DoublePerlinNoiseSampler> = LazyLock::new(|| {
DoublePerlinNoiseSampler::new(
&mut RandomGenerator::Xoroshiro(Xoroshiro::from_seed(42)),
&DoublePerlinNoiseParameters::new(-3, &[1f64; 4]),
)
});
const BLENDING_BIOME_DISTANCE_THRESHOLD: i32 = (7 << 2) - 1;
const BLENDING_CHUNK_DISTANCE_THRESHOLD: i32 = (BLENDING_BIOME_DISTANCE_THRESHOLD + 3) >> 2;
const CLOSE_BLENDING_DISTANCE_THRESHOLD: i32 = 5 >> 2;
#[derive(Clone)]
pub struct BlendResult {
alpha: f64,
offset: f64,
pub struct Blender {
// TODO
}
impl BlendResult {
pub fn new(alpha: f64, offset: f64) -> Self {
Self { alpha, offset }
}
pub fn alpha(&self) -> f64 {
self.alpha
}
pub fn offset(&self) -> f64 {
self.offset
}
}
#[enum_dispatch(BlenderImpl)]
#[derive(Clone)]
pub enum Blender {
None(NoBlendBlender),
Standard(StandardBlender),
}
#[enum_dispatch]
pub trait BlenderImpl {
fn apply_blend_density(&self, pos: &impl NoisePosImpl, density: f64) -> f64;
fn calculate(&self, block_x: i32, block_z: i32) -> BlendResult;
fn biome_supplier(&self, supplier: &BiomeSupplier) -> BiomeSupplier;
}
#[derive(Clone)]
pub struct NoBlendBlender {}
impl BlenderImpl for NoBlendBlender {
fn calculate(&self, _block_x: i32, _block_z: i32) -> BlendResult {
BlendResult {
alpha: 1f64,
offset: 0f64,
}
}
fn apply_blend_density(&self, _pos: &impl NoisePosImpl, density: f64) -> f64 {
density
}
fn biome_supplier(&self, supplier: &BiomeSupplier) -> BiomeSupplier {
supplier.clone()
}
}
#[derive(Clone)]
struct StandardBlender {
blend_data: Arc<HashMap<u64, BlendingData>>,
close_blend_data: Arc<HashMap<u64, BlendingData>>,
}
#[derive(Clone)]
enum BlendingSampleType {
Height,
Density,
}
impl StandardBlender {
fn new(
blend_data: HashMap<u64, BlendingData>,
close_blend_data: HashMap<u64, BlendingData>,
) -> Self {
Self {
blend_data: Arc::new(blend_data),
close_blend_data: Arc::new(close_blend_data),
}
}
fn blend_offset(height: f64) -> f64 {
let e = height + 0.5f64;
let f = ((e % 8f64) + 8f64) % 8f64;
(32f64 * (e - 128f64) - 3f64 * (e - 128f64) * f + 3f64 * f * f)
/ (128f64 * (32f64 - 3f64 * f))
}
fn sample_closest(
&self,
sample_type: BlendingSampleType,
biome_x: i32,
biome_y: i32,
biome_z: i32,
) -> f64 {
let i = biome_coords::to_chunk(biome_x);
let j = biome_coords::to_chunk(biome_z);
let bl = (biome_x & 3) == 0;
let bl2 = (biome_z & 3) == 0;
let mut d = self.sample(sample_type.clone(), i, j, biome_x, biome_y, biome_z);
if d == f64::MAX {
if bl && bl2 {
d = self.sample(sample_type.clone(), i - 1, j - 1, biome_x, biome_y, biome_z);
}
if d == f64::MAX && bl {
d = self.sample(sample_type.clone(), i - 1, j, biome_x, biome_y, biome_z);
}
if d == f64::MAX && bl2 {
d = self.sample(sample_type.clone(), i, j - 1, biome_x, biome_y, biome_z);
}
}
d
}
fn sample(
&self,
sample_type: BlendingSampleType,
chunk_x: i32,
chunk_z: i32,
biome_x: i32,
biome_y: i32,
biome_z: i32,
) -> f64 {
if let Some(blending_data) = self
.blend_data
.get(&ChunkPos::new(chunk_x, chunk_z).to_long())
{
match sample_type {
BlendingSampleType::Height => blending_data.height(
(biome_x - biome_coords::from_chunk(chunk_x)) as usize,
biome_y as usize,
(biome_z - biome_coords::from_chunk(chunk_z)) as usize,
),
BlendingSampleType::Density => blending_data.collidable_block_density(
(biome_x - biome_coords::from_chunk(chunk_x)) as usize,
biome_y,
(biome_z - biome_coords::from_chunk(chunk_z)) as usize,
),
}
} else {
f64::MAX
}
}
fn blend_biome(&self, x: i32, y: i32, z: i32) -> Option<Biome> {
for (k, v) in self.blend_data.iter() {
let biome_x = biome_coords::from_chunk(ChunkPos::packed_x(*k));
let biome_z = biome_coords::from_chunk(ChunkPos::packed_z(*k));
if y >= biome_coords::from_block(v.height_limit.bottom_y())
&& y < biome_coords::from_block(v.height_limit.top_y())
{
let mut val = f64::INFINITY;
let mut biome: Option<Biome> = None;
let i = biome_coords::from_block(v.height_limit.bottom_y());
for (j, biome_list) in v.biomes.iter().enumerate() {
if let Some(biome_list) = biome_list {
if let Some(internal_biome) = biome_list.get(i as usize) {
let biome_x = biome_x + BlendingData::x(j) as i32;
let biome_z = biome_z + BlendingData::z(j) as i32;
let dx = hypot((x - biome_x) as f64, (z - biome_z) as f64);
if dx <= BLENDING_BIOME_DISTANCE_THRESHOLD as f64 && dx < val {
val = dx;
biome = Some(*internal_biome)
}
}
}
}
if val < f64::MAX {
let d = OFFSET_NOISE.sample(x as f64, 0f64, z as f64) * 12f64;
let e = ((val + d) / (BLENDING_BIOME_DISTANCE_THRESHOLD + 1) as f64)
.clamp(0f64, 1f64);
if e <= 0.5f64 {
return Some(biome.unwrap());
}
}
}
}
None
}
}
impl BlenderImpl for StandardBlender {
fn calculate(&self, block_x: i32, block_z: i32) -> BlendResult {
let i = biome_coords::from_block(block_x);
let j = biome_coords::from_block(block_z);
let d = self.sample_closest(BlendingSampleType::Height, i, 0, j);
if d != f64::MAX {
BlendResult {
alpha: 0f64,
offset: Self::blend_offset(d),
}
} else {
let mut val1 = 0f64;
let mut val2 = 0f64;
let mut val3 = f64::INFINITY;
for (chunk_pos, data) in self.blend_data.iter() {
let biome_x = biome_coords::from_chunk(ChunkPos::packed_x(*chunk_pos));
let biome_z = biome_coords::from_chunk(ChunkPos::packed_z(*chunk_pos));
for (index, height) in data.surface_heights.iter().enumerate() {
if *height != f64::MAX {
let biome_x = biome_x + BlendingData::x(index) as i32;
let biome_z = biome_z + BlendingData::z(index) as i32;
let dx = hypot((i - biome_x) as f64, (j - biome_z) as f64);
if dx <= BLENDING_BIOME_DISTANCE_THRESHOLD as f64 {
if dx < val3 {
val3 = dx;
}
let ex = 1f64 / (dx * dx * dx * dx);
val2 += *height * ex;
val1 += ex;
}
}
}
}
if val3 == f64::INFINITY {
BlendResult {
alpha: 1f64,
offset: 0f64,
}
} else {
let e = val2 / val1;
let f = (val3 / (BLENDING_BIOME_DISTANCE_THRESHOLD + 1) as f64).clamp(0f64, 1f64);
let f = 3f64 * f * f - 2f64 * f * f * f;
BlendResult {
alpha: f,
offset: Self::blend_offset(e),
}
}
}
}
fn apply_blend_density(&self, pos: &impl NoisePosImpl, density: f64) -> f64 {
let i = biome_coords::from_block(pos.x());
let j = pos.y() / 8;
let k = biome_coords::from_block(pos.z());
let d = self.sample_closest(BlendingSampleType::Density, i, j, k);
if d != f64::MAX {
d
} else {
let mut val1 = 0f64;
let mut val2 = 0f64;
let mut val3 = f64::INFINITY;
for (chunk_pos, data) in self.close_blend_data.iter() {
let biome_x = biome_coords::from_chunk(ChunkPos::packed_z(*chunk_pos));
let biome_y = biome_coords::from_chunk(ChunkPos::packed_z(*chunk_pos));
let min_half_section_y = j - 1;
let max_half_section_y = j + 1;
let one_above = data.bottom_half_section_y() + 1;
let j = 0.max(min_half_section_y - one_above);
let k = data
.vertical_half_section_count()
.min(max_half_section_y - one_above);
for (index, density_vec) in data.collidable_block_densities.iter().enumerate() {
if let Some(density_vec) = density_vec {
let m = biome_x + BlendingData::x(index) as i32;
let n = biome_y + BlendingData::z(index) as i32;
for o in j..k {
let biome_x = m;
let half_section_y = o + one_above;
let biome_z = n;
let density = density_vec[o as usize] * 0.1f64;
let dx = magnitude(
(i - biome_x) as f64,
((j - half_section_y) * 2) as f64,
(k - biome_z) as f64,
);
if dx <= 2f64 {
if dx < val3 {
val3 = dx;
}
let ex = 1f64 / (dx * dx * dx * dx);
val2 += density * ex;
val1 += ex;
}
}
}
}
}
if val3 == f64::INFINITY {
density
} else {
let e = val2 / val1;
let f = (val3 / 3f64).clamp(0f64, 1f64);
lerp(f, e, density)
}
}
}
fn biome_supplier(&self, _supplier: &BiomeSupplier) -> BiomeSupplier {
BiomeSupplier::Static(StaticBiomeSupplier {})
impl Blender {
pub fn apply_blend_density(&self, _pos: &NoisePos, _density: f64) -> f64 {
todo!()
}
}

View File

@@ -1,333 +0,0 @@
use std::{ops::Add, sync::Arc};
use num_traits::PrimInt;
use parking_lot::Mutex;
use crate::{
biome::Biome, block::BlockId, chunk::ChunkData, height::HeightLimitViewImpl,
world_gen::noise::chunk_sampler::ChunkNoiseSampler,
};
use super::{biome_coords, blender::data::BlendingData, heightmap::HeightMap};
#[derive(Clone, PartialEq, PartialOrd)]
pub enum GenerationState {
Empty,
StructureStart,
StructureRef,
Biome,
Noise,
Surface,
Carver,
Feature,
InitLight,
Light,
Spawn,
Full,
}
pub struct BlockPos {
x: i32,
y: i32,
z: i32,
}
impl BlockPos {
pub fn new(x: i32, y: i32, z: i32) -> Self {
Self { x, y, z }
}
pub fn x(&self) -> i32 {
self.x
}
pub fn y(&self) -> i32 {
self.y
}
pub fn z(&self) -> i32 {
self.z
}
pub fn down(&self) -> Self {
self.down_by(1)
}
pub fn down_by(&self, count: i32) -> Self {
Self {
x: self.x,
y: self.y - count,
z: self.z,
}
}
pub fn up(&self) -> Self {
self.up_by(1)
}
pub fn up_by(&self, count: i32) -> Self {
Self {
x: self.x,
y: self.y + count,
z: self.z,
}
}
pub fn north(&self) -> Self {
self.north_by(1)
}
pub fn north_by(&self, count: i32) -> Self {
Self {
x: self.x,
y: self.y,
z: self.z - count,
}
}
pub fn south(&self) -> Self {
self.south_by(1)
}
pub fn south_by(&self, count: i32) -> Self {
Self {
x: self.x,
y: self.y,
z: self.z + count,
}
}
pub fn west(&self) -> Self {
self.west_by(1)
}
pub fn west_by(&self, count: i32) -> Self {
Self {
x: self.x - count,
y: self.y,
z: self.z,
}
}
pub fn east(&self) -> Self {
self.east_by(1)
}
pub fn east_by(&self, count: i32) -> Self {
Self {
x: self.x + count,
y: self.y,
z: self.z,
}
}
}
impl Add for BlockPos {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self {
x: self.x + rhs.x,
y: self.y + rhs.y,
z: self.z + rhs.z,
}
}
}
pub enum HeightMapType {
WorldGenSurface,
WorldSurface,
WorldGenOceanFloor,
OceanFloor,
MotionBlocking,
MotionBlockingNoLeaves,
}
pub struct VoxelShape {}
impl VoxelShape {
pub fn is_empty(&self) -> bool {
unimplemented!()
}
}
pub struct BlockState {}
impl BlockState {
pub fn block(&self) -> BlockId {
unimplemented!()
}
pub fn is_air(&self) -> bool {
unimplemented!()
}
pub fn has_tag(&self, tag: &str) -> bool {
unimplemented!()
}
pub fn is_block(&self, id: BlockId) -> bool {
unimplemented!()
}
pub fn collision_shape(&self, chunk: &Chunk, pos: &BlockPos) -> VoxelShape {
unimplemented!()
}
}
pub const CHUNK_MARKER: u64 = ChunkPos {
x: 1875066,
z: 1875066,
}
.to_long();
pub struct ChunkPos {
x: i32,
z: i32,
}
impl ChunkPos {
pub const fn new(x: i32, z: i32) -> Self {
Self { x, z }
}
pub const fn to_long(&self) -> u64 {
(self.x as u64 & 4294967295u64) | ((self.z as u64 & 4294967295u64) << 32)
}
pub fn packed_x(pos: u64) -> i32 {
(pos & 4294967295u64) as i32
}
pub fn packed_z(pos: u64) -> i32 {
(pos.unsigned_shr(32) & 4294967295u64) as i32
}
pub fn get_start_x(&self) -> i32 {
self.x << 4
}
pub fn get_start_z(&self) -> i32 {
self.z << 4
}
}
pub struct Chunk {
pos: ChunkPos,
data: ChunkData,
state: GenerationState,
}
impl Chunk {
pub fn get_block_state(&self, pos: &BlockPos) -> BlockState {
unimplemented!()
}
pub fn sample_height_map(&self, map: HeightMapType, x: i32, y: i32) -> Option<i32> {
unimplemented!()
}
pub fn biome_for_noise_gen(&self, biome_x: i32, biome_y: i32, biome_z: i32) -> Biome {
unimplemented!()
}
pub fn blending_data(&self) -> Option<BlendingData> {
unimplemented!()
}
pub fn status(&self) -> GenerationState {
self.state.clone()
}
pub fn get_or_create_noise_sampler(&self) -> Arc<Mutex<ChunkNoiseSampler>> {
unimplemented!()
}
pub fn get_height_map(&self, map: HeightMapType) -> &HeightMap {
unimplemented!()
}
pub fn pos(&self) -> &ChunkPos {
&self.pos
}
}
impl HeightLimitViewImpl for Chunk {
fn bottom_y(&self) -> i32 {
unimplemented!()
}
fn height(&self) -> i32 {
unimplemented!()
}
}
#[derive(Clone)]
pub struct GenerationShapeConfig {
y_min: i32,
height: i32,
horizontal: i32,
vertical: i32,
}
//Bits avaliable to encode y-pos
pub const SIZE_BITS_Y: i32 = 12;
pub const MAX_HEIGHT: i32 = (1 << SIZE_BITS_Y) - 32;
pub const MAX_COLUMN_HEIGHT: i32 = (MAX_HEIGHT >> 1) - 1;
pub const MIN_HEIGHT: i32 = MAX_COLUMN_HEIGHT - MAX_HEIGHT + 1;
impl GenerationShapeConfig {
fn new(y_min: i32, height: i32, horizontal: i32, vertical: i32) -> Self {
if (y_min + height) > (MAX_COLUMN_HEIGHT + 1) {
panic!("Cannot be higher than max column height");
} else if height % 16 != 0 {
panic!("Height must be a multiple of 16");
} else if y_min % 16 != 0 {
panic!("Y min must be a multiple of 16");
}
Self {
y_min,
height,
horizontal,
vertical,
}
}
pub fn trim_height(&self, view: &impl HeightLimitViewImpl) -> Self {
let i = self.y_min.max(view.bottom_y());
let j = (self.y_min + self.height).min(view.top_y()) - i;
Self {
y_min: i,
height: j,
horizontal: self.horizontal,
vertical: self.vertical,
}
}
pub fn min_y(&self) -> i32 {
self.y_min
}
pub fn height(&self) -> i32 {
self.height
}
pub fn vertical_cell_block_count(&self) -> i32 {
biome_coords::to_block(self.vertical)
}
pub fn horizontal_cell_block_count(&self) -> i32 {
biome_coords::to_block(self.horizontal)
}
}
pub mod shape_configs {
use super::GenerationShapeConfig;
pub const surface_config: GenerationShapeConfig = GenerationShapeConfig {
y_min: -64,
height: 384,
horizontal: 1,
vertical: 2,
};
}

View File

@@ -1,11 +1,8 @@
use enum_dispatch::enum_dispatch;
use crate::world_gen::chunk::Chunk;
#[enum_dispatch]
pub enum HeightLimitView {
Standard(StandardHeightLimitView),
Chunk(Chunk),
}
#[enum_dispatch(HeightLimitView)]

View File

@@ -1 +0,0 @@
pub enum HeightMap {}

View File

@@ -1,72 +0,0 @@
use crate::{self as pumpkin_world, world_gen::noise::density::NoisePosImpl};
use enum_dispatch::enum_dispatch;
use pumpkin_macros::block_id;
use crate::block::BlockId;
#[derive(Clone)]
struct FluidLevel {
y: i32,
state: BlockId,
}
impl FluidLevel {
fn get_block(&self, y: i32) -> BlockId {
if y < self.y {
self.state
} else {
block_id!("minecraft:air")
}
}
}
#[derive(Clone)]
pub struct FluidLevelSampler {
sea_level: i32,
fluid_level_1: FluidLevel,
fluid_level_2: FluidLevel,
}
impl FluidLevelSampler {
fn get_fluid_level(&self, _x: i32, y: i32, _z: i32) -> FluidLevel {
if y < (-51).min(self.sea_level) {
self.fluid_level_1.clone()
} else {
self.fluid_level_2.clone()
}
}
}
#[enum_dispatch(AquiferSamplerImpl)]
pub enum AquifierSampler {
SeaLevel(AquiferSeaLevel),
}
#[enum_dispatch]
pub trait AquiferSamplerImpl {
fn apply(&self, pos: &impl NoisePosImpl, density: f64) -> Option<BlockId>;
}
pub struct AquiferSeaLevel {
level_sampler: FluidLevelSampler,
}
impl AquiferSeaLevel {
pub fn new(level_sampler: FluidLevelSampler) -> Self {
Self { level_sampler }
}
}
impl AquiferSamplerImpl for AquiferSeaLevel {
fn apply(&self, pos: &impl NoisePosImpl, density: f64) -> Option<BlockId> {
if density > 0f64 {
None
} else {
Some(
self.level_sampler
.get_fluid_level(pos.x(), pos.y(), pos.z())
.get_block(pos.y()),
)
}
}
}

View File

@@ -1,58 +1,2 @@
use crate::height::HeightLimitViewImpl;
use super::{
blender::Blender,
chunk::{Chunk, GenerationShapeConfig, HeightMapType},
noise::config::NoiseConfig,
};
pub mod aquifer;
pub mod overworld;
pub mod superflat;
mod surface_builder;
pub fn populate_noise(
chunk: &mut Chunk,
blender: &Blender,
config: &NoiseConfig,
shape: &GenerationShapeConfig,
) {
let shape = shape.trim_height(chunk);
let i = shape.min_y();
let j = i / shape.vertical_cell_block_count();
let k = shape.height() / shape.vertical_cell_block_count();
internal_populate_noise(chunk, blender, config, j, k);
}
fn internal_populate_noise(
chunk: &mut Chunk,
blender: &Blender,
config: &NoiseConfig,
min_cell_y: i32,
cell_height: i32,
) {
let sampler = chunk.get_or_create_noise_sampler();
let height_map = chunk.get_height_map(HeightMapType::WorldGenOceanFloor);
let height_map2 = chunk.get_height_map(HeightMapType::WorldGenSurface);
let pos = chunk.pos();
let i = pos.get_start_x();
let j = pos.get_start_z();
let mut sampler = sampler.lock();
sampler.sample_start_density();
let k = sampler.horizontal_cell_block_count();
let l = sampler.vertical_cell_block_count();
let m = 16 / k;
let n = 16 / k;
for o in 0..m {
sampler.sample_end_density(o);
for p in 0..n {
let q = chunk.vertical_section_count() - 1;
}
}
}

View File

@@ -1,2 +1 @@
pub mod biome;
pub mod terrain_params;

View File

@@ -1,13 +1,15 @@
#![allow(dead_code)]
mod blender;
pub mod chunk;
mod generator;
mod generic_generator;
mod heightmap;
pub mod height_limit;
mod implementation;
mod noise;
pub mod sampler;
mod positions;
mod proto_chunk;
mod sampler;
mod seed;
mod supplier;
pub use generator::WorldGenerator;
use implementation::overworld::biome::plains::PlainsGenerator;

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +0,0 @@
use pumpkin_core::random::RandomDeriver;
use crate::world_gen::supplier::MultiNoiseSampler;
use super::router::NoiseRouter;
pub struct NoiseConfig<'a> {
random_deriver: RandomDeriver,
router: NoiseRouter<'a>,
}
impl<'a> NoiseConfig<'a> {
pub fn noise_router(&self) -> NoiseRouter<'a> {
self.router.clone()
}
}

View File

@@ -1,7 +1,5 @@
use std::sync::Arc;
use crate::world_gen::blender::BlenderImpl;
use super::{
Applier, ApplierImpl, DensityFunction, DensityFunctionImpl, NoisePos, NoisePosImpl, Visitor,
VisitorImpl,

View File

@@ -8,28 +8,13 @@ use math::{BinaryFunction, BinaryType, LinearFunction};
use noise::{InternalNoise, InterpolatedNoiseSampler, NoiseFunction, ShiftedNoiseFunction};
use offset::{ShiftAFunction, ShiftBFunction};
use spline::SplineFunction;
use terrain_helpers::{create_factor_spline, create_jaggedness_spline, create_offset_spline};
use unary::{ClampFunction, UnaryFunction, UnaryType};
use weird::{RarityMapper, WierdScaledFunction};
use crate::world_gen::{
blender::{Blender, NoBlendBlender},
chunk::{MAX_COLUMN_HEIGHT, MIN_HEIGHT},
implementation::overworld::terrain_params::{
create_factor_spline, create_jaggedness_spline, create_offset_spline,
},
};
use crate::world_gen::blender::Blender;
use super::{
chunk_sampler::{
BlendAlphaDensityFunction, BlendOffsetDensityFunction, Cache2DDensityFunction,
CacheOnceDensityFunction, CellCacheDensityFunctionWrapper, ChunkNoiseSamplerWrapper,
ChunkSamplerDensityFunctionConverter, FlatCacheDensityFunction, InterpolationApplier,
InterpolatorDensityFunctionWrapper,
},
clamped_map,
perlin::DoublePerlinNoiseParameters,
BuiltInNoiseParams,
};
use super::{clamped_map, perlin::DoublePerlinNoiseParameters, BuiltInNoiseParams};
pub mod blend;
mod end;
@@ -37,6 +22,7 @@ mod math;
pub mod noise;
mod offset;
pub mod spline;
mod terrain_helpers;
mod unary;
mod weird;
@@ -90,6 +76,12 @@ pub struct BuiltInNoiseFunctions<'a> {
caves_pillars_overworld: Arc<DensityFunction<'a>>,
}
//Bits avaliable to encode y-pos
pub const SIZE_BITS_Y: i32 = 12;
pub const MAX_HEIGHT: i32 = (1 << SIZE_BITS_Y) - 32;
pub const MAX_COLUMN_HEIGHT: i32 = (MAX_HEIGHT >> 1) - 1;
pub const MIN_HEIGHT: i32 = MAX_COLUMN_HEIGHT - MAX_HEIGHT + 1;
impl<'a> BuiltInNoiseFunctions<'a> {
pub fn new(built_in_noise_params: &BuiltInNoiseParams<'a>) -> Self {
let blend_alpha = Arc::new(DensityFunction::BlendAlpha(BlendAlphaFunction {}));
@@ -626,6 +618,7 @@ impl<'a> BuiltInNoiseFunctions<'a> {
}
}
#[allow(clippy::too_many_arguments)]
fn sloped_cheese_function<'a>(
jagged_noise: Arc<DensityFunction<'a>>,
continents: Arc<DensityFunction<'a>>,
@@ -640,13 +633,14 @@ fn sloped_cheese_function<'a>(
) -> SlopedCheeseResult<'a> {
let offset = Arc::new(apply_blending(
Arc::new(
DensityFunction::Spline(SplineFunction::new(Arc::new(create_offset_spline(
continents.clone(),
erosion.clone(),
ridges.clone(),
amplified,
))))
.add_const(-0.50375f32 as f64),
DensityFunction::Constant(ConstantFunction::new(-0.50375f32 as f64)).add(Arc::new(
DensityFunction::Spline(SplineFunction::new(Arc::new(create_offset_spline(
continents.clone(),
erosion.clone(),
ridges_folded.clone(),
amplified,
)))),
)),
),
blend_offset,
));
@@ -734,7 +728,6 @@ fn apply_blending<'a>(
function: Arc<DensityFunction<'a>>,
blend: Arc<DensityFunction<'a>>,
) -> DensityFunction<'a> {
//let function = lerp_density(built_in_noises::BLEND_ALPHA.clone(), blend, function);
let function = lerp_density(
Arc::new(DensityFunction::BlendAlpha(BlendAlphaFunction {})),
blend,
@@ -799,14 +792,6 @@ pub enum DensityFunction<'a> {
Wierd(WierdScaledFunction<'a>),
Range(RangeFunction<'a>),
Wrapper(WrapperFunction<'a>),
ChunkCacheFlatCache(FlatCacheDensityFunction<'a>),
ChunkCacheInterpolator(InterpolatorDensityFunctionWrapper<'a>),
ChunkCacheBlendAlpha(BlendAlphaDensityFunction<'a>),
ChunkCacheBlendOffset(BlendOffsetDensityFunction<'a>),
ChunkCacheCellCache(CellCacheDensityFunctionWrapper<'a>),
ChunkCache2DCache(Cache2DDensityFunction<'a>),
ChunkCacheOnceCache(CacheOnceDensityFunction<'a>),
Beardifyer(BeardifyerFunction),
}
impl<'a> DensityFunction<'a> {
@@ -885,10 +870,44 @@ impl<'a> DensityFunction<'a> {
}
}
pub struct Unused<'a> {
_x: &'a str,
}
impl<'a> NoisePosImpl for Unused<'a> {
fn x(&self) -> i32 {
todo!()
}
fn y(&self) -> i32 {
todo!()
}
fn z(&self) -> i32 {
todo!()
}
}
impl<'a> ApplierImpl<'a> for Unused<'a> {
fn at(&self, _index: usize) -> NoisePos<'a> {
todo!()
}
fn fill(&self, _densities: &mut [f64], _function: &DensityFunction<'a>) {
todo!()
}
}
impl<'a> VisitorImpl<'a> for Unused<'a> {
fn apply(&self, _function: Arc<DensityFunction<'a>>) -> Arc<DensityFunction<'a>> {
todo!()
}
}
#[enum_dispatch(NoisePosImpl)]
pub enum NoisePos<'a> {
Unblended(UnblendedNoisePos),
ChunkNoise(ChunkNoiseSamplerWrapper<'a>),
Todo(Unused<'a>),
}
pub struct UnblendedNoisePos {
@@ -924,14 +943,13 @@ pub trait NoisePosImpl {
fn z(&self) -> i32;
fn get_blender(&self) -> Blender {
Blender::None(NoBlendBlender {})
unimplemented!()
}
}
#[enum_dispatch(ApplierImpl)]
pub enum Applier<'a> {
ChunkNoise(ChunkNoiseSamplerWrapper<'a>),
Interpolation(InterpolationApplier<'a>),
Todo(Unused<'a>),
}
#[enum_dispatch]
@@ -944,7 +962,7 @@ pub trait ApplierImpl<'a> {
#[enum_dispatch(VisitorImpl)]
pub enum Visitor<'a> {
Unwrap(UnwrapVisitor),
ChunkSampler(ChunkSamplerDensityFunctionConverter<'a>),
Todo(Unused<'a>),
}
pub struct UnwrapVisitor {}
@@ -1134,31 +1152,6 @@ impl<'a> DensityFunctionImpl<'a> for RangeFunction<'a> {
}
}
#[derive(Clone)]
pub struct BeardifyerFunction {}
impl<'a> DensityFunctionImpl<'a> for BeardifyerFunction {
fn sample(&self, _pos: &NoisePos) -> f64 {
0f64
}
fn fill(&self, densities: &mut [f64], _applier: &Applier<'a>) {
densities.fill(0f64)
}
fn min(&self) -> f64 {
0f64
}
fn max(&self) -> f64 {
0f64
}
fn apply(&self, visitor: &Visitor<'a>) -> Arc<DensityFunction<'a>> {
visitor.apply(Arc::new(DensityFunction::Beardifyer(BeardifyerFunction {})))
}
}
#[derive(Clone)]
pub struct YClampedFunction {
from: i32,
@@ -1244,3 +1237,129 @@ pub fn lerp_density_static_start<'a>(
) -> DensityFunction<'a> {
delta.mul(Arc::new(end.add_const(-start))).add_const(start)
}
#[cfg(test)]
mod test {
use crate::world_gen::noise::{density::DensityFunctionImpl, BuiltInNoiseParams};
use super::{BuiltInNoiseFunctions, NoisePos, UnblendedNoisePos};
#[test]
fn test_density_function_correctness() {
let noise_params = BuiltInNoiseParams::new();
let noise_functions = BuiltInNoiseFunctions::new(&noise_params);
let pos = NoisePos::Unblended(UnblendedNoisePos { x: 0, y: 0, z: 0 });
assert_eq!(noise_functions.blend_alpha.sample(&pos), 1f64);
assert_eq!(noise_functions.blend_alpha.min(), 1f64);
assert_eq!(noise_functions.blend_alpha.max(), 1f64);
assert_eq!(noise_functions.blend_offset.sample(&pos), 0f64);
assert_eq!(noise_functions.blend_offset.min(), 0f64);
assert_eq!(noise_functions.blend_offset.max(), 0f64);
assert_eq!(noise_functions.zero.sample(&pos), 0f64);
assert_eq!(noise_functions.zero.min(), 0f64);
assert_eq!(noise_functions.zero.max(), 0f64);
assert_eq!(noise_functions.y.sample(&pos), 0f64);
assert_eq!(noise_functions.y.min(), -4064f64);
assert_eq!(noise_functions.y.max(), 4062f64);
assert_eq!(noise_functions.shift_x.sample(&pos), 0f64);
assert_eq!(noise_functions.shift_x.min(), -8f64);
assert_eq!(noise_functions.shift_x.max(), 8f64);
assert_eq!(noise_functions.shift_z.sample(&pos), 0f64);
assert_eq!(noise_functions.shift_z.min(), -8f64);
assert_eq!(noise_functions.shift_z.max(), 8f64);
assert_eq!(
noise_functions.base_3d_noise_overworld.sample(&pos),
0.05283727086562935f64
);
assert_eq!(
noise_functions.base_3d_noise_overworld.min(),
-87.55150000000002f64
);
assert_eq!(
noise_functions.base_3d_noise_overworld.max(),
87.55150000000002f64
);
assert_eq!(
noise_functions.base_3d_noise_nether.sample(&pos),
0.05283727086562935f64
);
assert_eq!(
noise_functions.base_3d_noise_nether.min(),
-258.65450000000004f64
);
assert_eq!(
noise_functions.base_3d_noise_nether.max(),
258.65450000000004f64
);
assert_eq!(
noise_functions.base_3d_noise_end.sample(&pos),
0.05283727086562935f64
);
assert_eq!(
noise_functions.base_3d_noise_end.min(),
-173.10299999999998f64
);
assert_eq!(
noise_functions.base_3d_noise_end.max(),
173.10299999999998f64
);
assert_eq!(noise_functions.continents_overworld.sample(&pos), 0f64);
assert_eq!(noise_functions.continents_overworld.min(), -2f64);
assert_eq!(noise_functions.continents_overworld.max(), 2f64);
assert_eq!(noise_functions.erosion_overworld.sample(&pos), 0f64);
assert_eq!(noise_functions.erosion_overworld.min(), -2f64);
assert_eq!(noise_functions.erosion_overworld.max(), 2f64);
assert_eq!(noise_functions.ridges_overworld.sample(&pos), 0f64);
assert_eq!(noise_functions.ridges_overworld.min(), -2f64);
assert_eq!(noise_functions.ridges_overworld.max(), 2f64);
assert_eq!(noise_functions.ridges_folded_overworld.sample(&pos), -1f64);
assert_eq!(
noise_functions.ridges_folded_overworld.min(),
-3.000000000000001f64
);
assert_eq!(noise_functions.ridges_folded_overworld.max(), 1f64);
assert_eq!(
noise_functions.offset_overworld.sample(&pos),
-0.6037500277161598f64
);
assert_eq!(
noise_functions.offset_overworld.min(),
-1.3752707839012146f64
);
assert_eq!(
noise_functions.offset_overworld.max(),
0.9962499737739563f64
);
assert_eq!(noise_functions.y.sample(&pos), 0f64);
assert_eq!(noise_functions.y.min(), -4064f64);
assert_eq!(noise_functions.y.max(), 4062f64);
assert_eq!(noise_functions.y.sample(&pos), 0f64);
assert_eq!(noise_functions.y.min(), -4064f64);
assert_eq!(noise_functions.y.max(), 4062f64);
assert_eq!(noise_functions.y.sample(&pos), 0f64);
assert_eq!(noise_functions.y.min(), -4064f64);
assert_eq!(noise_functions.y.max(), 4062f64);
assert_eq!(noise_functions.y.sample(&pos), 0f64);
assert_eq!(noise_functions.y.min(), -4064f64);
assert_eq!(noise_functions.y.max(), 4062f64);
}
}

View File

@@ -1,6 +1,6 @@
use std::sync::Arc;
use crate::world_gen::noise::lerp_32;
use crate::world_gen::noise::lerp;
use super::{
Applier, ApplierImpl, DensityFunction, DensityFunctionImpl, NoisePos, Visitor, VisitorImpl,
@@ -56,6 +56,11 @@ pub struct Spline<'a> {
max: f32,
}
enum Range {
In(usize),
Below,
}
impl<'a> Spline<'a> {
fn sample_outside_range(point: f32, value: f32, points: &[SplinePoint], i: usize) -> f32 {
let f = points[i].derivative;
@@ -66,8 +71,29 @@ impl<'a> Spline<'a> {
}
}
fn find_range_for_location(locations: &[f32], x: f32) -> i32 {
locations.partition_point(|val| x < *val) as i32 - 1
fn binary_walk(min: usize, max: usize, pred: impl Fn(usize) -> bool) -> usize {
let mut i = max - min;
let mut min = min;
while i > 0 {
let j = i / 2;
let k = min + j;
if pred(k) {
i = j;
} else {
min = k + 1;
i -= j + 1;
}
}
min
}
fn find_range_for_location(points: &[SplinePoint], x: f32) -> Range {
let index_greater_than_x = Self::binary_walk(0, points.len(), |i| x < points[i].location);
if index_greater_than_x == 0 {
Range::Below
} else {
Range::In(index_greater_than_x - 1)
}
}
pub fn new(function: Arc<DensityFunction<'a>>, points: &[SplinePoint<'a>]) -> Self {
@@ -143,31 +169,34 @@ impl<'a> Spline<'a> {
pub fn apply(&self, pos: &NoisePos) -> f32 {
let f = self.function.sample(pos) as f32;
let i = Self::find_range_for_location(
self.points
.iter()
.map(|p| p.location)
.collect::<Vec<f32>>()
.as_ref(),
f,
);
let j = self.points.len() - 1;
let i = Self::find_range_for_location(&self.points, f);
if i < 0 {
Self::sample_outside_range(f, self.points[0].value.apply(pos), &self.points, 0)
} else if i == j as i32 {
Self::sample_outside_range(f, self.points[j].value.apply(pos), &self.points, j)
} else {
let point_1 = &self.points[i as usize];
let point_2 = &self.points[i as usize + 1];
let k = (f - point_1.location) / (point_2.location - point_1.location);
match i {
Range::In(index) => {
let last_index = self.points.len() - 1;
if index == last_index {
Self::sample_outside_range(
f,
self.points[last_index].value.apply(pos),
&self.points,
last_index,
)
} else {
let point_1 = &self.points[index];
let point_2 = &self.points[index + 1];
let k = (f - point_1.location) / (point_2.location - point_1.location);
let n = point_1.value.apply(pos);
let o = point_2.value.apply(pos);
let n = point_1.value.apply(pos);
let o = point_2.value.apply(pos);
let p = point_1.derivative * (point_2.location - point_1.location) - (o - n);
let q = -point_2.derivative * (point_2.location - point_1.location) + (o - n);
lerp_32(k, n, o) + k * (1f32 - k) * lerp_32(k, p, q)
let p = point_1.derivative * (point_2.location - point_1.location) - (o - n);
let q = -point_2.derivative * (point_2.location - point_1.location) + (o - n);
lerp(k, n, o) + k * (1f32 - k) * lerp(k, p, q)
}
}
Range::Below => {
Self::sample_outside_range(f, self.points[0].value.apply(pos), &self.points, 0)
}
}
}
@@ -225,7 +254,9 @@ impl<'a> DensityFunctionImpl<'a> for SplineFunction<'a> {
#[derive(Clone)]
pub enum FloatAmplifier {
Identity,
Amplifier,
OffsetAmplifier,
FactorAmplifier,
JaggednessAmplifier,
}
impl FloatAmplifier {
@@ -233,13 +264,15 @@ impl FloatAmplifier {
pub fn apply(&self, f: f32) -> f32 {
match self {
Self::Identity => f,
Self::Amplifier => {
Self::OffsetAmplifier => {
if f < 0f32 {
f
} else {
f * 2f32
}
}
Self::FactorAmplifier => 1.25f32 - 6.25f32 / (f + 5f32),
Self::JaggednessAmplifier => f * 2f32,
}
}
}
@@ -293,3 +326,34 @@ impl<'a> SplineBuilder<'a> {
Spline::new(self.function.clone(), &self.points)
}
}
#[cfg(test)]
mod test {
use crate::world_gen::noise::{
density::{BuiltInNoiseFunctions, NoisePos, UnblendedNoisePos},
BuiltInNoiseParams,
};
use super::{FloatAmplifier, SplineBuilder};
#[test]
fn test_correctness() {
let noise_params = BuiltInNoiseParams::new();
let noise_functions = BuiltInNoiseFunctions::new(&noise_params);
let pos = NoisePos::Unblended(UnblendedNoisePos { x: 0, y: 0, z: 0 });
let spline = SplineBuilder::new(
noise_functions.continents_overworld,
FloatAmplifier::Identity,
)
.add_value(-1.1f32, 0.044f32, 0f32)
.add_value(-1.02f32, -0.2222f32, 0f32)
.add_value(-0.51f32, -0.2222f32, 0f32)
.add_value(-0.44f32, -0.12f32, 0f32)
.add_value(-0.18f32, -0.12f32, 0f32)
.build();
assert_eq!(spline.apply(&pos), -0.12f32);
}
}

View File

@@ -6,7 +6,7 @@ use crate::world_gen::noise::density::spline::{
FloatAmplifier, Spline, SplineBuilder, SplineValue,
};
use crate::world_gen::noise::density::{peaks_valleys_noise, DensityFunction};
use crate::world_gen::noise::lerp_32;
use crate::world_gen::noise::lerp;
#[inline]
fn get_offset_value(f: f32, g: f32, h: f32) -> f32 {
@@ -69,7 +69,7 @@ fn meth_42050(
let builder = if bl {
builder
.add_value(-1f32, 0.2f32.max(i), 0f32)
.add_value(0f32, lerp_32(0.5f32, i, k), n)
.add_value(0f32, lerp(0.5f32, i, k), n)
} else {
builder.add_value(-1f32, i, n)
};
@@ -216,11 +216,8 @@ fn method_42052<'a>(
builder.build()
}
fn method_42049<'a>(
ridges: Arc<DensityFunction<'a>>,
f: f32,
amplifier: FloatAmplifier,
) -> Spline<'a> {
#[inline]
fn method_42049(ridges: Arc<DensityFunction>, f: f32, amplifier: FloatAmplifier) -> Spline {
let g = 0.63f32 * f;
let h = 0.3f32 * f;
SplineBuilder::new(ridges, amplifier)
@@ -229,6 +226,8 @@ fn method_42049<'a>(
.build()
}
#[allow(clippy::too_many_arguments)]
#[inline]
fn method_42053<'a>(
erosion: Arc<DensityFunction<'a>>,
ridges: Arc<DensityFunction<'a>>,
@@ -257,7 +256,7 @@ fn method_42053<'a>(
SplineBuilder::new(erosion, amplifier)
.add_spline(-1f32, SplineValue::Spline(spline), 0f32)
.add_spline(-0.78, SplineValue::Spline(spline2.clone()), 0f32)
.add_spline(-05775f32, SplineValue::Spline(spline2), 0f32)
.add_spline(-0.5775f32, SplineValue::Spline(spline2), 0f32)
.add_value(-0.375f32, 0f32, 0f32)
.build()
}
@@ -278,13 +277,13 @@ fn create_continental_offset_spline<'a>(
) -> Spline<'a> {
let spline = meth_42050(
ridges.clone(),
lerp_32(h, 0.6f32, 1.5f32),
lerp(h, 0.6f32, 1.5f32),
bl2,
amplifier.clone(),
);
let spline2 = meth_42050(
ridges.clone(),
lerp_32(h, 0.6f32, 1f32),
lerp(h, 0.6f32, 1f32),
bl2,
amplifier.clone(),
);
@@ -293,7 +292,7 @@ fn create_continental_offset_spline<'a>(
ridges.clone(),
continental - 0.15f32,
0.5f32 * h,
lerp_32(0.5f32, 0.5f32, 0.5f32) * h,
lerp(0.5f32, 0.5f32, 0.5f32) * h,
0.5f32 * h,
0.6f32 * h,
0.5f32,
@@ -378,7 +377,7 @@ pub fn create_offset_spline<'a>(
amplified: bool,
) -> Spline<'a> {
let amplification = if amplified {
FloatAmplifier::Amplifier
FloatAmplifier::OffsetAmplifier
} else {
FloatAmplifier::Identity
};
@@ -458,7 +457,7 @@ pub fn create_factor_spline<'a>(
amplified: bool,
) -> Spline<'a> {
let amplification = if amplified {
FloatAmplifier::Amplifier
FloatAmplifier::FactorAmplifier
} else {
FloatAmplifier::Identity
};
@@ -524,7 +523,7 @@ pub fn create_jaggedness_spline<'a>(
amplified: bool,
) -> Spline<'a> {
let amplification = if amplified {
FloatAmplifier::Amplifier
FloatAmplifier::JaggednessAmplifier
} else {
FloatAmplifier::Identity
};
@@ -561,3 +560,82 @@ pub fn create_jaggedness_spline<'a>(
)
.build()
}
#[cfg(test)]
mod test {
use crate::world_gen::noise::{
density::{
spline::FloatAmplifier, terrain_helpers::create_offset_spline, BuiltInNoiseFunctions,
NoisePos, UnblendedNoisePos,
},
BuiltInNoiseParams,
};
use super::create_continental_offset_spline;
#[test]
fn test_offset_correctness() {
let noise_params = BuiltInNoiseParams::new();
let noise_functions = BuiltInNoiseFunctions::new(&noise_params);
let pos = NoisePos::Unblended(UnblendedNoisePos { x: 0, y: 0, z: 0 });
let spline = create_continental_offset_spline(
noise_functions.erosion_overworld.clone(),
noise_functions.ridges_folded_overworld.clone(),
1f32,
1f32,
1f32,
1f32,
1f32,
1f32,
true,
true,
FloatAmplifier::Identity,
);
assert_eq!(spline.apply(&pos), 1f32);
let pos = NoisePos::Unblended(UnblendedNoisePos {
x: 10,
y: 10,
z: 10,
});
let spline = create_continental_offset_spline(
noise_functions.erosion_overworld.clone(),
noise_functions.ridges_folded_overworld.clone(),
2f32,
2f32,
2f32,
2f32,
2f32,
2f32,
true,
true,
FloatAmplifier::Identity,
);
assert_eq!(spline.apply(&pos), 2f32);
let pos = NoisePos::Unblended(UnblendedNoisePos { x: 0, y: 0, z: 0 });
let spline = create_offset_spline(
noise_functions.continents_overworld.clone(),
noise_functions.erosion_overworld.clone(),
noise_functions.ridges_folded_overworld.clone(),
true,
);
assert_eq!(spline.apply(&pos), -0.1f32);
let spline = create_offset_spline(
noise_functions.continents_overworld,
noise_functions.erosion_overworld.clone(),
noise_functions.ridges_folded_overworld.clone(),
false,
);
assert_eq!(spline.apply(&pos), -0.1f32);
}
}

View File

@@ -1,9 +1,6 @@
#![allow(dead_code)]
use derive_getters::Getters;
use num_traits::Float;
use perlin::DoublePerlinNoiseParameters;
pub mod chunk_sampler;
pub mod config;
pub mod density;
pub mod perlin;
mod router;
@@ -188,11 +185,10 @@ impl<'a> BuiltInNoiseParams<'a> {
}
}
pub fn lerp_32(delta: f32, start: f32, end: f32) -> f32 {
start + delta * (end - start)
}
pub fn lerp(delta: f64, start: f64, end: f64) -> f64 {
pub fn lerp<T>(delta: T, start: T, end: T) -> T
where
T: Float,
{
start + delta * (end - start)
}

View File

@@ -0,0 +1,28 @@
pub mod chunk_pos {
use noise::Vector2;
const MARKER: u64 = packed(Vector2 {
x: 1875066,
y: 1875066,
});
pub const fn packed(vec: Vector2<i32>) -> u64 {
(vec.x as u64 & 4294967295u64) | ((vec.y as u64 & 4294967295u64) << 32)
}
pub const fn unpack_x(packed: u64) -> i32 {
(packed & 4294967295u64) as i32
}
pub const fn unpack_z(packed: u64) -> i32 {
((packed >> 32) & 4294967295u64) as i32
}
pub const fn start_x(vec: Vector2<i32>) -> i32 {
vec.x << 4
}
pub const fn start_y(vec: Vector2<i32>) -> i32 {
vec.y << 4
}
}

View File

@@ -0,0 +1,29 @@
use pumpkin_core::math::vector3::Vector3;
use crate::block::block_state::BlockState;
pub struct ProtoChunk {
state: GenerationState,
}
impl ProtoChunk {
pub fn get_block_state(&self, _pos: &Vector3<i32>) -> BlockState {
unimplemented!()
}
}
#[derive(Clone, PartialEq, PartialOrd)]
pub enum GenerationState {
Empty,
StructureStart,
StructureRef,
Biome,
Noise,
Surface,
Carver,
Feature,
InitLight,
Light,
Spawn,
Full,
}

View File

@@ -1,26 +0,0 @@
use enum_dispatch::enum_dispatch;
use crate::biome::Biome;
#[derive(Clone)]
#[enum_dispatch]
pub enum BiomeSupplier {
Static(StaticBiomeSupplier),
}
#[enum_dispatch(BiomeSupplier)]
pub trait BiomeSupplierImpl {
fn biome(&self, x: i32, y: i32, z: i32, noise: &MultiNoiseSampler) -> Biome;
}
#[derive(Clone)]
pub struct StaticBiomeSupplier {}
impl BiomeSupplierImpl for StaticBiomeSupplier {
fn biome(&self, _x: i32, _y: i32, _z: i32, _noise: &MultiNoiseSampler) -> Biome {
Biome::Plains
}
}
pub struct MultiNoiseSampler {
}