Added town road construction

This commit is contained in:
Joshua Barretto 2019-08-24 14:23:42 +01:00
parent aaade23eef
commit d42485238e
7 changed files with 234 additions and 17 deletions

View File

@ -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)
}
}

View File

@ -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<f64>| {

View File

@ -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<i32>), Sample = Option<Block>>
Sampler<'a, Index = (&'a T, Vec3<i32>, &'a ColumnSample<'a>), Sample = Option<Block>>
{
fn get_z_limits(&self, state: &'a T, wpos: Vec2<i32>, sample: &ColumnSample) -> (f32, f32);
}

View File

@ -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<i32>,
pub radius: i32,
pub grid: Grid<TownCell>,
}
impl TownState {
pub fn generate(center: Vec2<i32>, seed: u32, sim: &mut WorldSim) -> Option<Self> {
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::<Vec<_>>();
// 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<i32>) -> &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<i32>);
type Index = (&'a TownState, Vec3<i32>, &'a ColumnSample<'a>);
type Sample = Option<Block>;
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<i32>,
sample: &ColumnSample,
) -> (f32, f32) {
(sample.alt - 32.0, sample.alt + 64.0)
}
}

View File

@ -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<i32>) -> 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<i32>) -> 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 {

46
world/src/util/grid.rs Normal file
View File

@ -0,0 +1,46 @@
use vek::*;
pub struct Grid<T> {
cells: Vec<T>,
size: Vec2<i32>,
}
impl<T: Clone> Grid<T> {
pub fn new(default_cell: T, size: Vec2<i32>) -> Self {
Self {
cells: vec![default_cell; size.product() as usize],
size,
}
}
fn idx(&self, pos: Vec2<i32>) -> usize {
(pos.y * self.size.x + pos.x) as usize
}
pub fn size(&self) -> Vec2<i32> {
self.size
}
pub fn get(&self, pos: Vec2<i32>) -> Option<&T> {
self.cells.get(self.idx(pos))
}
pub fn get_mut(&mut self, pos: Vec2<i32>) -> Option<&mut T> {
let idx = self.idx(pos);
self.cells.get_mut(idx)
}
pub fn set(&mut self, pos: Vec2<i32>, cell: T) {
let idx = self.idx(pos);
self.cells.get_mut(idx).map(|c| *c = cell);
}
pub fn iter(&self) -> impl Iterator<Item = (Vec2<i32>, &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()
}
}

View File

@ -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},