Added rare structures, totally refactored structure spawning

This commit is contained in:
Joshua Barretto 2019-07-07 22:31:47 +01:00
parent 29803524fd
commit 44b5473a82
17 changed files with 171 additions and 82 deletions

BIN
assets/world/structure/natural/Witch_Hut.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/structure/natural/tower_ruin.vox (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -31,7 +31,7 @@ float get_sun_brightness(vec3 sun_dir) {
const float PERSISTENT_AMBIANCE = 0.008; const float PERSISTENT_AMBIANCE = 0.008;
vec3 get_sun_diffuse(vec3 norm, float time_of_day) { vec3 get_sun_diffuse(vec3 norm, float time_of_day) {
const float SUN_AMBIANCE = 0.1; const float SUN_AMBIANCE = 0.075;
vec3 sun_dir = get_sun_dir(time_of_day); vec3 sun_dir = get_sun_dir(time_of_day);

View File

@ -1,6 +1,7 @@
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub enum ForestKind { pub enum ForestKind {
Palm, Palm,
Savannah,
Oak, Oak,
Pine, Pine,
SnowPine, SnowPine,

View File

@ -1,4 +1,4 @@
mod tree; mod natural;
use crate::{ use crate::{
column::{ColumnGen, ColumnSample}, column::{ColumnGen, ColumnSample},
@ -6,7 +6,7 @@ use crate::{
World, World,
}; };
use common::{ use common::{
terrain::{structure::StructureBlock, Block}, terrain::{structure::StructureBlock, Block, Structure},
vol::{ReadVol, Vox}, vol::{ReadVol, Vox},
}; };
use noise::NoiseFn; use noise::NoiseFn;
@ -86,19 +86,34 @@ impl<'a> BlockGen<'a> {
let sample = Self::sample_column(column_gen, column_cache, wpos)?; let sample = Self::sample_column(column_gen, column_cache, wpos)?;
// Tree samples // Tree samples
let mut tree_samples = [None, None, None, None, None, None, None, None, None]; let mut structure_samples = [None, None, None, None, None, None, None, None, None];
for i in 0..tree_samples.len() { for i in 0..structure_samples.len() {
tree_samples[i] = Self::sample_column( let st_sample = Self::sample_column(
column_gen, column_gen,
column_cache, column_cache,
Vec2::from(sample.close_trees[i].0), Vec2::from(sample.close_trees[i].0),
); );
structure_samples[i] = st_sample;
} }
Some(ZCache { let mut structures = [None, None, None, None, None, None, None, None, None];
sample, for i in 0..structures.len() {
tree_samples, let (st_pos, st_seed) = sample.close_trees[i];
}) let st_info = natural::structure_gen(
column_gen,
column_cache,
i,
st_pos,
st_seed,
&structure_samples,
);
if let (Some(st_info), Some(st_sample)) = (st_info, structure_samples[i].clone()) {
structures[i] = Some((st_info, st_sample));
}
}
Some(ZCache { sample, structures })
} }
pub fn get_with_z_cache(&mut self, wpos: Vec3<i32>, z_cache: Option<&ZCache>) -> Option<Block> { pub fn get_with_z_cache(&mut self, wpos: Vec3<i32>, z_cache: Option<&ZCache>) -> Option<Block> {
@ -128,9 +143,8 @@ impl<'a> BlockGen<'a> {
.. ..
} = &z_cache?.sample; } = &z_cache?.sample;
let tree_samples = &z_cache?.tree_samples; let structures = &z_cache?.structures;
let wpos2d = Vec2::from(wpos);
let wposf = wpos.map(|e| e as f64); let wposf = wpos.map(|e| e as f64);
let (definitely_underground, height, water_height) = let (definitely_underground, height, water_height) =
@ -193,7 +207,7 @@ impl<'a> BlockGen<'a> {
// let warm_stone = Block::new(1, Rgb::new(165, 165, 130)); // let warm_stone = Block::new(1, Rgb::new(165, 165, 130));
let water = Block::new(1, Rgb::new(100, 150, 255)); let water = Block::new(1, Rgb::new(100, 150, 255));
let grass_depth = 2.0; let grass_depth = 1.5 + 2.0 * chaos;
let block = if (wposf.z as f32) < height - grass_depth { let block = if (wposf.z as f32) < height - grass_depth {
let col = Lerp::lerp( let col = Lerp::lerp(
sub_surface_color.map(|e| (e * 255.0) as u8), sub_surface_color.map(|e| (e * 255.0) as u8),
@ -307,55 +321,32 @@ impl<'a> BlockGen<'a> {
let block = if definitely_underground { let block = if definitely_underground {
block.unwrap_or(Block::empty()) block.unwrap_or(Block::empty())
} else { } else {
match block {
Some(block) => block,
None => (&close_trees).iter().enumerate().fold(
air,
|block, (tree_idx, (tree_pos, tree_seed))| {
if !block.is_empty() {
block block
} else { .or_else(|| {
match &tree_samples[tree_idx] { structures.iter().find_map(|st| {
Some(tree_sample) let (st, st_sample) = st.as_ref()?;
if wpos2d.distance_squared(*tree_pos) < 28 * 28
&& tree_sample.tree_density
> 0.5 + (*tree_seed as f32 / 1000.0).fract() * 0.2
&& tree_sample.alt > tree_sample.water_level
&& tree_sample.spawn_rate > 0.5 =>
{
let cliff_height = Self::get_cliff_height(
column_gen,
column_cache,
tree_pos.map(|e| e as f32),
&tree_sample.close_cliffs,
cliff_hill,
);
let height = tree_sample.alt.max(cliff_height);
let tree_pos3d =
Vec3::new(tree_pos.x, tree_pos.y, height as i32);
let rpos = wpos - tree_pos3d;
let trees = tree::kinds(tree_sample.forest_kind); // Choose tree kind let rpos = wpos - st.pos;
let block_pos = Vec3::unit_z() * rpos.z
+ Vec3::from(st.units.0) * rpos.x
+ Vec3::from(st.units.1) * rpos.y;
block.or(trees[*tree_seed as usize % trees.len()] st.volume
.get((rpos * 128) / 128) // Scaling .get((block_pos * 128) / 128) // Scaling
.map(|b| { .map(|b| {
block_from_structure( block_from_structure(
*b, *b,
rpos, block_pos,
*tree_pos, st.pos.into(),
*tree_seed, st.seed,
&tree_sample, st_sample,
) )
}) })
.unwrap_or(Block::empty())) .ok()
} .filter(|block| !block.is_empty())
_ => block, })
} })
} .unwrap_or(Block::empty())
},
),
}
}; };
Some(block) Some(block)
@ -364,7 +355,14 @@ impl<'a> BlockGen<'a> {
pub struct ZCache<'a> { pub struct ZCache<'a> {
sample: ColumnSample<'a>, sample: ColumnSample<'a>,
tree_samples: [Option<ColumnSample<'a>>; 9], structures: [Option<(StructureInfo, ColumnSample<'a>)>; 9],
}
pub struct StructureInfo {
pos: Vec3<i32>,
seed: u32,
units: (Vec2<i32>, Vec2<i32>),
volume: &'static Structure,
} }
impl<'a> SamplerMut for BlockGen<'a> { impl<'a> SamplerMut for BlockGen<'a> {

View File

@ -1,16 +1,65 @@
use crate::all::ForestKind; use super::{BlockGen, StructureInfo, ZCache};
use crate::{
all::ForestKind,
column::{ColumnGen, ColumnSample},
util::{HashCache, RandomPerm, Sampler},
};
use common::{assets, terrain::Structure}; use common::{assets, terrain::Structure};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use std::sync::Arc; use std::sync::Arc;
use vek::*; use vek::*;
pub fn kinds(forest_kind: ForestKind) -> &'static [Arc<Structure>] { static VOLUME_RAND: RandomPerm = RandomPerm::new(0);
match forest_kind { static UNIT_RAND: RandomPerm = RandomPerm::new(1);
static QUIRKY_RAND: RandomPerm = RandomPerm::new(2);
pub fn structure_gen<'a>(
column_gen: &ColumnGen<'a>,
column_cache: &mut HashCache<Vec2<i32>, Option<ColumnSample<'a>>>,
idx: usize,
st_pos: Vec2<i32>,
st_seed: u32,
structure_samples: &[Option<ColumnSample>; 9],
) -> Option<StructureInfo> {
let st_sample = &structure_samples[idx].as_ref()?;
// Assuming it's a tree... figure out when it SHOULDN'T spawn
if st_sample.tree_density < 0.5 + (st_seed as f32 / 1000.0).fract() * 0.2
|| st_sample.alt < st_sample.water_level
|| st_sample.spawn_rate < 0.5
{
return None;
}
let cliff_height = BlockGen::get_cliff_height(
column_gen,
column_cache,
st_pos.map(|e| e as f32),
&st_sample.close_cliffs,
st_sample.cliff_hill,
);
let wheight = st_sample.alt.max(cliff_height);
let st_pos3d = Vec3::new(st_pos.x, st_pos.y, wheight as i32);
let volumes: &'static [_] = match st_sample.forest_kind {
ForestKind::Palm => &PALMS, ForestKind::Palm => &PALMS,
ForestKind::Savannah => &PALMS,
ForestKind::Oak if QUIRKY_RAND.get(st_seed) % 64 == 0 => &QUIRKY,
ForestKind::Oak if QUIRKY_RAND.get(st_seed) % 16 == 0 => &OAK_STUMPS,
ForestKind::Oak => &OAKS, ForestKind::Oak => &OAKS,
ForestKind::Pine => &PINES, ForestKind::Pine => &PINES,
ForestKind::SnowPine => &SNOW_PINES, ForestKind::SnowPine => &SNOW_PINES,
} };
const UNIT_CHOICES: [(Vec2<i32>, Vec2<i32>); 1] = [(Vec2 { x: 1, y: 0 }, Vec2 { x: 0, y: 1 })];
Some(StructureInfo {
pos: st_pos3d,
seed: st_seed,
units: UNIT_CHOICES[UNIT_RAND.get(st_seed) as usize % UNIT_CHOICES.len()],
volume: &volumes[VOLUME_RAND.get(st_seed) as usize % volumes.len()],
})
} }
lazy_static! { lazy_static! {
@ -43,6 +92,9 @@ lazy_static! {
assets::load_map("world/tree/oak_green/9.vox", |s: Structure| s assets::load_map("world/tree/oak_green/9.vox", |s: Structure| s
.with_center(Vec3::new(26, 26, 14))) .with_center(Vec3::new(26, 26, 14)))
.unwrap(), .unwrap(),
];
pub static ref OAK_STUMPS: Vec<Arc<Structure>> = vec![
// oak stumps // oak stumps
assets::load_map("world/tree/oak_stump/1.vox", |s: Structure| s assets::load_map("world/tree/oak_stump/1.vox", |s: Structure| s
.with_center(Vec3::new(15, 18, 10))) .with_center(Vec3::new(15, 18, 10)))
@ -350,4 +402,10 @@ lazy_static! {
.unwrap(), .unwrap(),
]; ];
*/ */
pub static ref QUIRKY: Vec<Arc<Structure>> = vec![
assets::load_map("world/structure/natural/tower_ruin.vox", |s: Structure| s
.with_center(Vec3::new(11, 14, 7)))
.unwrap(),
];
} }

View File

@ -61,7 +61,7 @@ impl<'a> Sampler for ColumnGen<'a> {
let riverless_alt = sim.get_interpolated(wpos, |chunk| chunk.alt)? let riverless_alt = sim.get_interpolated(wpos, |chunk| chunk.alt)?
+ (sim.gen_ctx.small_nz.get((wposf.div(256.0)).into_array()) as f32) + (sim.gen_ctx.small_nz.get((wposf.div(256.0)).into_array()) as f32)
.abs() .abs()
.mul(chaos.max(0.2)) .mul(chaos.max(0.15))
.mul(64.0); .mul(64.0);
let cliffs = sim_chunk.cliffs; let cliffs = sim_chunk.cliffs;
@ -127,7 +127,7 @@ impl<'a> Sampler for ColumnGen<'a> {
.mul(256.0), .mul(256.0),
), ),
Rgb::lerp(tropical, sand, temp.sub(CONFIG.desert_temp).mul(32.0)), Rgb::lerp(tropical, sand, temp.sub(CONFIG.desert_temp).mul(32.0)),
temp.sub(CONFIG.tropical_temp).mul(128.0), temp.sub(CONFIG.tropical_temp).mul(64.0),
); );
// Work out if we're on a path or near a town // Work out if we're on a path or near a town

View File

@ -368,18 +368,18 @@ impl SimChunk {
.into_array(), .into_array(),
) as f32; ) as f32;
let chaos = (gen_ctx.chaos_nz.get((wposf.div(4_000.0)).into_array()) as f32) let chaos = (gen_ctx.chaos_nz.get((wposf.div(6_000.0)).into_array()) as f32)
.add(1.0) .add(1.0)
.mul(0.5) .mul(0.5)
.mul( .mul(
(gen_ctx.chaos_nz.get((wposf.div(6_000.0)).into_array()) as f32) (gen_ctx.chaos_nz.get((wposf.div(4_000.0)).into_array()) as f32)
.powf(2.0) .powf(2.0)
.add(0.5) .add(0.5)
.min(1.0), .min(1.0),
) )
.mul(temp.mul(4.0).sub(1.0).neg().add(0.25).max(0.05).min(1.0)) .add(0.1 * hill)
.powf(1.5) .mul(temp.mul(4.0).sub(1.0).neg().add(1.25).max(0.15).min(1.0))
.add(0.1 * hill); .powf(1.5);
let chaos = chaos + chaos.mul(16.0).sin().mul(0.02); let chaos = chaos + chaos.mul(16.0).sin().mul(0.02);
@ -397,10 +397,9 @@ impl SimChunk {
+ alt_base + alt_base
+ (0.0 + (0.0
+ alt_main + alt_main
+ gen_ctx.small_nz.get((wposf.div(300.0)).into_array()) as f32 + (gen_ctx.small_nz.get((wposf.div(300.0)).into_array()) as f32)
* alt_main.max(0.1) .mul(alt_main.max(0.1))
* chaos .mul(1.6))
* 1.6)
.add(1.0) .add(1.0)
.mul(0.5) .mul(0.5)
.mul(chaos) .mul(chaos)
@ -436,6 +435,8 @@ impl SimChunk {
forest_kind: if temp > 0.0 { forest_kind: if temp > 0.0 {
if temp > CONFIG.desert_temp { if temp > CONFIG.desert_temp {
ForestKind::Palm ForestKind::Palm
} else if temp > CONFIG.desert_temp {
ForestKind::Savannah
} else { } else {
ForestKind::Oak ForestKind::Oak
} }

View File

@ -6,7 +6,7 @@ pub mod structure;
// Reexports // Reexports
pub use self::{ pub use self::{
hash_cache::HashCache, hash_cache::HashCache,
random::RandomField, random::{RandomField, RandomPerm},
sampler::{Sampler, SamplerMut}, sampler::{Sampler, SamplerMut},
structure::StructureGen2d, structure::StructureGen2d,
}; };

View File

@ -6,7 +6,7 @@ pub struct RandomField {
} }
impl RandomField { impl RandomField {
pub fn new(seed: u32) -> Self { pub const fn new(seed: u32) -> Self {
Self { seed } Self { seed }
} }
} }
@ -35,3 +35,28 @@ impl Sampler for RandomField {
a a
} }
} }
pub struct RandomPerm {
seed: u32,
}
impl RandomPerm {
pub const fn new(seed: u32) -> Self {
Self { seed }
}
}
impl Sampler for RandomPerm {
type Index = u32;
type Sample = u32;
fn get(&self, perm: Self::Index) -> Self::Sample {
let mut a = perm;
a = (a ^ 61) ^ (a >> 16);
a = a + (a << 3);
a = a ^ (a >> 4);
a = a * 0x27d4eb2d;
a = a ^ (a >> 15);
a
}
}