mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Significantly better house generation
This commit is contained in:
parent
b0f9ef5f30
commit
c3e5b3057f
@ -4,6 +4,7 @@ use common::{
|
||||
terrain::{Block, BlockKind},
|
||||
vol::Vox,
|
||||
};
|
||||
use crate::util::{RandomField, Sampler};
|
||||
use super::{
|
||||
Archetype,
|
||||
super::skeleton::*,
|
||||
@ -11,6 +12,7 @@ use super::{
|
||||
|
||||
pub struct House {
|
||||
roof_color: Rgb<u8>,
|
||||
noise: RandomField,
|
||||
}
|
||||
|
||||
impl Archetype for House {
|
||||
@ -23,61 +25,83 @@ impl Archetype for House {
|
||||
rng.gen_range(50, 200),
|
||||
rng.gen_range(50, 200),
|
||||
),
|
||||
noise: RandomField::new(rng.gen()),
|
||||
}
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
dist: i32,
|
||||
offset: Vec2<i32>,
|
||||
bound_offset: Vec2<i32>,
|
||||
center_offset: Vec2<i32>,
|
||||
z: i32,
|
||||
branch: &Branch<Self::Attr>,
|
||||
) -> Option<Block> {
|
||||
let profile = Vec2::new(offset.x, z);
|
||||
) -> Option<Option<Block>> {
|
||||
let profile = Vec2::new(bound_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 make_block = |r, g, b| {
|
||||
let nz = self.noise.get(Vec3::new(center_offset.x, center_offset.y, z * 8));
|
||||
Some(Some(Block::new(BlockKind::Normal, Rgb::new(r, g, b) + (nz & 0x0F) as u8 - 8)))
|
||||
};
|
||||
|
||||
let foundation = make_block(100, 100, 100);
|
||||
let log = make_block(60, 45, 30);
|
||||
let floor = make_block(100, 75, 50);
|
||||
let wall = make_block(200, 180, 150);
|
||||
let roof = make_block(self.roof_color.r, self.roof_color.g, self.roof_color.b);
|
||||
let empty = Some(Some(Block::empty()));
|
||||
|
||||
let width = 3 + branch.locus;
|
||||
let roof_height = 8 + width;
|
||||
let ceil_height = 6;
|
||||
let width = 3 + branch.locus + if profile.y >= ceil_height { 1 } else { 0 };
|
||||
let foundation_height = 1 - (dist - width - 1).max(0);
|
||||
let roof_height = 8 + width;
|
||||
|
||||
if profile.y <= 1 - (dist - width - 1).max(0) && dist < width + 3 { // Foundations
|
||||
if dist < width { // Floor
|
||||
Some(floor)
|
||||
if center_offset.map(|e| e.abs()).reduce_max() == 0 && profile.y > foundation_height + 1 { // Chimney shaft
|
||||
empty
|
||||
} else if center_offset.map(|e| e.abs()).reduce_max() <= 1 && profile.y < roof_height + 2 { // Chimney
|
||||
if center_offset.product() == 0 && profile.y > foundation_height + 1 && profile.y <= foundation_height + 3 { // Fireplace
|
||||
empty
|
||||
} else {
|
||||
Some(foundation)
|
||||
foundation
|
||||
}
|
||||
} else if profile.y <= foundation_height && dist < width + 3 { // Foundations
|
||||
if dist == width - 1 { // Floor lining
|
||||
log
|
||||
} else if dist < width - 1 && profile.y == foundation_height { // Floor
|
||||
floor
|
||||
} else if dist < width && profile.y >= foundation_height - 3 { // Basement
|
||||
empty
|
||||
} else {
|
||||
foundation
|
||||
}
|
||||
} else if profile.y > roof_height - profile.x { // Air above roof
|
||||
None
|
||||
Some(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)
|
||||
if profile.x == 0 || dist == width + 2 || profile.x.abs() % 3 == 0 { // Eaves
|
||||
log
|
||||
} else {
|
||||
Some(roof)
|
||||
roof
|
||||
}
|
||||
} else if dist == width { // Wall
|
||||
if offset.x == offset.y || profile.y == ceil_height || offset.x == 0 {
|
||||
Some(log)
|
||||
if bound_offset.x == bound_offset.y || profile.y == ceil_height || bound_offset.x == 0 {
|
||||
log
|
||||
} else if profile.x >= 2 && profile.x <= width - 2 && profile.y >= foundation_height + 2 && profile.y <= foundation_height + 3 { // Windows
|
||||
empty
|
||||
} else {
|
||||
Some(wall)
|
||||
wall
|
||||
}
|
||||
} else if dist < width { // Internals
|
||||
if profile.y == ceil_height {
|
||||
if profile.x == 0 {// Rafters
|
||||
Some(log)
|
||||
log
|
||||
} else { // Ceiling
|
||||
Some(floor)
|
||||
floor
|
||||
}
|
||||
} else {
|
||||
Some(empty)
|
||||
empty
|
||||
}
|
||||
} else {
|
||||
None
|
||||
|
@ -21,33 +21,38 @@ impl Archetype for Keep {
|
||||
fn draw(
|
||||
&self,
|
||||
dist: i32,
|
||||
offset: Vec2<i32>,
|
||||
bound_offset: Vec2<i32>,
|
||||
center_offset: Vec2<i32>,
|
||||
z: i32,
|
||||
branch: &Branch<Self::Attr>,
|
||||
) -> Option<Block> {
|
||||
let profile = Vec2::new(offset.x, z);
|
||||
) -> Option<Option<Block>> {
|
||||
let profile = Vec2::new(bound_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 make_block = |r, g, b| {
|
||||
Some(Some(Block::new(BlockKind::Normal, Rgb::new(r, g, b))))
|
||||
};
|
||||
|
||||
let foundation = make_block(100, 100, 100);
|
||||
let log = make_block(60, 45, 30);
|
||||
let wall = make_block(75, 100, 125);
|
||||
let roof = make_block(150, 120, 50);
|
||||
let empty = Some(Some(Block::empty()));
|
||||
|
||||
let width = 3 + branch.locus;
|
||||
let rampart_width = 5 + branch.locus;
|
||||
let roof_height = 12 + width;
|
||||
let ceil_height = 8;
|
||||
let ceil_height = 16;
|
||||
|
||||
if profile.y <= 1 - (dist - width - 1).max(0) && dist < width + 3 { // Foundations
|
||||
Some(foundation)
|
||||
foundation
|
||||
} else if profile.y == ceil_height && dist < rampart_width {
|
||||
Some(roof)
|
||||
roof
|
||||
} else if dist == rampart_width && profile.y >= ceil_height && profile.y < ceil_height + 4 {
|
||||
Some(wall)
|
||||
wall
|
||||
} else if dist == width && profile.y <= ceil_height {
|
||||
Some(wall)
|
||||
wall
|
||||
} else {
|
||||
None
|
||||
empty
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,8 +13,9 @@ pub trait Archetype {
|
||||
fn draw(
|
||||
&self,
|
||||
dist: i32,
|
||||
offset: Vec2<i32>,
|
||||
bound_offset: Vec2<i32>,
|
||||
center_offset: Vec2<i32>,
|
||||
z: i32,
|
||||
branch: &Branch<Self::Attr>,
|
||||
) -> Option<Block>;
|
||||
) -> Option<Option<Block>>;
|
||||
}
|
||||
|
@ -25,13 +25,13 @@ impl<A: Archetype> Building<A> {
|
||||
let archetype = A::generate(rng);
|
||||
Self {
|
||||
skel: Skeleton {
|
||||
offset: -len / 2,
|
||||
offset: -rng.gen_range(-4, len + 4).clamped(0, len),
|
||||
ori: Ori::East,
|
||||
root: Branch {
|
||||
len,
|
||||
attr: A::Attr::default(),
|
||||
locus: 3 + rng.gen_range(0, 6),
|
||||
children: (0..rng.gen_range(1, 3))
|
||||
locus: 2 + rng.gen_range(0, 5),
|
||||
children: (0..rng.gen_range(0, 4))
|
||||
.map(|_| (rng.gen_range(0, len + 1), Branch {
|
||||
len: rng.gen_range(5, 12) * if rng.gen() { 1 } else { -1 },
|
||||
attr: A::Attr::default(),
|
||||
@ -49,23 +49,23 @@ impl<A: Archetype> Building<A> {
|
||||
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,
|
||||
min: Vec2::from(self.origin) + b.min - 14,
|
||||
max: Vec2::from(self.origin) + b.max + 14,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bounds(&self) -> Aabb<i32> {
|
||||
let aabr = self.bounds_2d();
|
||||
Aabb {
|
||||
min: Vec3::from(aabr.min) + Vec3::unit_z() * (self.origin.z - 5),
|
||||
min: Vec3::from(aabr.min) + Vec3::unit_z() * (self.origin.z - 8),
|
||||
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)
|
||||
self.skel.closest(rpos.into(), |dist, bound_offset, center_offset, branch| {
|
||||
self.archetype.draw(dist, bound_offset, center_offset, rpos.z, branch)
|
||||
}).flatten()
|
||||
}
|
||||
}
|
||||
|
@ -58,13 +58,13 @@ impl<T> Skeleton<T> {
|
||||
bounds
|
||||
}
|
||||
|
||||
pub fn closest(&self, pos: Vec2<i32>) -> (i32, Vec2<i32>, &Branch<T>) {
|
||||
pub fn closest<R>(&self, pos: Vec2<i32>, mut f: impl FnMut(i32, Vec2<i32>, Vec2<i32>, &Branch<T>) -> Option<R>) -> Option<R> {
|
||||
let mut min = None;
|
||||
self.for_each(|node, ori, 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 {
|
||||
let bound_offset = if ori == Ori::East {
|
||||
Vec2::new(
|
||||
node.y - pos.y,
|
||||
pos.x - pos.x.clamped(bounds.min.x, bounds.max.x)
|
||||
@ -75,12 +75,17 @@ impl<T> Skeleton<T> {
|
||||
pos.y - pos.y.clamped(bounds.min.y, bounds.max.y)
|
||||
)
|
||||
}.map(|e| e.abs());
|
||||
let dist = offs.reduce_max();
|
||||
let center_offset = if ori == Ori::East {
|
||||
Vec2::new(pos.y, pos.x)
|
||||
} else {
|
||||
Vec2::new(pos.x, pos.y)
|
||||
};
|
||||
let dist = bound_offset.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));
|
||||
if min.as_ref().map(|(min_dist_locus, _)| dist_locus < *min_dist_locus).unwrap_or(true) {
|
||||
min = f(dist, bound_offset, center_offset, branch).map(|r| (dist_locus, r));
|
||||
}
|
||||
});
|
||||
min.map(|(_, dist, offs, branch)| (dist, offs, branch)).unwrap()
|
||||
min.map(|(_, r)| r)
|
||||
}
|
||||
}
|
||||
|
@ -403,7 +403,7 @@ impl Settlement {
|
||||
|
||||
// Ground color
|
||||
if let Some(color) = self.get_color(rpos) {
|
||||
for z in -3..3 {
|
||||
for z in -3..5 {
|
||||
vol.set(
|
||||
Vec3::new(offs.x, offs.y, surface_z + z),
|
||||
if z >= 0 { Block::empty() } else { Block::new(BlockKind::Normal, color) },
|
||||
|
Loading…
Reference in New Issue
Block a user