Added skeleton house generation

This commit is contained in:
Joshua Barretto 2020-04-10 12:16:30 +01:00
parent 4830757bc9
commit b0f9ef5f30
7 changed files with 326 additions and 49 deletions

View File

@ -1568,6 +1568,10 @@ impl WorldSim {
}
}
pub fn get_alt_approx(&self, pos: Vec2<i32>) -> Option<f32> {
self.get_interpolated(pos, |chunk| chunk.alt)
}
pub fn get_wpos(&self, wpos: Vec2<i32>) -> Option<&SimChunk> {
self.get(
wpos.map2(Vec2::from(TerrainChunkSize::RECT_SIZE), |e, sz: u32| {

View File

@ -0,0 +1,86 @@
use vek::*;
use rand::prelude::*;
use common::{
terrain::{Block, BlockKind},
vol::Vox,
};
use super::{
Archetype,
super::skeleton::*,
};
pub struct House {
roof_color: Rgb<u8>,
}
impl Archetype for House {
type Attr = ();
fn generate<R: Rng>(rng: &mut R) -> Self {
Self {
roof_color: Rgb::new(
rng.gen_range(50, 200),
rng.gen_range(50, 200),
rng.gen_range(50, 200),
),
}
}
fn draw(
&self,
dist: i32,
offset: Vec2<i32>,
z: i32,
branch: &Branch<Self::Attr>,
) -> Option<Block> {
let profile = Vec2::new(offset.x, z);
let foundation = Block::new(BlockKind::Normal, Rgb::new(100, 100, 100));
let log = Block::new(BlockKind::Normal, Rgb::new(60, 45, 30));
let floor = Block::new(BlockKind::Normal, Rgb::new(100, 75, 50));
let wall = Block::new(BlockKind::Normal, Rgb::new(200, 180, 150));
let roof = Block::new(BlockKind::Normal, self.roof_color);
let empty = Block::empty();
let width = 3 + branch.locus;
let roof_height = 8 + width;
let ceil_height = 6;
if profile.y <= 1 - (dist - width - 1).max(0) && dist < width + 3 { // Foundations
if dist < width { // Floor
Some(floor)
} else {
Some(foundation)
}
} else if profile.y > roof_height - profile.x { // Air above roof
None
} else if profile.y == roof_height - profile.x
&& profile.y >= ceil_height
&& dist <= width + 2
{ // Roof
if profile.x == 0 || dist == width + 2 { // Eaves
Some(log)
} else {
Some(roof)
}
} else if dist == width { // Wall
if offset.x == offset.y || profile.y == ceil_height || offset.x == 0 {
Some(log)
} else {
Some(wall)
}
} else if dist < width { // Internals
if profile.y == ceil_height {
if profile.x == 0 {// Rafters
Some(log)
} else { // Ceiling
Some(floor)
}
} else {
Some(empty)
}
} else {
None
}
}
}

View File

@ -0,0 +1,53 @@
use vek::*;
use rand::prelude::*;
use common::{
terrain::{Block, BlockKind},
vol::Vox,
};
use super::{
Archetype,
super::skeleton::*,
};
pub struct Keep;
impl Archetype for Keep {
type Attr = ();
fn generate<R: Rng>(rng: &mut R) -> Self {
Self
}
fn draw(
&self,
dist: i32,
offset: Vec2<i32>,
z: i32,
branch: &Branch<Self::Attr>,
) -> Option<Block> {
let profile = Vec2::new(offset.x, z);
let foundation = Block::new(BlockKind::Normal, Rgb::new(100, 100, 100));
let log = Block::new(BlockKind::Normal, Rgb::new(60, 45, 30));
let wall = Block::new(BlockKind::Normal, Rgb::new(75, 100, 125));
let roof = Block::new(BlockKind::Normal, Rgb::new(150, 120, 50));
let empty = Block::empty();
let width = 3 + branch.locus;
let rampart_width = 5 + branch.locus;
let roof_height = 12 + width;
let ceil_height = 8;
if profile.y <= 1 - (dist - width - 1).max(0) && dist < width + 3 { // Foundations
Some(foundation)
} else if profile.y == ceil_height && dist < rampart_width {
Some(roof)
} else if dist == rampart_width && profile.y >= ceil_height && profile.y < ceil_height + 4 {
Some(wall)
} else if dist == width && profile.y <= ceil_height {
Some(wall)
} else {
None
}
}
}

View File

@ -0,0 +1,20 @@
pub mod house;
pub mod keep;
use vek::*;
use rand::prelude::*;
use common::terrain::Block;
use super::skeleton::*;
pub trait Archetype {
type Attr: Default;
fn generate<R: Rng>(rng: &mut R) -> Self where Self: Sized;
fn draw(
&self,
dist: i32,
offset: Vec2<i32>,
z: i32,
branch: &Branch<Self::Attr>,
) -> Option<Block>;
}

View File

@ -1 +1,71 @@
mod skeleton;
mod archetype;
// Reexports
pub use self::archetype::Archetype;
use vek::*;
use rand::prelude::*;
use self::skeleton::*;
use common::terrain::Block;
pub type HouseBuilding = Building<archetype::house::House>;
pub struct Building<A: Archetype> {
skel: Skeleton<A::Attr>,
archetype: A,
origin: Vec3<i32>,
}
impl<A: Archetype> Building<A> {
pub fn generate(rng: &mut impl Rng, origin: Vec3<i32>) -> Self
where A: Sized
{
let len = rng.gen_range(-8, 12).max(0);
let archetype = A::generate(rng);
Self {
skel: Skeleton {
offset: -len / 2,
ori: Ori::East,
root: Branch {
len,
attr: A::Attr::default(),
locus: 3 + rng.gen_range(0, 6),
children: (0..rng.gen_range(1, 3))
.map(|_| (rng.gen_range(0, len + 1), Branch {
len: rng.gen_range(5, 12) * if rng.gen() { 1 } else { -1 },
attr: A::Attr::default(),
locus: 1 + rng.gen_range(0, 3),
children: Vec::new(),
}))
.collect(),
},
},
archetype,
origin,
}
}
pub fn bounds_2d(&self) -> Aabr<i32> {
let b = self.skel.bounds();
Aabr {
min: Vec2::from(self.origin) + b.min - 12,
max: Vec2::from(self.origin) + b.max + 12,
}
}
pub fn bounds(&self) -> Aabb<i32> {
let aabr = self.bounds_2d();
Aabb {
min: Vec3::from(aabr.min) + Vec3::unit_z() * (self.origin.z - 5),
max: Vec3::from(aabr.max) + Vec3::unit_z() * (self.origin.z + 32),
}
}
pub fn sample(&self, pos: Vec3<i32>) -> Option<Block> {
let rpos = pos - self.origin;
let (dist, offset, branch) = self.skel.closest(rpos.into());
self.archetype.draw(dist, offset, rpos.z, branch)
}
}

View File

@ -1,6 +1,6 @@
use vek::*;
#[derive(Copy, Clone)]
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Ori {
East,
North,
@ -22,14 +22,15 @@ impl Ori {
}
}
pub struct Branch {
len: i32,
locus: i32,
children: Vec<(i32, Branch)>,
pub struct Branch<T> {
pub len: i32,
pub attr: T,
pub locus: i32,
pub children: Vec<(i32, Branch<T>)>,
}
impl Branch {
fn for_each<'a>(&'a self, node: Vec2<i32>, ori: Ori, f: &mut impl FnMut(Vec2<i32>, Ori, &'a Branch)) {
impl<T> Branch<T> {
fn for_each<'a>(&'a self, node: Vec2<i32>, ori: Ori, f: &mut impl FnMut(Vec2<i32>, Ori, &'a Branch<T>)) {
f(node, ori, self);
for (offset, child) in &self.children {
child.for_each(node + ori.dir() * *offset, ori.flip(), f);
@ -37,28 +38,49 @@ impl Branch {
}
}
pub struct Skeleton {
offset: i32,
ori: Ori,
root: Branch,
pub struct Skeleton<T> {
pub offset: i32,
pub ori: Ori,
pub root: Branch<T>,
}
impl Skeleton {
pub fn for_each<'a>(&'a self, mut f: impl FnMut(Vec2<i32>, Ori, &'a Branch)) {
impl<T> Skeleton<T> {
pub fn for_each<'a>(&'a self, mut f: impl FnMut(Vec2<i32>, Ori, &'a Branch<T>)) {
self.root.for_each(self.ori.dir() * self.offset, self.ori, &mut f);
}
pub fn closest(&self, pos: Vec2<i32>) -> (i32, &Branch) {
pub fn bounds(&self) -> Aabr<i32> {
let mut bounds = Aabr::new_empty(self.ori.dir() * self.offset);
self.for_each(|node, ori, branch| {
bounds.expand_to_contain(Aabr::new_empty(node - ori.flip().dir() * branch.locus)
.expanded_to_contain_point(node + ori.dir() * branch.len + ori.flip().dir() * branch.locus));
});
bounds
}
pub fn closest(&self, pos: Vec2<i32>) -> (i32, Vec2<i32>, &Branch<T>) {
let mut min = None;
self.for_each(|node, ori, branch| {
let bounds = Aabr::new_empty(node - ori.flip().dir() * branch.locus)
.expanded_to_contain_point(node + ori.dir() * branch.len + ori.flip().dir() * branch.locus);
let projected = pos.map2(bounds.min.zip(bounds.max), |e, (min, max)| Clamp::clamp(e, min, max));
let dist = (projected - pos).map(|e| e.abs()).reduce_max();
if min.map(|(min_dist, _)| dist < min_dist).unwrap_or(true) {
min = Some((dist, branch));
let node2 = node + ori.dir() * branch.len;
let bounds = Aabr::new_empty(node)
.expanded_to_contain_point(node2);
let offs = if ori == Ori::East {
Vec2::new(
node.y - pos.y,
pos.x - pos.x.clamped(bounds.min.x, bounds.max.x)
)
} else {
Vec2::new(
node.x - pos.x,
pos.y - pos.y.clamped(bounds.min.y, bounds.max.y)
)
}.map(|e| e.abs());
let dist = offs.reduce_max();
let dist_locus = dist - branch.locus;
if min.map(|(min_dist_locus, _, _, _)| dist_locus < min_dist_locus).unwrap_or(true) {
min = Some((dist_locus, dist, offs, branch));
}
});
min.unwrap()
min.map(|(_, dist, offs, branch)| (dist, offs, branch)).unwrap()
}
}

View File

@ -5,6 +5,7 @@ use crate::{
sim::{SimChunk, WorldSim},
util::{Grid, RandomField, Sampler, StructureGen2d},
};
use self::building::HouseBuilding;
use super::SpawnRules;
use common::{
astar::Astar,
@ -81,12 +82,11 @@ const AREA_SIZE: u32 = 32;
fn to_tile(e: i32) -> i32 { ((e as f32).div_euclid(AREA_SIZE as f32)).floor() as i32 }
pub enum StructureKind {
House,
House(HouseBuilding),
}
pub struct Structure {
kind: StructureKind,
bounds: Aabr<i32>,
}
pub struct Settlement {
@ -105,25 +105,31 @@ pub struct Farm {
base_tile: Vec2<i32>,
}
pub struct GenCtx<'a, R: Rng> {
sim: Option<&'a WorldSim>,
rng: &'a mut R,
}
impl Settlement {
pub fn generate(wpos: Vec2<i32>, sim: Option<&WorldSim>, rng: &mut impl Rng) -> Self {
let mut ctx = GenCtx { sim, rng };
let mut this = Self {
origin: wpos,
land: Land::new(rng),
land: Land::new(ctx.rng),
farms: Store::default(),
structures: Vec::new(),
town: None,
};
if let Some(sim) = sim {
this.designate_from_world(sim, rng);
if let Some(sim) = ctx.sim {
this.designate_from_world(sim, ctx.rng);
}
//this.place_river(rng);
this.place_farms(rng);
this.place_town(rng);
this.place_paths(rng);
this.place_farms(&mut ctx);
this.place_town(ctx.rng);
this.place_paths(ctx.rng);
this
}
@ -295,8 +301,8 @@ impl Settlement {
.write_path(&wall_path, WayKind::Wall, buildable, true);
}
pub fn place_farms(&mut self, rng: &mut impl Rng) {
const FARM_COUNT: usize = 4;
pub fn place_farms(&mut self, ctx: &mut GenCtx<impl Rng>) {
const FARM_COUNT: usize = 6;
const FIELDS_PER_FARM: usize = 5;
for _ in 0..FARM_COUNT {
@ -309,23 +315,26 @@ impl Settlement {
self.land.set(base_tile, farmhouse);
// Farmhouses
for _ in 0..rng.gen_range(1, 3) {
for _ in 0..ctx.rng.gen_range(1, 3) {
let house_pos = base_tile.map(|e| e * AREA_SIZE as i32 + AREA_SIZE as i32 / 2)
+ Vec2::new(rng.gen_range(-16, 16), rng.gen_range(-16, 16));
+ Vec2::new(ctx.rng.gen_range(-16, 16), ctx.rng.gen_range(-16, 16));
self.structures.push(Structure {
kind: StructureKind::House,
bounds: Aabr {
min: house_pos - Vec2::new(rng.gen_range(4, 6), rng.gen_range(4, 6)),
max: house_pos + Vec2::new(rng.gen_range(4, 6), rng.gen_range(4, 6)),
},
kind: StructureKind::House(HouseBuilding::generate(ctx.rng, Vec3::new(
house_pos.x,
house_pos.y,
ctx.sim
.and_then(|sim| sim.get_alt_approx(self.origin + house_pos))
.unwrap_or(0.0)
.ceil() as i32,
))),
});
}
// Fields
let farmland = self.farms.insert(Farm { base_tile });
for _ in 0..FIELDS_PER_FARM {
self.place_field(farmland, base_tile, rng);
self.place_field(farmland, base_tile, ctx.rng);
}
}
}
@ -451,19 +460,32 @@ impl Settlement {
}
}
}
// Apply structures
for structure in &self.structures {
match &structure.kind {
StructureKind::House(b) => {
let centre = b.bounds_2d().center();
let bounds = b.bounds();
for x in bounds.min.x..bounds.max.x {
for y in bounds.min.y..bounds.max.y {
for z in bounds.min.z..bounds.max.z {
let rpos = Vec3::new(x, y, z);
let wpos = Vec3::from(self.origin) + rpos;
let coffs = wpos - Vec3::from(wpos2d);
if let Some(block) = b.sample(rpos) {
vol.set(coffs, block);
}
}
}
}
},
}
}
}
pub fn get_color(&self, pos: Vec2<i32>) -> Option<Rgb<u8>> {
if let Some(structure) = self
.structures
.iter()
.find(|s| s.bounds.contains_point(pos))
{
return Some(match structure.kind {
StructureKind::House => Rgb::new(200, 80, 50),
});
}
let sample = self.land.get_at_block(pos);
match sample.tower {