Added new settlement generation to world, basic rendering

This commit is contained in:
Joshua Barretto 2020-02-07 22:19:25 +00:00
parent 48fb14116a
commit 0021bd6452
11 changed files with 124 additions and 63 deletions

View File

@ -4,8 +4,8 @@
const float PI = 3.141592;
const vec3 SKY_DAY_TOP = vec3(0.1, 0.2, 0.9);
const vec3 SKY_DAY_MID = vec3(0.02, 0.08, 0.8);
const vec3 SKY_DAY_TOP = vec3(0.1, 0.5, 0.9);
const vec3 SKY_DAY_MID = vec3(0.02, 0.28, 0.8);
const vec3 SKY_DAY_BOT = vec3(0.1, 0.2, 0.3);
const vec3 DAY_LIGHT = vec3(1.2, 1.0, 1.0);
const vec3 SUN_HALO_DAY = vec3(0.35, 0.35, 0.0);

View File

@ -10,7 +10,7 @@ use super::SceneData;
use common::{
assets,
figure::Segment,
spiral::Spiral2D,
spiral::Spiral2d,
terrain::{Block, BlockKind, TerrainChunk},
vol::{BaseVol, ReadVol, RectRasterableVol, SampleVol, Vox},
volumes::vol_grid_2d::{VolGrid2d, VolGrid2dError},

View File

@ -9,7 +9,7 @@ fn main() {
let mut win =
minifb::Window::new("Settlement Viewer", W, H, minifb::WindowOptions::default()).unwrap();
let settlement = Settlement::generate(&mut thread_rng());
let settlement = Settlement::generate(Vec2::zero(), &mut thread_rng());
let mut focus = Vec2::<f32>::zero();
let mut zoom = 1.0;
@ -24,7 +24,7 @@ fn main() {
for j in 0..H {
let pos = focus + win_to_pos(Vec2::new(i, j)) * zoom;
let color = settlement.get_color(pos);
let color = settlement.get_color(pos).unwrap_or(Rgb::new(35, 50, 20));
buf[j * W + i] = u32::from_le_bytes([color.b, color.g, color.r, 255]);
}

View File

@ -398,12 +398,14 @@ impl<'a> BlockGen<'a> {
};
// Structures (like towns)
/*
let block = chunk
.structures
.town
.as_ref()
.and_then(|town| TownGen.get((town, wpos, sample, height)))
.or(block);
*/
let block = structures
.iter()
@ -472,6 +474,7 @@ impl<'a> ZCache<'a> {
let max = (ground_max + structure_max).max(self.sample.water_level + 2.0);
// Structures
/*
let (min, max) = self
.sample
.chunk
@ -483,6 +486,7 @@ impl<'a> ZCache<'a> {
(town_min.min(min), town_max.max(max))
})
.unwrap_or((min, max));
*/
let structures_only_min_z = ground_max.max(self.sample.water_level + 2.0);

View File

@ -1000,6 +1000,13 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
humidity.sub(CONFIG.jungle_hum).mul(1.0),
);
let ground = sim_chunk.sites.iter().fold(ground, |ground, site| {
site.get_surface(wpos)
.and_then(|block| block.get_color())
.map(|col| col.map(|e| e as f32 / 255.0))
.unwrap_or(ground)
});
// Snow covering
let snow_cover = temp
.sub(CONFIG.snow_temp)
@ -1095,6 +1102,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
stone_col,
chunk: sim_chunk,
/*
spawn_rules: sim_chunk
.structures
.town
@ -1105,6 +1113,11 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
cliffs: !in_water,
trees: true,
}),
*/
spawn_rules: SpawnRules {
cliffs: !in_water,
trees: true,
},
})
}
}

View File

