331 lines
10 KiB
Rust
331 lines
10 KiB
Rust
use pumpkin_data::{Block, BlockState};
|
|
use pumpkin_util::{
|
|
DoublePerlinNoiseParametersCodec,
|
|
math::{
|
|
clamped_map,
|
|
int_provider::IntProvider,
|
|
pool::{Pool, Weighted},
|
|
position::BlockPos,
|
|
},
|
|
random::{RandomGenerator, RandomImpl, legacy_rand::LegacyRand},
|
|
};
|
|
|
|
use super::noise::perlin::DoublePerlinNoiseSampler;
|
|
use crate::generation::block_predicate::BlockPredicate;
|
|
use crate::generation::proto_chunk::GenerationCache;
|
|
use crate::world::{BlockAccessor, WorldPortalExt};
|
|
|
|
pub enum BlockStateProvider {
|
|
Simple(SimpleStateProvider),
|
|
Weighted(WeightedBlockStateProvider),
|
|
NoiseThreshold(NoiseThresholdBlockStateProvider),
|
|
NoiseProvider(NoiseBlockStateProvider),
|
|
DualNoise(DualNoiseBlockStateProvider),
|
|
Pillar(PillarBlockStateProvider),
|
|
RandomizedInt(RandomizedIntBlockStateProvider),
|
|
Rule(RuleBasedBlockStateProvider),
|
|
}
|
|
|
|
impl BlockStateProvider {
|
|
pub fn get_for_bonemeal(
|
|
&self,
|
|
random: &mut RandomGenerator,
|
|
pos: BlockPos,
|
|
) -> Option<&'static BlockState> {
|
|
match self {
|
|
Self::NoiseThreshold(provider) => Some(provider.get(random, pos)),
|
|
Self::NoiseProvider(provider) => Some(provider.get(pos)),
|
|
Self::Simple(provider) => Some(provider.get(pos)),
|
|
Self::Weighted(provider) => Some(provider.get(random)),
|
|
Self::DualNoise(provider) => Some(provider.get(pos)),
|
|
Self::Pillar(provider) => Some(provider.get(pos)),
|
|
Self::RandomizedInt(_) | Self::Rule(_) => None,
|
|
}
|
|
}
|
|
|
|
pub fn get_state_for_world(
|
|
&self,
|
|
world: &dyn BlockAccessor,
|
|
random: &mut RandomGenerator,
|
|
pos: BlockPos,
|
|
) -> &'static BlockState {
|
|
match self {
|
|
Self::Simple(provider) => provider.get(pos),
|
|
Self::Weighted(provider) => provider.get(random),
|
|
Self::NoiseThreshold(provider) => provider.get(random, pos),
|
|
Self::NoiseProvider(provider) => provider.get(pos),
|
|
Self::DualNoise(provider) => provider.get(pos),
|
|
Self::Pillar(provider) => provider.get(pos),
|
|
Self::RandomizedInt(provider) => {
|
|
provider.source.get_state_for_world(world, random, pos)
|
|
}
|
|
Self::Rule(provider) => {
|
|
for rule in &provider.rules {
|
|
if rule.if_true.test_world(world, None, &pos) {
|
|
return rule.then.get_state_for_world(world, random, pos);
|
|
}
|
|
}
|
|
provider.fallback.as_ref().map_or_else(
|
|
|| world.get_block_state(&pos),
|
|
|f| f.get_state_for_world(world, random, pos),
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn get<T: GenerationCache>(
|
|
&self,
|
|
random: &mut RandomGenerator,
|
|
pos: BlockPos,
|
|
chunk: &T,
|
|
block_registry: &dyn WorldPortalExt,
|
|
) -> &'static BlockState {
|
|
match self {
|
|
Self::NoiseThreshold(provider) => provider.get(random, pos),
|
|
Self::NoiseProvider(provider) => provider.get(pos),
|
|
Self::Simple(provider) => provider.get(pos),
|
|
Self::Weighted(provider) => provider.get(random),
|
|
Self::DualNoise(provider) => provider.get(pos),
|
|
Self::Pillar(provider) => provider.get(pos),
|
|
Self::RandomizedInt(provider) => provider.get(random, pos, chunk, block_registry),
|
|
Self::Rule(provider) => provider.get(block_registry, chunk, random, pos),
|
|
}
|
|
}
|
|
|
|
pub fn get_with_context<T: GenerationCache>(
|
|
&self,
|
|
block_registry: &dyn WorldPortalExt,
|
|
chunk: &T,
|
|
random: &mut RandomGenerator,
|
|
pos: BlockPos,
|
|
) -> &'static BlockState {
|
|
match self {
|
|
Self::Rule(provider) => provider.get(block_registry, chunk, random, pos),
|
|
_ => self.get(random, pos, chunk, block_registry),
|
|
}
|
|
}
|
|
|
|
pub fn get_optional<T: GenerationCache>(
|
|
&self,
|
|
block_registry: &dyn WorldPortalExt,
|
|
chunk: &T,
|
|
random: &mut RandomGenerator,
|
|
pos: BlockPos,
|
|
) -> Option<&'static BlockState> {
|
|
match self {
|
|
Self::Rule(provider) => provider.get_optional(block_registry, chunk, random, pos),
|
|
_ => Some(self.get(random, pos, chunk, block_registry)),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct RuleBasedBlockStateProvider {
|
|
pub fallback: Option<Box<BlockStateProvider>>,
|
|
pub rules: Vec<BlockStateRule>,
|
|
}
|
|
|
|
impl RuleBasedBlockStateProvider {
|
|
pub fn get<T: GenerationCache>(
|
|
&self,
|
|
block_registry: &dyn WorldPortalExt,
|
|
chunk: &T,
|
|
random: &mut RandomGenerator,
|
|
pos: BlockPos,
|
|
) -> &'static BlockState {
|
|
if let Some(optional) = self.get_optional(block_registry, chunk, random, pos) {
|
|
return optional;
|
|
}
|
|
GenerationCache::get_block_state(chunk, &pos.0).to_state()
|
|
}
|
|
pub fn get_optional<T: GenerationCache>(
|
|
&self,
|
|
block_registry: &dyn WorldPortalExt,
|
|
chunk: &T,
|
|
random: &mut RandomGenerator,
|
|
pos: BlockPos,
|
|
) -> Option<&'static BlockState> {
|
|
for rule in &self.rules {
|
|
if rule.if_true.test(block_registry, chunk, &pos) {
|
|
return Some(
|
|
rule.then
|
|
.get_with_context(block_registry, chunk, random, pos),
|
|
);
|
|
}
|
|
}
|
|
self.fallback
|
|
.as_ref()
|
|
.map(|f| f.get(random, pos, chunk, block_registry))
|
|
}
|
|
}
|
|
|
|
pub struct BlockStateRule {
|
|
pub if_true: BlockPredicate,
|
|
pub then: BlockStateProvider,
|
|
}
|
|
|
|
pub struct RandomizedIntBlockStateProvider {
|
|
pub source: Box<BlockStateProvider>,
|
|
pub property: String,
|
|
pub values: IntProvider,
|
|
}
|
|
|
|
impl RandomizedIntBlockStateProvider {
|
|
pub fn get<T: GenerationCache>(
|
|
&self,
|
|
random: &mut RandomGenerator,
|
|
pos: BlockPos,
|
|
chunk: &T,
|
|
block_registry: &dyn WorldPortalExt,
|
|
) -> &'static BlockState {
|
|
// TODO
|
|
self.source.get(random, pos, chunk, block_registry)
|
|
}
|
|
}
|
|
|
|
pub struct PillarBlockStateProvider {
|
|
pub state: &'static BlockState,
|
|
}
|
|
|
|
impl PillarBlockStateProvider {
|
|
#[must_use]
|
|
pub const fn get(&self, _pos: BlockPos) -> &'static BlockState {
|
|
// TODO: random axis
|
|
self.state
|
|
}
|
|
}
|
|
|
|
pub struct DualNoiseBlockStateProvider {
|
|
pub base: NoiseBlockStateProvider,
|
|
pub variety: [u32; 2],
|
|
pub slow_noise: DoublePerlinNoiseParametersCodec,
|
|
pub slow_scale: f64,
|
|
}
|
|
|
|
impl DualNoiseBlockStateProvider {
|
|
#[must_use]
|
|
pub fn get(&self, pos: BlockPos) -> &'static BlockState {
|
|
let sampler = DoublePerlinNoiseSampler::new(
|
|
&mut RandomGenerator::Legacy(LegacyRand::from_seed(self.base.base.seed as u64)),
|
|
self.slow_noise.first_octave,
|
|
&self.slow_noise.amplitudes,
|
|
self.slow_noise.amplitude,
|
|
false,
|
|
);
|
|
let slow_noise =
|
|
self.get_slow_noise(pos.0.x as f64, pos.0.y as f64, pos.0.z as f64, &sampler);
|
|
let mapped = clamped_map(
|
|
slow_noise,
|
|
-1.0,
|
|
1.0,
|
|
self.variety[0] as f64,
|
|
self.variety[1] as f64 + 1.0,
|
|
) as i32;
|
|
let mut list = Vec::with_capacity(mapped as usize);
|
|
for i in 0..mapped {
|
|
let value = self.get_slow_noise(i as f64 * 54545.0, 0.0, i as f64 * 34234.0, &sampler);
|
|
list.push(NoiseBlockStateProvider::get_state_by_value(
|
|
&self.base.states,
|
|
value,
|
|
));
|
|
}
|
|
let value = self.base.base.get_noise(pos);
|
|
NoiseBlockStateProvider::get_state_by_value(&list, value)
|
|
}
|
|
|
|
fn get_slow_noise(&self, x: f64, y: f64, z: f64, sampler: &DoublePerlinNoiseSampler) -> f64 {
|
|
sampler.sample(
|
|
x * self.slow_scale,
|
|
y * self.slow_scale,
|
|
z * self.slow_scale,
|
|
)
|
|
}
|
|
}
|
|
|
|
pub struct WeightedBlockStateProvider {
|
|
pub entries: Vec<Weighted<&'static BlockState>>,
|
|
}
|
|
|
|
impl WeightedBlockStateProvider {
|
|
pub fn get(&self, random: &mut RandomGenerator) -> &'static BlockState {
|
|
Pool::get(&self.entries, random)
|
|
.copied()
|
|
.unwrap_or(Block::AIR.default_state)
|
|
}
|
|
}
|
|
|
|
pub struct SimpleStateProvider {
|
|
pub state: &'static BlockState,
|
|
}
|
|
|
|
impl SimpleStateProvider {
|
|
#[must_use]
|
|
pub const fn get(&self, _pos: BlockPos) -> &'static BlockState {
|
|
self.state
|
|
}
|
|
}
|
|
|
|
pub struct NoiseBlockStateProviderBase {
|
|
pub seed: i64,
|
|
pub noise: DoublePerlinNoiseParametersCodec,
|
|
pub scale: f32,
|
|
}
|
|
|
|
impl NoiseBlockStateProviderBase {
|
|
#[must_use]
|
|
pub fn get_noise(&self, pos: BlockPos) -> f64 {
|
|
let sampler = DoublePerlinNoiseSampler::new(
|
|
&mut RandomGenerator::Legacy(LegacyRand::from_seed(self.seed as u64)),
|
|
self.noise.first_octave,
|
|
&self.noise.amplitudes,
|
|
self.noise.amplitude,
|
|
false,
|
|
);
|
|
sampler.sample(
|
|
pos.0.x as f64 * self.scale as f64,
|
|
pos.0.y as f64 * self.scale as f64,
|
|
pos.0.z as f64 * self.scale as f64,
|
|
)
|
|
}
|
|
}
|
|
|
|
pub struct NoiseBlockStateProvider {
|
|
pub base: NoiseBlockStateProviderBase,
|
|
pub states: Vec<&'static BlockState>,
|
|
}
|
|
|
|
impl NoiseBlockStateProvider {
|
|
#[must_use]
|
|
pub fn get(&self, pos: BlockPos) -> &'static BlockState {
|
|
let value = self.base.get_noise(pos);
|
|
Self::get_state_by_value(&self.states, value)
|
|
}
|
|
|
|
fn get_state_by_value(states: &[&'static BlockState], value: f64) -> &'static BlockState {
|
|
let val = f64::midpoint(1.0, value).clamp(0.0, 0.9999);
|
|
states[(val * states.len() as f64) as usize]
|
|
}
|
|
}
|
|
|
|
pub struct NoiseThresholdBlockStateProvider {
|
|
pub base: NoiseBlockStateProviderBase,
|
|
pub threshold: f32,
|
|
pub high_chance: f32,
|
|
pub default_state: &'static BlockState,
|
|
pub low_states: Vec<&'static BlockState>,
|
|
pub high_states: Vec<&'static BlockState>,
|
|
}
|
|
|
|
impl NoiseThresholdBlockStateProvider {
|
|
pub fn get(&self, random: &mut RandomGenerator, pos: BlockPos) -> &'static BlockState {
|
|
let value = self.base.get_noise(pos);
|
|
if value < self.threshold as f64 {
|
|
return self.low_states[random.next_bounded_i32(self.low_states.len() as i32) as usize];
|
|
}
|
|
if random.next_f32() < self.high_chance {
|
|
return self.high_states
|
|
[random.next_bounded_i32(self.high_states.len() as i32) as usize];
|
|
}
|
|
self.default_state
|
|
}
|
|
}
|