Improved worldgen performance with more precise z bound checks

This commit is contained in:
Joshua Barretto 2019-07-08 15:51:38 +01:00
parent 71f4704502
commit c2a11ed6b2
10 changed files with 119 additions and 70 deletions

View File

@ -1,7 +1,7 @@
use super::Block;
use crate::{
assets::{self, Asset},
vol::{BaseVol, ReadVol, Vox, WriteVol},
vol::{BaseVol, ReadVol, SizedVol, Vox, WriteVol},
volumes::dyna::{Dyna, DynaErr},
};
use dot_vox::DotVoxData;
@ -44,6 +44,13 @@ impl Structure {
self.center = center;
self
}
pub fn get_bounds(&self) -> Aabb<i32> {
Aabb {
min: -self.center,
max: self.vol.get_size().map(|e| e as i32) - self.center,
}
}
}
impl BaseVol for Structure {

View File

@ -84,7 +84,7 @@ impl Server {
let mut state = State::default();
state
.ecs_mut()
.add_resource(SpawnPoint(Vec3::new(16_384.0, 16_384.0, 305.0)));
.add_resource(SpawnPoint(Vec3::new(16_384.0, 16_384.0, 380.0)));
let this = Self {
state,

View File

@ -6,7 +6,7 @@ const vec3 SKY_DAY_BOT = vec3(0.02, 0.1, 0.3);
const vec3 DAY_LIGHT = vec3(0.5, 0.5, 1.0);
const vec3 SKY_DUSK_TOP = vec3(0.1, 0.15, 0.3);
const vec3 SKY_DUSK_MID = vec3(0.9, 0.3, 0.2);
const vec3 SKY_DUSK_MID = vec3(0.75, 0.3, 0.25);
const vec3 SKY_DUSK_BOT = vec3(0.01, 0.05, 0.15);
const vec3 DUSK_LIGHT = vec3(0.9, 0.2, 0.1);

View File

@ -3,7 +3,7 @@ mod natural;
use crate::{
column::{ColumnGen, ColumnSample},
util::{HashCache, RandomField, Sampler, SamplerMut},
World,
World, CONFIG,
};
use common::{
terrain::{structure::StructureBlock, Block, Structure},
@ -52,7 +52,7 @@ impl<'a> BlockGen<'a> {
cache,
Vec2::from(*cliff_pos),
) {
Some(cliff_sample) if cliff_sample.cliffs && cliff_sample.spawn_rate > 0.5 => {
Some(cliff_sample) if cliff_sample.is_cliffs && cliff_sample.spawn_rate > 0.5 => {
let cliff_pos3d = Vec3::from(*cliff_pos);
let height = RandomField::new(seed + 1).get(cliff_pos3d) % 48;
@ -113,7 +113,11 @@ impl<'a> BlockGen<'a> {
}
}
Some(ZCache { sample, structures })
Some(ZCache {
wpos,
sample,
structures,
})
}
pub fn get_with_z_cache(&mut self, wpos: Vec3<i32>, z_cache: Option<&ZCache>) -> Option<Block> {
@ -354,10 +358,44 @@ impl<'a> BlockGen<'a> {
}
pub struct ZCache<'a> {
wpos: Vec2<i32>,
sample: ColumnSample<'a>,
structures: [Option<(StructureInfo, ColumnSample<'a>)>; 9],
}
impl<'a> ZCache<'a> {
pub fn get_z_limits(&self) -> (f32, f32) {
let cave_depth = if self.sample.cave_xy.abs() > 0.9 {
(self.sample.alt - self.sample.cave_alt) + 8.0
} else {
0.0
};
let min = self.sample.alt - (self.sample.chaos * 48.0 + cave_depth) - 4.0;
let cliff = if self.sample.near_cliffs { 48.0 } else { 0.0 };
let warp = self.sample.chaos * 48.0;
let structure = self.structures.iter().filter_map(|st| st.as_ref()).fold(
0,
|a, (st_info, st_sample)| {
let bounds = st_info.volume.get_bounds();
let min = Vec2::from(bounds.min + st_info.pos);
let max = Vec2::from(bounds.max + st_info.pos);
if (Aabr { min, max }).contains_point(self.wpos) {
a.max(bounds.max.z)
} else {
a
}
},
) as f32;
let max =
(self.sample.alt + cliff + structure + warp + 8.0).max(self.sample.water_level + 2.0);
(min, max)
}
}
pub struct StructureInfo {
pos: Vec3<i32>,
seed: u32,

View File

@ -9,9 +9,9 @@ use lazy_static::lazy_static;
use std::sync::Arc;
use vek::*;
static VOLUME_RAND: RandomPerm = RandomPerm::new(0);
static UNIT_RAND: RandomPerm = RandomPerm::new(1);
static QUIRKY_RAND: RandomPerm = RandomPerm::new(2);
static VOLUME_RAND: RandomPerm = RandomPerm::new(0xDB21C052);
static UNIT_RAND: RandomPerm = RandomPerm::new(0x700F4EC7);
static QUIRKY_RAND: RandomPerm = RandomPerm::new(0xA634460F);
pub fn structure_gen<'a>(
column_gen: &ColumnGen<'a>,
@ -42,26 +42,43 @@ pub fn structure_gen<'a>(
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,
let volumes: &'static [_] = if QUIRKY_RAND.get(st_seed) % 64 == 17 {
&QUIRKY
} else {
match st_sample.forest_kind {
ForestKind::Palm => &PALMS,
ForestKind::Savannah => &PALMS,
ForestKind::Oak if QUIRKY_RAND.get(st_seed) % 16 == 7 => &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 })];
const UNIT_CHOICES: [(Vec2<i32>, Vec2<i32>); 8] = [
(Vec2 { x: 1, y: 0 }, Vec2 { x: 0, y: 1 }),
(Vec2 { x: 1, y: 0 }, Vec2 { x: 0, y: -1 }),
(Vec2 { x: -1, y: 0 }, Vec2 { x: 0, y: 1 }),
(Vec2 { x: -1, y: 0 }, Vec2 { x: 0, y: -1 }),
(Vec2 { x: 0, y: 1 }, Vec2 { x: 1, y: 0 }),
(Vec2 { x: 0, y: 1 }, Vec2 { x: -1, y: 0 }),
(Vec2 { x: 0, y: -1 }, Vec2 { x: 1, y: 0 }),
(Vec2 { x: 0, y: -1 }, Vec2 { x: -1, y: 0 }),
];
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()],
volume: &volumes[(VOLUME_RAND.get(st_seed) / 13) as usize % volumes.len()],
})
}
fn st_asset(path: &str, offset: impl Into<Vec3<i32>>) -> Arc<Structure> {
assets::load_map(path, |s: Structure| s.with_center(offset.into()))
.expect("Failed to load structure asset")
}
lazy_static! {
pub static ref OAKS: Vec<Arc<Structure>> = vec![
// green oaks
@ -330,30 +347,14 @@ lazy_static! {
pub static ref SNOW_PINES: Vec<Arc<Structure>> = vec![
// snow pines
assets::load_map("world/tree/snow_pine/1.vox", |s: Structure| s
.with_center(Vec3::new(15, 15, 14)))
.unwrap(),
assets::load_map("world/tree/snow_pine/2.vox", |s: Structure| s
.with_center(Vec3::new(15, 15, 14)))
.unwrap(),
assets::load_map("world/tree/snow_pine/3.vox", |s: Structure| s
.with_center(Vec3::new(17, 15, 12)))
.unwrap(),
assets::load_map("world/tree/snow_pine/4.vox", |s: Structure| s
.with_center(Vec3::new(10, 8, 12)))
.unwrap(),
assets::load_map("world/tree/snow_pine/5.vox", |s: Structure| s
.with_center(Vec3::new(12, 12, 12)))
.unwrap(),
assets::load_map("world/tree/snow_pine/6.vox", |s: Structure| s
.with_center(Vec3::new(11, 10, 12)))
.unwrap(),
assets::load_map("world/tree/snow_pine/7.vox", |s: Structure| s
.with_center(Vec3::new(16, 15, 12)))
.unwrap(),
assets::load_map("world/tree/snow_pine/8.vox", |s: Structure| s
.with_center(Vec3::new(12, 10, 12)))
.unwrap(),
st_asset("world/tree/snow_pine/1.vox", (15, 15, 14)),
st_asset("world/tree/snow_pine/2.vox", (15, 15, 14)),
st_asset("world/tree/snow_pine/3.vox", (17, 15, 12)),
st_asset("world/tree/snow_pine/4.vox", (10, 8, 12)),
st_asset("world/tree/snow_pine/5.vox", (12, 12, 12)),
st_asset("world/tree/snow_pine/6.vox", (11, 10, 12)),
st_asset("world/tree/snow_pine/7.vox", (16, 15, 12)),
st_asset("world/tree/snow_pine/8.vox", (12, 10, 12)),
];
/*
// snow birches -> need roots!
@ -404,8 +405,7 @@ lazy_static! {
*/
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(),
st_asset("world/structure/natural/tower-ruin.vox", (11, 14, 5)),
st_asset("world/structure/natural/witch-hut.vox", (10, 13, 3)),
];
}

View File

@ -64,7 +64,8 @@ impl<'a> Sampler for ColumnGen<'a> {
.mul(chaos.max(0.15))
.mul(64.0);
let cliffs = sim_chunk.cliffs;
let is_cliffs = sim_chunk.is_cliffs;
let near_cliffs = sim_chunk.near_cliffs;
let alt = riverless_alt
- (1.0 - river)
@ -249,7 +250,8 @@ impl<'a> Sampler for ColumnGen<'a> {
cave_xy,
cave_alt,
rock,
cliffs,
is_cliffs,
near_cliffs,
cliff_hill,
close_cliffs: sim.gen_ctx.cliff_gen.get(wpos),
temp,
@ -273,7 +275,8 @@ pub struct ColumnSample<'a> {
pub cave_xy: f32,
pub cave_alt: f32,
pub rock: f32,
pub cliffs: bool,
pub is_cliffs: bool,
pub near_cliffs: bool,
pub cliff_hill: f32,
pub close_cliffs: [(Vec2<i32>, u32); 9],
pub temp: f32,

View File

@ -84,8 +84,8 @@ impl World {
for y in 0..TerrainChunkSize::SIZE.y as i32 {
let wpos2d = Vec2::new(x, y)
+ Vec3::from(chunk_pos) * TerrainChunkSize::SIZE.map(|e| e as i32);
let _wposf2d = wpos2d.map(|e| e as f64);
/*
let min_z = self
.sim
.get_interpolated(wpos2d, |chunk| chunk.get_min_z())
@ -95,15 +95,25 @@ impl World {
.sim
.get_interpolated(wpos2d, |chunk| chunk.get_max_z())
.unwrap_or(0.0) as i32;
*/
let z_cache = sampler.get_z_cache(wpos2d);
let z_cache = match sampler.get_z_cache(wpos2d) {
Some(z_cache) => z_cache,
None => continue,
};
for z in min_z..max_z {
let (min_z, max_z) = z_cache.get_z_limits();
for z in base_z - 16..min_z as i32 {
let _ = chunk.set(Vec3::new(x, y, z), stone);
}
for z in min_z as i32..max_z as i32 {
let lpos = Vec3::new(x, y, z);
let wpos =
lpos + Vec3::from(chunk_pos) * TerrainChunkSize::SIZE.map(|e| e as i32);
if let Some(block) = sampler.get_with_z_cache(wpos, z_cache.as_ref()) {
if let Some(block) = sampler.get_with_z_cache(wpos, Some(&z_cache)) {
let _ = chunk.set(lpos, block);
}
}

View File

@ -306,8 +306,6 @@ impl WorldSim {
}
}
const Z_TOLERANCE: (f32, f32) = (100.0, 128.0);
pub struct SimChunk {
pub chaos: f32,
pub alt_base: f32,
@ -315,7 +313,7 @@ pub struct SimChunk {
pub temp: f32,
pub dryness: f32,
pub rockiness: f32,
pub cliffs: bool,
pub is_cliffs: bool,
pub near_cliffs: bool,
pub tree_density: f32,
pub forest_kind: ForestKind,
@ -421,21 +419,23 @@ impl SimChunk {
.sub(0.1)
.mul(1.3)
.max(0.0),
cliffs: cliff > 0.5
is_cliffs: cliff > 0.5
&& dryness > 0.05
&& alt > CONFIG.sea_level + 5.0
&& dryness.abs() > 0.075,
near_cliffs: cliff > 0.4,
near_cliffs: cliff > 0.325,
tree_density: (gen_ctx.tree_nz.get((wposf.div(1024.0)).into_array()) as f32)
.mul(1.5)
.add(1.0)
.mul(0.5)
.mul(1.2 - chaos * 0.95)
.add(0.1)
.add(0.05)
.mul(if alt > CONFIG.sea_level + 5.0 {
1.0
} else {
0.0
}),
})
.max(0.0),
forest_kind: if temp > 0.0 {
if temp > CONFIG.desert_temp {
ForestKind::Palm
@ -457,16 +457,7 @@ impl SimChunk {
}
pub fn get_base_z(&self) -> f32 {
self.alt - Z_TOLERANCE.0 * self.chaos
}
pub fn get_min_z(&self) -> f32 {
self.alt - Z_TOLERANCE.0 * (self.chaos * 1.2 + 0.3)
}
pub fn get_max_z(&self) -> f32 {
(self.alt + Z_TOLERANCE.1 * if self.near_cliffs { 1.0 } else { 0.5 })
.max(CONFIG.sea_level + 2.0)
self.alt - self.chaos * 50.0
}
pub fn get_name(&self, world: &WorldSim) -> Option<String> {