From 71fcb70a518803aee21fbd02b2cb12f8f22cf782 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sat, 24 Aug 2019 23:57:55 +0100 Subject: [PATCH] Initial implementation of towns --- assets/world/structure/human/mage_tower.vox | Bin 125468 -> 41004 bytes 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, 319 insertions(+), 86 deletions(-) diff --git a/assets/world/structure/human/mage_tower.vox b/assets/world/structure/human/mage_tower.vox index 8121d97ec1ab2f201dbfc7b72981dfcc2050e7d1..31c86be2d82524f88125aa67f1915a0577ebe063 100644 GIT binary patch delta 41 wcmbPpg?-HdCXq1z2!&}33=F=Go_;`zL1F;|!(@dyG8+v(%--%S%*ew60RE2)e*gdg literal 125468 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 QB2V?#|g+HD*7--reQR{#J2 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() }