also fix: https://github.com/Pumpkin-MC/Pumpkin/issues/1217 also includes some bedrock work, but still broken
417 lines
13 KiB
Rust
417 lines
13 KiB
Rust
use pumpkin_data::{Block, BlockState};
|
|
use pumpkin_util::math::{floor_div, floor_mod};
|
|
|
|
use crate::generation::section_coords;
|
|
|
|
use super::{
|
|
GlobalRandomConfig,
|
|
aquifer_sampler::{
|
|
AquiferSampler, AquiferSamplerImpl, FluidLevelSampler, SeaLevelAquiferSampler,
|
|
WorldAquiferSampler,
|
|
},
|
|
biome_coords,
|
|
noise::router::{
|
|
chunk_density_function::{
|
|
ChunkNoiseFunctionBuilderOptions, ChunkNoiseFunctionSampleOptions, SampleAction,
|
|
WrapperData,
|
|
},
|
|
chunk_noise_router::ChunkNoiseRouter,
|
|
density_function::{IndexToNoisePos, NoisePos, UnblendedNoisePos},
|
|
proto_noise_router::ProtoNoiseRouter,
|
|
surface_height_sampler::SurfaceHeightEstimateSampler,
|
|
},
|
|
ore_sampler::OreVeinSampler,
|
|
settings::GenerationShapeConfig,
|
|
};
|
|
|
|
pub const LAVA_BLOCK: Block = Block::LAVA;
|
|
pub const WATER_BLOCK: Block = Block::WATER;
|
|
|
|
pub const CHUNK_DIM: u8 = 16;
|
|
|
|
pub enum BlockStateSampler {
|
|
Aquifer(AquiferSampler),
|
|
Ore(OreVeinSampler),
|
|
}
|
|
|
|
impl BlockStateSampler {
|
|
pub fn sample(
|
|
&mut self,
|
|
router: &mut ChunkNoiseRouter,
|
|
pos: &impl NoisePos,
|
|
sample_options: &ChunkNoiseFunctionSampleOptions,
|
|
height_estimator: &mut SurfaceHeightEstimateSampler,
|
|
) -> Option<&'static BlockState> {
|
|
match self {
|
|
Self::Aquifer(aquifer) => aquifer.apply(router, pos, sample_options, height_estimator),
|
|
Self::Ore(ore) => ore.sample(router, pos, sample_options),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct ChainedBlockStateSampler {
|
|
pub(crate) samplers: Box<[BlockStateSampler]>,
|
|
}
|
|
|
|
impl ChainedBlockStateSampler {
|
|
pub fn new(samplers: Box<[BlockStateSampler]>) -> Self {
|
|
Self { samplers }
|
|
}
|
|
|
|
fn sample(
|
|
&mut self,
|
|
router: &mut ChunkNoiseRouter,
|
|
pos: &impl NoisePos,
|
|
sample_options: &ChunkNoiseFunctionSampleOptions,
|
|
height_estimator: &mut SurfaceHeightEstimateSampler,
|
|
) -> Option<&'static BlockState> {
|
|
for sampler in self.samplers.iter_mut() {
|
|
if let Some(state) = sampler.sample(router, pos, sample_options, height_estimator) {
|
|
return Some(state);
|
|
}
|
|
}
|
|
None
|
|
}
|
|
}
|
|
|
|
struct InterpolationIndexMapper {
|
|
x: i32,
|
|
z: i32,
|
|
|
|
minimum_cell_y: i32,
|
|
vertical_cell_block_count: i32,
|
|
}
|
|
|
|
impl IndexToNoisePos for InterpolationIndexMapper {
|
|
fn at(
|
|
&self,
|
|
index: usize,
|
|
sample_data: Option<&mut ChunkNoiseFunctionSampleOptions>,
|
|
) -> impl NoisePos + 'static {
|
|
if let Some(sample_data) = sample_data {
|
|
sample_data.cache_result_unique_id += 1;
|
|
sample_data.fill_index = index;
|
|
}
|
|
|
|
let y = (index as i32 + self.minimum_cell_y) * self.vertical_cell_block_count;
|
|
|
|
// TODO: Change this when Blender is implemented
|
|
UnblendedNoisePos::new(self.x, y, self.z)
|
|
}
|
|
}
|
|
|
|
struct ChunkIndexMapper {
|
|
start_x: i32,
|
|
start_y: i32,
|
|
start_z: i32,
|
|
|
|
horizontal_cell_block_count: usize,
|
|
vertical_cell_block_count: usize,
|
|
}
|
|
|
|
impl IndexToNoisePos for ChunkIndexMapper {
|
|
fn at(
|
|
&self,
|
|
index: usize,
|
|
sample_options: Option<&mut ChunkNoiseFunctionSampleOptions>,
|
|
) -> impl NoisePos + 'static {
|
|
let cell_z_position = floor_mod(index, self.horizontal_cell_block_count);
|
|
let xy_portion = floor_div(index, self.horizontal_cell_block_count);
|
|
let cell_x_position = floor_mod(xy_portion, self.horizontal_cell_block_count);
|
|
let cell_y_position = self.vertical_cell_block_count
|
|
- 1
|
|
- floor_div(xy_portion, self.horizontal_cell_block_count);
|
|
|
|
if let Some(sample_options) = sample_options {
|
|
sample_options.fill_index = index;
|
|
if let SampleAction::CellCaches(wrapper_data) = &mut sample_options.action {
|
|
wrapper_data.update_position(cell_x_position, cell_y_position, cell_z_position);
|
|
}
|
|
}
|
|
|
|
// TODO: Change this when Blender is implemented
|
|
UnblendedNoisePos::new(
|
|
self.start_x + cell_x_position as i32,
|
|
self.start_y + cell_y_position as i32,
|
|
self.start_z + cell_z_position as i32,
|
|
)
|
|
}
|
|
}
|
|
|
|
pub struct ChunkNoiseGenerator<'a> {
|
|
pub state_sampler: ChainedBlockStateSampler,
|
|
generation_shape: &'a GenerationShapeConfig,
|
|
start_cell_pos_x: i32,
|
|
start_cell_pos_z: i32,
|
|
|
|
vertical_cell_count: usize,
|
|
minimum_cell_y: i32,
|
|
|
|
cache_fill_unique_id: u64,
|
|
cache_result_unique_id: u64,
|
|
|
|
pub router: ChunkNoiseRouter<'a>,
|
|
}
|
|
|
|
impl<'a> ChunkNoiseGenerator<'a> {
|
|
#[expect(clippy::too_many_arguments)]
|
|
pub fn new(
|
|
noise_router_base: &'a ProtoNoiseRouter,
|
|
random_config: &GlobalRandomConfig,
|
|
horizontal_cell_count: usize,
|
|
start_block_x: i32,
|
|
start_block_z: i32,
|
|
generation_shape: &'a GenerationShapeConfig,
|
|
level_sampler: FluidLevelSampler,
|
|
aquifers: bool,
|
|
ore_veins: bool,
|
|
) -> Self {
|
|
let start_cell_pos_x = floor_div(
|
|
start_block_x,
|
|
generation_shape.horizontal_cell_block_count() as i32,
|
|
);
|
|
let start_cell_pos_z = floor_div(
|
|
start_block_z,
|
|
generation_shape.horizontal_cell_block_count() as i32,
|
|
);
|
|
|
|
let horizontal_biome_end = biome_coords::from_block(
|
|
horizontal_cell_count * generation_shape.horizontal_cell_block_count() as usize,
|
|
);
|
|
let vertical_cell_count = floor_div(
|
|
generation_shape.height as usize,
|
|
generation_shape.vertical_cell_block_count() as usize,
|
|
);
|
|
let minimum_cell_y = floor_div(
|
|
generation_shape.min_y as i32,
|
|
generation_shape.vertical_cell_block_count() as i32,
|
|
);
|
|
let vertical_cell_block_count = generation_shape.vertical_cell_block_count();
|
|
let horizontal_cell_block_count = generation_shape.horizontal_cell_block_count();
|
|
|
|
let builder_options = ChunkNoiseFunctionBuilderOptions::new(
|
|
horizontal_cell_block_count as usize,
|
|
vertical_cell_block_count as usize,
|
|
vertical_cell_count,
|
|
horizontal_cell_count,
|
|
biome_coords::from_block(start_block_x),
|
|
biome_coords::from_block(start_block_z),
|
|
horizontal_biome_end,
|
|
);
|
|
|
|
let aquifer_sampler = if aquifers {
|
|
let section_x = section_coords::block_to_section(start_block_x);
|
|
let section_z = section_coords::block_to_section(start_block_z);
|
|
AquiferSampler::Aquifer(WorldAquiferSampler::new(
|
|
section_x,
|
|
section_z,
|
|
&random_config.aquifer_random_deriver,
|
|
generation_shape.min_y,
|
|
generation_shape.height,
|
|
level_sampler,
|
|
))
|
|
} else {
|
|
AquiferSampler::SeaLevel(SeaLevelAquiferSampler::new(level_sampler))
|
|
};
|
|
|
|
let samplers: Box<[BlockStateSampler]> = if ore_veins {
|
|
let ore_sampler = OreVeinSampler::new(random_config.ore_random_deriver.clone());
|
|
Box::new([
|
|
BlockStateSampler::Aquifer(aquifer_sampler),
|
|
BlockStateSampler::Ore(ore_sampler),
|
|
])
|
|
} else {
|
|
Box::new([BlockStateSampler::Aquifer(aquifer_sampler)])
|
|
};
|
|
let state_sampler = ChainedBlockStateSampler::new(samplers);
|
|
|
|
let router = ChunkNoiseRouter::generate(noise_router_base, &builder_options);
|
|
|
|
Self {
|
|
state_sampler,
|
|
generation_shape,
|
|
start_cell_pos_x,
|
|
start_cell_pos_z,
|
|
vertical_cell_count,
|
|
minimum_cell_y,
|
|
|
|
cache_fill_unique_id: 0,
|
|
cache_result_unique_id: 0,
|
|
|
|
router,
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
pub fn sample_start_density(&mut self) {
|
|
self.cache_result_unique_id = 0;
|
|
self.sample_density(true, self.start_cell_pos_x);
|
|
}
|
|
|
|
#[inline]
|
|
pub fn sample_end_density(&mut self, cell_x: i32) {
|
|
self.sample_density(false, self.start_cell_pos_x + cell_x + 1);
|
|
}
|
|
|
|
fn sample_density(&mut self, start: bool, current_x: i32) {
|
|
let x = current_x * self.horizontal_cell_block_count() as i32;
|
|
|
|
for cell_z in 0..=(16 / self.horizontal_cell_block_count()) {
|
|
let current_cell_z_pos = self.start_cell_pos_z + cell_z as i32;
|
|
let z = current_cell_z_pos * self.horizontal_cell_block_count() as i32;
|
|
self.cache_fill_unique_id += 1;
|
|
|
|
let mapper = InterpolationIndexMapper {
|
|
x,
|
|
z,
|
|
minimum_cell_y: self.minimum_cell_y,
|
|
vertical_cell_block_count: self.vertical_cell_block_count() as i32,
|
|
};
|
|
|
|
let mut options = ChunkNoiseFunctionSampleOptions::new(
|
|
false,
|
|
SampleAction::CellCaches(WrapperData::new(
|
|
0,
|
|
0,
|
|
0,
|
|
self.horizontal_cell_block_count() as usize,
|
|
self.vertical_cell_block_count() as usize,
|
|
)),
|
|
self.cache_result_unique_id,
|
|
self.cache_fill_unique_id,
|
|
0,
|
|
);
|
|
|
|
self.fill_interpolator_buffers(start, cell_z as usize, &mapper, &mut options);
|
|
self.cache_result_unique_id = options.cache_result_unique_id;
|
|
}
|
|
self.cache_fill_unique_id += 1;
|
|
}
|
|
|
|
#[inline]
|
|
fn fill_interpolator_buffers(
|
|
&mut self,
|
|
start: bool,
|
|
cell_z: usize,
|
|
mapper: &impl IndexToNoisePos,
|
|
sample_options: &mut ChunkNoiseFunctionSampleOptions,
|
|
) {
|
|
self.router
|
|
.fill_interpolator_buffers(start, cell_z, mapper, sample_options);
|
|
}
|
|
|
|
#[inline]
|
|
pub fn interpolate_x(&mut self, delta: f64) {
|
|
self.router.interpolate_x(delta);
|
|
}
|
|
|
|
#[inline]
|
|
pub fn interpolate_y(&mut self, delta: f64) {
|
|
self.router.interpolate_y(delta);
|
|
}
|
|
|
|
#[inline]
|
|
pub fn interpolate_z(&mut self, delta: f64) {
|
|
self.cache_result_unique_id += 1;
|
|
self.router.interpolate_z(delta);
|
|
}
|
|
|
|
#[inline]
|
|
pub fn swap_buffers(&mut self) {
|
|
self.router.swap_buffers();
|
|
}
|
|
|
|
pub fn on_sampled_cell_corners(&mut self, cell_x: i32, cell_y: i32, cell_z: i32) {
|
|
self.router
|
|
.on_sampled_cell_corners(cell_y as usize, cell_z as usize);
|
|
self.cache_fill_unique_id += 1;
|
|
|
|
let start_x = (self.start_cell_pos_x + cell_x) * self.horizontal_cell_block_count() as i32;
|
|
let start_y = (cell_y + self.minimum_cell_y) * self.vertical_cell_block_count() as i32;
|
|
let start_z = (self.start_cell_pos_z + cell_z) * self.horizontal_cell_block_count() as i32;
|
|
|
|
let mapper = ChunkIndexMapper {
|
|
start_x,
|
|
start_y,
|
|
start_z,
|
|
horizontal_cell_block_count: self.horizontal_cell_block_count() as usize,
|
|
vertical_cell_block_count: self.vertical_cell_block_count() as usize,
|
|
};
|
|
|
|
let mut sample_options = ChunkNoiseFunctionSampleOptions::new(
|
|
true,
|
|
SampleAction::CellCaches(WrapperData::new(
|
|
0,
|
|
0,
|
|
0,
|
|
self.horizontal_cell_block_count() as usize,
|
|
self.vertical_cell_block_count() as usize,
|
|
)),
|
|
self.cache_result_unique_id,
|
|
self.cache_fill_unique_id,
|
|
0,
|
|
);
|
|
|
|
self.router.fill_cell_caches(&mapper, &mut sample_options);
|
|
self.cache_fill_unique_id += 1;
|
|
}
|
|
|
|
#[expect(clippy::too_many_arguments)]
|
|
pub fn sample_block_state(
|
|
&mut self,
|
|
start_x: i32,
|
|
start_y: i32,
|
|
start_z: i32,
|
|
cell_x: i32,
|
|
cell_y: i32,
|
|
cell_z: i32,
|
|
height_estimator: &mut SurfaceHeightEstimateSampler,
|
|
) -> Option<&'static BlockState> {
|
|
//TODO: Fix this when Blender is added
|
|
let pos = UnblendedNoisePos::new(start_x + cell_x, start_y + cell_y, start_z + cell_z);
|
|
|
|
let options = ChunkNoiseFunctionSampleOptions::new(
|
|
false,
|
|
SampleAction::CellCaches(WrapperData::new(
|
|
cell_x as usize,
|
|
cell_y as usize,
|
|
cell_z as usize,
|
|
self.horizontal_cell_block_count() as usize,
|
|
self.vertical_cell_block_count() as usize,
|
|
)),
|
|
self.cache_result_unique_id,
|
|
self.cache_fill_unique_id,
|
|
0,
|
|
);
|
|
|
|
self.state_sampler
|
|
.sample(&mut self.router, &pos, &options, height_estimator)
|
|
}
|
|
|
|
#[inline]
|
|
pub fn horizontal_cell_block_count(&self) -> u8 {
|
|
self.generation_shape.horizontal_cell_block_count()
|
|
}
|
|
|
|
#[inline]
|
|
pub fn vertical_cell_block_count(&self) -> u8 {
|
|
self.generation_shape.vertical_cell_block_count()
|
|
}
|
|
|
|
/// Aka bottom_y
|
|
#[inline]
|
|
pub fn min_y(&self) -> i8 {
|
|
self.generation_shape.min_y
|
|
}
|
|
|
|
#[inline]
|
|
pub fn height(&self) -> u16 {
|
|
self.generation_shape.height
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
// TODO: Add test to verify the height estimator has no interpolators or cell caches
|
|
}
|