mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
More cavern details
This commit is contained in:
parent
ed40e92801
commit
bfc55d7b17
@ -178,7 +178,8 @@ impl Block {
|
||||
pub fn get_glow(&self) -> Option<u8> {
|
||||
match self.kind() {
|
||||
BlockKind::Lava => Some(24),
|
||||
BlockKind::GlowingRock | BlockKind::GlowingWeakRock => Some(12),
|
||||
BlockKind::GlowingRock | BlockKind::GlowingWeakRock => Some(15),
|
||||
BlockKind::BlockKind::GlowingMushroom => Some(20),
|
||||
_ => match self.get_sprite()? {
|
||||
SpriteKind::StreetLamp | SpriteKind::StreetLampTall => Some(24),
|
||||
SpriteKind::Ember => Some(20),
|
||||
|
@ -69,8 +69,9 @@ impl<V, S: RectVolSize, M: Clone> Chonk<V, S, M> {
|
||||
self.sub_chunks.iter().map(SubChunk::num_groups).sum()
|
||||
}
|
||||
|
||||
/// Iterate through the voxels in this chunk, attempting to avoid those that are unchanged (i.e: match the `below`
|
||||
/// and `above` voxels). This is generally useful for performance reasons.
|
||||
/// Iterate through the voxels in this chunk, attempting to avoid those that
|
||||
/// are unchanged (i.e: match the `below` and `above` voxels). This is
|
||||
/// generally useful for performance reasons.
|
||||
pub fn iter_changed(&self) -> impl Iterator<Item = (Vec3<i32>, &V)> + '_ {
|
||||
self.sub_chunks
|
||||
.iter()
|
||||
@ -78,8 +79,7 @@ impl<V, S: RectVolSize, M: Clone> Chonk<V, S, M> {
|
||||
.filter(|(_, sc)| sc.num_groups() > 0)
|
||||
.map(move |(i, sc)| {
|
||||
let z_offset = self.z_offset + i as i32 * SubChunkSize::<S>::SIZE.z as i32;
|
||||
sc
|
||||
.vol_iter(Vec3::zero(), SubChunkSize::<S>::SIZE.map(|e| e as i32))
|
||||
sc.vol_iter(Vec3::zero(), SubChunkSize::<S>::SIZE.map(|e| e as i32))
|
||||
.map(move |(pos, vox)| (pos + Vec3::unit_z() * z_offset, vox))
|
||||
})
|
||||
.flatten()
|
||||
|
@ -60,27 +60,54 @@ impl BlocksOfInterest {
|
||||
|
||||
let mut rng = ChaCha8Rng::from_seed(thread_rng().gen());
|
||||
|
||||
chunk
|
||||
.iter_changed()
|
||||
.for_each(|(pos, block)| {
|
||||
match block.kind() {
|
||||
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 rng.gen_range(0..16) == 0 {
|
||||
grass.push(pos);
|
||||
}
|
||||
match rng.gen_range(0..8192) {
|
||||
1 => cricket1.push(pos),
|
||||
2 => cricket2.push(pos),
|
||||
3 => cricket3.push(pos),
|
||||
_ => {},
|
||||
chunk.iter_changed().for_each(|(pos, block)| {
|
||||
match block.kind() {
|
||||
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 rng.gen_range(0..16) == 0 {
|
||||
grass.push(pos);
|
||||
}
|
||||
match rng.gen_range(0..8192) {
|
||||
1 => cricket1.push(pos),
|
||||
2 => cricket2.push(pos),
|
||||
3 => cricket3.push(pos),
|
||||
_ => {},
|
||||
}
|
||||
},
|
||||
BlockKind::Water if chunk.meta().contains_river() && rng.gen_range(0..16) == 0 => {
|
||||
river.push(pos)
|
||||
},
|
||||
BlockKind::Snow if rng.gen_range(0..16) == 0 => snow.push(pos),
|
||||
_ => match block.get_sprite() {
|
||||
Some(SpriteKind::Ember) => {
|
||||
fires.push(pos);
|
||||
smokers.push(pos);
|
||||
},
|
||||
// Offset positions to account for block height.
|
||||
// TODO: Is this a good idea?
|
||||
Some(SpriteKind::StreetLamp) => fire_bowls.push(pos + Vec3::unit_z() * 2),
|
||||
Some(SpriteKind::FireBowlGround) => fire_bowls.push(pos + Vec3::unit_z()),
|
||||
Some(SpriteKind::StreetLampTall) => fire_bowls.push(pos + Vec3::unit_z() * 4),
|
||||
Some(SpriteKind::WallSconce) => fire_bowls.push(pos + Vec3::unit_z()),
|
||||
Some(SpriteKind::Beehive) => beehives.push(pos),
|
||||
Some(SpriteKind::CrystalHigh) => fireflies.push(pos),
|
||||
Some(SpriteKind::Reed) => {
|
||||
reeds.push(pos);
|
||||
fireflies.push(pos);
|
||||
if rng.gen_range(0..12) == 0 {
|
||||
frogs.push(pos);
|
||||
}
|
||||
},
|
||||
BlockKind::Water
|
||||
if chunk.meta().contains_river() && rng.gen_range(0..16) == 0 =>
|
||||
{
|
||||
river.push(pos)
|
||||
Some(SpriteKind::CaveMushroom) => fireflies.push(pos),
|
||||
Some(SpriteKind::PinkFlower) => flowers.push(pos),
|
||||
Some(SpriteKind::PurpleFlower) => flowers.push(pos),
|
||||
Some(SpriteKind::RedFlower) => flowers.push(pos),
|
||||
Some(SpriteKind::WhiteFlower) => flowers.push(pos),
|
||||
Some(SpriteKind::YellowFlower) => flowers.push(pos),
|
||||
Some(SpriteKind::Sunflower) => flowers.push(pos),
|
||||
Some(SpriteKind::CraftingBench) => {
|
||||
interactables.push((pos, Interaction::Craft(CraftingTab::All)))
|
||||
},
|
||||
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),
|
||||
@ -143,14 +170,16 @@ impl BlocksOfInterest {
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
}
|
||||
if block.is_collectible() {
|
||||
interactables.push((pos, Interaction::Collect));
|
||||
}
|
||||
if let Some(glow) = block.get_glow() {
|
||||
lights.push((pos, glow));
|
||||
}
|
||||
});
|
||||
_ => {},
|
||||
},
|
||||
}
|
||||
if block.is_collectible() {
|
||||
interactables.push((pos, Interaction::Collect));
|
||||
}
|
||||
if let Some(glow) = block.get_glow() {
|
||||
lights.push((pos, glow));
|
||||
}
|
||||
});
|
||||
|
||||
Self {
|
||||
leaves,
|
||||
|
@ -10,8 +10,8 @@ pub use self::{
|
||||
|
||||
use crate::{
|
||||
column::ColumnSample,
|
||||
util::{FastNoise, RandomField, Sampler, RandomPerm},
|
||||
config::CONFIG,
|
||||
util::{FastNoise, RandomField, RandomPerm, Sampler},
|
||||
Canvas, IndexRef,
|
||||
};
|
||||
use common::{
|
||||
@ -21,6 +21,7 @@ use common::{
|
||||
terrain::{Block, BlockKind, SpriteKind},
|
||||
vol::{BaseVol, ReadVol, RectSizedVol, WriteVol},
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use noise::NoiseFn;
|
||||
use rand::prelude::*;
|
||||
use serde::Deserialize;
|
||||
@ -28,7 +29,6 @@ use std::{
|
||||
f32,
|
||||
ops::{Mul, Range, Sub},
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use vek::*;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@ -592,27 +592,48 @@ pub fn apply_caverns_to<R: Rng>(canvas: &mut Canvas, dynamic_rng: &mut R) {
|
||||
let cavern_avg_height = Lerp::lerp(
|
||||
height_range.start,
|
||||
height_range.end,
|
||||
info.index().noise.cave_nz.get((wpos2d.map(|e| e as f64) / 300.0).into_array()) as f32 * 0.5 + 0.5,
|
||||
info.index()
|
||||
.noise
|
||||
.cave_nz
|
||||
.get((wpos2d.map(|e| e as f64) / 300.0).into_array()) as f32
|
||||
* 0.5
|
||||
+ 0.5,
|
||||
);
|
||||
|
||||
let cavern_avg_alt = CONFIG.sea_level.min(alt * 0.25) - height_range.end - surface_clearance;
|
||||
let cavern_avg_alt =
|
||||
CONFIG.sea_level.min(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 = ((cavern_nz * 0.5 + 0.5 - (1.0 - common)).max(0.0) / common).powf(common * 2.0);
|
||||
let cavern_nz = info
|
||||
.index()
|
||||
.noise
|
||||
.cave_nz
|
||||
.get((wpos2d.map(|e| e as f64) / scale).into_array()) as f32;
|
||||
let cavern =
|
||||
((cavern_nz * 0.5 + 0.5 - (1.0 - common)).max(0.0) / common).powf(common * 2.0);
|
||||
let cavern_height = cavern * cavern_avg_height;
|
||||
|
||||
// Stalagtites
|
||||
let stalagtite = info.index().noise.cave_nz
|
||||
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);
|
||||
|
||||
let hill = info.index().noise.cave_nz.get((wpos2d.map(|e| e as f64) / 96.0).into_array()) as f32 * cavern * 24.0;
|
||||
let hill = info
|
||||
.index()
|
||||
.noise
|
||||
.cave_nz
|
||||
.get((wpos2d.map(|e| e as f64) / 96.0).into_array()) as f32
|
||||
* cavern
|
||||
* 24.0;
|
||||
let rugged = 0.4; // How bumpy should the floor be relative to the ceiling?
|
||||
let cavern_bottom = (cavern_avg_alt - cavern_height * rugged + hill) as i32;
|
||||
let cavern_avg_bottom = (cavern_avg_alt - ((height_range.start + height_range.end) * 0.5) * 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;
|
||||
|
||||
@ -621,7 +642,14 @@ pub fn apply_caverns_to<R: Rng>(canvas: &mut Canvas, dynamic_rng: &mut R) {
|
||||
|
||||
let floor = stalagmite as i32;
|
||||
|
||||
(cavern_bottom, cavern_top, cavern_avg_bottom, cavern_avg_top, floor, stalagtite)
|
||||
(
|
||||
cavern_bottom,
|
||||
cavern_top,
|
||||
cavern_avg_bottom,
|
||||
cavern_avg_top,
|
||||
floor,
|
||||
stalagtite,
|
||||
)
|
||||
};
|
||||
|
||||
let mut mushroom_cache = HashMap::new();
|
||||
@ -635,53 +663,76 @@ pub fn apply_caverns_to<R: Rng>(canvas: &mut Canvas, dynamic_rng: &mut R) {
|
||||
// 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 mushroom = if let Some(mushroom) =
|
||||
mushroom_cache.entry(wpos2d).or_insert_with(|| {
|
||||
let mut rng = RandomPerm::new(seed);
|
||||
let (cavern_bottom, cavern_top, _, _, floor, _) = cavern_at(wpos2d);
|
||||
if rng.gen_bool(0.1) && cavern_top - cavern_bottom > 32 {
|
||||
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)),
|
||||
stalk: 12.0 + rng.gen::<f32>().powf(2.0) * 35.0,
|
||||
head_color: Rgb::new(
|
||||
50,
|
||||
rng.gen_range(70..110),
|
||||
rng.gen_range(100..200),
|
||||
),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
{
|
||||
}) {
|
||||
mushroom
|
||||
} else {
|
||||
continue
|
||||
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 warp_amp = Vec3::new(12.0, 12.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
|
||||
* (wposf.z as f32 - mushroom.pos.z as f32)
|
||||
.mul(0.1)
|
||||
.clamped(0.0, 1.0);
|
||||
|
||||
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 stalk_radius = 2.5f32;
|
||||
let head_radius = 18.0f32;
|
||||
let head_height = 16.0;
|
||||
|
||||
let dist_sq = rpos.xy().magnitude_squared();
|
||||
if dist_sq < head_radius.powi(2) {
|
||||
let dist = dist_sq.sqrt();
|
||||
let head_dist = ((rpos - Vec3::unit_z() * mushroom.stalk)
|
||||
/ Vec2::broadcast(head_radius).with_z(head_height))
|
||||
.magnitude();
|
||||
// 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)));
|
||||
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)
|
||||
{
|
||||
if head_dist < 0.85 {
|
||||
return Some(Block::new(BlockKind::GlowingMushroom, Rgb::new(90, 50, 0)));
|
||||
} else if head_dist < 1.0 {
|
||||
return Some(Block::new(BlockKind::Wood, mushroom.head_color));
|
||||
}
|
||||
}
|
||||
|
||||
if rpos.z <= mushroom.stalk
|
||||
&& dist_sq
|
||||
< (stalk_radius * Lerp::lerp(1.5, 0.75, rpos.z / mushroom.stalk)).powi(2)
|
||||
{
|
||||
// Stalk
|
||||
return Some(Block::new(BlockKind::Wood, Rgb::new(25, 60, 90)));
|
||||
} else if ((mushroom.stalk - 0.5)..mushroom.stalk).contains(&rpos.z) // Hanging orbs
|
||||
&& ((head_radius * 0.5)..(head_radius * 0.8)).contains(&dist)
|
||||
&& dynamic_rng.gen_bool(0.025)
|
||||
&& ((head_radius * 0.7)..(head_radius * 0.9)).contains(&dist)
|
||||
&& dynamic_rng.gen_bool(0.1)
|
||||
{
|
||||
return Some(Block::air(SpriteKind::Orb));
|
||||
}
|
||||
@ -692,39 +743,76 @@ pub fn apply_caverns_to<R: Rng>(canvas: &mut Canvas, dynamic_rng: &mut R) {
|
||||
};
|
||||
|
||||
canvas.foreach_col(|canvas, wpos2d, _col| {
|
||||
let (cavern_bottom, cavern_top, cavern_avg_bottom, cavern_avg_top, floor, stalagtite) = cavern_at(wpos2d);
|
||||
let (cavern_bottom, cavern_top, cavern_avg_bottom, cavern_avg_top, floor, stalagtite) =
|
||||
cavern_at(wpos2d);
|
||||
|
||||
let mini_stalagtite = info.index().noise.cave_nz
|
||||
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(
|
||||
((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 water_level = cavern_avg_bottom as i32 + 16;
|
||||
|
||||
let cavern_top = cavern_top as i32;
|
||||
let mut on_ground = true;
|
||||
for z in cavern_bottom..cavern_top {
|
||||
for z in cavern_bottom - 1..cavern_top {
|
||||
use SpriteKind::*;
|
||||
|
||||
let wpos = wpos2d.with_z(z);
|
||||
|
||||
let block = if z < cavern_bottom + floor {
|
||||
let block = if z < cavern_bottom {
|
||||
if z > water_level + dynamic_rng.gen_range(4..16) {
|
||||
Block::new(BlockKind::Leaves, Rgb::new(40, 85, 70))
|
||||
} else {
|
||||
Block::new(BlockKind::Rock, Rgb::new(50, 40, 10))
|
||||
}
|
||||
} else if z < cavern_bottom + 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
|
||||
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 + 4 {
|
||||
} else if z < water_level {
|
||||
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_bottom + floor && dynamic_rng.gen_bool(0.025) && on_ground {
|
||||
Block::air(
|
||||
*[
|
||||
CrystalLow,
|
||||
CaveMushroom,
|
||||
LeafyPlant,
|
||||
Fern,
|
||||
Reed,
|
||||
Pyrebloom,
|
||||
Moonbell,
|
||||
Welwitch,
|
||||
LongGrass,
|
||||
MediumGrass,
|
||||
ShortGrass,
|
||||
]
|
||||
.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())
|
||||
Block::air(
|
||||
*[CrystalHigh, CeilingMushroom, Orb]
|
||||
.choose(dynamic_rng)
|
||||
.unwrap(),
|
||||
)
|
||||
} else {
|
||||
Block::empty()
|
||||
};
|
||||
|
@ -52,11 +52,10 @@ use common::{
|
||||
vol::{ReadVol, RectVolSize, WriteVol},
|
||||
};
|
||||
use common_net::msg::{world_msg, WorldMapMsg};
|
||||
use rand::Rng;
|
||||
use rand::{prelude::*, Rng};
|
||||
use rand_chacha::ChaCha8Rng;
|
||||
use serde::Deserialize;
|
||||
use std::time::Duration;
|
||||
use rand_chacha::ChaCha8Rng;
|
||||
use rand::prelude::*;
|
||||
use vek::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
Loading…
Reference in New Issue
Block a user