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;
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);

View File

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

View File

@ -1,4 +1,4 @@
mod tree;
mod natural;
use crate::{
column::{ColumnGen, ColumnSample},
@ -6,7 +6,7 @@ use crate::{
World,
};
use common::{
terrain::{structure::StructureBlock, Block},
terrain::{structure::StructureBlock, Block, Structure},
vol::{ReadVol, Vox},
};
use noise::NoiseFn;
@ -86,19 +86,34 @@ impl<'a> BlockGen<'a> {
let sample = Self::sample_column(column_gen, column_cache, wpos)?;
// Tree samples
let mut tree_samples = [None, None, None, None, None, None, None, None, None];
for i in 0..tree_samples.len() {
tree_samples[i] = Self::sample_column(
let mut structure_samples = [None, None, None, None, None, None, None, None, None];
for i in 0..structure_samples.len() {
let st_sample = Self::sample_column(
column_gen,
column_cache,
Vec2::from(sample.close_trees[i].0),
);
structure_samples[i] = st_sample;
}
Some(ZCache {
sample,
tree_samples,
})
let mut structures = [None, None, None, None, None, None, None, None, None];
for i in 0..structures.len() {
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> {
@ -128,9 +143,8 @@ impl<'a> BlockGen<'a> {
..
} = &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 (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 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 col = Lerp::lerp(
sub_surface_color.map(|e| (e * 255.0) as u8),
@ -307,55 +321,32 @@ impl<'a> BlockGen<'a> {
let block = if definitely_underground {
block.unwrap_or(Block::empty())
} 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
} else {
match &tree_samples[tree_idx] {
Some(tree_sample)
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;
block
.or_else(|| {
structures.iter().find_map(|st| {
let (st, st_sample) = st.as_ref()?;
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()]
.get((rpos * 128) / 128) // Scaling
.map(|b| {
block_from_structure(
*b,
rpos,
*tree_pos,
*tree_seed,
&tree_sample,
)
})
.unwrap_or(Block::empty()))
}
_ => block,
}
}
},
),
}
st.volume
.get((block_pos * 128) / 128) // Scaling
.map(|b| {
block_from_structure(
*b,
block_pos,
st.pos.into(),
st.seed,
st_sample,
)
})
.ok()
.filter(|block| !block.is_empty())
})
})
.unwrap_or(Block::empty())
};
Some(block)
@ -364,7 +355,14 @@ impl<'a> BlockGen<'a> {
pub struct ZCache<'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> {

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 lazy_static::lazy_static;
use std::sync::Arc;
use vek::*;
pub fn kinds(forest_kind: ForestKind) -> &'static [Arc<Structure>] {
match forest_kind {
static VOLUME_RAND: RandomPerm = RandomPerm::new(0);
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::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::Pine => &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! {
@ -43,6 +92,9 @@ lazy_static! {
assets::load_map("world/tree/oak_green/9.vox", |s: Structure| s
.with_center(Vec3::new(26, 26, 14)))
.unwrap(),
];
pub static ref OAK_STUMPS: Vec<Arc<Structure>> = vec![
// oak stumps
assets::load_map("world/tree/oak_stump/1.vox", |s: Structure| s
.with_center(Vec3::new(15, 18, 10)))
@ -350,4 +402,10 @@ lazy_static! {
.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)?
+ (sim.gen_ctx.small_nz.get((wposf.div(256.0)).into_array()) as f32)
.abs()
.mul(chaos.max(0.2))
.mul(chaos.max(0.15))
.mul(64.0);
let cliffs = sim_chunk.cliffs;
@ -127,7 +127,7 @@ impl<'a> Sampler for ColumnGen<'a> {
.mul(256.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

View File

@ -368,18 +368,18 @@ impl SimChunk {
.into_array(),
) 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)
.mul(0.5)
.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)
.add(0.5)
.min(1.0),
)
.mul(temp.mul(4.0).sub(1.0).neg().add(0.25).max(0.05).min(1.0))
.powf(1.5)
.add(0.1 * hill);
.add(0.1 * hill)
.mul(temp.mul(4.0).sub(1.0).neg().add(1.25).max(0.15).min(1.0))
.powf(1.5);
let chaos = chaos + chaos.mul(16.0).sin().mul(0.02);
@ -397,14 +397,13 @@ impl SimChunk {
+ alt_base
+ (0.0
+ alt_main
+ gen_ctx.small_nz.get((wposf.div(300.0)).into_array()) as f32
* alt_main.max(0.1)
* chaos
* 1.6)
.add(1.0)
.mul(0.5)
.mul(chaos)
.mul(CONFIG.mountain_scale);
+ (gen_ctx.small_nz.get((wposf.div(300.0)).into_array()) as f32)
.mul(alt_main.max(0.1))
.mul(1.6))
.add(1.0)
.mul(0.5)
.mul(chaos)
.mul(CONFIG.mountain_scale);
let cliff = gen_ctx.cliff_nz.get((wposf.div(2048.0)).into_array()) as f32 + chaos * 0.2;
@ -436,6 +435,8 @@ impl SimChunk {
forest_kind: if temp > 0.0 {
if temp > CONFIG.desert_temp {
ForestKind::Palm
} else if temp > CONFIG.desert_temp {
ForestKind::Savannah
} else {
ForestKind::Oak
}

View File

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

View File

@ -6,7 +6,7 @@ pub struct RandomField {
}
impl RandomField {
pub fn new(seed: u32) -> Self {
pub const fn new(seed: u32) -> Self {
Self { seed }
}
}
@ -35,3 +35,28 @@ impl Sampler for RandomField {
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
}
}