Added caverns, fixed chunk load performance issue

This commit is contained in:
Joshua Barretto 2021-07-18 23:15:56 +01:00
parent 0f2afbec8f
commit 1cfad833c7
6 changed files with 192 additions and 12 deletions

View File

@ -644,6 +644,11 @@ impl<const AVERAGE_PALETTE: bool> VoxelImageDecoding for TriPngEncoding<AVERAGE_
g: 206,
b: 64,
},
GlowingMushroom => Rgb {
r: 50,
g: 250,
b: 250,
},
Misc => Rgb {
r: 255,
g: 0,

View File

@ -48,7 +48,8 @@ make_case_elim!(
// 0x32 <= x < 0x40 is reserved for future earths/muds/gravels/sands/etc.
Wood = 0x40,
Leaves = 0x41,
// 0x42 <= x < 0x50 is reserved for future tree parts
GlowingMushroom = 0x42,
// 0x43 <= x < 0x50 is reserved for future tree parts
// Covers all other cases (we sometimes have bizarrely coloured misc blocks, and also we
// often want to experiment with new kinds of block without allocating them a
// dedicated block kind.

View File

@ -106,6 +106,7 @@ native-dialog = { version = "0.5.2", optional = true }
num = "0.4"
ordered-float = { version = "2.0.1", default-features = false }
rand = "0.8"
rand_chacha = "0.3"
rayon = "1.5"
rodio = {version = "0.14", default-features = false, features = ["vorbis"]}
ron = {version = "0.6", default-features = false}

View File

@ -5,6 +5,7 @@ use common::{
};
use common_base::span;
use rand::prelude::*;
use rand_chacha::ChaCha8Rng;
use vek::*;
#[derive(Copy, Clone, Debug)]
@ -60,6 +61,8 @@ impl BlocksOfInterest {
let mut cricket3 = Vec::new();
let mut frogs = Vec::new();
let mut rng = ChaCha8Rng::from_seed(thread_rng().gen());
chunk
.vol_iter(
Vec3::new(0, 0, chunk.get_min_z()),
@ -71,13 +74,13 @@ impl BlocksOfInterest {
)
.for_each(|(pos, block)| {
match block.kind() {
BlockKind::Leaves if thread_rng().gen_range(0..16) == 0 => leaves.push(pos),
BlockKind::WeakRock if thread_rng().gen_range(0..6) == 0 => drip.push(pos),
BlockKind::Leaves if rng.gen_range(0..16) == 0 => leaves.push(pos),
BlockKind::WeakRock if rng.gen_range(0..6) == 0 => drip.push(pos),
BlockKind::Grass => {
if thread_rng().gen_range(0..16) == 0 {
if rng.gen_range(0..16) == 0 {
grass.push(pos);
}
match thread_rng().gen_range(0..8192) {
match rng.gen_range(0..8192) {
1 => cricket1.push(pos),
2 => cricket2.push(pos),
3 => cricket3.push(pos),
@ -85,14 +88,12 @@ impl BlocksOfInterest {
}
},
BlockKind::Water
if chunk.meta().contains_river() && thread_rng().gen_range(0..16) == 0 =>
if chunk.meta().contains_river() && rng.gen_range(0..16) == 0 =>
{
river.push(pos)
},
BlockKind::Lava if thread_rng().gen_range(0..5) == 0 => {
fires.push(pos + Vec3::unit_z())
},
BlockKind::Snow if thread_rng().gen_range(0..16) == 0 => snow.push(pos),
BlockKind::Lava if rng.gen_range(0..5) == 0 => fires.push(pos + Vec3::unit_z()),
BlockKind::Snow if rng.gen_range(0..16) == 0 => snow.push(pos),
_ => match block.get_sprite() {
Some(SpriteKind::Ember) => {
fires.push(pos);
@ -114,7 +115,7 @@ impl BlocksOfInterest {
Some(SpriteKind::Reed) => {
reeds.push(pos);
fireflies.push(pos);
if thread_rng().gen_range(0..12) == 0 {
if rng.gen_range(0..12) == 0 {
frogs.push(pos);
}
},

View File

@ -10,7 +10,7 @@ pub use self::{
use crate::{
column::ColumnSample,
util::{FastNoise, RandomField, Sampler},
util::{FastNoise, RandomField, Sampler, RandomPerm},
Canvas, IndexRef,
};
use common::{
@ -27,6 +27,7 @@ use std::{
f32,
ops::{Mul, Range, Sub},
};
use hashbrown::HashMap;
use vek::*;
#[derive(Deserialize)]
@ -570,3 +571,173 @@ pub fn apply_coral_to(canvas: &mut Canvas) {
}
});
}
pub fn apply_caverns_to<R: Rng>(canvas: &mut Canvas, dynamic_rng: &mut R) {
let info = canvas.info();
// Get cavern attributes at a position
let cavern_at = |wpos2d| {
let alt = info.land().get_alt_approx(wpos2d);
// Horizontal average scale of caverns
let scale = 2048.0;
// How common should they be? (0.0 - 1.0)
let common = 0.15;
// Range of heights for the caverns
let height_range = 48.0..300.0;
// Minimum distance below the surface
let surface_clearance = 64.0;
let cavern_avg_height = Lerp::lerp(
height_range.start,
height_range.end,
info.index().noise.cave_nz.get((wpos2d.map(|e| e as f64) / 128.0).into_array()) as f32 * 0.5 + 0.5,
);
let cavern_avg_alt = alt * 0.25 - height_range.end - surface_clearance;
let cavern_nz = info.index().noise.cave_nz.get((wpos2d.map(|e| e as f64) / scale).into_array()) as f32;
let cavern_height = ((cavern_nz * 0.5 + 0.5 - (1.0 - common)).max(0.0) / common).powf(common * 2.0) * cavern_avg_height;
// Stalagtites
let stalagtite = info.index().noise.cave_nz
.get(wpos2d.map(|e| e as f64 * 0.015).into_array())
.sub(0.5)
.max(0.0)
.mul((cavern_height as f64 - 5.0).mul(0.15).clamped(0.0, 1.0))
.mul(32.0 + cavern_avg_height as f64 * 0.85);
let lake = info.index().noise.cave_nz
.get(wpos2d.map(|e| e as f64 * 0.01).into_array())
.sub(0.5)
.max(0.0)
.mul(2.0)
.mul(80.0);
let lake = 0.0;
let rugged = 0.25; // How bumpy should the floor be relative to the ceiling?
let cavern_bottom = (cavern_avg_alt - cavern_height * rugged) as i32;
let cavern_avg_bottom = (cavern_avg_alt - ((height_range.start + height_range.end) * 0.5) * rugged) as i32;
let cavern_top = (cavern_avg_alt + cavern_height) as i32;
let cavern_avg_top = (cavern_avg_alt + cavern_avg_height) as i32;
// Stalagmites rise up to meet stalagtites
let stalagmite = stalagtite * 0.3;
let floor = stalagmite as i32;
(cavern_bottom, cavern_top, cavern_avg_bottom, cavern_avg_top, floor, lake, stalagtite)
};
let mut mushroom_cache = HashMap::new();
struct Mushroom {
pos: Vec3<i32>,
stalk: f32,
head_color: Rgb<u8>,
}
// Get mushroom block, if any, at a position
let mut get_mushroom = |wpos: Vec3<i32>, dynamic_rng: &mut R| {
for (wpos2d, seed) in info.chunks().gen_ctx.structure_gen.get(wpos.xy()) {
let mushroom = if let Some(mushroom) = mushroom_cache
.entry(wpos2d)
.or_insert_with(|| {
let mut rng = RandomPerm::new(seed);
let (cavern_bottom, _, _, _, floor, _, _) = cavern_at(wpos2d);
if rng.gen_bool(0.1) {
Some(Mushroom {
pos: wpos2d.with_z(cavern_bottom + floor),
stalk: rng.gen_range(8.0..26.0),
head_color: Rgb::new(50, rng.gen_range(70..110), rng.gen_range(100..200)),
})
} else {
None
}
})
{
mushroom
} else {
continue
};
let wposf = wpos.map(|e| e as f64);
let warp_freq = 1.0 / 32.0;
let warp_amp = Vec3::new(8.0, 8.0, 12.0);
let wposf_warped = wposf.map(|e| e as f32) + Vec3::new(
FastNoise::new(seed + 0).get(wposf * warp_freq) as f32,
FastNoise::new(seed + 1).get(wposf * warp_freq) as f32,
FastNoise::new(seed + 2).get(wposf * warp_freq) as f32,
) * warp_amp;
let rpos = wposf_warped - mushroom.pos.map(|e| e as f32).map(|e| e as f32);
let stalk_radius = 2.0f32;
let head_radius = 12.0f32;
let head_height = 10.0;
let dist_sq = rpos.xy().magnitude_squared();
if dist_sq < head_radius.powi(2) {
let dist = dist_sq.sqrt();
// Head
if rpos.z > mushroom.stalk && rpos.z < mushroom.stalk + head_height && dist < head_radius * (1.0 - (rpos.z - mushroom.stalk) / head_height).powf(0.125) {
return Some(Block::new(BlockKind::GlowingMushroom, mushroom.head_color));
} else if rpos.z <= mushroom.stalk && rpos.xy().magnitude_squared() < stalk_radius.powi(2) { // Stalk
return Some(Block::new(BlockKind::Wood, Rgb::new(50, 120, 180)));
} else if ((mushroom.stalk - 1.0)..mushroom.stalk).contains(&rpos.z) // Hanging orbs
&& ((head_radius * 0.5)..(head_radius * 0.8)).contains(&dist)
&& dynamic_rng.gen_bool(0.025)
{
return Some(Block::air(SpriteKind::Orb));
}
}
}
None
};
canvas.foreach_col(|canvas, wpos2d, _col| {
let (cavern_bottom, cavern_top, cavern_avg_bottom, cavern_avg_top, floor, lake, stalagtite) = cavern_at(wpos2d);
let mini_stalagtite = info.index().noise.cave_nz
.get(wpos2d.map(|e| e as f64 * 0.08).into_array())
.sub(0.5)
.max(0.0)
.mul(((cavern_top - cavern_bottom) as f64 - 5.0).mul(0.15).clamped(0.0, 1.0))
.mul(24.0 + (cavern_avg_top - cavern_avg_bottom) as f64 * 0.2);
let stalagtite_height = (stalagtite + mini_stalagtite) as i32;
let cavern_top = cavern_top as i32;
let lower_bound = cavern_bottom - lake as i32;
let mut on_ground = true;
for z in lower_bound..cavern_top {
use SpriteKind::*;
let wpos = wpos2d.with_z(z);
let block = if z < lower_bound + floor {
Block::new(BlockKind::WeakRock, Rgb::new(110, 120, 150))
} else if z > cavern_top - stalagtite_height {
if dynamic_rng.gen_bool(0.0035) { // Glowing rock in stalagtites
Block::new(BlockKind::GlowingRock, Rgb::new(30, 150, 120))
} else {
Block::new(BlockKind::WeakRock, Rgb::new(110, 120, 150))
}
} else if let Some(mushroom_block) = get_mushroom(wpos, dynamic_rng) {
mushroom_block
} else if z < cavern_avg_bottom as i32 + 16 {
Block::water(SpriteKind::Empty)
} else if z == cavern_bottom + floor && dynamic_rng.gen_bool(0.005) && on_ground {
Block::air(*[CrystalLow, CaveMushroom].choose(dynamic_rng).unwrap())
} else if z == cavern_top - 1 && dynamic_rng.gen_bool(0.01) {
Block::air(*[CrystalHigh, CeilingMushroom, Orb].choose(dynamic_rng).unwrap())
} else {
Block::empty()
};
on_ground |= block.is_solid();
let _ = canvas.set(wpos, block);
}
});
}

View File

@ -346,6 +346,7 @@ impl World {
entities: Vec::new(),
};
layer::apply_caverns_to(&mut canvas, &mut dynamic_rng);
layer::apply_caves_to(&mut canvas, &mut dynamic_rng);
layer::apply_shrubs_to(&mut canvas, &mut dynamic_rng);
layer::apply_trees_to(&mut canvas, &mut dynamic_rng);