feat: add chunk blender
This commit is contained in:
parent
f2c1d8407a
commit
af10507137
@ -226,6 +226,7 @@ impl PerlinNoiseSampler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Data for a single octave in an octave Perlin noise sampler.
|
/// Data for a single octave in an octave Perlin noise sampler.
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct SamplerData {
|
pub struct SamplerData {
|
||||||
/// The Perlin noise sampler for this octave.
|
/// The Perlin noise sampler for this octave.
|
||||||
pub sampler: PerlinNoiseSampler,
|
pub sampler: PerlinNoiseSampler,
|
||||||
@ -242,6 +243,7 @@ pub struct SamplerData {
|
|||||||
/// Octave noise, also known as fractal noise, combines multiple octaves of Perlin noise
|
/// Octave noise, also known as fractal noise, combines multiple octaves of Perlin noise
|
||||||
/// with different frequencies and amplitudes to create more complex, natural-looking patterns.
|
/// with different frequencies and amplitudes to create more complex, natural-looking patterns.
|
||||||
/// Each octave adds finer detail to the overall noise.
|
/// Each octave adds finer detail to the overall noise.
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct OctavePerlinNoiseSampler {
|
pub struct OctavePerlinNoiseSampler {
|
||||||
/// The list of samplers for each octave, with their associated parameters.
|
/// The list of samplers for each octave, with their associated parameters.
|
||||||
pub samplers: Box<[SamplerData]>,
|
pub samplers: Box<[SamplerData]>,
|
||||||
|
|||||||
@ -149,23 +149,22 @@ pub fn get_region_seed(world_seed: u64, region_x: i32, region_z: i32, salt: u32)
|
|||||||
/// Carver seeds are used for terrain carving features like caves and ravines.
|
/// Carver seeds are used for terrain carving features like caves and ravines.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// - `random` – The random number generator to use.
|
/// - `world_seed` – The base world seed (plus carver index).
|
||||||
/// - `world_seed` – The base world seed.
|
|
||||||
/// - `chunk_x` – The X chunk coordinate.
|
/// - `chunk_x` – The X chunk coordinate.
|
||||||
/// - `chunk_z` – The Z chunk coordinate.
|
/// - `chunk_z` – The Z chunk coordinate.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
/// A carver seed for the given chunk.
|
/// A carver seed for the given chunk.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_carver_seed(
|
#[must_use]
|
||||||
random: &mut RandomGenerator,
|
pub fn get_carver_seed(world_seed: u64, chunk_x: i32, chunk_z: i32) -> u64 {
|
||||||
world_seed: u64,
|
let mut random = LegacyRand::from_seed(world_seed);
|
||||||
chunk_x: i32,
|
let l = random.next_i64() | 1;
|
||||||
chunk_z: i32,
|
let m = random.next_i64() | 1;
|
||||||
) -> u64 {
|
((chunk_x as i64)
|
||||||
let x = random.next_i64();
|
.wrapping_mul(l)
|
||||||
let z = random.next_i64();
|
.wrapping_add((chunk_z as i64).wrapping_mul(m)) as u64)
|
||||||
(chunk_x as u64).wrapping_mul(x as u64) ^ (chunk_z as u64).wrapping_mul(z as u64) ^ world_seed
|
^ world_seed
|
||||||
}
|
}
|
||||||
|
|
||||||
#[enum_dispatch]
|
#[enum_dispatch]
|
||||||
|
|||||||
@ -193,6 +193,7 @@ impl ChunkData {
|
|||||||
light_engine: std::sync::Mutex::new(light_engine),
|
light_engine: std::sync::Mutex::new(light_engine),
|
||||||
light_populated: AtomicBool::new(chunk_data.light_correct),
|
light_populated: AtomicBool::new(chunk_data.light_correct),
|
||||||
status: chunk_data.status,
|
status: chunk_data.status,
|
||||||
|
blending_data: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -80,6 +80,7 @@ pub struct ChunkData {
|
|||||||
pub light_engine: std::sync::Mutex<ChunkLight>,
|
pub light_engine: std::sync::Mutex<ChunkLight>,
|
||||||
pub light_populated: AtomicBool,
|
pub light_populated: AtomicBool,
|
||||||
pub status: ChunkStatus,
|
pub status: ChunkStatus,
|
||||||
|
pub blending_data: Option<crate::generation::blender::blending_data::BlendingData>,
|
||||||
pub dirty: AtomicBool,
|
pub dirty: AtomicBool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -229,6 +229,7 @@ impl Chunk {
|
|||||||
light_engine: Mutex::new(ChunkLight::default()),
|
light_engine: Mutex::new(ChunkLight::default()),
|
||||||
light_populated: AtomicBool::new(false),
|
light_populated: AtomicBool::new(false),
|
||||||
status: ChunkStatus::Empty,
|
status: ChunkStatus::Empty,
|
||||||
|
blending_data: None,
|
||||||
dirty: AtomicBool::new(false),
|
dirty: AtomicBool::new(false),
|
||||||
})),
|
})),
|
||||||
) {
|
) {
|
||||||
@ -317,6 +318,7 @@ impl Chunk {
|
|||||||
fluid_ticks: Default::default(),
|
fluid_ticks: Default::default(),
|
||||||
block_entities: Mutex::new(block_entities),
|
block_entities: Mutex::new(block_entities),
|
||||||
status: proto_chunk.stage.into(),
|
status: proto_chunk.stage.into(),
|
||||||
|
blending_data: proto_chunk.blending_data.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
chunk.heightmap = Mutex::new(chunk.calculate_heightmap());
|
chunk.heightmap = Mutex::new(chunk.calculate_heightmap());
|
||||||
|
|||||||
@ -323,6 +323,24 @@ impl GenerationCache for Cache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_blending_data(
|
||||||
|
&self,
|
||||||
|
chunk_x: i32,
|
||||||
|
chunk_z: i32,
|
||||||
|
) -> Option<&crate::generation::blender::blending_data::BlendingData> {
|
||||||
|
let dx = chunk_x - self.x;
|
||||||
|
let dz = chunk_z - self.z;
|
||||||
|
|
||||||
|
if dx < 0 || dx >= self.size || dz < 0 || dz >= self.size {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
match &self.chunks[(dx * self.size + dz) as usize] {
|
||||||
|
Chunk::Proto(chunk) => chunk.blending_data.as_ref(),
|
||||||
|
Chunk::Level(data) => data.blending_data.as_ref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn is_air(&self, local_pos: &Vector3<i32>) -> bool {
|
fn is_air(&self, local_pos: &Vector3<i32>) -> bool {
|
||||||
is_air(GenerationCache::get_block_state(self, local_pos).0)
|
is_air(GenerationCache::get_block_state(self, local_pos).0)
|
||||||
}
|
}
|
||||||
|
|||||||
72
pumpkin-world/src/generation/blender/blending_data.rs
Normal file
72
pumpkin-world/src/generation/blender/blending_data.rs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
use pumpkin_data::chunk::Biome;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct BlendingData {
|
||||||
|
pub min_y: i32,
|
||||||
|
pub max_y: i32,
|
||||||
|
// Heights at quart positions (16x16 per chunk)
|
||||||
|
pub heights: Vec<f64>,
|
||||||
|
// Densities at quart positions (16x16 per chunk, but at what Y levels? 1.18+ uses 4 blocks quart)
|
||||||
|
// Simplified: only store for current chunk's Y levels
|
||||||
|
pub densities: Vec<f64>,
|
||||||
|
// Biomes at quart positions (16x16 per chunk)
|
||||||
|
pub biomes: Vec<&'static Biome>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlendingData {
|
||||||
|
pub fn get_height(&self, cell_x: i32, _cell_y: i32, cell_z: i32) -> f64 {
|
||||||
|
if !(0..16).contains(&cell_x) || !(0..16).contains(&cell_z) {
|
||||||
|
return f64::MAX;
|
||||||
|
}
|
||||||
|
self.heights[(cell_z * 16 + cell_x) as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_density(&self, cell_x: i32, _cell_y: i32, cell_z: i32) -> f64 {
|
||||||
|
// cell_y is block_y / 8 in Blender.java
|
||||||
|
// We'll need a better storage for this if we want full fidelity
|
||||||
|
if !(0..16).contains(&cell_x) || !(0..16).contains(&cell_z) {
|
||||||
|
return f64::MAX;
|
||||||
|
}
|
||||||
|
// Dummy implementation
|
||||||
|
f64::MAX
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iterate_heights<F>(&self, quart_x: i32, quart_z: i32, mut consumer: F)
|
||||||
|
where
|
||||||
|
F: FnMut(i32, i32, f64),
|
||||||
|
{
|
||||||
|
for z in 0..16 {
|
||||||
|
for x in 0..16 {
|
||||||
|
let h = self.heights[z * 16 + x];
|
||||||
|
if h != f64::MAX {
|
||||||
|
consumer(quart_x + x as i32, quart_z + z as i32, h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iterate_densities<F>(
|
||||||
|
&self,
|
||||||
|
_quart_x: i32,
|
||||||
|
_quart_z: i32,
|
||||||
|
_min_cell_y: i32,
|
||||||
|
_max_cell_y: i32,
|
||||||
|
_consumer: F,
|
||||||
|
) where
|
||||||
|
F: FnMut(i32, i32, i32, f64),
|
||||||
|
{
|
||||||
|
// TODO: implement density iteration
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iterate_biomes<F>(&self, quart_x: i32, _quart_y: i32, quart_z: i32, mut consumer: F)
|
||||||
|
where
|
||||||
|
F: FnMut(i32, i32, &'static Biome),
|
||||||
|
{
|
||||||
|
for z in 0..16 {
|
||||||
|
for x in 0..16 {
|
||||||
|
let biome = self.biomes[z * 16 + x];
|
||||||
|
consumer(quart_x + x as i32, quart_z + z as i32, biome);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,62 +1,361 @@
|
|||||||
use enum_dispatch::enum_dispatch;
|
pub mod blending_data;
|
||||||
|
|
||||||
|
use std::f64;
|
||||||
|
|
||||||
|
use crate::biome::BiomeSupplier;
|
||||||
|
use crate::generation::biome_coords;
|
||||||
|
use crate::generation::noise::perlin::DoublePerlinNoiseSampler;
|
||||||
|
use crate::generation::noise::router::multi_noise_sampler::MultiNoiseSampler;
|
||||||
|
use crate::generation::proto_chunk::GenerationCache;
|
||||||
|
use blending_data::BlendingData;
|
||||||
use pumpkin_data::chunk::Biome;
|
use pumpkin_data::chunk::Biome;
|
||||||
|
use pumpkin_data::noise_parameter::DoublePerlinNoiseParameters;
|
||||||
use pumpkin_util::math::vector3::Vector3;
|
use pumpkin_util::math::vector3::Vector3;
|
||||||
|
use pumpkin_util::random::xoroshiro128::Xoroshiro;
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use crate::{
|
pub struct BlendingOutput {
|
||||||
biome::BiomeSupplier, generation::noise::router::multi_noise_sampler::MultiNoiseSampler,
|
pub alpha: f64,
|
||||||
};
|
pub blending_offset: f64,
|
||||||
|
|
||||||
pub struct BlendResult {
|
|
||||||
alpha: f64,
|
|
||||||
offset: f64,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlendResult {
|
pub struct Blender {
|
||||||
pub const fn new(alpha: f64, offset: f64) -> Self {
|
height_and_biome_blending_data: FxHashMap<u64, BlendingData>,
|
||||||
Self { alpha, offset }
|
density_blending_data: FxHashMap<u64, BlendingData>,
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[enum_dispatch(BlenderImpl)]
|
const HEIGHT_BLENDING_RANGE_CELLS: i32 = (7 << 2) - 1; // QuartPos.fromSection(7) - 1
|
||||||
pub enum Blender {
|
const HEIGHT_BLENDING_RANGE_CHUNKS: i32 = (HEIGHT_BLENDING_RANGE_CELLS + 3) >> 2; // QuartPos.toSection(...)
|
||||||
NoBlend(NoBlendBlender),
|
const DENSITY_BLENDING_RANGE_CELLS: i32 = 2;
|
||||||
}
|
const DENSITY_BLENDING_RANGE_CHUNKS: i32 = 5 >> 2; // QuartPos.toSection(5)
|
||||||
|
|
||||||
impl Blender {
|
impl Blender {
|
||||||
pub const NO_BLEND: Self = Self::NoBlend(NoBlendBlender {});
|
pub fn empty() -> Self {
|
||||||
|
Self {
|
||||||
|
height_and_biome_blending_data: FxHashMap::default(),
|
||||||
|
density_blending_data: FxHashMap::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn of<C: GenerationCache>(cache: &C) -> Self {
|
||||||
|
let center_chunk = cache.get_center_chunk();
|
||||||
|
let center_x = center_chunk.x;
|
||||||
|
let center_z = center_chunk.z;
|
||||||
|
|
||||||
|
let mut height_and_biome_data = FxHashMap::default();
|
||||||
|
let mut density_data = FxHashMap::default();
|
||||||
|
|
||||||
|
let max_dist_sq = (HEIGHT_BLENDING_RANGE_CHUNKS + 1) * (HEIGHT_BLENDING_RANGE_CHUNKS + 1);
|
||||||
|
|
||||||
|
for dx in -HEIGHT_BLENDING_RANGE_CHUNKS..=HEIGHT_BLENDING_RANGE_CHUNKS {
|
||||||
|
for dz in -HEIGHT_BLENDING_RANGE_CHUNKS..=HEIGHT_BLENDING_RANGE_CHUNKS {
|
||||||
|
if dx * dx + dz * dz <= max_dist_sq {
|
||||||
|
let chunk_x = center_x + dx;
|
||||||
|
let chunk_z = center_z + dz;
|
||||||
|
|
||||||
|
if let Some(blending_data) = cache.get_blending_data(chunk_x, chunk_z) {
|
||||||
|
let packed = (chunk_x as u32 as u64) | ((chunk_z as u32 as u64) << 32);
|
||||||
|
height_and_biome_data.insert(packed, blending_data.clone());
|
||||||
|
|
||||||
|
if (-DENSITY_BLENDING_RANGE_CHUNKS..=DENSITY_BLENDING_RANGE_CHUNKS)
|
||||||
|
.contains(&dx)
|
||||||
|
&& (-DENSITY_BLENDING_RANGE_CHUNKS..=DENSITY_BLENDING_RANGE_CHUNKS)
|
||||||
|
.contains(&dz)
|
||||||
|
{
|
||||||
|
density_data.insert(packed, blending_data.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if height_and_biome_data.is_empty() && density_data.is_empty() {
|
||||||
|
Self::empty()
|
||||||
|
} else {
|
||||||
|
Self {
|
||||||
|
height_and_biome_blending_data: height_and_biome_data,
|
||||||
|
density_blending_data: density_data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.height_and_biome_blending_data.is_empty() && self.density_blending_data.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn blend_offset_and_factor(&self, block_x: i32, block_z: i32) -> BlendingOutput {
|
||||||
|
let cell_x = biome_coords::from_block(block_x);
|
||||||
|
let cell_z = biome_coords::from_block(block_z);
|
||||||
|
|
||||||
|
let fixed_height = self
|
||||||
|
.get_blending_data_value(cell_x, 0, cell_z, |data, x, y, z| data.get_height(x, y, z));
|
||||||
|
|
||||||
|
if fixed_height != f64::MAX {
|
||||||
|
return BlendingOutput {
|
||||||
|
alpha: 0.0,
|
||||||
|
blending_offset: Self::height_to_offset(fixed_height),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut total_weight = 0.0;
|
||||||
|
let mut weighted_heights = 0.0;
|
||||||
|
let mut closest_distance = f64::INFINITY;
|
||||||
|
|
||||||
|
for (&packed_pos, blending_data) in &self.height_and_biome_blending_data {
|
||||||
|
let chunk_x = (packed_pos & 0xFFFFFFFF) as i32;
|
||||||
|
let chunk_z = (packed_pos >> 32) as i32;
|
||||||
|
|
||||||
|
blending_data.iterate_heights(
|
||||||
|
biome_coords::from_chunk(chunk_x),
|
||||||
|
biome_coords::from_chunk(chunk_z),
|
||||||
|
|test_cell_x, test_cell_z, height| {
|
||||||
|
let dx = (cell_x - test_cell_x) as f64;
|
||||||
|
let dz = (cell_z - test_cell_z) as f64;
|
||||||
|
let distance = (dx * dx + dz * dz).sqrt();
|
||||||
|
|
||||||
|
if distance <= HEIGHT_BLENDING_RANGE_CELLS as f64 {
|
||||||
|
if distance < closest_distance {
|
||||||
|
closest_distance = distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
let weight = 1.0 / (distance * distance * distance * distance);
|
||||||
|
weighted_heights += height * weight;
|
||||||
|
total_weight += weight;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if closest_distance == f64::INFINITY {
|
||||||
|
BlendingOutput {
|
||||||
|
alpha: 1.0,
|
||||||
|
blending_offset: 0.0,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let average_height = weighted_heights / total_weight;
|
||||||
|
let mut alpha =
|
||||||
|
(closest_distance / (HEIGHT_BLENDING_RANGE_CELLS + 1) as f64).clamp(0.0, 1.0);
|
||||||
|
alpha = 3.0 * alpha * alpha - 2.0 * alpha * alpha * alpha;
|
||||||
|
BlendingOutput {
|
||||||
|
alpha,
|
||||||
|
blending_offset: Self::height_to_offset(average_height),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn height_to_offset(height: f64) -> f64 {
|
||||||
|
let target_y = height + 0.5;
|
||||||
|
let target_y_mod = target_y.rem_euclid(8.0);
|
||||||
|
(32.0 * (target_y - 128.0) - 3.0 * (target_y - 120.0) * target_y_mod
|
||||||
|
+ 3.0 * target_y_mod * target_y_mod)
|
||||||
|
/ (128.0 * (32.0 - 3.0 * target_y_mod))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn blend_density(&self, block_x: i32, block_y: i32, block_z: i32, noise_value: f64) -> f64 {
|
||||||
|
let cell_x = biome_coords::from_block(block_x);
|
||||||
|
let cell_y = block_y / 8;
|
||||||
|
let cell_z = biome_coords::from_block(block_z);
|
||||||
|
|
||||||
|
let fixed_density =
|
||||||
|
self.get_blending_data_value(cell_x, cell_y, cell_z, |data, x, y, z| {
|
||||||
|
data.get_density(x, y, z)
|
||||||
|
});
|
||||||
|
|
||||||
|
if fixed_density != f64::MAX {
|
||||||
|
return fixed_density;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut total_weight = 0.0;
|
||||||
|
let mut weighted_densities = 0.0;
|
||||||
|
let mut closest_distance = f64::INFINITY;
|
||||||
|
|
||||||
|
for (&packed_pos, blending_data) in &self.density_blending_data {
|
||||||
|
let chunk_x = (packed_pos & 0xFFFFFFFF) as i32;
|
||||||
|
let chunk_z = (packed_pos >> 32) as i32;
|
||||||
|
|
||||||
|
blending_data.iterate_densities(
|
||||||
|
biome_coords::from_chunk(chunk_x),
|
||||||
|
biome_coords::from_chunk(chunk_z),
|
||||||
|
cell_y - 1,
|
||||||
|
cell_y + 1,
|
||||||
|
|test_cell_x, test_cell_y, test_cell_z, density| {
|
||||||
|
let dx = (cell_x - test_cell_x) as f64;
|
||||||
|
let dy = ((cell_y - test_cell_y) * 2) as f64;
|
||||||
|
let dz = (cell_z - test_cell_z) as f64;
|
||||||
|
let distance = (dx * dx + dy * dy + dz * dz).sqrt();
|
||||||
|
|
||||||
|
if distance <= 2.0 {
|
||||||
|
if distance < closest_distance {
|
||||||
|
closest_distance = distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
let weight = 1.0 / (distance * distance * distance * distance);
|
||||||
|
weighted_densities += density * weight;
|
||||||
|
total_weight += weight;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if closest_distance == f64::INFINITY {
|
||||||
|
noise_value
|
||||||
|
} else {
|
||||||
|
let average_density = weighted_densities / total_weight;
|
||||||
|
let alpha = (closest_distance / 3.0).clamp(0.0, 1.0);
|
||||||
|
alpha * noise_value + (1.0 - alpha) * average_density
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_blending_data_value<F>(&self, cell_x: i32, cell_y: i32, cell_z: i32, getter: F) -> f64
|
||||||
|
where
|
||||||
|
F: Fn(&BlendingData, i32, i32, i32) -> f64,
|
||||||
|
{
|
||||||
|
let chunk_x = biome_coords::to_chunk(cell_x);
|
||||||
|
let chunk_z = biome_coords::to_chunk(cell_z);
|
||||||
|
let min_x = (cell_x & 3) == 0;
|
||||||
|
let min_z = (cell_z & 3) == 0;
|
||||||
|
|
||||||
|
let mut value = self.get_data_value(&getter, chunk_x, chunk_z, cell_x, cell_y, cell_z);
|
||||||
|
if value == f64::MAX {
|
||||||
|
if min_x && min_z {
|
||||||
|
value =
|
||||||
|
self.get_data_value(&getter, chunk_x - 1, chunk_z - 1, cell_x, cell_y, cell_z);
|
||||||
|
}
|
||||||
|
|
||||||
|
if value == f64::MAX {
|
||||||
|
if min_x {
|
||||||
|
value =
|
||||||
|
self.get_data_value(&getter, chunk_x - 1, chunk_z, cell_x, cell_y, cell_z);
|
||||||
|
}
|
||||||
|
|
||||||
|
if value == f64::MAX && min_z {
|
||||||
|
value =
|
||||||
|
self.get_data_value(&getter, chunk_x, chunk_z - 1, cell_x, cell_y, cell_z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_data_value<F>(
|
||||||
|
&self,
|
||||||
|
getter: &F,
|
||||||
|
chunk_x: i32,
|
||||||
|
chunk_z: i32,
|
||||||
|
cell_x: i32,
|
||||||
|
cell_y: i32,
|
||||||
|
cell_z: i32,
|
||||||
|
) -> f64
|
||||||
|
where
|
||||||
|
F: Fn(&BlendingData, i32, i32, i32) -> f64,
|
||||||
|
{
|
||||||
|
let packed = (chunk_x as u32 as u64) | ((chunk_z as u32 as u64) << 32);
|
||||||
|
if let Some(data) = self.height_and_biome_blending_data.get(&packed) {
|
||||||
|
getter(
|
||||||
|
data,
|
||||||
|
cell_x - biome_coords::from_chunk(chunk_x),
|
||||||
|
cell_y,
|
||||||
|
cell_z - biome_coords::from_chunk(chunk_z),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
f64::MAX
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn blend_biome(
|
||||||
|
&self,
|
||||||
|
quart_x: i32,
|
||||||
|
quart_y: i32,
|
||||||
|
quart_z: i32,
|
||||||
|
shift_noise: &DoublePerlinNoiseSampler,
|
||||||
|
) -> Option<&'static Biome> {
|
||||||
|
let mut closest_distance = f64::INFINITY;
|
||||||
|
let mut closest_biome = None;
|
||||||
|
|
||||||
|
for (&packed_pos, blending_data) in &self.height_and_biome_blending_data {
|
||||||
|
let chunk_x = (packed_pos & 0xFFFFFFFF) as i32;
|
||||||
|
let chunk_z = (packed_pos >> 32) as i32;
|
||||||
|
|
||||||
|
blending_data.iterate_biomes(
|
||||||
|
biome_coords::from_chunk(chunk_x),
|
||||||
|
quart_y,
|
||||||
|
biome_coords::from_chunk(chunk_z),
|
||||||
|
|test_cell_x, test_cell_z, biome| {
|
||||||
|
let dx = (quart_x - test_cell_x) as f64;
|
||||||
|
let dz = (quart_z - test_cell_z) as f64;
|
||||||
|
let distance = (dx * dx + dz * dz).sqrt();
|
||||||
|
|
||||||
|
if distance <= HEIGHT_BLENDING_RANGE_CELLS as f64 && distance < closest_distance
|
||||||
|
{
|
||||||
|
closest_biome = Some(biome);
|
||||||
|
closest_distance = distance;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if closest_distance == f64::INFINITY {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let shift = shift_noise.sample(quart_x as f64, 0.0, quart_z as f64) * 12.0;
|
||||||
|
let alpha = ((closest_distance + shift) / (HEIGHT_BLENDING_RANGE_CELLS + 1) as f64)
|
||||||
|
.clamp(0.0, 1.0);
|
||||||
|
if alpha > 0.5 { None } else { closest_biome }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BlenderBiomeSupplier<'a> {
|
pub struct BlenderBiomeSupplier<'a> {
|
||||||
base: &'a dyn BiomeSupplier,
|
base: &'a dyn BiomeSupplier,
|
||||||
|
blender: &'a Blender,
|
||||||
|
shift_noise: DoublePerlinNoiseSampler,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BiomeSupplier for BlenderBiomeSupplier<'_> {
|
impl BiomeSupplier for BlenderBiomeSupplier<'_> {
|
||||||
fn biome(&self, x: i32, y: i32, z: i32, sampler: &mut MultiNoiseSampler<'_>) -> &'static Biome {
|
fn biome(&self, x: i32, y: i32, z: i32, sampler: &mut MultiNoiseSampler<'_>) -> &'static Biome {
|
||||||
|
if let Some(blended) = self.blender.blend_biome(x, y, z, &self.shift_noise) {
|
||||||
|
blended
|
||||||
|
} else {
|
||||||
self.base.biome(x, y, z, sampler)
|
self.base.biome(x, y, z, sampler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[enum_dispatch]
|
|
||||||
pub trait BlenderImpl {
|
pub trait BlenderImpl {
|
||||||
fn calculate(&self, block_x: i32, block_z: i32) -> BlendResult;
|
fn blend_offset_and_factor(&self, block_x: i32, block_z: i32) -> BlendingOutput;
|
||||||
|
|
||||||
fn apply_blend_density(&self, pos: &Vector3<i32>, density: f64) -> f64;
|
fn blend_density(&self, pos: &Vector3<i32>, density: f64) -> f64;
|
||||||
|
|
||||||
fn get_biome_supplier<'a>(&self, supplier: &'a dyn BiomeSupplier) -> BlenderBiomeSupplier<'a>;
|
fn get_biome_supplier<'a>(
|
||||||
|
&'a self,
|
||||||
|
supplier: &'a dyn BiomeSupplier,
|
||||||
|
) -> BlenderBiomeSupplier<'a>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct NoBlendBlender {}
|
impl BlenderImpl for Blender {
|
||||||
|
fn blend_offset_and_factor(&self, block_x: i32, block_z: i32) -> BlendingOutput {
|
||||||
impl BlenderImpl for NoBlendBlender {
|
self.blend_offset_and_factor(block_x, block_z)
|
||||||
fn calculate(&self, _block_x: i32, _block_z: i32) -> BlendResult {
|
|
||||||
BlendResult::new(1f64, 1f64)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_blend_density(&self, _pos: &Vector3<i32>, density: f64) -> f64 {
|
fn blend_density(&self, pos: &Vector3<i32>, density: f64) -> f64 {
|
||||||
density
|
self.blend_density(pos.x, pos.y, pos.z, density)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_biome_supplier<'a>(&self, supplier: &'a dyn BiomeSupplier) -> BlenderBiomeSupplier<'a> {
|
fn get_biome_supplier<'a>(
|
||||||
BlenderBiomeSupplier { base: supplier }
|
&'a self,
|
||||||
|
supplier: &'a dyn BiomeSupplier,
|
||||||
|
) -> BlenderBiomeSupplier<'a> {
|
||||||
|
let mut random = Xoroshiro::from_seed(42);
|
||||||
|
let shift_noise = DoublePerlinNoiseSampler::from_params(
|
||||||
|
&mut random,
|
||||||
|
&DoublePerlinNoiseParameters::OFFSET,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
BlenderBiomeSupplier {
|
||||||
|
base: supplier,
|
||||||
|
blender: self,
|
||||||
|
shift_noise,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,7 @@ impl Carver for CanyonCarver {
|
|||||||
random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
_chunk_pos: &Vector2<i32>,
|
_chunk_pos: &Vector2<i32>,
|
||||||
carver_chunk_pos: &Vector2<i32>,
|
carver_chunk_pos: &Vector2<i32>,
|
||||||
|
legacy_random_source: bool,
|
||||||
) {
|
) {
|
||||||
let CarverAdditionalConfig::Canyon(ref canyon_config) = config.additional else {
|
let CarverAdditionalConfig::Canyon(ref canyon_config) = config.additional else {
|
||||||
return;
|
return;
|
||||||
@ -51,6 +52,7 @@ impl Carver for CanyonCarver {
|
|||||||
0,
|
0,
|
||||||
distance,
|
distance,
|
||||||
y_scale,
|
y_scale,
|
||||||
|
legacy_random_source,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,10 +73,17 @@ impl CanyonCarver {
|
|||||||
step: i32,
|
step: i32,
|
||||||
distance: i32,
|
distance: i32,
|
||||||
y_scale: f64,
|
y_scale: f64,
|
||||||
|
legacy_random_source: bool,
|
||||||
) {
|
) {
|
||||||
let mut random = RandomGenerator::Xoroshiro(
|
let mut random = if legacy_random_source {
|
||||||
pumpkin_util::random::xoroshiro128::Xoroshiro::from_seed(tunnel_seed as u64),
|
RandomGenerator::Legacy(pumpkin_util::random::legacy_rand::LegacyRand::from_seed(
|
||||||
);
|
tunnel_seed as u64,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
RandomGenerator::Xoroshiro(pumpkin_util::random::xoroshiro128::Xoroshiro::from_seed(
|
||||||
|
tunnel_seed as u64,
|
||||||
|
))
|
||||||
|
};
|
||||||
let width_factor_per_height =
|
let width_factor_per_height =
|
||||||
self.init_width_factors(chunk.height() as usize, config, &mut random);
|
self.init_width_factors(chunk.height() as usize, config, &mut random);
|
||||||
let mut y_rota = 0.0f32;
|
let mut y_rota = 0.0f32;
|
||||||
@ -234,8 +243,7 @@ impl CanyonCarver {
|
|||||||
let zd = (world_z as f64 + 0.5 - z) / horizontal_radius;
|
let zd = (world_z as f64 + 0.5 - z) / horizontal_radius;
|
||||||
|
|
||||||
if xd * xd + zd * zd < 1.0 {
|
if xd * xd + zd * zd < 1.0 {
|
||||||
let mut has_grass = false;
|
for world_y in (min_y + 1..=max_y).rev() {
|
||||||
for world_y in (min_y..=max_y).rev() {
|
|
||||||
let yd = (world_y as f64 - 0.5 - y) / vertical_radius;
|
let yd = (world_y as f64 - 0.5 - y) / vertical_radius;
|
||||||
|
|
||||||
if !self.should_skip(
|
if !self.should_skip(
|
||||||
@ -248,8 +256,7 @@ impl CanyonCarver {
|
|||||||
) && !chunk.carving_mask.get(world_x, world_y, world_z)
|
) && !chunk.carving_mask.get(world_x, world_y, world_z)
|
||||||
{
|
{
|
||||||
chunk.carving_mask.set(world_x, world_y, world_z);
|
chunk.carving_mask.set(world_x, world_y, world_z);
|
||||||
has_grass |= self
|
self.carve_block(chunk, config, world_x, world_y, world_z);
|
||||||
.carve_block(chunk, config, world_x, world_y, world_z, has_grass);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -280,7 +287,6 @@ impl CanyonCarver {
|
|||||||
x: i32,
|
x: i32,
|
||||||
y: i32,
|
y: i32,
|
||||||
z: i32,
|
z: i32,
|
||||||
mut has_grass: bool,
|
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let local_y = y - chunk.bottom_y() as i32;
|
let local_y = y - chunk.bottom_y() as i32;
|
||||||
let state_id = chunk.get_block_state_raw(x & 15, local_y, z & 15);
|
let state_id = chunk.get_block_state_raw(x & 15, local_y, z & 15);
|
||||||
@ -290,12 +296,6 @@ impl CanyonCarver {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if block.id == pumpkin_data::Block::GRASS_BLOCK.id
|
|
||||||
|| block.id == pumpkin_data::Block::MYCELIUM.id
|
|
||||||
{
|
|
||||||
has_grass = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.replaceable.1.contains(&block.id) {
|
if config.replaceable.1.contains(&block.id) {
|
||||||
let air = BlockState::from_id(pumpkin_data::Block::AIR.default_state.id);
|
let air = BlockState::from_id(pumpkin_data::Block::AIR.default_state.id);
|
||||||
let lava = BlockState::from_id(pumpkin_data::Block::LAVA.default_state.id);
|
let lava = BlockState::from_id(pumpkin_data::Block::LAVA.default_state.id);
|
||||||
@ -310,18 +310,6 @@ impl CanyonCarver {
|
|||||||
chunk.set_block_state(x & 15, local_y, z & 15, air);
|
chunk.set_block_state(x & 15, local_y, z & 15, air);
|
||||||
}
|
}
|
||||||
|
|
||||||
if has_grass {
|
|
||||||
let down_y = y - 1;
|
|
||||||
let local_down_y = down_y - chunk.bottom_y() as i32;
|
|
||||||
if (0..chunk.height() as i32).contains(&local_down_y) {
|
|
||||||
let down_state_id = chunk.get_block_state_raw(x & 15, local_down_y, z & 15);
|
|
||||||
if pumpkin_data::Block::from_state_id(down_state_id).id
|
|
||||||
== pumpkin_data::Block::DIRT.id
|
|
||||||
{
|
|
||||||
// dirt replacement skipped
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
|
|||||||
@ -16,6 +16,7 @@ impl Carver for CaveCarver {
|
|||||||
random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
_chunk_pos: &Vector2<i32>,
|
_chunk_pos: &Vector2<i32>,
|
||||||
carver_chunk_pos: &Vector2<i32>,
|
carver_chunk_pos: &Vector2<i32>,
|
||||||
|
legacy_random_source: bool,
|
||||||
) {
|
) {
|
||||||
let (is_nether, cave_config) = match config.additional {
|
let (is_nether, cave_config) = match config.additional {
|
||||||
CarverAdditionalConfig::Cave(ref c) => (false, c),
|
CarverAdditionalConfig::Cave(ref c) => (false, c),
|
||||||
@ -85,6 +86,7 @@ impl Carver for CaveCarver {
|
|||||||
if is_nether { 5.0 } else { 1.0 }, // this.getYScale()
|
if is_nether { 5.0 } else { 1.0 }, // this.getYScale()
|
||||||
floor_level,
|
floor_level,
|
||||||
is_nether,
|
is_nether,
|
||||||
|
legacy_random_source,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -151,10 +153,17 @@ impl CaveCarver {
|
|||||||
y_scale: f64,
|
y_scale: f64,
|
||||||
floor_level: f64,
|
floor_level: f64,
|
||||||
is_nether: bool,
|
is_nether: bool,
|
||||||
|
legacy_random_source: bool,
|
||||||
) {
|
) {
|
||||||
let mut random = RandomGenerator::Xoroshiro(
|
let mut random = if legacy_random_source {
|
||||||
pumpkin_util::random::xoroshiro128::Xoroshiro::from_seed(tunnel_seed as u64),
|
RandomGenerator::Legacy(pumpkin_util::random::legacy_rand::LegacyRand::from_seed(
|
||||||
);
|
tunnel_seed as u64,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
RandomGenerator::Xoroshiro(pumpkin_util::random::xoroshiro128::Xoroshiro::from_seed(
|
||||||
|
tunnel_seed as u64,
|
||||||
|
))
|
||||||
|
};
|
||||||
let split_point = random.next_bounded_i32(dist / 2) + dist / 4;
|
let split_point = random.next_bounded_i32(dist / 2) + dist / 4;
|
||||||
let steep = random.next_bounded_i32(6) == 0;
|
let steep = random.next_bounded_i32(6) == 0;
|
||||||
let mut y_rota = 0.0f32;
|
let mut y_rota = 0.0f32;
|
||||||
@ -195,6 +204,7 @@ impl CaveCarver {
|
|||||||
1.0,
|
1.0,
|
||||||
floor_level,
|
floor_level,
|
||||||
is_nether,
|
is_nether,
|
||||||
|
legacy_random_source,
|
||||||
);
|
);
|
||||||
self.create_tunnel(
|
self.create_tunnel(
|
||||||
config,
|
config,
|
||||||
@ -213,6 +223,7 @@ impl CaveCarver {
|
|||||||
1.0,
|
1.0,
|
||||||
floor_level,
|
floor_level,
|
||||||
is_nether,
|
is_nether,
|
||||||
|
legacy_random_source,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -301,17 +312,14 @@ impl CaveCarver {
|
|||||||
let zd = (world_z as f64 + 0.5 - z) / horizontal_radius;
|
let zd = (world_z as f64 + 0.5 - z) / horizontal_radius;
|
||||||
|
|
||||||
if xd * xd + zd * zd < 1.0 {
|
if xd * xd + zd * zd < 1.0 {
|
||||||
let mut has_grass = false;
|
for world_y in (min_y + 1..=max_y).rev() {
|
||||||
for world_y in (min_y..=max_y).rev() {
|
let yd = (world_y as f64 - 0.5 - y) / vertical_radius;
|
||||||
let yd = (world_y as f64 + 0.5 - y) / vertical_radius;
|
|
||||||
|
|
||||||
if !self.should_skip(xd, yd, zd, floor_level)
|
if !self.should_skip(xd, yd, zd, floor_level)
|
||||||
&& !chunk.carving_mask.get(world_x, world_y, world_z)
|
&& !chunk.carving_mask.get(world_x, world_y, world_z)
|
||||||
{
|
{
|
||||||
chunk.carving_mask.set(world_x, world_y, world_z);
|
chunk.carving_mask.set(world_x, world_y, world_z);
|
||||||
has_grass |= self.carve_block(
|
self.carve_block(chunk, config, world_x, world_y, world_z, is_nether);
|
||||||
chunk, config, world_x, world_y, world_z, is_nether, has_grass,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -336,7 +344,6 @@ impl CaveCarver {
|
|||||||
y: i32,
|
y: i32,
|
||||||
z: i32,
|
z: i32,
|
||||||
is_nether: bool,
|
is_nether: bool,
|
||||||
mut has_grass: bool,
|
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let local_y = y - chunk.bottom_y() as i32;
|
let local_y = y - chunk.bottom_y() as i32;
|
||||||
let state_id = chunk.get_block_state_raw(x & 15, local_y, z & 15);
|
let state_id = chunk.get_block_state_raw(x & 15, local_y, z & 15);
|
||||||
@ -346,12 +353,6 @@ impl CaveCarver {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if block.id == pumpkin_data::Block::GRASS_BLOCK.id
|
|
||||||
|| block.id == pumpkin_data::Block::MYCELIUM.id
|
|
||||||
{
|
|
||||||
has_grass = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only carve if it's replaceable
|
// Only carve if it's replaceable
|
||||||
if config.replaceable.1.contains(&block.id) {
|
if config.replaceable.1.contains(&block.id) {
|
||||||
// Replace with air or lava
|
// Replace with air or lava
|
||||||
@ -372,19 +373,6 @@ impl CaveCarver {
|
|||||||
chunk.set_block_state(x & 15, local_y, z & 15, air);
|
chunk.set_block_state(x & 15, local_y, z & 15, air);
|
||||||
}
|
}
|
||||||
|
|
||||||
if has_grass {
|
|
||||||
let down_y = y - 1;
|
|
||||||
let local_down_y = down_y - chunk.bottom_y() as i32;
|
|
||||||
if (0..chunk.height() as i32).contains(&local_down_y) {
|
|
||||||
let down_state_id = chunk.get_block_state_raw(x & 15, local_down_y, z & 15);
|
|
||||||
if pumpkin_data::Block::from_state_id(down_state_id).id
|
|
||||||
== pumpkin_data::Block::DIRT.id
|
|
||||||
{
|
|
||||||
// dirt replacement skipped
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
|
|||||||
@ -17,14 +17,11 @@ pub trait Carver {
|
|||||||
random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
chunk_pos: &Vector2<i32>,
|
chunk_pos: &Vector2<i32>,
|
||||||
carver_chunk_pos: &Vector2<i32>,
|
carver_chunk_pos: &Vector2<i32>,
|
||||||
|
legacy_random_source: bool,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn carve(chunk: &mut ProtoChunk, generator: &VanillaGenerator) {
|
pub fn carve(chunk: &mut ProtoChunk, generator: &VanillaGenerator) {
|
||||||
let mut random = RandomGenerator::Xoroshiro(
|
|
||||||
pumpkin_util::random::xoroshiro128::Xoroshiro::from_seed(generator.random_config.seed),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Vanilla applyCarvers uses a range of 8 chunks (17x17 area)
|
// Vanilla applyCarvers uses a range of 8 chunks (17x17 area)
|
||||||
let radius = 8;
|
let radius = 8;
|
||||||
let chunk_x = chunk.x;
|
let chunk_x = chunk.x;
|
||||||
@ -32,7 +29,6 @@ pub fn carve(chunk: &mut ProtoChunk, generator: &VanillaGenerator) {
|
|||||||
let chunk_pos = Vector2::new(chunk_x, chunk_z);
|
let chunk_pos = Vector2::new(chunk_x, chunk_z);
|
||||||
|
|
||||||
let overworld_carvers = [&CAVE, &CAVE_EXTRA_UNDERGROUND, &CANYON];
|
let overworld_carvers = [&CAVE, &CAVE_EXTRA_UNDERGROUND, &CANYON];
|
||||||
|
|
||||||
let nether_carvers = [&NETHER_CAVE];
|
let nether_carvers = [&NETHER_CAVE];
|
||||||
|
|
||||||
let carvers_to_use = if generator.dimension == pumpkin_data::dimension::Dimension::OVERWORLD {
|
let carvers_to_use = if generator.dimension == pumpkin_data::dimension::Dimension::OVERWORLD {
|
||||||
@ -56,14 +52,19 @@ pub fn carve(chunk: &mut ProtoChunk, generator: &VanillaGenerator) {
|
|||||||
// maintain the random seed logic.
|
// maintain the random seed logic.
|
||||||
for (index, &config) in carvers_to_use.iter().enumerate() {
|
for (index, &config) in carvers_to_use.iter().enumerate() {
|
||||||
let seed = get_carver_seed(
|
let seed = get_carver_seed(
|
||||||
&mut random,
|
|
||||||
generator.random_config.seed + index as u64,
|
generator.random_config.seed + index as u64,
|
||||||
carver_x,
|
carver_x,
|
||||||
carver_z,
|
carver_z,
|
||||||
);
|
);
|
||||||
let mut carver_random = RandomGenerator::Xoroshiro(
|
let mut carver_random = if generator.settings.legacy_random_source {
|
||||||
|
RandomGenerator::Legacy(
|
||||||
|
pumpkin_util::random::legacy_rand::LegacyRand::from_seed(seed),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
RandomGenerator::Xoroshiro(
|
||||||
pumpkin_util::random::xoroshiro128::Xoroshiro::from_seed(seed),
|
pumpkin_util::random::xoroshiro128::Xoroshiro::from_seed(seed),
|
||||||
);
|
)
|
||||||
|
};
|
||||||
|
|
||||||
if should_carve(config, &mut carver_random) {
|
if should_carve(config, &mut carver_random) {
|
||||||
match config.additional {
|
match config.additional {
|
||||||
@ -74,6 +75,7 @@ pub fn carve(chunk: &mut ProtoChunk, generator: &VanillaGenerator) {
|
|||||||
&mut carver_random,
|
&mut carver_random,
|
||||||
&chunk_pos,
|
&chunk_pos,
|
||||||
&carver_chunk_pos,
|
&carver_chunk_pos,
|
||||||
|
generator.settings.legacy_random_source,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
CarverAdditionalConfig::Canyon(_) => {
|
CarverAdditionalConfig::Canyon(_) => {
|
||||||
@ -83,6 +85,7 @@ pub fn carve(chunk: &mut ProtoChunk, generator: &VanillaGenerator) {
|
|||||||
&mut carver_random,
|
&mut carver_random,
|
||||||
&chunk_pos,
|
&chunk_pos,
|
||||||
&carver_chunk_pos,
|
&carver_chunk_pos,
|
||||||
|
generator.settings.legacy_random_source,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
mod biome;
|
mod biome;
|
||||||
mod blender;
|
pub mod blender;
|
||||||
mod block_predicate;
|
mod block_predicate;
|
||||||
mod block_state_provider;
|
mod block_state_provider;
|
||||||
pub mod carver;
|
pub mod carver;
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
use pumpkin_data::chunk::DoublePerlinNoiseParameters;
|
use pumpkin_data::chunk::DoublePerlinNoiseParameters;
|
||||||
use pumpkin_util::{noise::perlin::OctavePerlinNoiseSampler, random::RandomImpl};
|
use pumpkin_util::{noise::perlin::OctavePerlinNoiseSampler, random::RandomImpl};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct DoublePerlinNoiseSampler {
|
pub struct DoublePerlinNoiseSampler {
|
||||||
first_sampler: OctavePerlinNoiseSampler,
|
first_sampler: OctavePerlinNoiseSampler,
|
||||||
second_sampler: OctavePerlinNoiseSampler,
|
second_sampler: OctavePerlinNoiseSampler,
|
||||||
|
|||||||
@ -85,6 +85,11 @@ pub trait GenerationCache: HeightLimitView + BlockAccessor {
|
|||||||
fn ocean_floor_height_exclusive(&self, x: i32, z: i32) -> i32;
|
fn ocean_floor_height_exclusive(&self, x: i32, z: i32) -> i32;
|
||||||
fn is_air(&self, local_pos: &Vector3<i32>) -> bool;
|
fn is_air(&self, local_pos: &Vector3<i32>) -> bool;
|
||||||
fn get_biome_for_terrain_gen(&self, x: i32, y: i32, z: i32) -> &'static Biome;
|
fn get_biome_for_terrain_gen(&self, x: i32, y: i32, z: i32) -> &'static Biome;
|
||||||
|
fn get_blending_data(
|
||||||
|
&self,
|
||||||
|
chunk_x: i32,
|
||||||
|
chunk_z: i32,
|
||||||
|
) -> Option<&crate::generation::blender::blending_data::BlendingData>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AIR_BLOCK: Block = Block::AIR;
|
const AIR_BLOCK: Block = Block::AIR;
|
||||||
@ -169,6 +174,7 @@ pub struct ProtoChunk {
|
|||||||
pub stage: StagedChunkEnum,
|
pub stage: StagedChunkEnum,
|
||||||
pub light: ChunkLight,
|
pub light: ChunkLight,
|
||||||
pub carving_mask: crate::generation::carver::mask::CarvingMask,
|
pub carving_mask: crate::generation::carver::mask::CarvingMask,
|
||||||
|
pub blending_data: Option<crate::generation::blender::blending_data::BlendingData>,
|
||||||
/// Block entities pending creation when the chunk is finalized.
|
/// Block entities pending creation when the chunk is finalized.
|
||||||
/// These are created from structure templates during world generation.
|
/// These are created from structure templates during world generation.
|
||||||
pub pending_block_entities: Vec<NbtCompound>,
|
pub pending_block_entities: Vec<NbtCompound>,
|
||||||
@ -251,6 +257,7 @@ impl ProtoChunk {
|
|||||||
height as i32,
|
height as i32,
|
||||||
dimension.min_y,
|
dimension.min_y,
|
||||||
),
|
),
|
||||||
|
blending_data: None,
|
||||||
pending_block_entities: Vec::new(),
|
pending_block_entities: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -262,6 +269,7 @@ impl ProtoChunk {
|
|||||||
let mut proto_chunk = Self::new(chunk_data.x, chunk_data.z, generator);
|
let mut proto_chunk = Self::new(chunk_data.x, chunk_data.z, generator);
|
||||||
|
|
||||||
proto_chunk.light = chunk_data.light_engine.lock().unwrap().clone();
|
proto_chunk.light = chunk_data.light_engine.lock().unwrap().clone();
|
||||||
|
proto_chunk.blending_data = chunk_data.blending_data.clone();
|
||||||
|
|
||||||
let section_data = &chunk_data.section;
|
let section_data = &chunk_data.section;
|
||||||
let heightmap_data = chunk_data.heightmap.lock().unwrap();
|
let heightmap_data = chunk_data.heightmap.lock().unwrap();
|
||||||
@ -663,7 +671,8 @@ impl ProtoChunk {
|
|||||||
ActiveSupplier::Nether(s) => s,
|
ActiveSupplier::Nether(s) => s,
|
||||||
ActiveSupplier::Overworld(s) => s,
|
ActiveSupplier::Overworld(s) => s,
|
||||||
};
|
};
|
||||||
let biome_supplier = Blender::NO_BLEND.get_biome_supplier(base_supplier);
|
let blender = Blender::empty();
|
||||||
|
let biome_supplier = blender.get_biome_supplier(base_supplier);
|
||||||
let min_y = self.bottom_y();
|
let min_y = self.bottom_y();
|
||||||
let bottom_section = section_coords::block_to_section(min_y as i32);
|
let bottom_section = section_coords::block_to_section(min_y as i32);
|
||||||
let top_section = section_coords::block_to_section(min_y as i32 + self.height() as i32 - 1);
|
let top_section = section_coords::block_to_section(min_y as i32 + self.height() as i32 - 1);
|
||||||
@ -1135,9 +1144,7 @@ impl ProtoChunk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut candidates = set.structures.to_vec();
|
let mut candidates = set.structures.to_vec();
|
||||||
let mut random: RandomGenerator =
|
let carver_seed = get_carver_seed(seed, self.x, self.z);
|
||||||
RandomGenerator::Xoroshiro(Xoroshiro::from_seed(seed));
|
|
||||||
let carver_seed = get_carver_seed(&mut random, seed, self.x, self.z);
|
|
||||||
let mut random: RandomGenerator =
|
let mut random: RandomGenerator =
|
||||||
RandomGenerator::Xoroshiro(Xoroshiro::from_seed(carver_seed));
|
RandomGenerator::Xoroshiro(Xoroshiro::from_seed(carver_seed));
|
||||||
|
|
||||||
@ -1219,7 +1226,8 @@ impl ProtoChunk {
|
|||||||
ActiveSupplier::Nether(s) => s,
|
ActiveSupplier::Nether(s) => s,
|
||||||
ActiveSupplier::Overworld(s) => s,
|
ActiveSupplier::Overworld(s) => s,
|
||||||
};
|
};
|
||||||
let biome_supplier = Blender::NO_BLEND.get_biome_supplier(base_supplier);
|
let blender = Blender::empty();
|
||||||
|
let biome_supplier = blender.get_biome_supplier(base_supplier);
|
||||||
// Use an empty offset sampler since we are querying arbitrary world coordinates
|
// Use an empty offset sampler since we are querying arbitrary world coordinates
|
||||||
let multi_noise_config = MultiNoiseSamplerBuilderOptions::new(0, 0, 0);
|
let multi_noise_config = MultiNoiseSamplerBuilderOptions::new(0, 0, 0);
|
||||||
let mut multi_noise_sampler =
|
let mut multi_noise_sampler =
|
||||||
|
|||||||
@ -213,8 +213,7 @@ fn should_generate_frequency(
|
|||||||
random.next_f32() < frequency
|
random.next_f32() < frequency
|
||||||
}
|
}
|
||||||
FrequencyReductionMethod::LegacyType3 => {
|
FrequencyReductionMethod::LegacyType3 => {
|
||||||
let mut random = RandomGenerator::Xoroshiro(Xoroshiro::from_seed(seed as u64));
|
let carver_seed = get_carver_seed(seed as u64, chunk_x, chunk_z);
|
||||||
let carver_seed = get_carver_seed(&mut random, seed as u64, chunk_x, chunk_z);
|
|
||||||
let mut random = RandomGenerator::Xoroshiro(Xoroshiro::from_seed(carver_seed));
|
let mut random = RandomGenerator::Xoroshiro(Xoroshiro::from_seed(carver_seed));
|
||||||
random.next_f64() < f64::from(frequency)
|
random.next_f64() < f64::from(frequency)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -578,8 +578,7 @@ pub struct StructureGeneratorContext {
|
|||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn create_chunk_random(seed: i64, chunk_x: i32, chunk_z: i32) -> RandomGenerator {
|
pub fn create_chunk_random(seed: i64, chunk_x: i32, chunk_z: i32) -> RandomGenerator {
|
||||||
let mut random: RandomGenerator = RandomGenerator::Xoroshiro(Xoroshiro::from_seed(seed as u64));
|
let carver_seed = get_carver_seed(seed as u64, chunk_x, chunk_z);
|
||||||
let carver_seed = get_carver_seed(&mut random, seed as u64, chunk_x, chunk_z);
|
|
||||||
RandomGenerator::Xoroshiro(Xoroshiro::from_seed(carver_seed))
|
RandomGenerator::Xoroshiro(Xoroshiro::from_seed(carver_seed))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user