@ -2,12 +2,33 @@ pub mod settlement;
mod town;
// Reexports
pub use self::town::{TownGen, TownState};
pub use self::{
settlement::Settlement,
town::{TownGen, TownState},
};
use crate::{column::ColumnSample, util::Sampler};
use common::terrain::Block;
use std::sync::Arc;
use vek::*;
#[derive(Clone)]
pub enum Site {
Settlement(Arc<Settlement>),
}
impl Site {
pub fn get_surface(&self, wpos: Vec2<i32>) -> Option<Block> {
match self {
Site::Settlement(settlement) => settlement.get_surface(wpos),
}
}
}
impl From<Settlement> for Site {
fn from(settlement: Settlement) -> Self { Site::Settlement(Arc::new(settlement)) }
}
#[derive(Copy, Clone, Debug)]
pub struct SpawnRules {
pub trees: bool,

View File

@ -1,5 +1,10 @@
use crate::util::{Sampler, StructureGen2d};
use common::{astar::Astar, path::Path, spiral::Spiral2d};
use common::{
astar::Astar,
path::Path,
spiral::Spiral2d,
terrain::{Block, BlockKind},
};
use hashbrown::{HashMap, HashSet};
use rand::prelude::*;
use std::{collections::VecDeque, f32, marker::PhantomData};
@ -56,7 +61,7 @@ pub fn center_of(p: [Vec2<f32>; 3]) -> Vec2<f32> {
Vec2::new(x, y)
}
const AREA_SIZE: u32 = 48;
const AREA_SIZE: u32 = 64;
fn to_tile(e: i32) -> i32 { ((e as f32).div_euclid(AREA_SIZE as f32)).floor() as i32 }
@ -70,6 +75,7 @@ pub struct Structure {
}
pub struct Settlement {
origin: Vec2<i32>,
land: Land,
farms: Store<Farm>,
structures: Vec<Structure>,
@ -85,15 +91,16 @@ pub struct Farm {
}
impl Settlement {
pub fn generate(rng: &mut impl Rng) -> Self {
pub fn generate(wpos: Vec2<i32>, rng: &mut impl Rng) -> Self {
let mut this = Self {
origin: wpos,
land: Land::new(rng),
farms: Store::default(),
structures: Vec::new(),
town: None,
};
this.place_river(rng);
//this.place_river(rng);
this.place_farms(rng);
this.place_town(rng);
@ -296,7 +303,12 @@ impl Settlement {
}
}
pub fn get_color(&self, pos: Vec2<f32>) -> Rgb<u8> {
pub fn get_surface(&self, wpos: Vec2<i32>) -> Option<Block> {
self.get_color((wpos - self.origin).map(|e| e as f32))
.map(|col| Block::new(BlockKind::Normal, col))
}
pub fn get_color(&self, pos: Vec2<f32>) -> Option<Rgb<u8>> {
let pos = pos.map(|e| e.floor() as i32);
if let Some(structure) = self
@ -304,13 +316,13 @@ impl Settlement {
.iter()
.find(|s| s.bounds.contains_point(pos))
{
return match structure.kind {
return Some(match structure.kind {
StructureKind::House => Rgb::new(200, 80, 50),
};
});
}
match self.land.get_at_block(pos) {
Sample::Wilderness => Rgb::zero(),
Some(match self.land.get_at_block(pos) {
Sample::Wilderness => return None,
Sample::Way(WayKind::Path) => Rgb::new(130, 100, 0),
Sample::Way(WayKind::Hedge) => Rgb::new(0, 150, 0),
Sample::Way(WayKind::Wall) => Rgb::new(60, 60, 60),
@ -336,7 +348,7 @@ impl Settlement {
let furrow = (pos * furrow_dir).sum().rem_euclid(4) < 2;
Rgb::new(
if furrow {
150
120
} else {
48 + seed.to_le_bytes()[0] % 64
},
@ -344,7 +356,7 @@ impl Settlement {
16 + seed.to_le_bytes()[2] % 32,
)
},
}
})
}
}
@ -374,9 +386,9 @@ pub enum WayKind {
impl WayKind {
pub fn width(&self) -> f32 {
match self {
WayKind::Path => 2.5,
WayKind::Path => 4.0,
WayKind::Hedge => 1.5,
WayKind::Wall => 2.5,
WayKind::Wall => 3.5,
}
}
}
@ -386,6 +398,14 @@ pub enum Tower {
Wall,
}
impl Tower {
pub fn radius(&self) -> f32 {
match self {
Tower::Wall => 8.0,
}
}
}
pub struct Tile {
plot: Id<Plot>,
ways: [Option<WayKind>; 4],
@ -414,7 +434,7 @@ impl Land {
Self {
tiles: HashMap::new(),
plots: Store::default(),
sampler_warp: StructureGen2d::new(rng.gen(), AREA_SIZE, 16),
sampler_warp: StructureGen2d::new(rng.gen(), AREA_SIZE, AREA_SIZE * 2 / 5),
}
}
@ -428,8 +448,8 @@ impl Land {
let center_tile = self.tile_at(neighbors[4].0.map(to_tile));
if neighbors[4].0.distance_squared(pos) < 6i32.pow(2) {
if let Some(tower) = center_tile.and_then(|tile| tile.tower.as_ref()) {
if let Some(tower) = center_tile.and_then(|tile| tile.tower.as_ref()) {
if (neighbors[4].0.distance_squared(pos) as f32) < tower.radius().powf(2.0) {
return Sample::Tower(tower);
}
}

View File

@ -16,7 +16,7 @@ pub use crate::config::CONFIG;
use crate::{
block::BlockGen,
column::{ColumnGen, ColumnSample},
util::Sampler,
util::{Grid, Sampler},
};
use common::{
generation::{ChunkSupplement, EntityInfo, EntityKind},
@ -95,7 +95,11 @@ impl World {
let meta = TerrainChunkMeta::new(sim_chunk.get_name(&self.sim), sim_chunk.get_biome());
let mut sampler = self.sample_blocks();
let chunk_block_pos = Vec3::from(chunk_pos) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32);
let chunk_wpos2d = Vec2::from(chunk_pos) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32);
let zcache_grid =
Grid::populate_from(TerrainChunkSize::RECT_SIZE.map(|e| e as i32), |offs| {
sampler.get_z_cache(chunk_wpos2d + offs)
});
let mut chunk = TerrainChunk::new(base_z, stone, air, meta);
for y in 0..TerrainChunkSize::RECT_SIZE.y as i32 {
@ -103,12 +107,13 @@ impl World {
if should_continue() {
return Err(());
};
let wpos2d = Vec2::new(x, y)
+ Vec2::from(chunk_pos) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32);
let z_cache = match sampler.get_z_cache(wpos2d) {
Some(z_cache) => z_cache,
None => continue,
let offs = Vec2::new(x, y);
let wpos2d = chunk_wpos2d + offs;
let z_cache = match zcache_grid.get(offs) {
Some(Some(z_cache)) => z_cache,
_ => continue,
};
let (min_z, only_structures_min_z, max_z) = z_cache.get_z_limits(&mut sampler);
@ -119,7 +124,7 @@ impl World {
(min_z as i32..max_z as i32).for_each(|z| {
let lpos = Vec3::new(x, y, z);
let wpos = chunk_block_pos + lpos;
let wpos = Vec3::from(chunk_wpos2d) + lpos;
let only_structures = lpos.z >= only_structures_min_z as i32;
if let Some(block) =
@ -140,7 +145,7 @@ impl World {
lpos.z += 1;
}
(chunk_block_pos + lpos).map(|e| e as f32) + 0.5
(Vec3::from(chunk_wpos2d) + lpos).map(|e: i32| e as f32) + 0.5
};
const SPAWN_RATE: f32 = 0.1;

View File

@ -1,4 +1,3 @@
use super::Settlement;
use hashbrown::HashSet;
use rand::{seq::SliceRandom, Rng};
use vek::*;
@ -9,7 +8,6 @@ pub struct Location {
pub(crate) center: Vec2<i32>,
pub(crate) kingdom: Option<Kingdom>,
pub(crate) neighbours: HashSet<usize>,
pub(crate) settlement: Settlement,
}
impl Location {
@ -19,7 +17,6 @@ impl Location {
center,
kingdom: None,
neighbours: HashSet::default(),
settlement: Settlement::generate(rng),
}
}

View File

@ -15,7 +15,6 @@ pub use self::{
},
location::Location,
map::{MapConfig, MapDebug},
settlement::Settlement,
util::{
cdf_irwin_hall, downhill, get_oceans, local_cells, map_edge_factor, neighbors,
uniform_idx_as_vec2, uniform_noise, uphill, vec2_as_uniform_idx, InverseCdf, ScaleBias,
@ -27,7 +26,7 @@ use crate::{
all::ForestKind,
block::BlockGen,
column::ColumnGen,
generator::TownState,
generator::{Settlement, Site},
util::{seed_expan, FastNoise, RandomField, Sampler, StructureGen2d},
CONFIG,
};
@ -1450,7 +1449,7 @@ impl WorldSim {
e * sz as i32 + sz as i32 / 2
})
};
let maybe_towns = self
let sites = self
.gen_ctx
.town_gen
.par_iter(
@ -1462,11 +1461,12 @@ impl WorldSim {
|mut block_gen, (pos, seed)| {
let mut rng = ChaChaRng::from_seed(seed_expan::rng_state(seed));
// println!("Town: {:?}", town);
TownState::generate(pos, &mut block_gen, &mut rng).map(|t| (pos, Arc::new(t)))
//TownState::generate(pos, &mut block_gen, &mut rng).map(|t| (pos,
// Arc::new(t)))
(pos, Site::from(Settlement::generate(pos, &mut rng)))
},
)
.filter_map(|x| x)
.collect::<HashMap<_, _>>();
.collect::<Vec<_>>();
let gen_ctx = &self.gen_ctx;
self.chunks
@ -1476,21 +1476,13 @@ impl WorldSim {
let chunk_pos = uniform_idx_as_vec2(ij);
let wpos = chunk_idx_center(chunk_pos);
let near_towns = gen_ctx.town_gen.get(wpos);
let town = near_towns
if let Some((pos, site)) = sites
.iter()
.min_by_key(|(pos, _seed)| wpos.distance_squared(*pos));
let maybe_town = town
.and_then(|(pos, _seed)| maybe_towns.get(pos))
// Only care if we're close to the town
.filter(|town| {
Vec2::from(town.center()).distance_squared(wpos)
< town.radius().add(64).pow(2)
})
.cloned();
chunk.structures.town = maybe_town;
.filter(|(pos, _)| pos.distance_squared(wpos) < 1200i32.pow(2))
.min_by_key(|(pos, _)| wpos.distance_squared(*pos))
{
chunk.sites.push(site.clone());
}
});
// Create waypoints
@ -1771,7 +1763,7 @@ pub struct SimChunk {
pub location: Option<LocationInfo>,
pub river: RiverData,
pub structures: Structures,
pub sites: Vec<Site>,
pub contains_waypoint: bool,
}
@ -1789,11 +1781,6 @@ pub struct LocationInfo {
pub near: Vec<RegionInfo>,
}
#[derive(Clone)]
pub struct Structures {
pub town: Option<Arc<TownState>>,
}
impl SimChunk {
fn generate(posi: usize, gen_ctx: &GenCtx, gen_cdf: &GenCdf) -> Self {
let pos = uniform_idx_as_vec2(posi);
@ -2015,7 +2002,7 @@ impl SimChunk {
spawn_rate: 1.0,
location: None,
river,
structures: Structures { town: None },
sites: Vec::new(),
contains_waypoint: false,
}
}

View File

@ -5,8 +5,22 @@ pub struct Grid<T> {
size: Vec2<i32>,
}
impl<T: Clone> Grid<T> {
pub fn new(default_cell: T, size: Vec2<i32>) -> Self {
impl<T> Grid<T> {
pub fn populate_from(size: Vec2<i32>, mut f: impl FnMut(Vec2<i32>) -> T) -> Self {
Self {
cells: (0..size.y)
.map(|y| (0..size.x).map(move |x| Vec2::new(x, y)))
.flatten()
.map(&mut f)
.collect(),
size,
}
}
pub fn new(default_cell: T, size: Vec2<i32>) -> Self
where
T: Clone,
{
Self {
cells: vec![default_cell; size.product() as usize],
size,