From b12494d72b6b5a23c7d755579c364fed6394b06a Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Mon, 19 Jul 2021 02:42:10 +0100 Subject: [PATCH] More cavern details --- common/src/terrain/block.rs | 3 +- common/src/terrain/chonk.rs | 8 +- voxygen/src/scene/terrain/watcher.rs | 83 ++++++++----- world/src/layer/mod.rs | 174 ++++++++++++++++++++------- world/src/lib.rs | 5 +- 5 files changed, 195 insertions(+), 78 deletions(-) diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index 9615a46ce5..9b91989557 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -178,7 +178,8 @@ impl Block { pub fn get_glow(&self) -> Option { 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), diff --git a/common/src/terrain/chonk.rs b/common/src/terrain/chonk.rs index a71c15f41f..60e157da1e 100644 --- a/common/src/terrain/chonk.rs +++ b/common/src/terrain/chonk.rs @@ -69,8 +69,9 @@ impl Chonk { 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, &V)> + '_ { self.sub_chunks .iter() @@ -78,8 +79,7 @@ impl Chonk { .filter(|(_, sc)| sc.num_groups() > 0) .map(move |(i, sc)| { let z_offset = self.z_offset + i as i32 * SubChunkSize::::SIZE.z as i32; - sc - .vol_iter(Vec3::zero(), SubChunkSize::::SIZE.map(|e| e as i32)) + sc.vol_iter(Vec3::zero(), SubChunkSize::::SIZE.map(|e| e as i32)) .map(move |(pos, vox)| (pos + Vec3::unit_z() * z_offset, vox)) }) .flatten() diff --git a/voxygen/src/scene/terrain/watcher.rs b/voxygen/src/scene/terrain/watcher.rs index 3f067730ed..14acd1b2e6 100644 --- a/voxygen/src/scene/terrain/watcher.rs +++ b/voxygen/src/scene/terrain/watcher.rs @@ -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, diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index d7ba21b77c..e94ddc9a08 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -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(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(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(canvas: &mut Canvas, dynamic_rng: &mut R) { // Get mushroom block, if any, at a position let mut get_mushroom = |wpos: Vec3, 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::().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(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() }; diff --git a/world/src/lib.rs b/world/src/lib.rs index 2ceb854cfa..0cd1c4f8d1 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -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)]