From 4735d3d6da006aeb253549176dea2762f3544ac2 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 23 Aug 2019 15:12:27 +0100 Subject: [PATCH 01/24] More interesting hills --- world/src/column/mod.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index c89575e1e4..9e365db96a 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -175,8 +175,16 @@ impl<'a> Sampler for ColumnGen<'a> { .small_nz .get((wposf_turb.div(150.0)).into_array()) as f32) .abs() - .mul(chaos.max(0.15)) - .mul(64.0); + .mul(chaos.max(0.025)) + .mul(64.0) + + (sim + .gen_ctx + .small_nz + .get((wposf_turb.div(450.0)).into_array()) as f32) + .abs() + .mul(1.0 - chaos) + .mul(1.0 - humidity) + .mul(96.0); let is_cliffs = sim_chunk.is_cliffs; let near_cliffs = sim_chunk.near_cliffs; From 75f54195717db0e6b553376fc57c5e6dba2370be Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 23 Aug 2019 16:41:02 +0100 Subject: [PATCH 02/24] Removed dryness --- world/src/column/mod.rs | 1 - world/src/sim/mod.rs | 23 +---------------------- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index 9e365db96a..3ea1df255e 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -142,7 +142,6 @@ impl<'a> Sampler for ColumnGen<'a> { let alt_base = sim.get_interpolated(wpos, |chunk| chunk.alt_base)?; let chaos = sim.get_interpolated(wpos, |chunk| chunk.chaos)?; let temp = sim.get_interpolated(wpos, |chunk| chunk.temp)?; - let dryness = sim.get_interpolated(wpos, |chunk| chunk.dryness)?; let humidity = sim.get_interpolated(wpos, |chunk| chunk.humidity)?; let rockiness = sim.get_interpolated(wpos, |chunk| chunk.rockiness)?; let tree_density = sim.get_interpolated(wpos, |chunk| chunk.tree_density)?; diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index 5be3ae1e69..938ddc1783 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -491,7 +491,6 @@ pub struct SimChunk { pub alt_base: f32, pub alt: f32, pub temp: f32, - pub dryness: f32, pub humidity: f32, pub rockiness: f32, pub is_cliffs: bool, @@ -521,22 +520,6 @@ impl SimChunk { let pos = uniform_idx_as_vec2(posi); let wposf = (pos * TerrainChunkSize::SIZE.map(|e| e as i32)).map(|e| e as f64); - // FIXME: Currently unused, but should represent fresh groundwater level. - // Should be correlated a little with humidity, somewhat negatively with altitude, - // and very negatively with difference in temperature from zero. - let dryness = gen_ctx.dry_nz.get( - (wposf - .add(Vec2::new( - gen_ctx - .dry_nz - .get((wposf.add(10000.0).div(500.0)).into_array()) - * 150.0, - gen_ctx.dry_nz.get((wposf.add(0.0).div(500.0)).into_array()) * 150.0, - )) - .div(2_000.0)) - .into_array(), - ) as f32; - let (_, alt_base) = gen_cdf.alt_base[posi]; let map_edge_factor = map_edge_factor(posi); let (_, chaos) = gen_cdf.chaos[posi]; @@ -606,16 +589,12 @@ impl SimChunk { alt_base, alt, temp, - dryness, humidity, rockiness: (gen_ctx.rock_nz.get((wposf.div(1024.0)).into_array()) as f32) .sub(0.1) .mul(1.3) .max(0.0), - is_cliffs: cliff > 0.5 - && dryness > 0.05 - && alt > CONFIG.sea_level + 5.0 - && dryness.abs() > 0.075, + is_cliffs: cliff > 0.5 && alt > CONFIG.sea_level + 5.0, near_cliffs: cliff > 0.25, tree_density, forest_kind: if temp > 0.0 { From 1fcb4a0313046184f52955b55d71948c14423e04 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 23 Aug 2019 20:56:21 +0100 Subject: [PATCH 03/24] Added Generator trait, fixed ocean lighting --- voxygen/src/mesh/terrain.rs | 4 ++-- world/src/block/mod.rs | 2 +- world/src/column/mod.rs | 2 +- world/src/generator/mod.rs | 10 ++++++++++ world/src/generator/town.rs | 19 +++++++++++++++++++ world/src/lib.rs | 1 + world/src/util/random.rs | 4 ++-- world/src/util/sampler.rs | 12 ++++++------ world/src/util/structure.rs | 2 +- world/src/util/unit_chooser.rs | 2 +- 10 files changed, 44 insertions(+), 14 deletions(-) create mode 100644 world/src/generator/mod.rs create mode 100644 world/src/generator/town.rs diff --git a/voxygen/src/mesh/terrain.rs b/voxygen/src/mesh/terrain.rs index b3d479a672..d3543d7aad 100644 --- a/voxygen/src/mesh/terrain.rs +++ b/voxygen/src/mesh/terrain.rs @@ -104,8 +104,8 @@ impl + ReadVol + Debug, S: VolSize + Clone> .map(|vox| block_shadow_density(vox.kind())) .unwrap_or((0.0, 0.0)); - neighbour_light[0][i][j] = - (neighbour_light[0][i][j] * (1.0 - density)).max(cap); + neighbour_light[0][i][j] = (neighbour_light[0][i][j] * (1.0 - density)) + .max(cap.min(neighbour_light[1][i][j])); } } diff --git a/world/src/block/mod.rs b/world/src/block/mod.rs index 22e9d530e4..c0a2283620 100644 --- a/world/src/block/mod.rs +++ b/world/src/block/mod.rs @@ -410,7 +410,7 @@ impl<'a> ZCache<'a> { } } -impl<'a> SamplerMut for BlockGen<'a> { +impl<'a> SamplerMut<'static> for BlockGen<'a> { type Index = Vec3; type Sample = Option; diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index 3ea1df255e..5240bdb713 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -121,7 +121,7 @@ impl<'a> ColumnGen<'a> { } } -impl<'a> Sampler for ColumnGen<'a> { +impl<'a> Sampler<'a> for ColumnGen<'a> { type Index = Vec2; type Sample = Option>; diff --git a/world/src/generator/mod.rs b/world/src/generator/mod.rs new file mode 100644 index 0000000000..021235916a --- /dev/null +++ b/world/src/generator/mod.rs @@ -0,0 +1,10 @@ +mod town; + +// Reexports +pub use self::town::TownGen; + +use crate::util::Sampler; +use common::terrain::Block; +use vek::*; + +pub trait Generator<'a, T: 'a>: Sampler<'a, Index = (&'a T, Vec3), Sample = Block> {} diff --git a/world/src/generator/town.rs b/world/src/generator/town.rs new file mode 100644 index 0000000000..effe803c4b --- /dev/null +++ b/world/src/generator/town.rs @@ -0,0 +1,19 @@ +use super::Generator; +use crate::util::Sampler; +use common::terrain::Block; +use vek::*; + +pub struct TownState; + +pub struct TownGen; + +impl<'a> Sampler<'a> for TownGen { + type Index = (&'a TownState, Vec3); + type Sample = Block; + + fn get(&self, (town, pos): Self::Index) -> Self::Sample { + unimplemented!() + } +} + +impl<'a> Generator<'a, TownState> for TownGen {} diff --git a/world/src/lib.rs b/world/src/lib.rs index 5a58627c9c..69bd984cdd 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -10,6 +10,7 @@ mod all; mod block; mod column; pub mod config; +pub mod generator; pub mod sim; pub mod util; diff --git a/world/src/util/random.rs b/world/src/util/random.rs index ece87a30ce..7cb106e06c 100644 --- a/world/src/util/random.rs +++ b/world/src/util/random.rs @@ -11,7 +11,7 @@ impl RandomField { } } -impl Sampler for RandomField { +impl Sampler<'static> for RandomField { type Index = Vec3; type Sample = u32; @@ -46,7 +46,7 @@ impl RandomPerm { } } -impl Sampler for RandomPerm { +impl Sampler<'static> for RandomPerm { type Index = u32; type Sample = u32; diff --git a/world/src/util/sampler.rs b/world/src/util/sampler.rs index 921ffc7f33..14a720949e 100644 --- a/world/src/util/sampler.rs +++ b/world/src/util/sampler.rs @@ -1,13 +1,13 @@ -pub trait Sampler: Sized { - type Index; - type Sample; +pub trait Sampler<'a>: Sized { + type Index: 'a; + type Sample: 'a; fn get(&self, index: Self::Index) -> Self::Sample; } -pub trait SamplerMut: Sized { - type Index; - type Sample; +pub trait SamplerMut<'a>: Sized { + type Index: 'a; + type Sample: 'a; fn get(&mut self, index: Self::Index) -> Self::Sample; } diff --git a/world/src/util/structure.rs b/world/src/util/structure.rs index 512acd671d..6c2e570c49 100644 --- a/world/src/util/structure.rs +++ b/world/src/util/structure.rs @@ -21,7 +21,7 @@ impl StructureGen2d { } } -impl Sampler for StructureGen2d { +impl Sampler<'static> for StructureGen2d { type Index = Vec2; type Sample = [(Vec2, u32); 9]; diff --git a/world/src/util/unit_chooser.rs b/world/src/util/unit_chooser.rs index c5c0203090..882126e0da 100644 --- a/world/src/util/unit_chooser.rs +++ b/world/src/util/unit_chooser.rs @@ -24,7 +24,7 @@ impl UnitChooser { } } -impl Sampler for UnitChooser { +impl Sampler<'static> for UnitChooser { type Index = u32; type Sample = (Vec2, Vec2); From 4777921680df71d828ba8b3fb586c6a6ca8390ef Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 23 Aug 2019 22:33:14 +0100 Subject: [PATCH 04/24] Added test generator --- world/src/block/mod.rs | 12 ++++++++++++ world/src/column/mod.rs | 4 ++++ world/src/generator/mod.rs | 7 +++++-- world/src/generator/town.rs | 11 ++++++++--- world/src/sim/mod.rs | 11 +++++++++++ 5 files changed, 40 insertions(+), 5 deletions(-) diff --git a/world/src/block/mod.rs b/world/src/block/mod.rs index c0a2283620..4205fd060d 100644 --- a/world/src/block/mod.rs +++ b/world/src/block/mod.rs @@ -2,6 +2,7 @@ mod natural; use crate::{ column::{ColumnGen, ColumnSample, StructureData}, + generator::TownGen, util::{HashCache, RandomField, Sampler, SamplerMut}, World, CONFIG, }; @@ -155,6 +156,8 @@ impl<'a> BlockGen<'a> { cliff_hill, close_cliffs, temp, + + chunk, .. } = &z_cache?.sample; @@ -352,6 +355,15 @@ impl<'a> BlockGen<'a> { } }); + // Structures (like towns) + let block = block.or_else(|| { + chunk + .structures + .town + .as_ref() + .and_then(|town| TownGen.get((town, wpos))) + }); + let block = structures .iter() .find_map(|st| { diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index 5240bdb713..d5bd54560b 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -514,6 +514,8 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { temp, spawn_rate, location: sim_chunk.location.as_ref(), + + chunk: sim_chunk, }) } } @@ -541,6 +543,8 @@ pub struct ColumnSample<'a> { pub temp: f32, pub spawn_rate: f32, pub location: Option<&'a LocationInfo>, + + pub chunk: &'a SimChunk, } #[derive(Copy, Clone)] diff --git a/world/src/generator/mod.rs b/world/src/generator/mod.rs index 021235916a..8097b7b8e3 100644 --- a/world/src/generator/mod.rs +++ b/world/src/generator/mod.rs @@ -1,10 +1,13 @@ mod town; // Reexports -pub use self::town::TownGen; +pub use self::town::{TownGen, TownState}; use crate::util::Sampler; use common::terrain::Block; use vek::*; -pub trait Generator<'a, T: 'a>: Sampler<'a, Index = (&'a T, Vec3), Sample = Block> {} +pub trait Generator<'a, T: 'a>: + Sampler<'a, Index = (&'a T, Vec3), Sample = Option> +{ +} diff --git a/world/src/generator/town.rs b/world/src/generator/town.rs index effe803c4b..39c8dac312 100644 --- a/world/src/generator/town.rs +++ b/world/src/generator/town.rs @@ -1,18 +1,23 @@ use super::Generator; use crate::util::Sampler; -use common::terrain::Block; +use common::terrain::{Block, BlockKind}; use vek::*; +#[derive(Clone)] pub struct TownState; pub struct TownGen; impl<'a> Sampler<'a> for TownGen { type Index = (&'a TownState, Vec3); - type Sample = Block; + type Sample = Option; fn get(&self, (town, pos): Self::Index) -> Self::Sample { - unimplemented!() + if pos.z < 150 { + Some(Block::new(BlockKind::Normal, Rgb::broadcast(255))) + } else { + None + } } } diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index 938ddc1783..b894f992bb 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -11,6 +11,7 @@ use self::util::{ use crate::{ all::ForestKind, + generator::TownState, util::{seed_expan, Sampler, StructureGen2d}, CONFIG, }; @@ -26,6 +27,7 @@ use rand_chacha::ChaChaRng; use std::{ f32, ops::{Add, Div, Mul, Neg, Sub}, + sync::Arc, }; use vek::*; @@ -499,6 +501,8 @@ pub struct SimChunk { pub forest_kind: ForestKind, pub spawn_rate: f32, pub location: Option, + + pub structures: Structures, } #[derive(Copy, Clone)] @@ -515,6 +519,11 @@ pub struct LocationInfo { pub near: Vec, } +#[derive(Clone)] +pub struct Structures { + pub town: Option>, +} + impl SimChunk { fn generate(posi: usize, gen_ctx: &mut GenCtx, gen_cdf: &GenCdf) -> Self { let pos = uniform_idx_as_vec2(posi); @@ -657,6 +666,8 @@ impl SimChunk { }, spawn_rate: 1.0, location: None, + + structures: Structures { town: None }, } } From b0293ecce4ed88809d0ff65f1f62d18afa798020 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sat, 24 Aug 2019 00:25:01 +0100 Subject: [PATCH 05/24] Switched to FastNoise --- world/src/block/mod.rs | 23 ++++-------------- world/src/sim/mod.rs | 12 ++++++--- world/src/util/fast_noise.rs | 47 ++++++++++++++++++++++++++++++++++++ world/src/util/mod.rs | 2 ++ 4 files changed, 63 insertions(+), 21 deletions(-) create mode 100644 world/src/util/fast_noise.rs diff --git a/world/src/block/mod.rs b/world/src/block/mod.rs index 4205fd060d..50180e246c 100644 --- a/world/src/block/mod.rs +++ b/world/src/block/mod.rs @@ -171,31 +171,18 @@ impl<'a> BlockGen<'a> { (true, alt, CONFIG.sea_level /*water_level*/) } else { // Apply warping - let warp = (world - .sim() - .gen_ctx - .warp_nz - .get((wposf.div(Vec3::new(150.0, 150.0, 150.0))).into_array()) - as f32) + let warp = (world.sim().gen_ctx.warp_nz.get(wposf.div(16.0)) as f32) .mul((chaos - 0.1).max(0.0)) - .mul(96.0); + .mul(15.0); let height = if (wposf.z as f32) < alt + warp - 10.0 { // Shortcut cliffs alt + warp } else { let turb = Vec2::new( - world - .sim() - .gen_ctx - .turb_x_nz - .get((wposf.div(48.0)).into_array()) as f32, - world - .sim() - .gen_ctx - .turb_y_nz - .get((wposf.div(48.0)).into_array()) as f32, - ) * 12.0; + world.sim().gen_ctx.fast_turb_x_nz.get(wposf.div(16.0)) as f32, + world.sim().gen_ctx.fast_turb_y_nz.get(wposf.div(16.0)) as f32, + ) * 8.0; let wpos_turb = Vec2::from(wpos).map(|e: i32| e as f32) + turb; let cliff_height = Self::get_cliff_height( diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index b894f992bb..de972ed5eb 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -12,7 +12,7 @@ use self::util::{ use crate::{ all::ForestKind, generator::TownState, - util::{seed_expan, Sampler, StructureGen2d}, + util::{seed_expan, FastNoise, Sampler, StructureGen2d}, CONFIG, }; use common::{ @@ -75,7 +75,7 @@ pub(crate) struct GenCtx { pub small_nz: BasicMulti, pub rock_nz: HybridMulti, pub cliff_nz: HybridMulti, - pub warp_nz: BasicMulti, + pub warp_nz: FastNoise, pub tree_nz: BasicMulti, pub cave_0_nz: SuperSimplex, @@ -84,6 +84,9 @@ pub(crate) struct GenCtx { pub structure_gen: StructureGen2d, pub region_gen: StructureGen2d, pub cliff_gen: StructureGen2d, + + pub fast_turb_x_nz: FastNoise, + pub fast_turb_y_nz: FastNoise, } pub struct WorldSim { @@ -117,7 +120,7 @@ impl WorldSim { small_nz: BasicMulti::new().set_octaves(2).set_seed(gen_seed()), rock_nz: HybridMulti::new().set_persistence(0.3).set_seed(gen_seed()), cliff_nz: HybridMulti::new().set_persistence(0.3).set_seed(gen_seed()), - warp_nz: BasicMulti::new().set_octaves(3).set_seed(gen_seed()), + warp_nz: FastNoise::new(gen_seed()), //BasicMulti::new().set_octaves(3).set_seed(gen_seed()), tree_nz: BasicMulti::new() .set_octaves(12) .set_persistence(0.75) @@ -135,6 +138,9 @@ impl WorldSim { // .set_octaves(6) // .set_persistence(0.5) .set_seed(gen_seed()), + + fast_turb_x_nz: FastNoise::new(gen_seed()), + fast_turb_y_nz: FastNoise::new(gen_seed()), }; // "Base" of the chunk, to be multiplied by CONFIG.mountain_scale (multiplied value is diff --git a/world/src/util/fast_noise.rs b/world/src/util/fast_noise.rs new file mode 100644 index 0000000000..d2204280be --- /dev/null +++ b/world/src/util/fast_noise.rs @@ -0,0 +1,47 @@ +use super::{RandomField, Sampler}; +use std::f32; +use vek::*; + +pub struct FastNoise { + noise: RandomField, +} + +impl FastNoise { + pub const fn new(seed: u32) -> Self { + Self { + noise: RandomField::new(seed), + } + } + + fn noise_at(&self, pos: Vec3) -> f32 { + (self.noise.get(pos) % 4096) as f32 / 4096.0 + } +} + +impl Sampler<'static> for FastNoise { + type Index = Vec3; + type Sample = f32; + + fn get(&self, pos: Self::Index) -> Self::Sample { + let v000 = self.noise_at(pos.map(|e| e.floor() as i32) + Vec3::new(0, 0, 0)); + let v100 = self.noise_at(pos.map(|e| e.floor() as i32) + Vec3::new(1, 0, 0)); + let v010 = self.noise_at(pos.map(|e| e.floor() as i32) + Vec3::new(0, 1, 0)); + let v110 = self.noise_at(pos.map(|e| e.floor() as i32) + Vec3::new(1, 1, 0)); + let v001 = self.noise_at(pos.map(|e| e.floor() as i32) + Vec3::new(0, 0, 1)); + let v101 = self.noise_at(pos.map(|e| e.floor() as i32) + Vec3::new(1, 0, 1)); + let v011 = self.noise_at(pos.map(|e| e.floor() as i32) + Vec3::new(0, 1, 1)); + let v111 = self.noise_at(pos.map(|e| e.floor() as i32) + Vec3::new(1, 1, 1)); + + let factor = pos.map(|e| 0.5 - (e.fract() as f32 * f32::consts::PI).cos() * 0.5); + + let x00 = Lerp::lerp(v000, v100, factor.x); + let x10 = Lerp::lerp(v010, v110, factor.x); + let x01 = Lerp::lerp(v001, v101, factor.x); + let x11 = Lerp::lerp(v011, v111, factor.x); + + let y0 = Lerp::lerp(x00, x10, factor.y); + let y1 = Lerp::lerp(x01, x11, factor.y); + + Lerp::lerp(y0, y1, factor.z) * 2.0 - 1.0 + } +} diff --git a/world/src/util/mod.rs b/world/src/util/mod.rs index f0a44c87c8..37a62dc72e 100644 --- a/world/src/util/mod.rs +++ b/world/src/util/mod.rs @@ -1,3 +1,4 @@ +pub mod fast_noise; pub mod hash_cache; pub mod random; pub mod sampler; @@ -7,6 +8,7 @@ pub mod unit_chooser; // Reexports pub use self::{ + fast_noise::FastNoise, hash_cache::HashCache, random::{RandomField, RandomPerm}, sampler::{Sampler, SamplerMut}, From aaade23eefff43dabaf9a67bd5bafbded13a38d5 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sat, 24 Aug 2019 00:49:14 +0100 Subject: [PATCH 06/24] Better warping --- world/src/block/mod.rs | 4 ++-- world/src/util/fast_noise.rs | 18 ++++++++++-------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/world/src/block/mod.rs b/world/src/block/mod.rs index 50180e246c..f62b23e90b 100644 --- a/world/src/block/mod.rs +++ b/world/src/block/mod.rs @@ -171,9 +171,9 @@ impl<'a> BlockGen<'a> { (true, alt, CONFIG.sea_level /*water_level*/) } else { // Apply warping - let warp = (world.sim().gen_ctx.warp_nz.get(wposf.div(16.0)) as f32) + let warp = (world.sim().gen_ctx.warp_nz.get(wposf.div(24.0)) as f32) .mul((chaos - 0.1).max(0.0)) - .mul(15.0); + .mul(30.0); let height = if (wposf.z as f32) < alt + warp - 10.0 { // Shortcut cliffs diff --git a/world/src/util/fast_noise.rs b/world/src/util/fast_noise.rs index d2204280be..e25a61e980 100644 --- a/world/src/util/fast_noise.rs +++ b/world/src/util/fast_noise.rs @@ -23,14 +23,16 @@ impl Sampler<'static> for FastNoise { type Sample = f32; fn get(&self, pos: Self::Index) -> Self::Sample { - let v000 = self.noise_at(pos.map(|e| e.floor() as i32) + Vec3::new(0, 0, 0)); - let v100 = self.noise_at(pos.map(|e| e.floor() as i32) + Vec3::new(1, 0, 0)); - let v010 = self.noise_at(pos.map(|e| e.floor() as i32) + Vec3::new(0, 1, 0)); - let v110 = self.noise_at(pos.map(|e| e.floor() as i32) + Vec3::new(1, 1, 0)); - let v001 = self.noise_at(pos.map(|e| e.floor() as i32) + Vec3::new(0, 0, 1)); - let v101 = self.noise_at(pos.map(|e| e.floor() as i32) + Vec3::new(1, 0, 1)); - let v011 = self.noise_at(pos.map(|e| e.floor() as i32) + Vec3::new(0, 1, 1)); - let v111 = self.noise_at(pos.map(|e| e.floor() as i32) + Vec3::new(1, 1, 1)); + let near_pos = pos.map(|e| e.floor() as i32); + + let v000 = self.noise_at(near_pos + Vec3::new(0, 0, 0)); + let v100 = self.noise_at(near_pos + Vec3::new(1, 0, 0)); + let v010 = self.noise_at(near_pos + Vec3::new(0, 1, 0)); + let v110 = self.noise_at(near_pos + Vec3::new(1, 1, 0)); + let v001 = self.noise_at(near_pos + Vec3::new(0, 0, 1)); + let v101 = self.noise_at(near_pos + Vec3::new(1, 0, 1)); + let v011 = self.noise_at(near_pos + Vec3::new(0, 1, 1)); + let v111 = self.noise_at(near_pos + Vec3::new(1, 1, 1)); let factor = pos.map(|e| 0.5 - (e.fract() as f32 * f32::consts::PI).cos() * 0.5); From d42485238e447a8f1994d7cf81ec46d653be5d93 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sat, 24 Aug 2019 14:23:42 +0100 Subject: [PATCH 07/24] Added town road construction --- world/src/block/mod.rs | 27 ++++++-- world/src/column/mod.rs | 2 + world/src/generator/mod.rs | 5 +- world/src/generator/town.rs | 124 +++++++++++++++++++++++++++++++++--- world/src/sim/mod.rs | 45 ++++++++++++- world/src/util/grid.rs | 46 +++++++++++++ world/src/util/mod.rs | 2 + 7 files changed, 234 insertions(+), 17 deletions(-) create mode 100644 world/src/util/grid.rs diff --git a/world/src/block/mod.rs b/world/src/block/mod.rs index f62b23e90b..0e95adcf93 100644 --- a/world/src/block/mod.rs +++ b/world/src/block/mod.rs @@ -2,7 +2,7 @@ mod natural; use crate::{ column::{ColumnGen, ColumnSample, StructureData}, - generator::TownGen, + generator::{Generator, TownGen}, util::{HashCache, RandomField, Sampler, SamplerMut}, World, CONFIG, }; @@ -137,6 +137,7 @@ impl<'a> BlockGen<'a> { column_gen, } = self; + let sample = &z_cache?.sample; let &ColumnSample { alt, chaos, @@ -159,7 +160,7 @@ impl<'a> BlockGen<'a> { chunk, .. - } = &z_cache?.sample; + } = sample; let structures = &z_cache?.structures; @@ -171,9 +172,12 @@ impl<'a> BlockGen<'a> { (true, alt, CONFIG.sea_level /*water_level*/) } else { // Apply warping - let warp = (world.sim().gen_ctx.warp_nz.get(wposf.div(24.0)) as f32) + let warp = (world.sim().gen_ctx.warp_nz.get(wposf.div(48.0)) as f32) .mul((chaos - 0.1).max(0.0)) - .mul(30.0); + .mul(48.0) + + (world.sim().gen_ctx.warp_nz.get(wposf.div(15.0)) as f32) + .mul((chaos - 0.1).max(0.0)) + .mul(15.0); let height = if (wposf.z as f32) < alt + warp - 10.0 { // Shortcut cliffs @@ -348,7 +352,7 @@ impl<'a> BlockGen<'a> { .structures .town .as_ref() - .and_then(|town| TownGen.get((town, wpos))) + .and_then(|town| TownGen.get((town, wpos, sample))) }); let block = structures @@ -405,6 +409,19 @@ impl<'a> ZCache<'a> { .max(self.sample.water_level) .max(CONFIG.sea_level + 2.0); + // Structures + let (min, max) = self + .sample + .chunk + .structures + .town + .as_ref() + .map(|town| { + let (town_min, town_max) = TownGen.get_z_limits(town, self.wpos, &self.sample); + (town_min.min(min), town_max.max(max)) + }) + .unwrap_or((min, max)); + (min, max) } } diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index d5bd54560b..61731051b7 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -422,6 +422,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { // Cities // TODO: In a later MR + /* let building = match &sim_chunk.location { Some(loc) => { let loc = &sim.locations[loc.loc_idx]; @@ -440,6 +441,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { }; let alt = alt + building; + */ // Caves let cave_at = |wposf: Vec2| { diff --git a/world/src/generator/mod.rs b/world/src/generator/mod.rs index 8097b7b8e3..c255104205 100644 --- a/world/src/generator/mod.rs +++ b/world/src/generator/mod.rs @@ -3,11 +3,12 @@ mod town; // Reexports pub use self::town::{TownGen, TownState}; -use crate::util::Sampler; +use crate::{column::ColumnSample, util::Sampler}; use common::terrain::Block; use vek::*; pub trait Generator<'a, T: 'a>: - Sampler<'a, Index = (&'a T, Vec3), Sample = Option> + Sampler<'a, Index = (&'a T, Vec3, &'a ColumnSample<'a>), Sample = Option> { + fn get_z_limits(&self, state: &'a T, wpos: Vec2, sample: &ColumnSample) -> (f32, f32); } diff --git a/world/src/generator/town.rs b/world/src/generator/town.rs index 39c8dac312..69678e6d6f 100644 --- a/world/src/generator/town.rs +++ b/world/src/generator/town.rs @@ -1,24 +1,130 @@ use super::Generator; -use crate::util::Sampler; +use crate::{ + column::ColumnSample, + sim::WorldSim, + util::{seed_expan, Grid, Sampler}, +}; use common::terrain::{Block, BlockKind}; +use rand::prelude::*; +use rand_chacha::ChaChaRng; use vek::*; +const CELL_SIZE: i32 = 24; + #[derive(Clone)] -pub struct TownState; +pub enum TownCell { + Empty, + Junction, + Road, + House, +} + +pub struct TownState { + pub center: Vec2, + pub radius: i32, + pub grid: Grid, +} + +impl TownState { + pub fn generate(center: Vec2, seed: u32, sim: &mut WorldSim) -> Option { + let center_chunk = sim.get_wpos(center)?; + + // First, determine whether the location is even appropriate for a town + if center_chunk.chaos > 0.5 || center_chunk.near_cliffs { + return None; + } + + let radius = 150; + + let mut grid = Grid::new(TownCell::Empty, Vec2::broadcast(radius * 2 / CELL_SIZE)); + + grid.set(grid.size() / 2, TownCell::Junction); + + let mut create_road = || loop { + let junctions = grid + .iter() + .filter(|(_, cell)| { + if let TownCell::Junction = cell { + true + } else { + false + } + }) + .collect::>(); + + // Choose an existing junction for the road to start from + let start_pos = junctions.choose(&mut sim.rng).unwrap().0; // Can't fail + + // Choose a random direction and length for the road + let road_dir = { + let dirs = [-1, 0, 1, 0, -1]; + let idx = sim.rng.gen_range(0, 4); + Vec2::new(dirs[idx], dirs[idx + 1]) + }; + let road_len = sim.rng.gen_range(1, 4) * 2 + 1; + + // Make sure we aren't trying to create a road where a road already exists! + match grid.get(start_pos + road_dir) { + Some(TownCell::Empty) => {} + _ => continue, + } + + // Pave the road + for i in 1..road_len { + let cell_pos = start_pos + road_dir * i; + if let Some(TownCell::Empty) = grid.get(cell_pos) { + grid.set(cell_pos, TownCell::Road); + } + } + grid.set(start_pos + road_dir * road_len, TownCell::Junction); + + break; + }; + + for _ in 0..8 { + create_road(); + } + + Some(Self { + center, + radius, + grid, + }) + } + + fn get_cell(&self, wpos: Vec2) -> &TownCell { + self.grid + .get((wpos - self.center + self.radius) / CELL_SIZE) + .unwrap_or(&TownCell::Empty) + } +} pub struct TownGen; impl<'a> Sampler<'a> for TownGen { - type Index = (&'a TownState, Vec3); + type Index = (&'a TownState, Vec3, &'a ColumnSample<'a>); type Sample = Option; - fn get(&self, (town, pos): Self::Index) -> Self::Sample { - if pos.z < 150 { - Some(Block::new(BlockKind::Normal, Rgb::broadcast(255))) - } else { - None + fn get(&self, (town, wpos, sample): Self::Index) -> Self::Sample { + match town.get_cell(Vec2::from(wpos)) { + TownCell::Road if wpos.z < sample.alt as i32 + 4 => { + Some(Block::new(BlockKind::Normal, Rgb::new(255, 200, 150))) + } + TownCell::Junction if wpos.z < sample.alt as i32 + 4 => { + Some(Block::new(BlockKind::Normal, Rgb::new(255, 200, 250))) + } + _ => None, } } } -impl<'a> Generator<'a, TownState> for TownGen {} +impl<'a> Generator<'a, TownState> for TownGen { + fn get_z_limits( + &self, + town: &'a TownState, + wpos: Vec2, + sample: &ColumnSample, + ) -> (f32, f32) { + (sample.alt - 32.0, sample.alt + 64.0) + } +} diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index de972ed5eb..e11a979182 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -25,6 +25,7 @@ use noise::{ use rand::{Rng, SeedableRng}; use rand_chacha::ChaChaRng; use std::{ + collections::HashMap, f32, ops::{Add, Div, Mul, Neg, Sub}, sync::Arc, @@ -87,6 +88,8 @@ pub(crate) struct GenCtx { pub fast_turb_x_nz: FastNoise, pub fast_turb_y_nz: FastNoise, + + pub town_gen: StructureGen2d, } pub struct WorldSim { @@ -141,6 +144,8 @@ impl WorldSim { fast_turb_x_nz: FastNoise::new(gen_seed()), fast_turb_y_nz: FastNoise::new(gen_seed()), + + town_gen: StructureGen2d::new(gen_seed(), 1024, 512), }; // "Base" of the chunk, to be multiplied by CONFIG.mountain_scale (multiplied value is @@ -418,6 +423,38 @@ impl WorldSim { } } + // Stage 2 - towns! + let mut maybe_towns = HashMap::new(); + for i in 0..WORLD_SIZE.x { + for j in 0..WORLD_SIZE.y { + let chunk_pos = Vec2::new(i as i32, j as i32); + let wpos = chunk_pos.map2(Vec2::from(TerrainChunkSize::SIZE), |e, sz: u32| { + e * sz as i32 + sz as i32 / 2 + }); + + let near_towns = self.gen_ctx.town_gen.get(wpos); + let town = near_towns + .iter() + .min_by_key(|(pos, seed)| wpos.distance_squared(*pos)); + + if let Some((pos, seed)) = town { + let maybe_town = maybe_towns + .entry(*pos) + .or_insert_with(|| { + TownState::generate(*pos, *seed, self).map(|t| Arc::new(t)) + }) + .as_mut() + // Only care if we're close to the town + .filter(|town| { + town.center.distance_squared(wpos) < town.radius.add(64).pow(2) + }) + .cloned(); + + self.get_mut(chunk_pos).unwrap().structures.town = maybe_town; + } + } + } + self.rng = rng; self.locations = locations; } @@ -433,6 +470,12 @@ impl WorldSim { } } + pub fn get_wpos(&self, wpos: Vec2) -> Option<&SimChunk> { + self.get(wpos.map2(Vec2::from(TerrainChunkSize::SIZE), |e, sz: u32| { + e / sz as i32 + })) + } + pub fn get_mut(&mut self, chunk_pos: Vec2) -> Option<&mut SimChunk> { if chunk_pos .map2(WORLD_SIZE, |e, sz| e >= 0 && e < sz as i32) @@ -610,7 +653,7 @@ impl SimChunk { .mul(1.3) .max(0.0), is_cliffs: cliff > 0.5 && alt > CONFIG.sea_level + 5.0, - near_cliffs: cliff > 0.25, + near_cliffs: cliff > 0.2, tree_density, forest_kind: if temp > 0.0 { if temp > CONFIG.desert_temp { diff --git a/world/src/util/grid.rs b/world/src/util/grid.rs new file mode 100644 index 0000000000..e47995fe51 --- /dev/null +++ b/world/src/util/grid.rs @@ -0,0 +1,46 @@ +use vek::*; + +pub struct Grid { + cells: Vec, + size: Vec2, +} + +impl Grid { + pub fn new(default_cell: T, size: Vec2) -> Self { + Self { + cells: vec![default_cell; size.product() as usize], + size, + } + } + + fn idx(&self, pos: Vec2) -> usize { + (pos.y * self.size.x + pos.x) as usize + } + + pub fn size(&self) -> Vec2 { + self.size + } + + pub fn get(&self, pos: Vec2) -> Option<&T> { + self.cells.get(self.idx(pos)) + } + + pub fn get_mut(&mut self, pos: Vec2) -> Option<&mut T> { + let idx = self.idx(pos); + self.cells.get_mut(idx) + } + + pub fn set(&mut self, pos: Vec2, cell: T) { + let idx = self.idx(pos); + self.cells.get_mut(idx).map(|c| *c = cell); + } + + pub fn iter(&self) -> impl Iterator, &T)> + '_ { + (0..self.size.x) + .map(move |x| { + (0..self.size.y) + .map(move |y| (Vec2::new(x, y), &self.cells[self.idx(Vec2::new(x, y))])) + }) + .flatten() + } +} diff --git a/world/src/util/mod.rs b/world/src/util/mod.rs index 37a62dc72e..218901d903 100644 --- a/world/src/util/mod.rs +++ b/world/src/util/mod.rs @@ -1,4 +1,5 @@ pub mod fast_noise; +pub mod grid; pub mod hash_cache; pub mod random; pub mod sampler; @@ -9,6 +10,7 @@ pub mod unit_chooser; // Reexports pub use self::{ fast_noise::FastNoise, + grid::Grid, hash_cache::HashCache, random::{RandomField, RandomPerm}, sampler::{Sampler, SamplerMut}, From fe2ad92201dfa8be7f811fae922ffebd12afaf6f Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sat, 24 Aug 2019 23:57:55 +0100 Subject: [PATCH 08/24] Initial implementation of towns --- assets/world/structure/human/mage_tower.vox | 4 +- world/src/block/mod.rs | 15 +- world/src/column/mod.rs | 20 +- world/src/generator/mod.rs | 2 +- world/src/generator/town.rs | 314 ++++++++++++++++---- world/src/lib.rs | 7 +- world/src/sim/mod.rs | 4 +- world/src/util/grid.rs | 43 ++- 8 files changed, 321 insertions(+), 88 deletions(-) diff --git a/assets/world/structure/human/mage_tower.vox b/assets/world/structure/human/mage_tower.vox index 7df09363c0..0d2115c4c3 100644 --- a/assets/world/structure/human/mage_tower.vox +++ b/assets/world/structure/human/mage_tower.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b6ed2f574af13b79f6cb6df5450068cc637372a24a739a892d55d4b1ff4f287b -size 125468 +oid sha256:db4f7b604c35763c28aeafcfe7dc1d1722b2db77838677ffa5ef4fb08581be9d +size 41004 diff --git a/world/src/block/mod.rs b/world/src/block/mod.rs index 0e95adcf93..5e8b4688b3 100644 --- a/world/src/block/mod.rs +++ b/world/src/block/mod.rs @@ -347,13 +347,12 @@ impl<'a> BlockGen<'a> { }); // Structures (like towns) - let block = block.or_else(|| { - chunk - .structures - .town - .as_ref() - .and_then(|town| TownGen.get((town, wpos, sample))) - }); + let block = chunk + .structures + .town + .as_ref() + .and_then(|town| TownGen.get((town, wpos, sample, height))) + .or(block); let block = structures .iter() @@ -515,7 +514,7 @@ impl StructureInfo { } } -fn block_from_structure( +pub fn block_from_structure( sblock: StructureBlock, default_kind: BlockKind, pos: Vec3, diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index 61731051b7..34bac0c714 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -1,7 +1,7 @@ use crate::{ all::ForestKind, block::StructureMeta, - sim::{LocationInfo, SimChunk}, + sim::{LocationInfo, SimChunk, WorldSim}, util::{RandomPerm, Sampler, UnitChooser}, World, CONFIG, }; @@ -20,7 +20,7 @@ use std::{ use vek::*; pub struct ColumnGen<'a> { - world: &'a World, + pub sim: &'a WorldSim, } static UNIT_CHOOSER: UnitChooser = UnitChooser::new(0x700F4EC7); @@ -55,14 +55,13 @@ lazy_static! { } impl<'a> ColumnGen<'a> { - pub fn new(world: &'a World) -> Self { - Self { world } + pub fn new(sim: &'a WorldSim) -> Self { + Self { sim } } fn get_local_structure(&self, wpos: Vec2) -> Option { let (pos, seed) = self - .world - .sim() + .sim .gen_ctx .region_gen .get(wpos) @@ -74,7 +73,7 @@ impl<'a> ColumnGen<'a> { let chunk_pos = pos.map2(Vec2::from(TerrainChunkSize::SIZE), |e, sz: u32| { e / sz as i32 }); - let chunk = self.world.sim().get(chunk_pos)?; + let chunk = self.sim.get(chunk_pos)?; if seed % 5 == 2 && chunk.temp > CONFIG.desert_temp @@ -102,8 +101,7 @@ impl<'a> ColumnGen<'a> { fn gen_close_structures(&self, wpos: Vec2) -> [Option; 9] { let mut metas = [None; 9]; - self.world - .sim() + self.sim .gen_ctx .structure_gen .get(wpos) @@ -131,7 +129,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { e / sz as i32 }); - let sim = self.world.sim(); + let sim = &self.sim; let turb = Vec2::new( sim.gen_ctx.turb_x_nz.get((wposf.div(48.0)).into_array()) as f32, @@ -383,6 +381,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { .add((marble_small - 0.5) * 0.5), ); + /* // Work out if we're on a path or near a town let dist_to_path = match &sim_chunk.location { Some(loc) => { @@ -419,6 +418,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { } else { (alt, ground) }; + */ // Cities // TODO: In a later MR diff --git a/world/src/generator/mod.rs b/world/src/generator/mod.rs index c255104205..b24d0b3d84 100644 --- a/world/src/generator/mod.rs +++ b/world/src/generator/mod.rs @@ -8,7 +8,7 @@ use common::terrain::Block; use vek::*; pub trait Generator<'a, T: 'a>: - Sampler<'a, Index = (&'a T, Vec3, &'a ColumnSample<'a>), Sample = Option> + Sampler<'a, Index = (&'a T, Vec3, &'a ColumnSample<'a>, f32), Sample = Option> { fn get_z_limits(&self, state: &'a T, wpos: Vec2, sample: &ColumnSample) -> (f32, f32); } diff --git a/world/src/generator/town.rs b/world/src/generator/town.rs index 69678e6d6f..7f578a7514 100644 --- a/world/src/generator/town.rs +++ b/world/src/generator/town.rs @@ -1,22 +1,88 @@ use super::Generator; use crate::{ - column::ColumnSample, + block::block_from_structure, + column::{ColumnGen, ColumnSample}, sim::WorldSim, - util::{seed_expan, Grid, Sampler}, + util::{seed_expan, Grid, Sampler, UnitChooser}, }; -use common::terrain::{Block, BlockKind}; +use common::{ + assets, + terrain::{Block, BlockKind, Structure}, + vol::{ReadVol, Vox}, +}; +use lazy_static::lazy_static; use rand::prelude::*; use rand_chacha::ChaChaRng; +use std::sync::Arc; use vek::*; const CELL_SIZE: i32 = 24; +static UNIT_CHOOSER: UnitChooser = UnitChooser::new(0x100F4E37); + +lazy_static! { + pub static ref HOUSES: Vec> = + vec![ + assets::load_map("world.structure.human.house_1", |s: Structure| s + .with_center(Vec3::new(8, 10, 2))) + .unwrap(), + ]; + pub static ref BLACKSMITHS: Vec> = vec![ + assets::load_map("world.structure.human.blacksmith", |s: Structure| s + .with_center(Vec3::new(16, 19, 9))) + .unwrap(), + assets::load_map("world.structure.human.mage_tower", |s: Structure| s + .with_center(Vec3::new(13, 13, 4))) + .unwrap(), + ]; + pub static ref TOWNHALLS: Vec> = vec![ + assets::load_map("world.structure.human.town_hall_spire", |s: Structure| s + .with_center(Vec3::new(16, 16, 2))) + .unwrap(), + assets::load_map("world.structure.human.stables_1", |s: Structure| s + .with_center(Vec3::new(16, 23, 2))) + .unwrap(), + ]; +} + +#[derive(Clone)] +pub enum Building { + House, + Blacksmith, + TownHall, +} + #[derive(Clone)] pub enum TownCell { Empty, Junction, - Road, - House, + Street, + Building { + kind: Building, + wpos: Vec3, + size: Vec2, + units: (Vec2, Vec2), + seed: u32, + }, + PartOf(Vec2), +} + +impl TownCell { + fn is_road(&self) -> bool { + match self { + TownCell::Junction => true, + TownCell::Street => true, + _ => false, + } + } + + fn mergeable(&self) -> bool { + match self { + TownCell::Empty => true, + TownCell::Building { size, .. } if *size == Vec2::one() => true, + _ => false, + } + } } pub struct TownState { @@ -26,65 +92,166 @@ pub struct TownState { } impl TownState { - pub fn generate(center: Vec2, seed: u32, sim: &mut WorldSim) -> Option { - let center_chunk = sim.get_wpos(center)?; + pub fn generate( + center: Vec2, + seed: u32, + gen: &mut ColumnGen, + rng: &mut impl Rng, + ) -> Option { + let center_chunk = gen.sim.get_wpos(center)?; // First, determine whether the location is even appropriate for a town - if center_chunk.chaos > 0.5 || center_chunk.near_cliffs { + if center_chunk.chaos > 0.7 || center_chunk.near_cliffs { return None; } - let radius = 150; + let radius = 192; - let mut grid = Grid::new(TownCell::Empty, Vec2::broadcast(radius * 2 / CELL_SIZE)); + let mut grid = Grid::new( + TownCell::Empty, + Vec2::broadcast(radius * 3 / (CELL_SIZE * 2)), + ); grid.set(grid.size() / 2, TownCell::Junction); - let mut create_road = || loop { - let junctions = grid - .iter() - .filter(|(_, cell)| { - if let TownCell::Junction = cell { - true - } else { - false - } - }) - .collect::>(); + let mut create_road = || { + for _ in 0..10 { + let junctions = grid + .iter() + .filter(|(_, cell)| { + if let TownCell::Junction = cell { + true + } else { + false + } + }) + .collect::>(); - // Choose an existing junction for the road to start from - let start_pos = junctions.choose(&mut sim.rng).unwrap().0; // Can't fail + // Choose an existing junction for the road to start from + let start_pos = junctions.choose(rng).unwrap().0; // Can't fail - // Choose a random direction and length for the road - let road_dir = { - let dirs = [-1, 0, 1, 0, -1]; - let idx = sim.rng.gen_range(0, 4); - Vec2::new(dirs[idx], dirs[idx + 1]) - }; - let road_len = sim.rng.gen_range(1, 4) * 2 + 1; + // Choose a random direction and length for the road + let road_dir = { + let dirs = [-1, 0, 1, 0, -1]; + let idx = rng.gen_range(0, 4); + Vec2::new(dirs[idx], dirs[idx + 1]) + }; + let road_len = 2 + rng.gen_range(1, 3) * 2 + 1; - // Make sure we aren't trying to create a road where a road already exists! - match grid.get(start_pos + road_dir) { - Some(TownCell::Empty) => {} - _ => continue, - } - - // Pave the road - for i in 1..road_len { - let cell_pos = start_pos + road_dir * i; - if let Some(TownCell::Empty) = grid.get(cell_pos) { - grid.set(cell_pos, TownCell::Road); + // Make sure we aren't trying to create a road where a road already exists! + match grid.get(start_pos + road_dir) { + Some(TownCell::Empty) => {} + _ => continue, } - } - grid.set(start_pos + road_dir * road_len, TownCell::Junction); - break; + // Pave the road + for i in 1..road_len { + let cell_pos = start_pos + road_dir * i; + if let Some(TownCell::Empty) = grid.get(cell_pos) { + grid.set( + cell_pos, + if i == road_len - 1 { + TownCell::Junction + } else { + TownCell::Street + }, + ); + } else { + grid.set(cell_pos, TownCell::Junction); + break; + } + } + + break; + } }; - for _ in 0..8 { + // Create roads + for _ in 0..12 { create_road(); } + // Place houses + for x in 0..grid.size().x { + for y in 0..grid.size().y { + let pos = Vec2::new(x, y); + let wpos = center + (pos - grid.size() / 2) * CELL_SIZE + CELL_SIZE / 2; + + // Is this cell near a road? + let near_road = 'near_road: { + let dirs = [-1, 0, 1, 0]; + let offs = rng.gen_range(0, 4); + for i in 0..4 { + let dir = Vec2::new(dirs[(offs + i) % 4], dirs[(offs + i + 1) % 4]); + if grid.get(pos + dir).unwrap_or(&TownCell::Empty).is_road() { + break 'near_road Some(dir); + } + } + None + }; + + match (near_road, grid.get_mut(pos)) { + (Some(dir), Some(cell @ TownCell::Empty)) if rng.gen_range(0, 6) > 0 => { + let alt = gen.get(wpos).map(|sample| sample.alt).unwrap_or(0.0) as i32; + + *cell = TownCell::Building { + kind: Building::House, + wpos: Vec3::new(wpos.x, wpos.y, alt), + size: Vec2::one(), + units: ( + Vec2::new(dir.y, dir.x) * (rng.gen_range(0, 1) * 2 - 1), + -dir, + ), + seed: rng.gen(), + }; + } + _ => {} + } + } + } + + // Merge buildings + for x in 0..grid.size().x { + for y in 0..grid.size().y { + let pos = Vec2::new(x, y); + for offx in -1..1 { + for offy in -1..1 { + if grid + .iter_area(pos + Vec2::new(offx, offy), Vec2::broadcast(2)) + .any(|cell| cell.map(|(_, cell)| !cell.mergeable()).unwrap_or(true)) + { + continue; + } + + match grid.get_mut(pos) { + Some(TownCell::Building { + kind, wpos, size, .. + }) => { + *kind = if rng.gen() { + Building::Blacksmith + } else { + Building::TownHall + }; + *wpos += Vec3::new(CELL_SIZE / 2, CELL_SIZE / 2, 0) + * (Vec2::new(offx, offy) * 2 + 1); + *size = Vec2::broadcast(2); + } + _ => continue, + } + + for i in 0..2 { + for j in 0..2 { + let p = Vec2::new(i + offx, j + offy); + if pos + p != pos { + grid.set(pos + p, TownCell::PartOf(pos)); + } + } + } + } + } + } + } + Some(Self { center, radius, @@ -93,25 +260,64 @@ impl TownState { } fn get_cell(&self, wpos: Vec2) -> &TownCell { - self.grid - .get((wpos - self.center + self.radius) / CELL_SIZE) + let rpos = wpos - self.center; + match self + .grid + .get(rpos.map(|e| e.div_euclid(CELL_SIZE)) + self.grid.size() / 2) .unwrap_or(&TownCell::Empty) + { + TownCell::PartOf(pos) => self.grid.get(*pos).unwrap(), + cell => cell, + } } } pub struct TownGen; impl<'a> Sampler<'a> for TownGen { - type Index = (&'a TownState, Vec3, &'a ColumnSample<'a>); + type Index = (&'a TownState, Vec3, &'a ColumnSample<'a>, f32); type Sample = Option; - fn get(&self, (town, wpos, sample): Self::Index) -> Self::Sample { + fn get(&self, (town, wpos, sample, height): Self::Index) -> Self::Sample { match town.get_cell(Vec2::from(wpos)) { - TownCell::Road if wpos.z < sample.alt as i32 + 4 => { - Some(Block::new(BlockKind::Normal, Rgb::new(255, 200, 150))) + cell if cell.is_road() => { + if (wpos.z as f32) < height - 1.0 { + Some(Block::new( + BlockKind::Normal, + Lerp::lerp( + Rgb::new(150.0, 120.0, 50.0), + Rgb::new(100.0, 70.0, 20.0), + sample.marble_small, + ) + .map(|e| e as u8), + )) + } else { + Some(Block::empty()) + } } - TownCell::Junction if wpos.z < sample.alt as i32 + 4 => { - Some(Block::new(BlockKind::Normal, Rgb::new(255, 200, 250))) + TownCell::Building { + kind, + wpos: building_wpos, + units, + seed, + .. + } => { + let rpos = wpos - building_wpos; + let volumes: &'static [_] = match kind { + Building::House => &HOUSES, + Building::Blacksmith => &BLACKSMITHS, + Building::TownHall => &TOWNHALLS, + }; + volumes[*seed as usize % volumes.len()] + .get( + Vec3::from(units.0) * rpos.x + + Vec3::from(units.1) * rpos.y + + Vec3::unit_z() * rpos.z, + ) + .ok() + .and_then(|sb| { + block_from_structure(*sb, BlockKind::Normal, wpos, wpos.into(), 0, sample) + }) } _ => None, } @@ -125,6 +331,6 @@ impl<'a> Generator<'a, TownState> for TownGen { wpos: Vec2, sample: &ColumnSample, ) -> (f32, f32) { - (sample.alt - 32.0, sample.alt + 64.0) + (sample.alt - 32.0, sample.alt + 75.0) } } diff --git a/world/src/lib.rs b/world/src/lib.rs index 69bd984cdd..cb0deb7f1f 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -3,7 +3,8 @@ const_generics, euclidean_division, bind_by_move_pattern_guards, - option_flattening + option_flattening, + label_break_value )] mod all; @@ -57,11 +58,11 @@ impl World { pub fn sample_columns( &self, ) -> impl Sampler, Sample = Option> + '_ { - ColumnGen::new(self) + ColumnGen::new(&self.sim) } pub fn sample_blocks(&self) -> BlockGen { - BlockGen::new(self, ColumnGen::new(self)) + BlockGen::new(self, ColumnGen::new(&self.sim)) } pub fn generate_chunk(&self, chunk_pos: Vec2) -> (TerrainChunk, ChunkSupplement) { diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index e11a979182..5c1d1d9f2d 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -11,6 +11,7 @@ use self::util::{ use crate::{ all::ForestKind, + column::ColumnGen, generator::TownState, util::{seed_expan, FastNoise, Sampler, StructureGen2d}, CONFIG, @@ -441,7 +442,8 @@ impl WorldSim { let maybe_town = maybe_towns .entry(*pos) .or_insert_with(|| { - TownState::generate(*pos, *seed, self).map(|t| Arc::new(t)) + TownState::generate(*pos, *seed, &mut ColumnGen::new(self), &mut rng) + .map(|t| Arc::new(t)) }) .as_mut() // Only care if we're close to the town diff --git a/world/src/util/grid.rs b/world/src/util/grid.rs index e47995fe51..a30ca989a8 100644 --- a/world/src/util/grid.rs +++ b/world/src/util/grid.rs @@ -13,8 +13,12 @@ impl Grid { } } - fn idx(&self, pos: Vec2) -> usize { - (pos.y * self.size.x + pos.x) as usize + fn idx(&self, pos: Vec2) -> Option { + if pos.map2(self.size, |e, sz| e >= 0 && e < sz).reduce_and() { + Some((pos.y * self.size.x + pos.x) as usize) + } else { + None + } } pub fn size(&self) -> Vec2 { @@ -22,24 +26,45 @@ impl Grid { } pub fn get(&self, pos: Vec2) -> Option<&T> { - self.cells.get(self.idx(pos)) + self.cells.get(self.idx(pos)?) } pub fn get_mut(&mut self, pos: Vec2) -> Option<&mut T> { - let idx = self.idx(pos); + let idx = self.idx(pos)?; self.cells.get_mut(idx) } - pub fn set(&mut self, pos: Vec2, cell: T) { - let idx = self.idx(pos); - self.cells.get_mut(idx).map(|c| *c = cell); + pub fn set(&mut self, pos: Vec2, cell: T) -> Option<()> { + let idx = self.idx(pos)?; + self.cells.get_mut(idx).map(|c| *c = cell) } pub fn iter(&self) -> impl Iterator, &T)> + '_ { (0..self.size.x) .map(move |x| { - (0..self.size.y) - .map(move |y| (Vec2::new(x, y), &self.cells[self.idx(Vec2::new(x, y))])) + (0..self.size.y).map(move |y| { + ( + Vec2::new(x, y), + &self.cells[self.idx(Vec2::new(x, y)).unwrap()], + ) + }) + }) + .flatten() + } + + pub fn iter_area( + &self, + pos: Vec2, + size: Vec2, + ) -> impl Iterator, &T)>> + '_ { + (0..size.x) + .map(move |x| { + (0..size.y).map(move |y| { + Some(( + pos + Vec2::new(x, y), + &self.cells[self.idx(pos + Vec2::new(x, y))?], + )) + }) }) .flatten() } From 27c208d1a5e9fbd897c25cf05d261d0feb4bcf2d Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sun, 25 Aug 2019 09:40:07 +0100 Subject: [PATCH 09/24] Fixed terrain sprite transparency layering --- voxygen/src/scene/terrain.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index d01e5e52fa..de8e089805 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -602,16 +602,9 @@ impl Terrain { focus_pos: Vec3, ) { // Opaque - for (_, chunk) in &self.chunks { - if chunk.visible { - renderer.render_terrain_chunk(&chunk.opaque_model, globals, &chunk.locals, lights); - } - } - - // Translucent for (pos, chunk) in &self.chunks { if chunk.visible { - renderer.render_fluid_chunk(&chunk.fluid_model, globals, &chunk.locals, lights); + renderer.render_terrain_chunk(&chunk.opaque_model, globals, &chunk.locals, lights); const SPRITE_RENDER_DISTANCE: f32 = 128.0; @@ -632,5 +625,12 @@ impl Terrain { } } } + + // Translucent + for (_, chunk) in &self.chunks { + if chunk.visible { + renderer.render_fluid_chunk(&chunk.fluid_model, globals, &chunk.locals, lights); + } + } } } From 4de7a064a18719703199de5b2ab0766753174a1c Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sun, 25 Aug 2019 11:29:02 +0100 Subject: [PATCH 10/24] Added module parts, made cell size smaller --- assets/world/module/human/base-center.vox | 3 +++ assets/world/module/human/base-corner-blank.vox | 3 +++ assets/world/module/human/base-edge-blank.vox | 3 +++ assets/world/module/human/base-edge-door.vox | 3 +++ assets/world/module/human/base-edge-window.vox | 3 +++ assets/world/module/human/base-inner-blank.vox | 3 +++ assets/world/module/human/example.vox | 3 +++ world/src/generator/town.rs | 6 +++--- 8 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 assets/world/module/human/base-center.vox create mode 100644 assets/world/module/human/base-corner-blank.vox create mode 100644 assets/world/module/human/base-edge-blank.vox create mode 100644 assets/world/module/human/base-edge-door.vox create mode 100644 assets/world/module/human/base-edge-window.vox create mode 100644 assets/world/module/human/base-inner-blank.vox create mode 100644 assets/world/module/human/example.vox diff --git a/assets/world/module/human/base-center.vox b/assets/world/module/human/base-center.vox new file mode 100644 index 0000000000..29616bc3df --- /dev/null +++ b/assets/world/module/human/base-center.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3f9c2914651dc476e14edec2371c1779a01511deafd86693def5bfefc2c45ff2 +size 1760 diff --git a/assets/world/module/human/base-corner-blank.vox b/assets/world/module/human/base-corner-blank.vox new file mode 100644 index 0000000000..bf3ada77cf --- /dev/null +++ b/assets/world/module/human/base-corner-blank.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:27d1c3335daaff4d27c9d625f55d13138a06b0f5aee6fa27156a395b2fb850f8 +size 2092 diff --git a/assets/world/module/human/base-edge-blank.vox b/assets/world/module/human/base-edge-blank.vox new file mode 100644 index 0000000000..c2fa1cda63 --- /dev/null +++ b/assets/world/module/human/base-edge-blank.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:427fac732aeee541c3eaa2ffcf0da5e559cce737fac02d32e17f87b26c7c565f +size 1940 diff --git a/assets/world/module/human/base-edge-door.vox b/assets/world/module/human/base-edge-door.vox new file mode 100644 index 0000000000..b8973d9155 --- /dev/null +++ b/assets/world/module/human/base-edge-door.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:34cdbc35f3c8cbf6ef84f442d9d653e04a4118ee8c7397fcffbe311d3802cb72 +size 1924 diff --git a/assets/world/module/human/base-edge-window.vox b/assets/world/module/human/base-edge-window.vox new file mode 100644 index 0000000000..5ec7e4dab5 --- /dev/null +++ b/assets/world/module/human/base-edge-window.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:073a6e406ff0a486ad0f75af0689571a5339de5cf1ad52664e19d05769f2113e +size 1924 diff --git a/assets/world/module/human/base-inner-blank.vox b/assets/world/module/human/base-inner-blank.vox new file mode 100644 index 0000000000..ce1f42c2b3 --- /dev/null +++ b/assets/world/module/human/base-inner-blank.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6e96a9642c267631020a3131b6251e413bfbe737e28860a848aa5dcbc7c96379 +size 1884 diff --git a/assets/world/module/human/example.vox b/assets/world/module/human/example.vox new file mode 100644 index 0000000000..238911fcda --- /dev/null +++ b/assets/world/module/human/example.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:83e48f81d75edd5dde0db2938cf853c7554ab85541b96b9e14f14662bed1bda2 +size 8764 diff --git a/world/src/generator/town.rs b/world/src/generator/town.rs index 7f578a7514..fdb3478992 100644 --- a/world/src/generator/town.rs +++ b/world/src/generator/town.rs @@ -16,7 +16,7 @@ use rand_chacha::ChaChaRng; use std::sync::Arc; use vek::*; -const CELL_SIZE: i32 = 24; +const CELL_SIZE: i32 = 11; static UNIT_CHOOSER: UnitChooser = UnitChooser::new(0x100F4E37); @@ -136,7 +136,7 @@ impl TownState { let idx = rng.gen_range(0, 4); Vec2::new(dirs[idx], dirs[idx + 1]) }; - let road_len = 2 + rng.gen_range(1, 3) * 2 + 1; + let road_len = 2 + rng.gen_range(1, 6) * 2 + 1; // Make sure we aren't trying to create a road where a road already exists! match grid.get(start_pos + road_dir) { @@ -167,7 +167,7 @@ impl TownState { }; // Create roads - for _ in 0..12 { + for _ in 0..16 { create_road(); } From 17a61e0b7c54c93694cfec53c4d6acce66ed893c Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sun, 25 Aug 2019 17:42:18 +0100 Subject: [PATCH 11/24] Fixed terrain sprite transparency issue --- voxygen/src/scene/terrain.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index de8e089805..c93f03f3ba 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -605,7 +605,12 @@ impl Terrain { for (pos, chunk) in &self.chunks { if chunk.visible { renderer.render_terrain_chunk(&chunk.opaque_model, globals, &chunk.locals, lights); + } + } + // Terrain sprites + for (pos, chunk) in &self.chunks { + if chunk.visible { const SPRITE_RENDER_DISTANCE: f32 = 128.0; let chunk_center = pos.map2(Vec2::from(TerrainChunkSize::SIZE), |e, sz: u32| { From f8c5b277e3e3496bf0f1faaae0beb0cb9074ded3 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sun, 25 Aug 2019 18:04:18 +0100 Subject: [PATCH 12/24] Improved mountain slopes and cliffs --- world/src/block/mod.rs | 4 ++-- world/src/sim/mod.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/world/src/block/mod.rs b/world/src/block/mod.rs index 5e8b4688b3..ea1c17c14e 100644 --- a/world/src/block/mod.rs +++ b/world/src/block/mod.rs @@ -184,8 +184,8 @@ impl<'a> BlockGen<'a> { alt + warp } else { let turb = Vec2::new( - world.sim().gen_ctx.fast_turb_x_nz.get(wposf.div(16.0)) as f32, - world.sim().gen_ctx.fast_turb_y_nz.get(wposf.div(16.0)) as f32, + world.sim().gen_ctx.fast_turb_x_nz.get(wposf.div(25.0)) as f32, + world.sim().gen_ctx.fast_turb_y_nz.get(wposf.div(25.0)) as f32, ) * 8.0; let wpos_turb = Vec2::from(wpos).map(|e: i32| e as f32) + turb; diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index 5c1d1d9f2d..0725fc7b97 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -216,7 +216,7 @@ impl WorldSim { // maximal at 0. Also to be multiplied by CONFIG.mountain_scale. let alt_main = (gen_ctx.alt_nz.get((wposf.div(2_000.0)).into_array()) as f32) .abs() - .powf(1.45); + .powf(1.35); (0.0 + alt_main + (gen_ctx.small_nz.get((wposf.div(300.0)).into_array()) as f32) From d17356fb3846abde28caa00511f73254aa403522 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Mon, 26 Aug 2019 01:34:50 +0100 Subject: [PATCH 13/24] Started work on modular buildings --- world/src/generator/town.rs | 86 +++++++++++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 3 deletions(-) diff --git a/world/src/generator/town.rs b/world/src/generator/town.rs index fdb3478992..8ae353e387 100644 --- a/world/src/generator/town.rs +++ b/world/src/generator/town.rs @@ -13,7 +13,7 @@ use common::{ use lazy_static::lazy_static; use rand::prelude::*; use rand_chacha::ChaChaRng; -use std::sync::Arc; +use std::{collections::HashSet, sync::Arc}; use vek::*; const CELL_SIZE: i32 = 11; @@ -65,6 +65,7 @@ pub enum TownCell { seed: u32, }, PartOf(Vec2), + MemberOf(usize), } impl TownCell { @@ -85,10 +86,15 @@ impl TownCell { } } +pub enum Property { + House(Rgb), +} + pub struct TownState { pub center: Vec2, pub radius: i32, pub grid: Grid, + pub properties: Vec, } impl TownState { @@ -136,7 +142,7 @@ impl TownState { let idx = rng.gen_range(0, 4); Vec2::new(dirs[idx], dirs[idx + 1]) }; - let road_len = 2 + rng.gen_range(1, 6) * 2 + 1; + let road_len = 2 + rng.gen_range(1, 4) * 2 + 1; // Make sure we aren't trying to create a road where a road already exists! match grid.get(start_pos + road_dir) { @@ -167,10 +173,70 @@ impl TownState { }; // Create roads - for _ in 0..16 { + for _ in 0..25 { create_road(); } + let mut properties = Vec::new(); + let mut place_house = || 'house: loop { + let start_pos = 'start_pos: { + for _ in 0..30 { + let pos = Vec2::new( + rng.gen_range(0, grid.size().x), + rng.gen_range(0, grid.size().y), + ); + + match grid.get(pos) { + Some(TownCell::Empty) => break 'start_pos pos, + _ => {} + } + } + + break 'house; + }; + + let mut cells = HashSet::new(); + cells.insert(start_pos); + + let mut growth_energy = rng.gen_range(50, 80); + while growth_energy > 0 { + let pos = cells.iter().choose(rng).copied().unwrap(); + + let dir = { + let dirs = [-1, 0, 1, 0, -1]; + let idx = rng.gen_range(0, 4); + Vec2::new(dirs[idx], dirs[idx + 1]) + }; + + match grid.get(pos + dir) { + Some(TownCell::Empty) if !cells.contains(&(pos + dir)) => { + growth_energy -= 10; + cells.insert(pos + dir); + } + _ => growth_energy -= 1, + } + } + + if cells.len() < 3 { + break; + } + + let property_idx = properties.len(); + + for cell in cells.into_iter() { + grid.set(cell, TownCell::MemberOf(property_idx)); + } + + properties.push(Property::House(Rgb::new(rng.gen(), rng.gen(), rng.gen()))); + + break; + }; + + for _ in 0..50 { + place_house(); + } + + /* // Place houses for x in 0..grid.size().x { for y in 0..grid.size().y { @@ -251,11 +317,13 @@ impl TownState { } } } + */ Some(Self { center, radius, grid, + properties, }) } @@ -295,6 +363,18 @@ impl<'a> Sampler<'a> for TownGen { Some(Block::empty()) } } + TownCell::MemberOf(idx) => { + if (wpos.z as f32) < height + 8.0 { + Some(Block::new( + BlockKind::Normal, + match town.properties[*idx] { + Property::House(col) => col, + }, + )) + } else { + None + } + } TownCell::Building { kind, wpos: building_wpos, From adb62f448ec27c2e9816ea88cb002f1d45ad330c Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Mon, 26 Aug 2019 17:54:49 +0100 Subject: [PATCH 14/24] Added walls --- world/src/generator/town.rs | 276 ++++++++++++++++++++++++++++++++++-- 1 file changed, 262 insertions(+), 14 deletions(-) diff --git a/world/src/generator/town.rs b/world/src/generator/town.rs index 8ae353e387..73bde112f6 100644 --- a/world/src/generator/town.rs +++ b/world/src/generator/town.rs @@ -66,6 +66,8 @@ pub enum TownCell { }, PartOf(Vec2), MemberOf(usize), + Market, + Wall, } impl TownCell { @@ -142,7 +144,7 @@ impl TownState { let idx = rng.gen_range(0, 4); Vec2::new(dirs[idx], dirs[idx + 1]) }; - let road_len = 2 + rng.gen_range(1, 4) * 2 + 1; + let road_len = 2 + rng.gen_range(1, 3) * 2 + 1; // Make sure we aren't trying to create a road where a road already exists! match grid.get(start_pos + road_dir) { @@ -177,18 +179,77 @@ impl TownState { create_road(); } + let markets = rng.gen_range(1, 3); + let mut create_market = || { + for _ in 0..30 { + let start_pos = Vec2::new( + grid.size().x / 4 + rng.gen_range(0, grid.size().x / 2), + grid.size().y / 4 + rng.gen_range(0, grid.size().y / 2), + ); + + if let Some(TownCell::Empty) = grid.get(start_pos) { + let mut cells = HashSet::new(); + cells.insert(start_pos); + + let mut energy = 1000; + while energy > 0 { + energy -= 1; + + let pos = cells.iter().choose(rng).copied().unwrap(); + + let dir = { + let dirs = [-1, 0, 1, 0, -1]; + let idx = rng.gen_range(0, 4); + Vec2::new(dirs[idx], dirs[idx + 1]) + }; + + if cells.contains(&(pos + dir)) { + continue; + } + + if let Some(TownCell::Empty) = grid.get(pos + dir) { + cells.insert(pos + dir); + energy -= 10; + } + } + + if cells.len() >= 9 && cells.len() <= 25 { + for cell in cells.iter() { + grid.set(*cell, TownCell::Market); + } + + break; + } + } + } + }; + + for _ in 0..markets { + create_market(); + } + let mut properties = Vec::new(); let mut place_house = || 'house: loop { let start_pos = 'start_pos: { - for _ in 0..30 { + for _ in 0..50 { let pos = Vec2::new( - rng.gen_range(0, grid.size().x), - rng.gen_range(0, grid.size().y), + rng.gen_range(4, grid.size().x - 4), + rng.gen_range(4, grid.size().y - 4), ); - match grid.get(pos) { - Some(TownCell::Empty) => break 'start_pos pos, - _ => {} + let dirs = [-1, 0, 1, 0, -1]; + let road_neighbours = (0..4) + .filter(|idx| { + grid.get(pos + Vec2::new(dirs[*idx], dirs[*idx + 1])) + .map(|cell| cell.is_road()) + .unwrap_or(false) + }) + .count(); + + if road_neighbours > 0 { + if let Some(TownCell::Empty) = grid.get(pos) { + break 'start_pos pos; + } } } @@ -198,8 +259,10 @@ impl TownState { let mut cells = HashSet::new(); cells.insert(start_pos); - let mut growth_energy = rng.gen_range(50, 80); + let mut growth_energy = rng.gen_range(50, 160); while growth_energy > 0 { + growth_energy -= 1; + let pos = cells.iter().choose(rng).copied().unwrap(); let dir = { @@ -208,12 +271,16 @@ impl TownState { Vec2::new(dirs[idx], dirs[idx + 1]) }; + if cells.contains(&(pos + dir)) { + continue; + } + match grid.get(pos + dir) { - Some(TownCell::Empty) if !cells.contains(&(pos + dir)) => { + Some(TownCell::Empty) => { growth_energy -= 10; cells.insert(pos + dir); } - _ => growth_energy -= 1, + _ => {} } } @@ -223,19 +290,186 @@ impl TownState { let property_idx = properties.len(); - for cell in cells.into_iter() { - grid.set(cell, TownCell::MemberOf(property_idx)); + for _ in 0..100 { + let cell = match cells.iter().choose(rng) { + Some(cell) => *cell, + None => break, + }; + + let dirs = [-1, 0, 1, 0, -1]; + let neighbours = (0..4) + .filter(|idx| cells.contains(&(cell + Vec2::new(dirs[*idx], dirs[*idx + 1])))) + .count(); + + if neighbours < 2 { + cells.remove(&cell); + } } - properties.push(Property::House(Rgb::new(rng.gen(), rng.gen(), rng.gen()))); + for cell in cells.iter() { + grid.set(*cell, TownCell::MemberOf(property_idx)); + } + + if cells.len() > 0 { + properties.push(Property::House(Rgb::new(rng.gen(), rng.gen(), rng.gen()))); + } break; }; - for _ in 0..50 { + for _ in 0..40 { place_house(); } + /* + let mut create_walls = || { + for i in 0..grid.size().x { + grid.set(Vec2::new(i, 0), TownCell::Wall); + grid.set(Vec2::new(i, grid.size().y - 1), TownCell::Wall); + } + + for j in 0..grid.size().y { + grid.set(Vec2::new(0, j), TownCell::Wall); + grid.set(Vec2::new(grid.size().x - 1, j), TownCell::Wall); + } + }; + */ + + fn floodfill( + mut opens: HashSet>, + grid: &Grid, + mut f: impl FnMut(Vec2, &TownCell) -> bool, + ) -> HashSet> { + let mut closed = HashSet::new(); + + while opens.len() > 0 { + let mut new_opens = HashSet::new(); + + for open in opens.iter() { + for i in -1..2 { + for j in -1..2 { + let pos = *open + Vec2::new(i, j); + + if let Some(cell) = grid.get(pos) { + if f(pos, cell) && !closed.contains(&pos) && !opens.contains(&pos) { + new_opens.insert(pos); + } + } + } + } + } + + closed = closed.union(&opens).copied().collect(); + opens = new_opens; + } + + closed + } + + let mut create_walls = || { + let mut opens = HashSet::new(); + + for i in 0..grid.size().x { + opens.insert(Vec2::new(i, 0)); + opens.insert(Vec2::new(i, grid.size().y - 1)); + } + + for j in 0..grid.size().y { + opens.insert(Vec2::new(0, j)); + opens.insert(Vec2::new(grid.size().x - 1, j)); + } + + let outer = floodfill(opens, &grid, |_, cell| { + if let TownCell::Empty = cell { + true + } else { + false + } + }); + + let mut walls = HashSet::new(); + + floodfill( + [grid.size() / 2].iter().copied().collect(), + &grid, + |pos, _| { + if outer.contains(&pos) { + walls.insert(pos); + false + } else { + true + } + }, + ); + + for cell in walls.iter() { + grid.set(*cell, TownCell::Wall); + } + }; + + create_walls(); + + let mut remove_extra_walls = || { + for x in 0..grid.size().x { + for y in 0..grid.size().y { + let pos = Vec2::new(x, y); + let mut wall_count = 0; + for i in 0..2 { + for j in 0..2 { + if let Some(TownCell::Wall) = grid.get(pos + Vec2::new(i, j)) { + wall_count += 1; + } + } + } + + if wall_count == 4 { + grid.set(pos, TownCell::Empty); + } + } + } + }; + + remove_extra_walls(); + + let mut variate_walls = || { + for _ in 0..100 { + let pos = Vec2::new( + rng.gen_range(0, grid.size().x - 1), + rng.gen_range(0, grid.size().y - 1), + ); + + let (mut wall_count, mut empty_count) = (0, 0); + for i in 0..2 { + for j in 0..2 { + match grid.get(pos + Vec2::new(i, j)) { + Some(TownCell::Wall) => wall_count += 1, + Some(TownCell::Empty) => empty_count += 1, + _ => {} + } + } + } + + // Swap! + if (wall_count, empty_count) == (3, 1) { + let cell00 = grid.get(pos + Vec2::new(0, 0)).unwrap().clone(); + let cell10 = grid.get(pos + Vec2::new(1, 0)).unwrap().clone(); + let cell01 = grid.get(pos + Vec2::new(0, 1)).unwrap().clone(); + let cell11 = grid.get(pos + Vec2::new(1, 1)).unwrap().clone(); + + grid.set(pos + Vec2::new(0, 0), cell11); + grid.set(pos + Vec2::new(1, 0), cell01); + grid.set(pos + Vec2::new(0, 1), cell10); + grid.set(pos + Vec2::new(1, 1), cell00); + + break; + } + } + }; + + for _ in 0..100 { + variate_walls(); + } + /* // Place houses for x in 0..grid.size().x { @@ -375,6 +609,20 @@ impl<'a> Sampler<'a> for TownGen { None } } + TownCell::Market => { + if (wpos.z as f32) < height { + Some(Block::new(BlockKind::Normal, Rgb::new(255, 0, 0))) + } else { + None + } + } + TownCell::Wall => { + if (wpos.z as f32) < height + 20.0 { + Some(Block::new(BlockKind::Normal, Rgb::new(100, 100, 100))) + } else { + None + } + } TownCell::Building { kind, wpos: building_wpos, From 5a677b9c1ec32f5e51f7f4b7154b1528d54e7206 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Wed, 28 Aug 2019 19:32:44 +0100 Subject: [PATCH 15/24] Added modular building generation --- assets/voxygen/shaders/postprocess-frag.glsl | 2 +- assets/voxygen/shaders/terrain-vert.glsl | 2 +- .../world/module/human/balcony_upstairs.vox | 3 + assets/world/module/human/base-center.vox | 3 - .../world/module/human/base-corner-blank.vox | 3 - assets/world/module/human/base-edge-blank.vox | 3 - assets/world/module/human/base-edge-door.vox | 3 - .../world/module/human/base-edge-window.vox | 3 - .../world/module/human/base-inner-blank.vox | 3 - assets/world/module/human/chimney_roof.vox | 3 + assets/world/module/human/corner_ground.vox | 3 + assets/world/module/human/corner_roof.vox | 3 + assets/world/module/human/corner_upstairs.vox | 3 + assets/world/module/human/door_big.vox | 3 + assets/world/module/human/door_ground.vox | 3 + assets/world/module/human/example.vox | 3 - assets/world/module/human/floor_ground.vox | 3 + assets/world/module/human/floor_roof.vox | 3 + assets/world/module/human/floor_upstairs.vox | 3 + assets/world/module/human/stair_ground.vox | 3 + assets/world/module/human/wall_ground.vox | 3 + assets/world/module/human/wall_roof.vox | 3 + assets/world/module/human/wall_upstairs.vox | 3 + assets/world/module/human/window_ground.vox | 3 + assets/world/module/human/window_upstairs.vox | 3 + server-cli/.gitignore | 1 + world/src/block/natural.rs | 1 + world/src/column/mod.rs | 8 + world/src/generator/mod.rs | 20 + world/src/generator/{town.rs => town/mod.rs} | 623 +++++++++++++++++- world/src/generator/town/util.rs | 42 ++ world/src/generator/town/vol.rs | 203 ++++++ world/src/sim/mod.rs | 7 +- world/src/util/grid.rs | 23 +- 34 files changed, 957 insertions(+), 44 deletions(-) create mode 100644 assets/world/module/human/balcony_upstairs.vox delete mode 100644 assets/world/module/human/base-center.vox delete mode 100644 assets/world/module/human/base-corner-blank.vox delete mode 100644 assets/world/module/human/base-edge-blank.vox delete mode 100644 assets/world/module/human/base-edge-door.vox delete mode 100644 assets/world/module/human/base-edge-window.vox delete mode 100644 assets/world/module/human/base-inner-blank.vox create mode 100644 assets/world/module/human/chimney_roof.vox create mode 100644 assets/world/module/human/corner_ground.vox create mode 100644 assets/world/module/human/corner_roof.vox create mode 100644 assets/world/module/human/corner_upstairs.vox create mode 100644 assets/world/module/human/door_big.vox create mode 100644 assets/world/module/human/door_ground.vox delete mode 100644 assets/world/module/human/example.vox create mode 100644 assets/world/module/human/floor_ground.vox create mode 100644 assets/world/module/human/floor_roof.vox create mode 100644 assets/world/module/human/floor_upstairs.vox create mode 100644 assets/world/module/human/stair_ground.vox create mode 100644 assets/world/module/human/wall_ground.vox create mode 100644 assets/world/module/human/wall_roof.vox create mode 100644 assets/world/module/human/wall_upstairs.vox create mode 100644 assets/world/module/human/window_ground.vox create mode 100644 assets/world/module/human/window_upstairs.vox create mode 100644 server-cli/.gitignore rename world/src/generator/{town.rs => town/mod.rs} (50%) create mode 100644 world/src/generator/town/util.rs create mode 100644 world/src/generator/town/vol.rs diff --git a/assets/voxygen/shaders/postprocess-frag.glsl b/assets/voxygen/shaders/postprocess-frag.glsl index a9316ac993..f653f1d30c 100644 --- a/assets/voxygen/shaders/postprocess-frag.glsl +++ b/assets/voxygen/shaders/postprocess-frag.glsl @@ -170,7 +170,7 @@ void main() { hsva_color.y *= 1.45; hsva_color.z *= 0.85; //hsva_color.z = 1.0 - 1.0 / (1.0 * hsva_color.z + 1.0); - vec4 final_color = fxaa_color; + vec4 final_color = fxaa_color; //vec4 final_color = vec4(hsv2rgb(hsva_color.rgb), hsva_color.a); tgt_color = vec4(final_color.rgb, 1); diff --git a/assets/voxygen/shaders/terrain-vert.glsl b/assets/voxygen/shaders/terrain-vert.glsl index 14fe299613..bbfe953324 100644 --- a/assets/voxygen/shaders/terrain-vert.glsl +++ b/assets/voxygen/shaders/terrain-vert.glsl @@ -47,4 +47,4 @@ void main() { proj_mat * view_mat * vec4(f_pos, 1); -} \ No newline at end of file +} diff --git a/assets/world/module/human/balcony_upstairs.vox b/assets/world/module/human/balcony_upstairs.vox new file mode 100644 index 0000000000..49e0d6826e --- /dev/null +++ b/assets/world/module/human/balcony_upstairs.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a61471613997d50f2e3739e6417df4c60dfc83b8091859e8ed038fb8aca79e55 +size 1416 diff --git a/assets/world/module/human/base-center.vox b/assets/world/module/human/base-center.vox deleted file mode 100644 index 29616bc3df..0000000000 --- a/assets/world/module/human/base-center.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3f9c2914651dc476e14edec2371c1779a01511deafd86693def5bfefc2c45ff2 -size 1760 diff --git a/assets/world/module/human/base-corner-blank.vox b/assets/world/module/human/base-corner-blank.vox deleted file mode 100644 index bf3ada77cf..0000000000 --- a/assets/world/module/human/base-corner-blank.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:27d1c3335daaff4d27c9d625f55d13138a06b0f5aee6fa27156a395b2fb850f8 -size 2092 diff --git a/assets/world/module/human/base-edge-blank.vox b/assets/world/module/human/base-edge-blank.vox deleted file mode 100644 index c2fa1cda63..0000000000 --- a/assets/world/module/human/base-edge-blank.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:427fac732aeee541c3eaa2ffcf0da5e559cce737fac02d32e17f87b26c7c565f -size 1940 diff --git a/assets/world/module/human/base-edge-door.vox b/assets/world/module/human/base-edge-door.vox deleted file mode 100644 index b8973d9155..0000000000 --- a/assets/world/module/human/base-edge-door.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:34cdbc35f3c8cbf6ef84f442d9d653e04a4118ee8c7397fcffbe311d3802cb72 -size 1924 diff --git a/assets/world/module/human/base-edge-window.vox b/assets/world/module/human/base-edge-window.vox deleted file mode 100644 index 5ec7e4dab5..0000000000 --- a/assets/world/module/human/base-edge-window.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:073a6e406ff0a486ad0f75af0689571a5339de5cf1ad52664e19d05769f2113e -size 1924 diff --git a/assets/world/module/human/base-inner-blank.vox b/assets/world/module/human/base-inner-blank.vox deleted file mode 100644 index ce1f42c2b3..0000000000 --- a/assets/world/module/human/base-inner-blank.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6e96a9642c267631020a3131b6251e413bfbe737e28860a848aa5dcbc7c96379 -size 1884 diff --git a/assets/world/module/human/chimney_roof.vox b/assets/world/module/human/chimney_roof.vox new file mode 100644 index 0000000000..47a118d2a0 --- /dev/null +++ b/assets/world/module/human/chimney_roof.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:77920b4223fd4a05145ef2dd38e5fea91c7b0b1eb72fe1efa4325059e7429eb9 +size 1916 diff --git a/assets/world/module/human/corner_ground.vox b/assets/world/module/human/corner_ground.vox new file mode 100644 index 0000000000..e182e55db5 --- /dev/null +++ b/assets/world/module/human/corner_ground.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7642360b38812647493c24156f6ea85acad4b10019c417cf23b04e6756c4b021 +size 4012 diff --git a/assets/world/module/human/corner_roof.vox b/assets/world/module/human/corner_roof.vox new file mode 100644 index 0000000000..0571175893 --- /dev/null +++ b/assets/world/module/human/corner_roof.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dd97dbd7f0e27d4320d2bc91bd6d7799a9d7dec02e22c8b9f447f8982a7186d7 +size 1676 diff --git a/assets/world/module/human/corner_upstairs.vox b/assets/world/module/human/corner_upstairs.vox new file mode 100644 index 0000000000..7a4552d1e8 --- /dev/null +++ b/assets/world/module/human/corner_upstairs.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:45a6cff3756302dcca46fce84ca6bb5998b25ef2bdbe43794e3e048c1ada0613 +size 4012 diff --git a/assets/world/module/human/door_big.vox b/assets/world/module/human/door_big.vox new file mode 100644 index 0000000000..a7b1fb2f37 --- /dev/null +++ b/assets/world/module/human/door_big.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ea44b43263f79ca708b3b0fa196ed4eb2157521f4349b0c97524757950e4c59e +size 47099 diff --git a/assets/world/module/human/door_ground.vox b/assets/world/module/human/door_ground.vox new file mode 100644 index 0000000000..be08e4ac12 --- /dev/null +++ b/assets/world/module/human/door_ground.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ac34b6009acbce1d2a33bb527719b25e75e295d9f53ec83f1ecd45e0d4d3eb2f +size 3992 diff --git a/assets/world/module/human/example.vox b/assets/world/module/human/example.vox deleted file mode 100644 index 238911fcda..0000000000 --- a/assets/world/module/human/example.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:83e48f81d75edd5dde0db2938cf853c7554ab85541b96b9e14f14662bed1bda2 -size 8764 diff --git a/assets/world/module/human/floor_ground.vox b/assets/world/module/human/floor_ground.vox new file mode 100644 index 0000000000..bfb5262b55 --- /dev/null +++ b/assets/world/module/human/floor_ground.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9d143fd09da40dcdc3c26c662ed969dce98d6115c7d1fdc7a08e0be2a0fd1548 +size 4012 diff --git a/assets/world/module/human/floor_roof.vox b/assets/world/module/human/floor_roof.vox new file mode 100644 index 0000000000..a11cc4523e --- /dev/null +++ b/assets/world/module/human/floor_roof.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c58e5556c76ee6eac8d7f83af8ab66c237d85897fa62f8359872c15852df63bf +size 4012 diff --git a/assets/world/module/human/floor_upstairs.vox b/assets/world/module/human/floor_upstairs.vox new file mode 100644 index 0000000000..9b5c26e2fa --- /dev/null +++ b/assets/world/module/human/floor_upstairs.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dbcf0cf859fce0ecf8f302f669610317331a804cff57e5638a1872a52020c981 +size 4012 diff --git a/assets/world/module/human/stair_ground.vox b/assets/world/module/human/stair_ground.vox new file mode 100644 index 0000000000..bf6aafd99b --- /dev/null +++ b/assets/world/module/human/stair_ground.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f6e10cd6387dfbcf578f5dae2d243b8a35f17e2596856d7b8332ac05809f826b +size 2040 diff --git a/assets/world/module/human/wall_ground.vox b/assets/world/module/human/wall_ground.vox new file mode 100644 index 0000000000..091e29aaf2 --- /dev/null +++ b/assets/world/module/human/wall_ground.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:973892a68060cc889ec4b726dcb9794249442c2c6f3a59c6e8d4958f8cb1fb2e +size 4012 diff --git a/assets/world/module/human/wall_roof.vox b/assets/world/module/human/wall_roof.vox new file mode 100644 index 0000000000..6a1e5a12ff --- /dev/null +++ b/assets/world/module/human/wall_roof.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5b884335c307d2d0e82712c477c7ce33ad2f6b6ecdaa08a432142300c335b1ee +size 4012 diff --git a/assets/world/module/human/wall_upstairs.vox b/assets/world/module/human/wall_upstairs.vox new file mode 100644 index 0000000000..745551a64b --- /dev/null +++ b/assets/world/module/human/wall_upstairs.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d8dac53854429015007dfd44242e902f1d04fe1f11ef818a30225564607cef6e +size 4012 diff --git a/assets/world/module/human/window_ground.vox b/assets/world/module/human/window_ground.vox new file mode 100644 index 0000000000..56d4f885c7 --- /dev/null +++ b/assets/world/module/human/window_ground.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a84d68e3741154a0d4c9732e4f037e3efe67ca5f6c2e94d192c0773f88b6a6b6 +size 4012 diff --git a/assets/world/module/human/window_upstairs.vox b/assets/world/module/human/window_upstairs.vox new file mode 100644 index 0000000000..df35d807f3 --- /dev/null +++ b/assets/world/module/human/window_upstairs.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aa292366a8bc36921c42705db54e3c4ac88d7c833c20f4729b524b9f94aa7803 +size 4012 diff --git a/server-cli/.gitignore b/server-cli/.gitignore new file mode 100644 index 0000000000..dc83325b30 --- /dev/null +++ b/server-cli/.gitignore @@ -0,0 +1 @@ +settings.ron diff --git a/world/src/block/natural.rs b/world/src/block/natural.rs index 79fde45685..54c6f27306 100644 --- a/world/src/block/natural.rs +++ b/world/src/block/natural.rs @@ -30,6 +30,7 @@ pub fn structure_gen<'a>( if (st_sample.tree_density as f64) < random_seed || st_sample.alt < st_sample.water_level || st_sample.spawn_rate < 0.5 + || !st_sample.spawn_rules.trees { return None; } diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index 34bac0c714..04c1903ae0 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -1,6 +1,7 @@ use crate::{ all::ForestKind, block::StructureMeta, + generator::{Generator, SpawnRules, TownGen}, sim::{LocationInfo, SimChunk, WorldSim}, util::{RandomPerm, Sampler, UnitChooser}, World, CONFIG, @@ -518,6 +519,12 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { location: sim_chunk.location.as_ref(), chunk: sim_chunk, + spawn_rules: sim_chunk + .structures + .town + .as_ref() + .map(|town| TownGen.spawn_rules(town, wpos)) + .unwrap_or(SpawnRules::default()), }) } } @@ -547,6 +554,7 @@ pub struct ColumnSample<'a> { pub location: Option<&'a LocationInfo>, pub chunk: &'a SimChunk, + pub spawn_rules: SpawnRules, } #[derive(Copy, Clone)] diff --git a/world/src/generator/mod.rs b/world/src/generator/mod.rs index b24d0b3d84..b658a4b41b 100644 --- a/world/src/generator/mod.rs +++ b/world/src/generator/mod.rs @@ -7,8 +7,28 @@ use crate::{column::ColumnSample, util::Sampler}; use common::terrain::Block; use vek::*; +#[derive(Copy, Clone, Debug)] +pub struct SpawnRules { + pub trees: bool, +} + +impl Default for SpawnRules { + fn default() -> Self { + Self { trees: true } + } +} + +impl SpawnRules { + pub fn and(self, other: Self) -> Self { + Self { + trees: self.trees && other.trees, + } + } +} + pub trait Generator<'a, T: 'a>: Sampler<'a, Index = (&'a T, Vec3, &'a ColumnSample<'a>, f32), Sample = Option> { fn get_z_limits(&self, state: &'a T, wpos: Vec2, sample: &ColumnSample) -> (f32, f32); + fn spawn_rules(&self, town: &'a TownState, wpos: Vec2) -> SpawnRules; } diff --git a/world/src/generator/town.rs b/world/src/generator/town/mod.rs similarity index 50% rename from world/src/generator/town.rs rename to world/src/generator/town/mod.rs index 73bde112f6..997bfde100 100644 --- a/world/src/generator/town.rs +++ b/world/src/generator/town/mod.rs @@ -1,4 +1,7 @@ -use super::Generator; +mod util; +mod vol; + +use super::{Generator, SpawnRules}; use crate::{ block::block_from_structure, column::{ColumnGen, ColumnSample}, @@ -8,14 +11,617 @@ use crate::{ use common::{ assets, terrain::{Block, BlockKind, Structure}, - vol::{ReadVol, Vox}, + vol::{ReadVol, Vox, WriteVol}, }; +use hashbrown::HashSet; use lazy_static::lazy_static; use rand::prelude::*; use rand_chacha::ChaChaRng; -use std::{collections::HashSet, sync::Arc}; +use std::{ops::Add, sync::Arc}; use vek::*; +use self::vol::{ColumnKind, Module, TownCell, TownColumn, TownVol}; + +const CELL_SIZE: i32 = 9; +const CELL_HEIGHT: i32 = 9; + +pub struct TownGen; + +impl<'a> Sampler<'a> for TownGen { + type Index = (&'a TownState, Vec3, &'a ColumnSample<'a>, f32); + type Sample = Option; + + fn get(&self, (town, wpos, sample, height): Self::Index) -> Self::Sample { + let cell_pos = (wpos - town.center) + .map2(Vec3::new(CELL_SIZE, CELL_SIZE, CELL_HEIGHT), |e, sz| { + e.div_euclid(sz) + }) + .add(Vec3::from(town.vol.size() / 2)); + let inner_pos = (wpos - town.center) + .map2(Vec3::new(CELL_SIZE, CELL_SIZE, CELL_HEIGHT), |e, sz| { + e.rem_euclid(sz) + }); + + match town.vol.get(cell_pos).unwrap_or(&TownCell::Empty) { + TownCell::Empty => None, + TownCell::Park => None, + TownCell::Rock => Some(Block::new(BlockKind::Normal, Rgb::broadcast(100))), + TownCell::Wall => Some(Block::new(BlockKind::Normal, Rgb::broadcast(175))), + TownCell::Road => { + if (wpos.z as f32) < height - 1.0 { + Some(Block::new( + BlockKind::Normal, + Lerp::lerp( + Rgb::new(150.0, 140.0, 50.0), + Rgb::new(100.0, 95.0, 30.0), + sample.marble_small, + ) + .map(|e| e as u8), + )) + } else { + Some(Block::empty()) + } + } + TownCell::House { idx, module } => { + if let Some(module) = module { + let transform = [ + (Vec2::new(0, 0), Vec2::unit_x(), Vec2::unit_y()), + (Vec2::new(0, 1), -Vec2::unit_y(), Vec2::unit_x()), + (Vec2::new(1, 1), -Vec2::unit_x(), -Vec2::unit_y()), + (Vec2::new(1, 0), Vec2::unit_y(), -Vec2::unit_x()), + ]; + + MODULES[module.vol_idx] + .0 + .get( + Vec3::from( + transform[module.dir].0 * (CELL_SIZE - 1) + + transform[module.dir].1 * inner_pos.x + + transform[module.dir].2 * inner_pos.y, + ) + Vec3::unit_z() * inner_pos.z, + ) + .ok() + .and_then(|sb| { + block_from_structure( + *sb, + BlockKind::Normal, + wpos, + wpos.into(), + 0, + sample, + ) + }) + } else { + Some(Block::new(BlockKind::Normal, town.houses[*idx].color)) + } + } + } + } +} + +impl<'a> Generator<'a, TownState> for TownGen { + fn get_z_limits( + &self, + town: &'a TownState, + wpos: Vec2, + sample: &ColumnSample, + ) -> (f32, f32) { + (sample.alt - 32.0, sample.alt + 75.0) + } + + fn spawn_rules(&self, town: &'a TownState, wpos: Vec2) -> SpawnRules { + SpawnRules { trees: false } + } +} + +struct House { + color: Rgb, +} + +pub struct TownState { + center: Vec3, + radius: i32, + vol: TownVol, + houses: Vec, +} + +impl TownState { + pub fn generate(center: Vec2, gen: &mut ColumnGen, rng: &mut impl Rng) -> Option { + let radius = rng.gen_range(12, 24) * 9; + let size = Vec2::broadcast(radius * 2 / 9 - 2); + + let alt = gen.get(center).map(|sample| sample.alt).unwrap_or(0.0) as i32; + + let mut vol = TownVol::generate_from( + size, + |pos| { + let wpos = center + (pos - size / 2) * CELL_SIZE + CELL_SIZE / 2; + let rel_alt = gen.get(wpos).map(|sample| sample.alt).unwrap_or(0.0) as i32 + + CELL_HEIGHT / 2 + - alt; + + let col = TownColumn { + ground: rel_alt.div_euclid(CELL_HEIGHT), + kind: None, + }; + + (col.ground, col) + }, + |(col, pos)| { + if pos.z >= col.ground { + TownCell::Empty + } else { + TownCell::Rock + } + }, + ); + + // Generation passes + vol.setup(rng); + vol.gen_roads(rng, 30); + //vol.gen_parks(rng, 8); + vol.emplace_columns(); + let houses = vol.gen_houses(rng, 60); + vol.gen_walls(); + vol.resolve_modules(rng); + + Some(Self { + center: Vec3::new(center.x, center.y, alt), + radius, + vol, + houses, + }) + } + + pub fn center(&self) -> Vec3 { + self.center + } + + pub fn radius(&self) -> i32 { + self.radius + } +} + +impl TownVol { + fn floodfill( + &self, + mut opens: HashSet>, + mut f: impl FnMut(Vec2, &TownColumn) -> bool, + ) -> HashSet> { + let mut closed = HashSet::new(); + + while opens.len() > 0 { + let mut new_opens = HashSet::new(); + + for open in opens.iter() { + for i in -1..2 { + for j in -1..2 { + let pos = *open + Vec2::new(i, j); + + if let Some(col) = self.col(pos) { + if !closed.contains(&pos) && !opens.contains(&pos) && f(pos, col) { + new_opens.insert(pos); + } + } + } + } + } + + closed = closed.union(&opens).copied().collect(); + opens = new_opens; + } + + closed + } + + fn setup(&mut self, rng: &mut impl Rng) { + // Place a single road tile at first + let root_road = self + .size() + .map(|sz| (sz / 8) * 2 + rng.gen_range(0, sz / 4) * 2); + self.set_col_kind(root_road, Some(ColumnKind::Road)); + } + + fn gen_roads(&mut self, rng: &mut impl Rng, n: usize) { + const ATTEMPTS: usize = 5; + + let mut junctions = HashSet::new(); + junctions.insert(self.choose_column(rng, |_, col| col.is_road()).unwrap()); + + for road in 0..n { + for _ in 0..ATTEMPTS { + let start = *junctions.iter().choose(rng).unwrap(); + //let start = self.choose_column(rng, |pos, col| pos.map(|e| e % 2 == 0).reduce_and() && col.is_road()).unwrap(); + let dir = util::gen_dir(rng); + + // If the direction we want to paint a path in is obstructed, abandon this attempt + if self + .col(start + dir) + .map(|col| !col.is_empty()) + .unwrap_or(true) + { + continue; + } + + // How long should this road be? + let len = rng.gen_range(1, 10) * 2 + 1; + + // Paint the road until we hit an obstacle + let success = (1..len) + .map(|i| start + dir * i) + .try_for_each(|pos| { + if self.col(pos).map(|col| col.is_empty()).unwrap_or(false) { + self.set_col_kind(pos, Some(ColumnKind::Road)); + Ok(()) + } else { + junctions.insert(pos); + Err(()) + } + }) + .is_ok(); + + if success { + junctions.insert(start + dir * (len - 1)); + } + + break; + } + } + } + + fn gen_parks(&mut self, rng: &mut impl Rng, n: usize) { + const ATTEMPTS: usize = 5; + + for _ in 0..n { + for _ in 0..ATTEMPTS { + let start = self + .choose_column(rng, |pos, col| { + col.is_empty() + && (0..4).any(|i| { + self.col(pos + util::dir(i)) + .map(|col| col.is_road()) + .unwrap_or(false) + }) + }) + .unwrap(); + + let mut energy = 50; + let mut park = self.floodfill([start].iter().copied().collect(), |_, col| { + if col.is_empty() && energy > 0 { + energy -= 1; + true + } else { + false + } + }); + + if park.len() < 4 { + continue; + } + + for cell in park { + self.set_col_kind(cell, Some(ColumnKind::Internal)); + let col = self.col(cell).unwrap(); + let ground = col.ground; + for z in 0..2 { + self.set(Vec3::new(cell.x, cell.y, ground + z), TownCell::Park); + } + } + + break; + } + } + } + + fn gen_walls(&mut self) { + let mut outer = HashSet::new(); + for i in 0..self.size().x { + outer.insert(Vec2::new(i, 0)); + outer.insert(Vec2::new(i, self.size().y - 1)); + } + for j in 0..self.size().y { + outer.insert(Vec2::new(0, j)); + outer.insert(Vec2::new(self.size().x - 1, j)); + } + + let mut outer = self.floodfill(outer, |_, col| col.is_empty()); + + let mut walls = HashSet::new(); + self.floodfill([self.size() / 2].iter().copied().collect(), |pos, _| { + if outer.contains(&pos) { + walls.insert(pos); + false + } else { + true + } + }); + + while let Some(wall) = walls + .iter() + .filter(|pos| { + (0..4) + .filter(|i| walls.contains(&(**pos + util::dir(*i)))) + .count() + < 2 + }) + .next() + { + let wall = *wall; + walls.remove(&wall); + } + + for wall in walls.iter() { + let col = self.col(*wall).unwrap(); + let ground = col.ground; + for z in -1..2 { + self.set(Vec3::new(wall.x, wall.y, ground + z), TownCell::Wall); + } + } + } + + fn emplace_columns(&mut self) { + for i in 0..self.size().x { + for j in 0..self.size().y { + let col = self.col(Vec2::new(i, j)).unwrap(); + let ground = col.ground; + + match col.kind { + None => {} + Some(ColumnKind::Internal) => {} + Some(ColumnKind::External) => {} + Some(ColumnKind::Road) => { + for z in -1..2 { + self.set(Vec3::new(i, j, ground + z), TownCell::Road); + } + } + _ => unimplemented!(), + } + } + } + } + + fn gen_houses(&mut self, rng: &mut impl Rng, n: usize) -> Vec { + const ATTEMPTS: usize = 20; + + let mut houses = Vec::new(); + for _ in 0..n { + for _ in 0..ATTEMPTS { + let entrance = { + let start = self.choose_cell(rng, |_, cell| cell.is_road()).unwrap(); + let dir = Vec3::from(util::gen_dir(rng)); + + if self + .get(start + dir) + .map(|col| !col.is_empty()) + .unwrap_or(true) + || self + .get(start + dir - Vec3::unit_z()) + .map(|col| !col.is_foundation()) + .unwrap_or(true) + { + continue; + } else { + start + dir + } + }; + + let mut cells: HashSet<_> = Some(entrance).into_iter().collect(); + + let mut energy = 1000; + while energy > 0 { + energy -= 1; + + let parent = *cells.iter().choose(rng).unwrap(); + let dir = util::UNITS_3D + .choose_weighted(rng, |pos| 1 + pos.z.max(0)) + .unwrap(); + + if self + .get(parent + dir) + .map(|cell| cell.is_empty()) + .unwrap_or(false) + && self + .get(parent + dir - Vec3::unit_z()) + .map(|cell| { + cell.is_foundation() + || cells.contains(&(parent + dir - Vec3::unit_z())) + }) + .unwrap_or(false) + { + cells.insert(parent + dir); + energy -= 10; + } + } + + // Remove cells that are too isolated + loop { + let cells_copy = cells.clone(); + + let mut any_removed = false; + cells.retain(|pos| { + let neighbour_count = (0..6) + .filter(|i| { + let neighbour = pos + util::dir_3d(*i); + cells_copy.contains(&neighbour) + }) + .count(); + + if neighbour_count < 3 { + any_removed = true; + false + } else { + true + } + }); + + if !any_removed { + break; + } + } + + // Get rid of houses that are too small + if cells.len() < 6 { + continue; + } + + for cell in cells { + self.set( + cell, + TownCell::House { + idx: houses.len(), + module: None, + }, + ); + self.set_col_kind(Vec2::from(cell), Some(ColumnKind::Internal)); + } + + houses.push(House { + color: Rgb::new(rng.gen(), rng.gen(), rng.gen()), + }); + } + } + + houses + } + + fn resolve_modules(&mut self, rng: &mut impl Rng) { + fn classify(cell: &TownCell, this_house: usize) -> ModuleKind { + match cell { + TownCell::House { idx, .. } if *idx == this_house => ModuleKind::This, + _ => ModuleKind::That, + } + } + + for x in 0..self.size().x { + for y in 0..self.size().y { + 'cell: for z in self.col_range(Vec2::new(x, y)).unwrap() { + let pos = Vec3::new(x, y, z); + let this_idx = if let Ok(TownCell::House { idx, module }) = self.get(pos) { + idx + } else { + continue; + }; + + let mut signature = [ModuleKind::That; 6]; + for i in 0..6 { + signature[i] = self + .get(pos + util::dir_3d(i)) + .map(|cell| classify(cell, *this_idx)) + .unwrap_or(ModuleKind::That); + } + + let module = MODULES + .iter() + .enumerate() + .filter_map(|(i, module)| { + let perms = [[0, 1, 2, 3], [3, 0, 1, 2], [2, 3, 0, 1], [1, 2, 3, 0]]; + + let mut rotated_signature = [ModuleKind::That; 6]; + for (dir, perm) in perms.iter().enumerate() { + rotated_signature[perm[0]] = signature[0]; + rotated_signature[perm[1]] = signature[1]; + rotated_signature[perm[2]] = signature[2]; + rotated_signature[perm[3]] = signature[3]; + rotated_signature[4] = signature[4]; + rotated_signature[5] = signature[5]; + + if &module.1[0..6] == &rotated_signature[0..6] { + return Some(Module { vol_idx: i, dir }); + } + } + + None + }) + .choose(rng); + + self.set( + pos, + TownCell::House { + idx: *this_idx, + module, + }, + ); + } + } + } + } +} + +#[derive(Copy, Clone, PartialEq)] +pub enum ModuleKind { + This, + That, +} + +lazy_static! { + pub static ref MODULES: Vec<(Arc, [ModuleKind; 6])> = { + use ModuleKind::*; + vec![ + ( + assets::load("world.module.human.floor_ground").unwrap(), + [This, This, This, This, This, That], + ), + ( + assets::load("world.module.human.stair_ground").unwrap(), + [This, This, This, This, This, That], + ), + ( + assets::load("world.module.human.corner_ground").unwrap(), + [This, This, That, That, This, That], + ), + ( + assets::load("world.module.human.wall_ground").unwrap(), + [This, This, This, That, This, That], + ), + ( + assets::load("world.module.human.door_ground").unwrap(), + [This, This, This, That, This, That], + ), + ( + assets::load("world.module.human.window_ground").unwrap(), + [This, This, This, That, This, That], + ), + ( + assets::load("world.module.human.floor_roof").unwrap(), + [This, This, This, This, That, This], + ), + ( + assets::load("world.module.human.corner_roof").unwrap(), + [This, This, That, That, That, This], + ), + ( + assets::load("world.module.human.chimney_roof").unwrap(), + [This, This, That, That, That, This], + ), + ( + assets::load("world.module.human.wall_roof").unwrap(), + [This, This, This, That, That, This], + ), + ( + assets::load("world.module.human.floor_upstairs").unwrap(), + [This, This, This, This, This, This], + ), + ( + assets::load("world.module.human.balcony_upstairs").unwrap(), + [This, This, This, This, This, This], + ), + ( + assets::load("world.module.human.corner_upstairs").unwrap(), + [This, This, That, That, This, This], + ), + ( + assets::load("world.module.human.wall_upstairs").unwrap(), + [This, This, This, That, This, This], + ), + ( + assets::load("world.module.human.window_upstairs").unwrap(), + [This, This, This, That, This, This], + ), + ] + }; +} + +/* const CELL_SIZE: i32 = 11; static UNIT_CHOOSER: UnitChooser = UnitChooser::new(0x100F4E37); @@ -113,7 +719,7 @@ impl TownState { return None; } - let radius = 192; + let radius = 200; let mut grid = Grid::new( TownCell::Empty, @@ -144,7 +750,7 @@ impl TownState { let idx = rng.gen_range(0, 4); Vec2::new(dirs[idx], dirs[idx + 1]) }; - let road_len = 2 + rng.gen_range(1, 3) * 2 + 1; + let road_len = 2 + rng.gen_range(1, 5) * 2 + 1; // Make sure we aren't trying to create a road where a road already exists! match grid.get(start_pos + road_dir) { @@ -175,7 +781,7 @@ impl TownState { }; // Create roads - for _ in 0..25 { + for _ in 0..radius.pow(2) / 2000 { create_road(); } @@ -317,7 +923,7 @@ impl TownState { break; }; - for _ in 0..40 { + for _ in 0..radius.pow(2) / 1000 { place_house(); } @@ -467,7 +1073,7 @@ impl TownState { }; for _ in 0..100 { - variate_walls(); + //variate_walls(); } /* @@ -662,3 +1268,4 @@ impl<'a> Generator<'a, TownState> for TownGen { (sample.alt - 32.0, sample.alt + 75.0) } } +*/ diff --git a/world/src/generator/town/util.rs b/world/src/generator/town/util.rs new file mode 100644 index 0000000000..99cce9c784 --- /dev/null +++ b/world/src/generator/town/util.rs @@ -0,0 +1,42 @@ +use rand::prelude::*; +use vek::*; + +pub const UNITS: [Vec2; 4] = [ + Vec2 { x: 1, y: 0 }, + Vec2 { x: 0, y: 1 }, + Vec2 { x: -1, y: 0 }, + Vec2 { x: 0, y: -1 }, +]; + +pub fn dir(i: usize) -> Vec2 { + UNITS[i % 4] +} + +pub fn unit(i: usize) -> (Vec2, Vec2) { + (UNITS[i % 4], UNITS[(i + 1) % 4]) +} + +pub fn gen_unit(rng: &mut impl Rng) -> (Vec2, Vec2) { + unit(rng.gen_range(0, 4)) +} + +pub fn gen_dir(rng: &mut impl Rng) -> Vec2 { + UNITS[rng.gen_range(0, 4)] +} + +pub const UNITS_3D: [Vec3; 6] = [ + Vec3 { x: 1, y: 0, z: 0 }, + Vec3 { x: 0, y: 1, z: 0 }, + Vec3 { x: -1, y: 0, z: 0 }, + Vec3 { x: 0, y: -1, z: 0 }, + Vec3 { x: 0, y: 0, z: 1 }, + Vec3 { x: 0, y: 0, z: -1 }, +]; + +pub fn dir_3d(i: usize) -> Vec3 { + UNITS_3D[i % 6] +} + +pub fn gen_dir_3d(rng: &mut impl Rng) -> Vec3 { + UNITS_3D[rng.gen_range(0, 6)] +} diff --git a/world/src/generator/town/vol.rs b/world/src/generator/town/vol.rs new file mode 100644 index 0000000000..cde6b2fb3f --- /dev/null +++ b/world/src/generator/town/vol.rs @@ -0,0 +1,203 @@ +use crate::util::Grid; +use common::vol::{BaseVol, ReadVol, Vox, WriteVol}; +use rand::prelude::*; +use std::ops::Range; +use vek::*; + +#[derive(Clone)] +pub enum ColumnKind { + Road, + Wall, + Internal, + External, // Outside the boundary wall +} + +#[derive(Clone, Default)] +pub struct TownColumn { + pub ground: i32, + pub kind: Option, +} + +impl TownColumn { + pub fn is_empty(&self) -> bool { + self.kind.is_none() + } + + pub fn is_road(&self) -> bool { + self.kind + .as_ref() + .map(|kind| match kind { + ColumnKind::Road => true, + _ => false, + }) + .unwrap_or(false) + } +} + +#[derive(Clone)] +pub struct Module { + pub vol_idx: usize, + pub dir: usize, +} + +#[derive(Clone)] +pub enum TownCell { + Empty, + Park, + Rock, + Road, + Wall, + House { idx: usize, module: Option }, +} + +impl TownCell { + pub fn is_road(&self) -> bool { + match self { + TownCell::Road => true, + _ => false, + } + } + + pub fn is_foundation(&self) -> bool { + match self { + TownCell::Rock => true, + _ => false, + } + } +} + +impl Vox for TownCell { + fn empty() -> Self { + TownCell::Empty + } + + fn is_empty(&self) -> bool { + match self { + TownCell::Empty => true, + _ => false, + } + } +} + +#[derive(Debug)] +pub enum TownError { + OutOfBounds, +} + +const HEIGHT: usize = 24; +const UNDERGROUND_DEPTH: i32 = 5; + +type GridItem = (i32, TownColumn, Vec); + +pub struct TownVol { + grid: Grid, +} + +impl TownVol { + pub fn generate_from( + size: Vec2, + mut f: impl FnMut(Vec2) -> (i32, TownColumn), + mut g: impl FnMut((&TownColumn, Vec3)) -> TownCell, + ) -> Self { + let mut this = Self { + grid: Grid::new( + (0, TownColumn::default(), vec![TownCell::Empty; HEIGHT]), + size, + ), + }; + + for (pos, (base, col, cells)) in this.grid.iter_mut() { + let column = f(pos); + *base = column.0; + *col = column.1; + for z in 0..HEIGHT { + cells[z] = g(( + col, + Vec3::new(pos.x, pos.y, *base - UNDERGROUND_DEPTH + z as i32), + )); + } + } + + this + } + + pub fn size(&self) -> Vec2 { + self.grid.size() + } + + pub fn set_col_kind(&mut self, pos: Vec2, kind: Option) { + self.grid.get_mut(pos).map(|col| col.1.kind = kind); + } + + pub fn col(&self, pos: Vec2) -> Option<&TownColumn> { + self.grid.get(pos).map(|col| &col.1) + } + + pub fn col_range(&self, pos: Vec2) -> Option> { + self.grid.get(pos).map(|col| { + let lower = col.0 - UNDERGROUND_DEPTH; + lower..lower + HEIGHT as i32 + }) + } + + pub fn choose_column( + &self, + rng: &mut impl Rng, + mut f: impl FnMut(Vec2, &TownColumn) -> bool, + ) -> Option> { + self.grid + .iter() + .filter(|(pos, col)| f(*pos, &col.1)) + .choose(rng) + .map(|(pos, _)| pos) + } + + pub fn choose_cell( + &self, + rng: &mut impl Rng, + mut f: impl FnMut(Vec3, &TownCell) -> bool, + ) -> Option> { + self.grid + .iter() + .map(|(pos, (base, _, cells))| { + cells.iter().enumerate().map(move |(i, cell)| { + ( + Vec3::new(pos.x, pos.y, *base - UNDERGROUND_DEPTH + i as i32), + cell, + ) + }) + }) + .flatten() + .filter(|(pos, cell)| f(*pos, *cell)) + .choose(rng) + .map(|(pos, _)| pos) + } +} + +impl BaseVol for TownVol { + type Vox = TownCell; + type Err = TownError; +} + +impl ReadVol for TownVol { + fn get(&self, pos: Vec3) -> Result<&Self::Vox, Self::Err> { + match self.grid.get(Vec2::from(pos)) { + Some((base, _, cells)) => cells + .get((pos.z + UNDERGROUND_DEPTH - *base) as usize) + .ok_or(TownError::OutOfBounds), + None => Err(TownError::OutOfBounds), + } + } +} + +impl WriteVol for TownVol { + fn set(&mut self, pos: Vec3, vox: Self::Vox) -> Result<(), Self::Err> { + match self.grid.get_mut(Vec2::from(pos)) { + Some((base, _, cells)) => cells + .get_mut((pos.z + UNDERGROUND_DEPTH - *base) as usize) + .map(|cell| *cell = vox) + .ok_or(TownError::OutOfBounds), + None => Err(TownError::OutOfBounds), + } + } +} diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index 0725fc7b97..5360e000c6 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -438,17 +438,18 @@ impl WorldSim { .iter() .min_by_key(|(pos, seed)| wpos.distance_squared(*pos)); - if let Some((pos, seed)) = town { + if let Some((pos, _)) = town { let maybe_town = maybe_towns .entry(*pos) .or_insert_with(|| { - TownState::generate(*pos, *seed, &mut ColumnGen::new(self), &mut rng) + TownState::generate(*pos, &mut ColumnGen::new(self), &mut rng) .map(|t| Arc::new(t)) }) .as_mut() // Only care if we're close to the town .filter(|town| { - town.center.distance_squared(wpos) < town.radius.add(64).pow(2) + Vec2::from(town.center()).distance_squared(wpos) + < town.radius().add(64).pow(2) }) .cloned(); diff --git a/world/src/util/grid.rs b/world/src/util/grid.rs index a30ca989a8..188f96c3ef 100644 --- a/world/src/util/grid.rs +++ b/world/src/util/grid.rs @@ -40,16 +40,19 @@ impl Grid { } pub fn iter(&self) -> impl Iterator, &T)> + '_ { - (0..self.size.x) - .map(move |x| { - (0..self.size.y).map(move |y| { - ( - Vec2::new(x, y), - &self.cells[self.idx(Vec2::new(x, y)).unwrap()], - ) - }) - }) - .flatten() + let w = self.size.x; + self.cells + .iter() + .enumerate() + .map(move |(i, cell)| (Vec2::new(i as i32 % w, i as i32 / w), cell)) + } + + pub fn iter_mut(&mut self) -> impl Iterator, &mut T)> + '_ { + let w = self.size.x; + self.cells + .iter_mut() + .enumerate() + .map(move |(i, cell)| (Vec2::new(i as i32 % w, i as i32 / w), cell)) } pub fn iter_area( From 144703da1c78c59aff4cfcc7b8138499e3caa9c4 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Thu, 29 Aug 2019 13:12:14 +0100 Subject: [PATCH 16/24] Added wall module assets --- assets/world/module/wall/corner_ground.vox | 3 + assets/world/module/wall/edge_ground.vox | 3 + world/src/generator/town/mod.rs | 92 ++++++++-------------- 3 files changed, 41 insertions(+), 57 deletions(-) create mode 100644 assets/world/module/wall/corner_ground.vox create mode 100644 assets/world/module/wall/edge_ground.vox diff --git a/assets/world/module/wall/corner_ground.vox b/assets/world/module/wall/corner_ground.vox new file mode 100644 index 0000000000..768eca471b --- /dev/null +++ b/assets/world/module/wall/corner_ground.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:45748af2727abb70d77986e85dd9af2383381fd66efac50228d6d9eb068709e6 +size 2716 diff --git a/assets/world/module/wall/edge_ground.vox b/assets/world/module/wall/edge_ground.vox new file mode 100644 index 0000000000..5f646c9192 --- /dev/null +++ b/assets/world/module/wall/edge_ground.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c60695392ae89d3bf9ba8927d25c39b1da5eb173ac174238720604b9ca982eda +size 2608 diff --git a/world/src/generator/town/mod.rs b/world/src/generator/town/mod.rs index 997bfde100..70b4b961aa 100644 --- a/world/src/generator/town/mod.rs +++ b/world/src/generator/town/mod.rs @@ -71,7 +71,7 @@ impl<'a> Sampler<'a> for TownGen { (Vec2::new(1, 0), Vec2::unit_y(), -Vec2::unit_x()), ]; - MODULES[module.vol_idx] + HOUSE_MODULES[module.vol_idx] .0 .get( Vec3::from( @@ -510,7 +510,7 @@ impl TownVol { .unwrap_or(ModuleKind::That); } - let module = MODULES + let module = HOUSE_MODULES .iter() .enumerate() .filter_map(|(i, module)| { @@ -553,72 +553,50 @@ pub enum ModuleKind { That, } +fn module(name: &str, sig: [ModuleKind; 6]) -> (Arc, [ModuleKind; 6]) { + ( + assets::load(&format!("world.module.{}", name)).unwrap(), + sig, + ) +} + lazy_static! { - pub static ref MODULES: Vec<(Arc, [ModuleKind; 6])> = { + pub static ref HOUSE_MODULES: Vec<(Arc, [ModuleKind; 6])> = { use ModuleKind::*; vec![ - ( - assets::load("world.module.human.floor_ground").unwrap(), - [This, This, This, This, This, That], - ), - ( - assets::load("world.module.human.stair_ground").unwrap(), - [This, This, This, This, This, That], - ), - ( - assets::load("world.module.human.corner_ground").unwrap(), - [This, This, That, That, This, That], - ), - ( - assets::load("world.module.human.wall_ground").unwrap(), - [This, This, This, That, This, That], - ), - ( - assets::load("world.module.human.door_ground").unwrap(), - [This, This, This, That, This, That], - ), - ( - assets::load("world.module.human.window_ground").unwrap(), - [This, This, This, That, This, That], - ), - ( - assets::load("world.module.human.floor_roof").unwrap(), - [This, This, This, This, That, This], - ), - ( - assets::load("world.module.human.corner_roof").unwrap(), - [This, This, That, That, That, This], - ), - ( - assets::load("world.module.human.chimney_roof").unwrap(), - [This, This, That, That, That, This], - ), - ( - assets::load("world.module.human.wall_roof").unwrap(), - [This, This, This, That, That, This], - ), - ( - assets::load("world.module.human.floor_upstairs").unwrap(), + module("human.floor_ground", [This, This, This, This, This, That]), + module("human.stair_ground", [This, This, This, This, This, That]), + module("human.corner_ground", [This, This, That, That, This, That]), + module("human.wall_ground", [This, This, This, That, This, That]), + module("human.door_ground", [This, This, This, That, This, That]), + module("human.window_ground", [This, This, This, That, This, That]), + module("human.floor_roof", [This, This, This, This, That, This]), + module("human.corner_roof", [This, This, That, That, That, This]), + module("human.chimney_roof", [This, This, That, That, That, This]), + module("human.wall_roof", [This, This, This, That, That, This]), + module("human.floor_upstairs", [This, This, This, This, This, This]), + module( + "human.balcony_upstairs", [This, This, This, This, This, This], ), - ( - assets::load("world.module.human.balcony_upstairs").unwrap(), - [This, This, This, This, This, This], - ), - ( - assets::load("world.module.human.corner_upstairs").unwrap(), + module( + "human.corner_upstairs", [This, This, That, That, This, This], ), - ( - assets::load("world.module.human.wall_upstairs").unwrap(), - [This, This, This, That, This, This], - ), - ( - assets::load("world.module.human.window_upstairs").unwrap(), + module("human.wall_upstairs", [This, This, This, That, This, This]), + module( + "human.window_upstairs", [This, This, This, That, This, This], ), ] }; + pub static ref WALL_MODULES: Vec<(Arc, [ModuleKind; 6])> = { + use ModuleKind::*; + vec![ + module("wall.edge_ground", [This, That, This, That, This, That]), + module("wall.corner_ground", [This, This, That, That, This, That]), + ] + }; } /* From e3c4e34edb3bc313e023c66cbb47089be3d01e4b Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 30 Aug 2019 12:43:37 +0100 Subject: [PATCH 17/24] Added city walls --- assets/world/module/wall/corner_mid.vox | 3 + assets/world/module/wall/corner_top.vox | 3 + assets/world/module/wall/edge_mid.vox | 3 + assets/world/module/wall/edge_top.vox | 3 + assets/world/module/wall/end_top.vox | 3 + assets/world/module/wall/single_top.vox | 3 + world/src/generator/town/mod.rs | 170 +++++++++++++----------- world/src/generator/town/vol.rs | 35 +++-- 8 files changed, 134 insertions(+), 89 deletions(-) create mode 100644 assets/world/module/wall/corner_mid.vox create mode 100644 assets/world/module/wall/corner_top.vox create mode 100644 assets/world/module/wall/edge_mid.vox create mode 100644 assets/world/module/wall/edge_top.vox create mode 100644 assets/world/module/wall/end_top.vox create mode 100644 assets/world/module/wall/single_top.vox diff --git a/assets/world/module/wall/corner_mid.vox b/assets/world/module/wall/corner_mid.vox new file mode 100644 index 0000000000..bb36fa2b9e --- /dev/null +++ b/assets/world/module/wall/corner_mid.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6bfcb87955e2c7d3c05b818ec0275fc6e6942b91423941d7056fbb16cadea98b +size 2660 diff --git a/assets/world/module/wall/corner_top.vox b/assets/world/module/wall/corner_top.vox new file mode 100644 index 0000000000..224239b371 --- /dev/null +++ b/assets/world/module/wall/corner_top.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b185a18df4c044445463670caa04cbc9662398f799069a73b3c7b556ae542b78 +size 3312 diff --git a/assets/world/module/wall/edge_mid.vox b/assets/world/module/wall/edge_mid.vox new file mode 100644 index 0000000000..d894dfc34f --- /dev/null +++ b/assets/world/module/wall/edge_mid.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d5212e3429fd9f1877a3bdd6017400960c817cfae75c4e4f2f03509f6c07757f +size 2536 diff --git a/assets/world/module/wall/edge_top.vox b/assets/world/module/wall/edge_top.vox new file mode 100644 index 0000000000..cbeea4032a --- /dev/null +++ b/assets/world/module/wall/edge_top.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e53559945372e8e5924f6b6c7b97d6d35c9c7a5b4050b0c553e27a080587a8b9 +size 3268 diff --git a/assets/world/module/wall/end_top.vox b/assets/world/module/wall/end_top.vox new file mode 100644 index 0000000000..f82e62a208 --- /dev/null +++ b/assets/world/module/wall/end_top.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:80467fad1c417e70b98cf2694356de2a6fd152a9523c2fbff83c83ff7ca9f605 +size 3228 diff --git a/assets/world/module/wall/single_top.vox b/assets/world/module/wall/single_top.vox new file mode 100644 index 0000000000..dbf9f207d2 --- /dev/null +++ b/assets/world/module/wall/single_top.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:038b24785d2894a2fb15a479fe0277a3be1fcbadb02459bf5293cffaf4e7767a +size 3188 diff --git a/world/src/generator/town/mod.rs b/world/src/generator/town/mod.rs index 70b4b961aa..6d13cad0be 100644 --- a/world/src/generator/town/mod.rs +++ b/world/src/generator/town/mod.rs @@ -20,7 +20,7 @@ use rand_chacha::ChaChaRng; use std::{ops::Add, sync::Arc}; use vek::*; -use self::vol::{ColumnKind, Module, TownCell, TownColumn, TownVol}; +use self::vol::{CellKind, ColumnKind, Module, TownCell, TownColumn, TownVol}; const CELL_SIZE: i32 = 9; const CELL_HEIGHT: i32 = 9; @@ -42,59 +42,53 @@ impl<'a> Sampler<'a> for TownGen { e.rem_euclid(sz) }); - match town.vol.get(cell_pos).unwrap_or(&TownCell::Empty) { - TownCell::Empty => None, - TownCell::Park => None, - TownCell::Rock => Some(Block::new(BlockKind::Normal, Rgb::broadcast(100))), - TownCell::Wall => Some(Block::new(BlockKind::Normal, Rgb::broadcast(175))), - TownCell::Road => { - if (wpos.z as f32) < height - 1.0 { - Some(Block::new( - BlockKind::Normal, - Lerp::lerp( - Rgb::new(150.0, 140.0, 50.0), - Rgb::new(100.0, 95.0, 30.0), - sample.marble_small, - ) - .map(|e| e as u8), - )) - } else { - Some(Block::empty()) - } - } - TownCell::House { idx, module } => { - if let Some(module) = module { - let transform = [ - (Vec2::new(0, 0), Vec2::unit_x(), Vec2::unit_y()), - (Vec2::new(0, 1), -Vec2::unit_y(), Vec2::unit_x()), - (Vec2::new(1, 1), -Vec2::unit_x(), -Vec2::unit_y()), - (Vec2::new(1, 0), Vec2::unit_y(), -Vec2::unit_x()), - ]; + let cell = town.vol.get(cell_pos).ok()?; - HOUSE_MODULES[module.vol_idx] - .0 - .get( - Vec3::from( - transform[module.dir].0 * (CELL_SIZE - 1) - + transform[module.dir].1 * inner_pos.x - + transform[module.dir].2 * inner_pos.y, - ) + Vec3::unit_z() * inner_pos.z, - ) - .ok() - .and_then(|sb| { - block_from_structure( - *sb, - BlockKind::Normal, - wpos, - wpos.into(), - 0, - sample, - ) - }) - } else { - Some(Block::new(BlockKind::Normal, town.houses[*idx].color)) - } + match (modules_from_kind(&cell.kind), &cell.module) { + (Some(module_list), Some(module)) => { + let transform = [ + (Vec2::new(0, 0), Vec2::unit_x(), Vec2::unit_y()), + (Vec2::new(0, 1), -Vec2::unit_y(), Vec2::unit_x()), + (Vec2::new(1, 1), -Vec2::unit_x(), -Vec2::unit_y()), + (Vec2::new(1, 0), Vec2::unit_y(), -Vec2::unit_x()), + ]; + + module_list[module.vol_idx] + .0 + .get( + Vec3::from( + transform[module.dir].0 * (CELL_SIZE - 1) + + transform[module.dir].1 * inner_pos.x + + transform[module.dir].2 * inner_pos.y, + ) + Vec3::unit_z() * inner_pos.z, + ) + .ok() + .and_then(|sb| { + block_from_structure(*sb, BlockKind::Normal, wpos, wpos.into(), 0, sample) + }) } + _ => match cell.kind { + CellKind::Empty => None, + CellKind::Park => None, + CellKind::Rock => Some(Block::new(BlockKind::Normal, Rgb::broadcast(100))), + CellKind::Wall => Some(Block::new(BlockKind::Normal, Rgb::broadcast(175))), + CellKind::Road => { + if (wpos.z as f32) < height - 1.0 { + Some(Block::new( + BlockKind::Normal, + Lerp::lerp( + Rgb::new(150.0, 140.0, 50.0), + Rgb::new(100.0, 95.0, 30.0), + sample.marble_small, + ) + .map(|e| e as u8), + )) + } else { + Some(Block::empty()) + } + } + CellKind::House(idx) => Some(Block::new(BlockKind::Normal, town.houses[idx].color)), + }, } } } @@ -149,9 +143,9 @@ impl TownState { }, |(col, pos)| { if pos.z >= col.ground { - TownCell::Empty + TownCell::empty() } else { - TownCell::Rock + TownCell::from(CellKind::Rock) } }, ); @@ -304,7 +298,7 @@ impl TownVol { let col = self.col(cell).unwrap(); let ground = col.ground; for z in 0..2 { - self.set(Vec3::new(cell.x, cell.y, ground + z), TownCell::Park); + self.set(Vec3::new(cell.x, cell.y, ground + z), CellKind::Park.into()); } } @@ -354,7 +348,7 @@ impl TownVol { let col = self.col(*wall).unwrap(); let ground = col.ground; for z in -1..2 { - self.set(Vec3::new(wall.x, wall.y, ground + z), TownCell::Wall); + self.set(Vec3::new(wall.x, wall.y, ground + z), CellKind::Wall.into()); } } } @@ -371,7 +365,7 @@ impl TownVol { Some(ColumnKind::External) => {} Some(ColumnKind::Road) => { for z in -1..2 { - self.set(Vec3::new(i, j, ground + z), TownCell::Road); + self.set(Vec3::new(i, j, ground + z), CellKind::Road.into()); } } _ => unimplemented!(), @@ -465,13 +459,7 @@ impl TownVol { } for cell in cells { - self.set( - cell, - TownCell::House { - idx: houses.len(), - module: None, - }, - ); + self.set(cell, CellKind::House(houses.len()).into()); self.set_col_kind(Vec2::from(cell), Some(ColumnKind::Internal)); } @@ -485,19 +473,20 @@ impl TownVol { } fn resolve_modules(&mut self, rng: &mut impl Rng) { - fn classify(cell: &TownCell, this_house: usize) -> ModuleKind { - match cell { - TownCell::House { idx, .. } if *idx == this_house => ModuleKind::This, + fn classify(cell: &TownCell, this_cell: &TownCell) -> ModuleKind { + match (&cell.kind, &this_cell.kind) { + (CellKind::House(a), CellKind::House(b)) if a == b => ModuleKind::This, + (CellKind::Wall, CellKind::Wall) => ModuleKind::This, _ => ModuleKind::That, } } for x in 0..self.size().x { for y in 0..self.size().y { - 'cell: for z in self.col_range(Vec2::new(x, y)).unwrap() { + for z in self.col_range(Vec2::new(x, y)).unwrap() { let pos = Vec3::new(x, y, z); - let this_idx = if let Ok(TownCell::House { idx, module }) = self.get(pos) { - idx + let this_cell = if let Ok(this_cell) = self.get(pos) { + this_cell } else { continue; }; @@ -506,11 +495,17 @@ impl TownVol { for i in 0..6 { signature[i] = self .get(pos + util::dir_3d(i)) - .map(|cell| classify(cell, *this_idx)) + .map(|cell| classify(cell, this_cell)) .unwrap_or(ModuleKind::That); } - let module = HOUSE_MODULES + let module_list = if let Some(modules) = modules_from_kind(&this_cell.kind) { + modules + } else { + continue; + }; + + let module = module_list .iter() .enumerate() .filter_map(|(i, module)| { @@ -534,13 +529,16 @@ impl TownVol { }) .choose(rng); - self.set( - pos, - TownCell::House { - idx: *this_idx, - module, - }, - ); + if let Some(module) = module { + let kind = this_cell.kind.clone(); + self.set( + pos, + TownCell { + kind, + module: Some(module), + }, + ); + } } } } @@ -560,6 +558,14 @@ fn module(name: &str, sig: [ModuleKind; 6]) -> (Arc, [ModuleKind; 6]) ) } +fn modules_from_kind(kind: &CellKind) -> Option<&'static [(Arc, [ModuleKind; 6])]> { + match kind { + CellKind::House(_) => Some(&HOUSE_MODULES), + CellKind::Wall => Some(&WALL_MODULES), + _ => None, + } +} + lazy_static! { pub static ref HOUSE_MODULES: Vec<(Arc, [ModuleKind; 6])> = { use ModuleKind::*; @@ -594,7 +600,13 @@ lazy_static! { use ModuleKind::*; vec![ module("wall.edge_ground", [This, That, This, That, This, That]), + module("wall.edge_mid", [This, That, This, That, This, This]), + module("wall.edge_top", [This, That, This, That, That, This]), module("wall.corner_ground", [This, This, That, That, This, That]), + module("wall.corner_mid", [This, This, That, That, This, This]), + module("wall.corner_top", [This, This, That, That, That, This]), + module("wall.end_top", [That, This, That, That, That, This]), + module("wall.single_top", [That, That, That, That, That, This]), ] }; } diff --git a/world/src/generator/town/vol.rs b/world/src/generator/town/vol.rs index cde6b2fb3f..e6cb5454b3 100644 --- a/world/src/generator/town/vol.rs +++ b/world/src/generator/town/vol.rs @@ -41,26 +41,32 @@ pub struct Module { } #[derive(Clone)] -pub enum TownCell { +pub enum CellKind { Empty, Park, Rock, Road, Wall, - House { idx: usize, module: Option }, + House(usize), +} + +#[derive(Clone)] +pub struct TownCell { + pub kind: CellKind, + pub module: Option, } impl TownCell { pub fn is_road(&self) -> bool { - match self { - TownCell::Road => true, + match self.kind { + CellKind::Road => true, _ => false, } } pub fn is_foundation(&self) -> bool { - match self { - TownCell::Rock => true, + match self.kind { + CellKind::Rock => true, _ => false, } } @@ -68,17 +74,26 @@ impl TownCell { impl Vox for TownCell { fn empty() -> Self { - TownCell::Empty + Self { + kind: CellKind::Empty, + module: None, + } } fn is_empty(&self) -> bool { - match self { - TownCell::Empty => true, + match self.kind { + CellKind::Empty => true, _ => false, } } } +impl From for TownCell { + fn from(kind: CellKind) -> Self { + Self { kind, module: None } + } +} + #[derive(Debug)] pub enum TownError { OutOfBounds, @@ -101,7 +116,7 @@ impl TownVol { ) -> Self { let mut this = Self { grid: Grid::new( - (0, TownColumn::default(), vec![TownCell::Empty; HEIGHT]), + (0, TownColumn::default(), vec![TownCell::empty(); HEIGHT]), size, ), }; From 25d6ab03aa305ebaab6bdc6a4a1821e61ec7f015 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 30 Aug 2019 13:18:48 +0100 Subject: [PATCH 18/24] Removed extra foundations --- world/src/generator/town/mod.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/world/src/generator/town/mod.rs b/world/src/generator/town/mod.rs index 6d13cad0be..988c28220b 100644 --- a/world/src/generator/town/mod.rs +++ b/world/src/generator/town/mod.rs @@ -158,6 +158,7 @@ impl TownState { let houses = vol.gen_houses(rng, 60); vol.gen_walls(); vol.resolve_modules(rng); + vol.cull_unused(); Some(Self { center: Vec3::new(center.x, center.y, alt), @@ -321,7 +322,7 @@ impl TownVol { let mut outer = self.floodfill(outer, |_, col| col.is_empty()); let mut walls = HashSet::new(); - self.floodfill([self.size() / 2].iter().copied().collect(), |pos, _| { + let inner = self.floodfill([self.size() / 2].iter().copied().collect(), |pos, _| { if outer.contains(&pos) { walls.insert(pos); false @@ -472,6 +473,26 @@ impl TownVol { houses } + fn cull_unused(&mut self) { + for x in 0..self.size().x { + for y in 0..self.size().y { + for z in self.col_range(Vec2::new(x, y)).unwrap() { + let pos = Vec3::new(x, y, z); + + // Remove foundations that don't have anything on top of them + if self.get(pos).unwrap().is_foundation() + && self + .get(pos + Vec3::unit_z()) + .map(Vox::is_empty) + .unwrap_or(true) + { + self.set(pos, TownCell::empty()); + } + } + } + } + } + fn resolve_modules(&mut self, rng: &mut impl Rng) { fn classify(cell: &TownCell, this_cell: &TownCell) -> ModuleKind { match (&cell.kind, &this_cell.kind) { From c433ef36d7558cd0b8da1bb5c9b1c5dbc2d7a84b Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 30 Aug 2019 13:35:14 +0100 Subject: [PATCH 19/24] Sloped wall tops --- assets/world/module/wall/end_top.vox | 4 ++-- world/src/generator/town/mod.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/world/module/wall/end_top.vox b/assets/world/module/wall/end_top.vox index f82e62a208..8e246035af 100644 --- a/assets/world/module/wall/end_top.vox +++ b/assets/world/module/wall/end_top.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:80467fad1c417e70b98cf2694356de2a6fd152a9523c2fbff83c83ff7ca9f605 -size 3228 +oid sha256:df2dadec6a2f6862f9d7397051ae3ba74d6ed0fe5447a402893ab192dfca5735 +size 2120 diff --git a/world/src/generator/town/mod.rs b/world/src/generator/town/mod.rs index 988c28220b..73bb07d71b 100644 --- a/world/src/generator/town/mod.rs +++ b/world/src/generator/town/mod.rs @@ -348,7 +348,7 @@ impl TownVol { for wall in walls.iter() { let col = self.col(*wall).unwrap(); let ground = col.ground; - for z in -1..2 { + for z in -1..3 { self.set(Vec3::new(wall.x, wall.y, ground + z), CellKind::Wall.into()); } } From 1176fc4d0771db8e0e3597d4ff5e88cb251ab74a Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 30 Aug 2019 17:00:08 +0100 Subject: [PATCH 20/24] Better loading times --- world/src/generator/town/mod.rs | 4 ++-- world/src/sim/mod.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/world/src/generator/town/mod.rs b/world/src/generator/town/mod.rs index 73bb07d71b..4c5e5ef58d 100644 --- a/world/src/generator/town/mod.rs +++ b/world/src/generator/town/mod.rs @@ -121,7 +121,7 @@ pub struct TownState { impl TownState { pub fn generate(center: Vec2, gen: &mut ColumnGen, rng: &mut impl Rng) -> Option { - let radius = rng.gen_range(12, 24) * 9; + let radius = rng.gen_range(18, 20) * 9; let size = Vec2::broadcast(radius * 2 / 9 - 2); let alt = gen.get(center).map(|sample| sample.alt).unwrap_or(0.0) as i32; @@ -155,7 +155,7 @@ impl TownState { vol.gen_roads(rng, 30); //vol.gen_parks(rng, 8); vol.emplace_columns(); - let houses = vol.gen_houses(rng, 60); + let houses = vol.gen_houses(rng, 50); vol.gen_walls(); vol.resolve_modules(rng); vol.cull_unused(); diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index 5360e000c6..97a921f9d1 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -146,7 +146,7 @@ impl WorldSim { fast_turb_x_nz: FastNoise::new(gen_seed()), fast_turb_y_nz: FastNoise::new(gen_seed()), - town_gen: StructureGen2d::new(gen_seed(), 1024, 512), + town_gen: StructureGen2d::new(gen_seed(), 2048, 1024), }; // "Base" of the chunk, to be multiplied by CONFIG.mountain_scale (multiplied value is From 1eb0720cbb735ed92ed7ccac12716fc71e72fca2 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 30 Aug 2019 18:57:38 +0100 Subject: [PATCH 21/24] Cleaned up wall generation --- world/src/generator/town/mod.rs | 72 +++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 25 deletions(-) diff --git a/world/src/generator/town/mod.rs b/world/src/generator/town/mod.rs index 4c5e5ef58d..c052e11384 100644 --- a/world/src/generator/town/mod.rs +++ b/world/src/generator/town/mod.rs @@ -153,7 +153,7 @@ impl TownState { // Generation passes vol.setup(rng); vol.gen_roads(rng, 30); - //vol.gen_parks(rng, 8); + vol.gen_parks(rng, 4); vol.emplace_columns(); let houses = vol.gen_houses(rng, 50); vol.gen_walls(); @@ -180,6 +180,7 @@ impl TownState { impl TownVol { fn floodfill( &self, + limit: Option, mut opens: HashSet>, mut f: impl FnMut(Vec2, &TownColumn) -> bool, ) -> HashSet> { @@ -188,14 +189,24 @@ impl TownVol { while opens.len() > 0 { let mut new_opens = HashSet::new(); - for open in opens.iter() { + 'search: for open in opens.iter() { for i in -1..2 { for j in -1..2 { let pos = *open + Vec2::new(i, j); if let Some(col) = self.col(pos) { if !closed.contains(&pos) && !opens.contains(&pos) && f(pos, col) { - new_opens.insert(pos); + match limit { + Some(limit) + if limit + <= new_opens.len() + closed.len() + opens.len() => + { + break 'search + } + _ => { + new_opens.insert(pos); + } + } } } } @@ -280,15 +291,10 @@ impl TownVol { }) .unwrap(); - let mut energy = 50; - let mut park = self.floodfill([start].iter().copied().collect(), |_, col| { - if col.is_empty() && energy > 0 { - energy -= 1; - true - } else { - false - } - }); + let mut park = + self.floodfill(Some(25), [start].iter().copied().collect(), |_, col| { + col.is_empty() + }); if park.len() < 4 { continue; @@ -319,25 +325,41 @@ impl TownVol { outer.insert(Vec2::new(self.size().x - 1, j)); } - let mut outer = self.floodfill(outer, |_, col| col.is_empty()); + let mut outer = self.floodfill(None, outer, |_, col| col.is_empty()); let mut walls = HashSet::new(); - let inner = self.floodfill([self.size() / 2].iter().copied().collect(), |pos, _| { - if outer.contains(&pos) { - walls.insert(pos); - false - } else { - true - } - }); + let inner = self.floodfill( + None, + [self.size() / 2].iter().copied().collect(), + |pos, _| { + if outer.contains(&pos) { + walls.insert(pos); + false + } else { + true + } + }, + ); while let Some(wall) = walls .iter() .filter(|pos| { - (0..4) + let lateral_count = (0..4) .filter(|i| walls.contains(&(**pos + util::dir(*i)))) - .count() - < 2 + .count(); + let max_quadrant_count = (0..4) + .map(|i| { + let units = util::unit(i); + (0..2) + .map(|i| (0..2).map(move |j| (i, j))) + .flatten() + .filter(|(i, j)| walls.contains(&(**pos + units.0 * *i + units.1 * *j))) + .count() + }) + .max() + .unwrap(); + + lateral_count < 2 || (lateral_count == 2 && max_quadrant_count == 4) }) .next() { @@ -376,7 +398,7 @@ impl TownVol { } fn gen_houses(&mut self, rng: &mut impl Rng, n: usize) -> Vec { - const ATTEMPTS: usize = 20; + const ATTEMPTS: usize = 10; let mut houses = Vec::new(); for _ in 0..n { From 7321eb6999d720fe156df99f5bffd8f558d5b1bf Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 30 Aug 2019 19:51:56 +0100 Subject: [PATCH 22/24] Better town park generation --- world/src/generator/town/mod.rs | 8 ++++---- world/src/generator/town/vol.rs | 9 +++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/world/src/generator/town/mod.rs b/world/src/generator/town/mod.rs index c052e11384..0ba7e1e72e 100644 --- a/world/src/generator/town/mod.rs +++ b/world/src/generator/town/mod.rs @@ -153,7 +153,7 @@ impl TownState { // Generation passes vol.setup(rng); vol.gen_roads(rng, 30); - vol.gen_parks(rng, 4); + vol.gen_parks(rng, 3); vol.emplace_columns(); let houses = vol.gen_houses(rng, 50); vol.gen_walls(); @@ -292,7 +292,7 @@ impl TownVol { .unwrap(); let mut park = - self.floodfill(Some(25), [start].iter().copied().collect(), |_, col| { + self.floodfill(Some(16), [start].iter().copied().collect(), |_, col| { col.is_empty() }); @@ -498,14 +498,14 @@ impl TownVol { fn cull_unused(&mut self) { for x in 0..self.size().x { for y in 0..self.size().y { - for z in self.col_range(Vec2::new(x, y)).unwrap() { + for z in self.col_range(Vec2::new(x, y)).unwrap().rev() { let pos = Vec3::new(x, y, z); // Remove foundations that don't have anything on top of them if self.get(pos).unwrap().is_foundation() && self .get(pos + Vec3::unit_z()) - .map(Vox::is_empty) + .map(TownCell::is_space) .unwrap_or(true) { self.set(pos, TownCell::empty()); diff --git a/world/src/generator/town/vol.rs b/world/src/generator/town/vol.rs index e6cb5454b3..ecc610697c 100644 --- a/world/src/generator/town/vol.rs +++ b/world/src/generator/town/vol.rs @@ -64,6 +64,15 @@ impl TownCell { } } + pub fn is_space(&self) -> bool { + match self.kind { + CellKind::Empty => true, + CellKind::Park => true, + CellKind::Road => true, + _ => false, + } + } + pub fn is_foundation(&self) -> bool { match self.kind { CellKind::Rock => true, From cc214982bf4185eda9b4f0a2aeba9efffec4ceb4 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 30 Aug 2019 20:44:47 +0100 Subject: [PATCH 23/24] Fixed warnings --- voxygen/src/scene/terrain.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index c93f03f3ba..451402e298 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -602,7 +602,7 @@ impl Terrain { focus_pos: Vec3, ) { // Opaque - for (pos, chunk) in &self.chunks { + for (_, chunk) in &self.chunks { if chunk.visible { renderer.render_terrain_chunk(&chunk.opaque_model, globals, &chunk.locals, lights); } From d0768e9858e126dc1a4c8b4e8705a352fac7dd90 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sat, 31 Aug 2019 14:24:56 +0100 Subject: [PATCH 24/24] Faster prng --- world/src/block/mod.rs | 2 +- world/src/generator/town/mod.rs | 653 +------------------------------- world/src/util/random.rs | 26 +- 3 files changed, 17 insertions(+), 664 deletions(-) diff --git a/world/src/block/mod.rs b/world/src/block/mod.rs index ea1c17c14e..b73ae6fb96 100644 --- a/world/src/block/mod.rs +++ b/world/src/block/mod.rs @@ -177,7 +177,7 @@ impl<'a> BlockGen<'a> { .mul(48.0) + (world.sim().gen_ctx.warp_nz.get(wposf.div(15.0)) as f32) .mul((chaos - 0.1).max(0.0)) - .mul(15.0); + .mul(24.0); let height = if (wposf.z as f32) < alt + warp - 10.0 { // Shortcut cliffs diff --git a/world/src/generator/town/mod.rs b/world/src/generator/town/mod.rs index 0ba7e1e72e..181b3874b5 100644 --- a/world/src/generator/town/mod.rs +++ b/world/src/generator/town/mod.rs @@ -156,7 +156,7 @@ impl TownState { vol.gen_parks(rng, 3); vol.emplace_columns(); let houses = vol.gen_houses(rng, 50); - vol.gen_walls(); + vol.gen_walls(rng); vol.resolve_modules(rng); vol.cull_unused(); @@ -314,7 +314,7 @@ impl TownVol { } } - fn gen_walls(&mut self) { + fn gen_walls(&mut self, rng: &mut impl Rng) { let mut outer = HashSet::new(); for i in 0..self.size().x { outer.insert(Vec2::new(i, 0)); @@ -653,652 +653,3 @@ lazy_static! { ] }; } - -/* -const CELL_SIZE: i32 = 11; - -static UNIT_CHOOSER: UnitChooser = UnitChooser::new(0x100F4E37); - -lazy_static! { - pub static ref HOUSES: Vec> = - vec![ - assets::load_map("world.structure.human.house_1", |s: Structure| s - .with_center(Vec3::new(8, 10, 2))) - .unwrap(), - ]; - pub static ref BLACKSMITHS: Vec> = vec![ - assets::load_map("world.structure.human.blacksmith", |s: Structure| s - .with_center(Vec3::new(16, 19, 9))) - .unwrap(), - assets::load_map("world.structure.human.mage_tower", |s: Structure| s - .with_center(Vec3::new(13, 13, 4))) - .unwrap(), - ]; - pub static ref TOWNHALLS: Vec> = vec![ - assets::load_map("world.structure.human.town_hall_spire", |s: Structure| s - .with_center(Vec3::new(16, 16, 2))) - .unwrap(), - assets::load_map("world.structure.human.stables_1", |s: Structure| s - .with_center(Vec3::new(16, 23, 2))) - .unwrap(), - ]; -} - -#[derive(Clone)] -pub enum Building { - House, - Blacksmith, - TownHall, -} - -#[derive(Clone)] -pub enum TownCell { - Empty, - Junction, - Street, - Building { - kind: Building, - wpos: Vec3, - size: Vec2, - units: (Vec2, Vec2), - seed: u32, - }, - PartOf(Vec2), - MemberOf(usize), - Market, - Wall, -} - -impl TownCell { - fn is_road(&self) -> bool { - match self { - TownCell::Junction => true, - TownCell::Street => true, - _ => false, - } - } - - fn mergeable(&self) -> bool { - match self { - TownCell::Empty => true, - TownCell::Building { size, .. } if *size == Vec2::one() => true, - _ => false, - } - } -} - -pub enum Property { - House(Rgb), -} - -pub struct TownState { - pub center: Vec2, - pub radius: i32, - pub grid: Grid, - pub properties: Vec, -} - -impl TownState { - pub fn generate( - center: Vec2, - seed: u32, - gen: &mut ColumnGen, - rng: &mut impl Rng, - ) -> Option { - let center_chunk = gen.sim.get_wpos(center)?; - - // First, determine whether the location is even appropriate for a town - if center_chunk.chaos > 0.7 || center_chunk.near_cliffs { - return None; - } - - let radius = 200; - - let mut grid = Grid::new( - TownCell::Empty, - Vec2::broadcast(radius * 3 / (CELL_SIZE * 2)), - ); - - grid.set(grid.size() / 2, TownCell::Junction); - - let mut create_road = || { - for _ in 0..10 { - let junctions = grid - .iter() - .filter(|(_, cell)| { - if let TownCell::Junction = cell { - true - } else { - false - } - }) - .collect::>(); - - // Choose an existing junction for the road to start from - let start_pos = junctions.choose(rng).unwrap().0; // Can't fail - - // Choose a random direction and length for the road - let road_dir = { - let dirs = [-1, 0, 1, 0, -1]; - let idx = rng.gen_range(0, 4); - Vec2::new(dirs[idx], dirs[idx + 1]) - }; - let road_len = 2 + rng.gen_range(1, 5) * 2 + 1; - - // Make sure we aren't trying to create a road where a road already exists! - match grid.get(start_pos + road_dir) { - Some(TownCell::Empty) => {} - _ => continue, - } - - // Pave the road - for i in 1..road_len { - let cell_pos = start_pos + road_dir * i; - if let Some(TownCell::Empty) = grid.get(cell_pos) { - grid.set( - cell_pos, - if i == road_len - 1 { - TownCell::Junction - } else { - TownCell::Street - }, - ); - } else { - grid.set(cell_pos, TownCell::Junction); - break; - } - } - - break; - } - }; - - // Create roads - for _ in 0..radius.pow(2) / 2000 { - create_road(); - } - - let markets = rng.gen_range(1, 3); - let mut create_market = || { - for _ in 0..30 { - let start_pos = Vec2::new( - grid.size().x / 4 + rng.gen_range(0, grid.size().x / 2), - grid.size().y / 4 + rng.gen_range(0, grid.size().y / 2), - ); - - if let Some(TownCell::Empty) = grid.get(start_pos) { - let mut cells = HashSet::new(); - cells.insert(start_pos); - - let mut energy = 1000; - while energy > 0 { - energy -= 1; - - let pos = cells.iter().choose(rng).copied().unwrap(); - - let dir = { - let dirs = [-1, 0, 1, 0, -1]; - let idx = rng.gen_range(0, 4); - Vec2::new(dirs[idx], dirs[idx + 1]) - }; - - if cells.contains(&(pos + dir)) { - continue; - } - - if let Some(TownCell::Empty) = grid.get(pos + dir) { - cells.insert(pos + dir); - energy -= 10; - } - } - - if cells.len() >= 9 && cells.len() <= 25 { - for cell in cells.iter() { - grid.set(*cell, TownCell::Market); - } - - break; - } - } - } - }; - - for _ in 0..markets { - create_market(); - } - - let mut properties = Vec::new(); - let mut place_house = || 'house: loop { - let start_pos = 'start_pos: { - for _ in 0..50 { - let pos = Vec2::new( - rng.gen_range(4, grid.size().x - 4), - rng.gen_range(4, grid.size().y - 4), - ); - - let dirs = [-1, 0, 1, 0, -1]; - let road_neighbours = (0..4) - .filter(|idx| { - grid.get(pos + Vec2::new(dirs[*idx], dirs[*idx + 1])) - .map(|cell| cell.is_road()) - .unwrap_or(false) - }) - .count(); - - if road_neighbours > 0 { - if let Some(TownCell::Empty) = grid.get(pos) { - break 'start_pos pos; - } - } - } - - break 'house; - }; - - let mut cells = HashSet::new(); - cells.insert(start_pos); - - let mut growth_energy = rng.gen_range(50, 160); - while growth_energy > 0 { - growth_energy -= 1; - - let pos = cells.iter().choose(rng).copied().unwrap(); - - let dir = { - let dirs = [-1, 0, 1, 0, -1]; - let idx = rng.gen_range(0, 4); - Vec2::new(dirs[idx], dirs[idx + 1]) - }; - - if cells.contains(&(pos + dir)) { - continue; - } - - match grid.get(pos + dir) { - Some(TownCell::Empty) => { - growth_energy -= 10; - cells.insert(pos + dir); - } - _ => {} - } - } - - if cells.len() < 3 { - break; - } - - let property_idx = properties.len(); - - for _ in 0..100 { - let cell = match cells.iter().choose(rng) { - Some(cell) => *cell, - None => break, - }; - - let dirs = [-1, 0, 1, 0, -1]; - let neighbours = (0..4) - .filter(|idx| cells.contains(&(cell + Vec2::new(dirs[*idx], dirs[*idx + 1])))) - .count(); - - if neighbours < 2 { - cells.remove(&cell); - } - } - - for cell in cells.iter() { - grid.set(*cell, TownCell::MemberOf(property_idx)); - } - - if cells.len() > 0 { - properties.push(Property::House(Rgb::new(rng.gen(), rng.gen(), rng.gen()))); - } - - break; - }; - - for _ in 0..radius.pow(2) / 1000 { - place_house(); - } - - /* - let mut create_walls = || { - for i in 0..grid.size().x { - grid.set(Vec2::new(i, 0), TownCell::Wall); - grid.set(Vec2::new(i, grid.size().y - 1), TownCell::Wall); - } - - for j in 0..grid.size().y { - grid.set(Vec2::new(0, j), TownCell::Wall); - grid.set(Vec2::new(grid.size().x - 1, j), TownCell::Wall); - } - }; - */ - - fn floodfill( - mut opens: HashSet>, - grid: &Grid, - mut f: impl FnMut(Vec2, &TownCell) -> bool, - ) -> HashSet> { - let mut closed = HashSet::new(); - - while opens.len() > 0 { - let mut new_opens = HashSet::new(); - - for open in opens.iter() { - for i in -1..2 { - for j in -1..2 { - let pos = *open + Vec2::new(i, j); - - if let Some(cell) = grid.get(pos) { - if f(pos, cell) && !closed.contains(&pos) && !opens.contains(&pos) { - new_opens.insert(pos); - } - } - } - } - } - - closed = closed.union(&opens).copied().collect(); - opens = new_opens; - } - - closed - } - - let mut create_walls = || { - let mut opens = HashSet::new(); - - for i in 0..grid.size().x { - opens.insert(Vec2::new(i, 0)); - opens.insert(Vec2::new(i, grid.size().y - 1)); - } - - for j in 0..grid.size().y { - opens.insert(Vec2::new(0, j)); - opens.insert(Vec2::new(grid.size().x - 1, j)); - } - - let outer = floodfill(opens, &grid, |_, cell| { - if let TownCell::Empty = cell { - true - } else { - false - } - }); - - let mut walls = HashSet::new(); - - floodfill( - [grid.size() / 2].iter().copied().collect(), - &grid, - |pos, _| { - if outer.contains(&pos) { - walls.insert(pos); - false - } else { - true - } - }, - ); - - for cell in walls.iter() { - grid.set(*cell, TownCell::Wall); - } - }; - - create_walls(); - - let mut remove_extra_walls = || { - for x in 0..grid.size().x { - for y in 0..grid.size().y { - let pos = Vec2::new(x, y); - let mut wall_count = 0; - for i in 0..2 { - for j in 0..2 { - if let Some(TownCell::Wall) = grid.get(pos + Vec2::new(i, j)) { - wall_count += 1; - } - } - } - - if wall_count == 4 { - grid.set(pos, TownCell::Empty); - } - } - } - }; - - remove_extra_walls(); - - let mut variate_walls = || { - for _ in 0..100 { - let pos = Vec2::new( - rng.gen_range(0, grid.size().x - 1), - rng.gen_range(0, grid.size().y - 1), - ); - - let (mut wall_count, mut empty_count) = (0, 0); - for i in 0..2 { - for j in 0..2 { - match grid.get(pos + Vec2::new(i, j)) { - Some(TownCell::Wall) => wall_count += 1, - Some(TownCell::Empty) => empty_count += 1, - _ => {} - } - } - } - - // Swap! - if (wall_count, empty_count) == (3, 1) { - let cell00 = grid.get(pos + Vec2::new(0, 0)).unwrap().clone(); - let cell10 = grid.get(pos + Vec2::new(1, 0)).unwrap().clone(); - let cell01 = grid.get(pos + Vec2::new(0, 1)).unwrap().clone(); - let cell11 = grid.get(pos + Vec2::new(1, 1)).unwrap().clone(); - - grid.set(pos + Vec2::new(0, 0), cell11); - grid.set(pos + Vec2::new(1, 0), cell01); - grid.set(pos + Vec2::new(0, 1), cell10); - grid.set(pos + Vec2::new(1, 1), cell00); - - break; - } - } - }; - - for _ in 0..100 { - //variate_walls(); - } - - /* - // Place houses - for x in 0..grid.size().x { - for y in 0..grid.size().y { - let pos = Vec2::new(x, y); - let wpos = center + (pos - grid.size() / 2) * CELL_SIZE + CELL_SIZE / 2; - - // Is this cell near a road? - let near_road = 'near_road: { - let dirs = [-1, 0, 1, 0]; - let offs = rng.gen_range(0, 4); - for i in 0..4 { - let dir = Vec2::new(dirs[(offs + i) % 4], dirs[(offs + i + 1) % 4]); - if grid.get(pos + dir).unwrap_or(&TownCell::Empty).is_road() { - break 'near_road Some(dir); - } - } - None - }; - - match (near_road, grid.get_mut(pos)) { - (Some(dir), Some(cell @ TownCell::Empty)) if rng.gen_range(0, 6) > 0 => { - let alt = gen.get(wpos).map(|sample| sample.alt).unwrap_or(0.0) as i32; - - *cell = TownCell::Building { - kind: Building::House, - wpos: Vec3::new(wpos.x, wpos.y, alt), - size: Vec2::one(), - units: ( - Vec2::new(dir.y, dir.x) * (rng.gen_range(0, 1) * 2 - 1), - -dir, - ), - seed: rng.gen(), - }; - } - _ => {} - } - } - } - - // Merge buildings - for x in 0..grid.size().x { - for y in 0..grid.size().y { - let pos = Vec2::new(x, y); - for offx in -1..1 { - for offy in -1..1 { - if grid - .iter_area(pos + Vec2::new(offx, offy), Vec2::broadcast(2)) - .any(|cell| cell.map(|(_, cell)| !cell.mergeable()).unwrap_or(true)) - { - continue; - } - - match grid.get_mut(pos) { - Some(TownCell::Building { - kind, wpos, size, .. - }) => { - *kind = if rng.gen() { - Building::Blacksmith - } else { - Building::TownHall - }; - *wpos += Vec3::new(CELL_SIZE / 2, CELL_SIZE / 2, 0) - * (Vec2::new(offx, offy) * 2 + 1); - *size = Vec2::broadcast(2); - } - _ => continue, - } - - for i in 0..2 { - for j in 0..2 { - let p = Vec2::new(i + offx, j + offy); - if pos + p != pos { - grid.set(pos + p, TownCell::PartOf(pos)); - } - } - } - } - } - } - } - */ - - Some(Self { - center, - radius, - grid, - properties, - }) - } - - fn get_cell(&self, wpos: Vec2) -> &TownCell { - let rpos = wpos - self.center; - match self - .grid - .get(rpos.map(|e| e.div_euclid(CELL_SIZE)) + self.grid.size() / 2) - .unwrap_or(&TownCell::Empty) - { - TownCell::PartOf(pos) => self.grid.get(*pos).unwrap(), - cell => cell, - } - } -} - -pub struct TownGen; - -impl<'a> Sampler<'a> for TownGen { - type Index = (&'a TownState, Vec3, &'a ColumnSample<'a>, f32); - type Sample = Option; - - fn get(&self, (town, wpos, sample, height): Self::Index) -> Self::Sample { - match town.get_cell(Vec2::from(wpos)) { - cell if cell.is_road() => { - if (wpos.z as f32) < height - 1.0 { - Some(Block::new( - BlockKind::Normal, - Lerp::lerp( - Rgb::new(150.0, 120.0, 50.0), - Rgb::new(100.0, 70.0, 20.0), - sample.marble_small, - ) - .map(|e| e as u8), - )) - } else { - Some(Block::empty()) - } - } - TownCell::MemberOf(idx) => { - if (wpos.z as f32) < height + 8.0 { - Some(Block::new( - BlockKind::Normal, - match town.properties[*idx] { - Property::House(col) => col, - }, - )) - } else { - None - } - } - TownCell::Market => { - if (wpos.z as f32) < height { - Some(Block::new(BlockKind::Normal, Rgb::new(255, 0, 0))) - } else { - None - } - } - TownCell::Wall => { - if (wpos.z as f32) < height + 20.0 { - Some(Block::new(BlockKind::Normal, Rgb::new(100, 100, 100))) - } else { - None - } - } - TownCell::Building { - kind, - wpos: building_wpos, - units, - seed, - .. - } => { - let rpos = wpos - building_wpos; - let volumes: &'static [_] = match kind { - Building::House => &HOUSES, - Building::Blacksmith => &BLACKSMITHS, - Building::TownHall => &TOWNHALLS, - }; - volumes[*seed as usize % volumes.len()] - .get( - Vec3::from(units.0) * rpos.x - + Vec3::from(units.1) * rpos.y - + Vec3::unit_z() * rpos.z, - ) - .ok() - .and_then(|sb| { - block_from_structure(*sb, BlockKind::Normal, wpos, wpos.into(), 0, sample) - }) - } - _ => None, - } - } -} - -impl<'a> Generator<'a, TownState> for TownGen { - fn get_z_limits( - &self, - town: &'a TownState, - wpos: Vec2, - sample: &ColumnSample, - ) -> (f32, f32) { - (sample.alt - 32.0, sample.alt + 75.0) - } -} -*/ diff --git a/world/src/util/random.rs b/world/src/util/random.rs index 7cb106e06c..da56a76ee2 100644 --- a/world/src/util/random.rs +++ b/world/src/util/random.rs @@ -16,21 +16,21 @@ impl Sampler<'static> for RandomField { type Sample = u32; fn get(&self, pos: Self::Index) -> Self::Sample { - let pos = pos.map(|e| (e * 13 + (1 << 31)) as u32); + let pos = pos.map(|e| u32::from_le_bytes(e.to_le_bytes())); let mut a = self.seed; a = (a ^ 61) ^ (a >> 16); - a = a + (a << 3); + a = a.wrapping_add(a << 3); a = a ^ pos.x; a = a ^ (a >> 4); - a = a * 0x27d4eb2d; + a = a.wrapping_mul(0x27d4eb2d); a = a ^ (a >> 15); a = a ^ pos.y; a = (a ^ 61) ^ (a >> 16); - a = a + (a << 3); + a = a.wrapping_add(a << 3); a = a ^ (a >> 4); a = a ^ pos.z; - a = a * 0x27d4eb2d; + a = a.wrapping_mul(0x27d4eb2d); a = a ^ (a >> 15); a } @@ -51,12 +51,14 @@ impl Sampler<'static> for RandomPerm { 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 + let a = self + .seed + .wrapping_mul(3471) + .wrapping_add(perm) + .wrapping_add(0x3BE7172B) + .wrapping_mul(perm) + .wrapping_add(0x172A3BE1); + let b = a.wrapping_mul(a); + b ^ (a >> 17) ^ b >> 15 } }