mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Better house roofs
This commit is contained in:
parent
eb3cc2fd22
commit
78aa587722
@ -29,7 +29,7 @@ opt-level = 2
|
||||
overflow-checks = true
|
||||
debug-assertions = true
|
||||
panic = "abort"
|
||||
debug = false
|
||||
debug = true
|
||||
codegen-units = 8
|
||||
lto = false
|
||||
# TEMP false to avoid fingerprints bug
|
||||
|
@ -108,7 +108,7 @@ impl Civs {
|
||||
attempt(5, || {
|
||||
let (kind, size) = match ctx.rng.gen_range(0..64) {
|
||||
0..=4 => (SiteKind::Castle, 3),
|
||||
// 5..=28 => (SiteKind::Refactor, 6),
|
||||
5..=28 => (SiteKind::Refactor, 6),
|
||||
29..=31 => (SiteKind::Tree, 4),
|
||||
_ => (SiteKind::Dungeon, 0),
|
||||
};
|
||||
|
@ -10,9 +10,12 @@ use vek::*;
|
||||
pub enum Primitive {
|
||||
Empty, // Placeholder
|
||||
|
||||
Plot, // A primitive that fits the floor plan of the plot
|
||||
Void, // A primitive that fits the floor plan of void tiles
|
||||
|
||||
// Shapes
|
||||
Aabb(Aabb<i32>),
|
||||
Pyramid { aabb: Aabb<i32>, inset: i32 },
|
||||
Pyramid { aabb: Aabb<i32>, inset: Vec2<i32> },
|
||||
Cylinder(Aabb<i32>),
|
||||
Cone(Aabb<i32>),
|
||||
Sphere(Aabb<i32>),
|
||||
@ -20,21 +23,31 @@ pub enum Primitive {
|
||||
|
||||
// Combinators
|
||||
And(Id<Primitive>, Id<Primitive>),
|
||||
AndNot(Id<Primitive>, Id<Primitive>), // Not second
|
||||
Or(Id<Primitive>, Id<Primitive>),
|
||||
Xor(Id<Primitive>, Id<Primitive>),
|
||||
// Not commutative
|
||||
Diff(Id<Primitive>, Id<Primitive>),
|
||||
// Operators
|
||||
Rotate(Id<Primitive>, Mat3<i32>),
|
||||
Offset(Id<Primitive>, Vec3<i32>),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Fill {
|
||||
Block(Block),
|
||||
Brick(BlockKind, Rgb<u8>, u8),
|
||||
}
|
||||
|
||||
impl Fill {
|
||||
fn contains_at(&self, tree: &Store<Primitive>, prim: Id<Primitive>, pos: Vec3<i32>) -> bool {
|
||||
fn contains_at(
|
||||
&self,
|
||||
tree: &Store<Primitive>,
|
||||
prim: Id<Primitive>,
|
||||
pos: Vec3<i32>,
|
||||
is_plot: &impl Fn(Vec2<i32>) -> bool,
|
||||
is_void: &impl Fn(Vec2<i32>) -> bool,
|
||||
) -> bool {
|
||||
// Custom closure because vek's impl of `contains_point` is inclusive :(
|
||||
let aabb_contains = |aabb: Aabb<i32>, pos: Vec3<i32>| {
|
||||
(aabb.min.x..aabb.max.x).contains(&pos.x)
|
||||
@ -45,18 +58,21 @@ impl Fill {
|
||||
match &tree[prim] {
|
||||
Primitive::Empty => false,
|
||||
|
||||
Primitive::Plot => is_plot(pos.xy()),
|
||||
Primitive::Void => is_void(pos.xy()),
|
||||
|
||||
Primitive::Aabb(aabb) => aabb_contains(*aabb, pos),
|
||||
Primitive::Pyramid { aabb, inset } => {
|
||||
let inset = (*inset).max(aabb.size().reduce_min());
|
||||
let inset = inset.map2(Vec2::new(aabb.size().w, aabb.size().h), |i, sz| i.min(sz));
|
||||
let inner = Aabr {
|
||||
min: aabb.min.xy() - 1 + inset,
|
||||
max: aabb.max.xy() - inset,
|
||||
};
|
||||
aabb_contains(*aabb, pos)
|
||||
&& (inner.projected_point(pos.xy()) - pos.xy())
|
||||
&& ((inner.projected_point(pos.xy()) - pos.xy())
|
||||
.map(|e| e.abs())
|
||||
.reduce_max() as f32
|
||||
/ (inset as f32)
|
||||
.map2(inset, |e, i| e as f32 / (i as f32).max(0.00001))
|
||||
.reduce_partial_max() as f32)
|
||||
< 1.0
|
||||
- ((pos.z - aabb.min.z) as f32 + 0.5) / (aabb.max.z - aabb.min.z) as f32
|
||||
},
|
||||
@ -96,21 +112,38 @@ impl Fill {
|
||||
.dot(*gradient) as i32)
|
||||
},
|
||||
Primitive::And(a, b) => {
|
||||
self.contains_at(tree, *a, pos) && self.contains_at(tree, *b, pos)
|
||||
self.contains_at(tree, *a, pos, is_plot, is_void)
|
||||
&& self.contains_at(tree, *b, pos, is_plot, is_void)
|
||||
},
|
||||
Primitive::AndNot(a, b) => {
|
||||
self.contains_at(tree, *a, pos, is_plot, is_void)
|
||||
&& !self.contains_at(tree, *b, pos, is_plot, is_void)
|
||||
},
|
||||
Primitive::Or(a, b) => {
|
||||
self.contains_at(tree, *a, pos) || self.contains_at(tree, *b, pos)
|
||||
self.contains_at(tree, *a, pos, is_plot, is_void)
|
||||
|| self.contains_at(tree, *b, pos, is_plot, is_void)
|
||||
},
|
||||
Primitive::Xor(a, b) => {
|
||||
self.contains_at(tree, *a, pos) ^ self.contains_at(tree, *b, pos)
|
||||
self.contains_at(tree, *a, pos, is_plot, is_void)
|
||||
^ self.contains_at(tree, *b, pos, is_plot, is_void)
|
||||
},
|
||||
Primitive::Diff(a, b) => {
|
||||
self.contains_at(tree, *a, pos) && !self.contains_at(tree, *b, pos)
|
||||
self.contains_at(tree, *a, pos, is_plot, is_void)
|
||||
&& !self.contains_at(tree, *b, pos, is_plot, is_void)
|
||||
},
|
||||
Primitive::Rotate(prim, mat) => {
|
||||
let aabb = self.get_bounds(tree, *prim);
|
||||
let diff = pos - (aabb.min + mat.cols.map(|x| x.reduce_min()));
|
||||
self.contains_at(tree, *prim, aabb.min + mat.transposed() * diff)
|
||||
self.contains_at(
|
||||
tree,
|
||||
*prim,
|
||||
aabb.min + mat.transposed() * diff,
|
||||
is_plot,
|
||||
is_void,
|
||||
)
|
||||
},
|
||||
Primitive::Offset(prim, offset) => {
|
||||
self.contains_at(tree, *prim, pos - offset, is_plot, is_void)
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -120,8 +153,10 @@ impl Fill {
|
||||
tree: &Store<Primitive>,
|
||||
prim: Id<Primitive>,
|
||||
pos: Vec3<i32>,
|
||||
is_plot: impl Fn(Vec2<i32>) -> bool,
|
||||
is_void: impl Fn(Vec2<i32>) -> bool,
|
||||
) -> Option<Block> {
|
||||
if self.contains_at(tree, prim, pos) {
|
||||
if self.contains_at(tree, prim, pos, &is_plot, &is_void) {
|
||||
match self {
|
||||
Fill::Block(block) => Some(*block),
|
||||
Fill::Brick(bk, col, range) => Some(Block::new(
|
||||
@ -146,7 +181,7 @@ impl Fill {
|
||||
}
|
||||
|
||||
Some(match &tree[prim] {
|
||||
Primitive::Empty => return None,
|
||||
Primitive::Empty | Primitive::Plot | Primitive::Void => return None,
|
||||
Primitive::Aabb(aabb) => *aabb,
|
||||
Primitive::Pyramid { aabb, .. } => *aabb,
|
||||
Primitive::Cylinder(aabb) => *aabb,
|
||||
@ -174,6 +209,7 @@ impl Fill {
|
||||
self.get_bounds_inner(tree, *b),
|
||||
|a, b| a.intersection(b),
|
||||
)?,
|
||||
Primitive::AndNot(a, _) => self.get_bounds_inner(tree, *a)?,
|
||||
Primitive::Or(a, b) | Primitive::Xor(a, b) => or_zip_with(
|
||||
self.get_bounds_inner(tree, *a),
|
||||
self.get_bounds_inner(tree, *b),
|
||||
@ -189,6 +225,13 @@ impl Fill {
|
||||
};
|
||||
new_aabb.made_valid()
|
||||
},
|
||||
Primitive::Offset(prim, offset) => {
|
||||
let aabb = self.get_bounds_inner(tree, *prim)?;
|
||||
Aabb {
|
||||
min: aabb.min - offset,
|
||||
max: aabb.max - offset,
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,7 @@ impl Site {
|
||||
}
|
||||
|
||||
pub fn bounds(&self) -> Aabr<i32> {
|
||||
let border = 1;
|
||||
let border = 2;
|
||||
Aabr {
|
||||
min: self.origin + self.tile_wpos(self.tiles.bounds.min - border),
|
||||
max: self.origin + self.tile_wpos(self.tiles.bounds.max + 1 + border),
|
||||
@ -287,7 +287,7 @@ impl Site {
|
||||
|
||||
site.make_plaza(land, &mut rng);
|
||||
|
||||
let build_chance = Lottery::from(vec![(64.0, 1), (5.0, 2), (8.0, 3), (0.75, 4)]);
|
||||
let build_chance = Lottery::from(vec![(128.0, 1), (5.0, 2), (8.0, 3), (0.75, 4)]);
|
||||
|
||||
let mut castles = 0;
|
||||
|
||||
@ -606,8 +606,8 @@ impl Site {
|
||||
if let TileKind::Road { a, b, w } = &tile.kind {
|
||||
if let Some(PlotKind::Road(path)) = tile.plot.map(|p| &self.plot(p).kind) {
|
||||
Some((LineSegment2 {
|
||||
start: self.tile_center_wpos(path.nodes()[*a as usize]).map(|e| e as f32),
|
||||
end: self.tile_center_wpos(path.nodes()[*b as usize]).map(|e| e as f32),
|
||||
start: self.tile_center_wpos(path.nodes()[*a as usize]).map(|e| e as f32) - TILE_SIZE as f32 / 2.0,
|
||||
end: self.tile_center_wpos(path.nodes()[*b as usize]).map(|e| e as f32) - TILE_SIZE as f32 / 2.0,
|
||||
}, *w))
|
||||
} else {
|
||||
None
|
||||
@ -622,16 +622,19 @@ impl Site {
|
||||
.map(|(line, w)| (line.distance_to_point(wpos2df) - w as f32 * 2.0).max(0.0))
|
||||
.min_by_key(|d| (*d * 100.0) as i32);
|
||||
|
||||
if dist.map_or(false, |d| d <= 0.75) {
|
||||
let alt = canvas.col(wpos2d).map_or(0, |col| col.alt as i32);
|
||||
if let Some(col) = canvas.col(wpos2d).filter(|_| dist.map_or(false, |d| d <= 0.75)) {
|
||||
let surf = col.alt.max(col.water_level + 5.0) as i32;
|
||||
let is_pier = col.water_dist.map_or(false, |d| d < 3.0);
|
||||
(-6..4).for_each(|z| canvas.map(
|
||||
Vec3::new(wpos2d.x, wpos2d.y, alt + z),
|
||||
Vec3::new(wpos2d.x, wpos2d.y, surf + z),
|
||||
|b| if z >= 0 {
|
||||
if b.is_filled() {
|
||||
Block::empty()
|
||||
} else {
|
||||
b.with_sprite(SpriteKind::Empty)
|
||||
}
|
||||
} else if is_pier {
|
||||
Block::new(BlockKind::Wood, Rgb::new(110, 65, 15))
|
||||
} else {
|
||||
Block::new(BlockKind::Rock, Rgb::new(55, 45, 50))
|
||||
},
|
||||
@ -720,8 +723,12 @@ impl Site {
|
||||
for y in aabb.min.y..aabb.max.y {
|
||||
for z in aabb.min.z..aabb.max.z {
|
||||
let pos = Vec3::new(x, y, z);
|
||||
let is_plot = self.wpos_tile(pos.xy()).plot == Some(plot);
|
||||
let is_void = self.wpos_tile(pos.xy()).is_void();
|
||||
|
||||
if let Some(block) = fill.sample_at(&prim_tree, prim, pos) {
|
||||
if let Some(block) =
|
||||
fill.sample_at(&prim_tree, prim, pos, |_| is_plot, |_| is_void)
|
||||
{
|
||||
canvas.set(pos, block);
|
||||
}
|
||||
}
|
||||
|
@ -184,7 +184,7 @@ impl Structure for Castle {
|
||||
max: (wpos + ts + 3 + roof_lip)
|
||||
.with_z(tower_total_height + roof_height),
|
||||
},
|
||||
inset: roof_height,
|
||||
inset: Vec2::broadcast(roof_height),
|
||||
}),
|
||||
Fill::Brick(BlockKind::Wood, Rgb::new(40, 5, 11), 10),
|
||||
);
|
||||
|
@ -11,6 +11,7 @@ pub struct House {
|
||||
alt: i32,
|
||||
levels: u32,
|
||||
roof_color: Rgb<u8>,
|
||||
roof_inset: Vec2<bool>,
|
||||
}
|
||||
|
||||
impl House {
|
||||
@ -42,6 +43,11 @@ impl House {
|
||||
];
|
||||
*colors.choose(rng).unwrap()
|
||||
},
|
||||
roof_inset: match rng.gen_range(0..3) {
|
||||
0 => Vec2::new(true, false),
|
||||
1 => Vec2::new(false, true),
|
||||
_ => Vec2::new(true, true),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -56,6 +62,13 @@ impl Structure for House {
|
||||
let storey = 5;
|
||||
let roof = storey * self.levels as i32;
|
||||
let foundations = 12;
|
||||
let plot = prim(Primitive::Plot);
|
||||
let void = prim(Primitive::Void);
|
||||
let can_spill = prim(Primitive::Or(plot, void));
|
||||
|
||||
//let wall_block = Fill::Brick(BlockKind::Rock, Rgb::new(80, 75, 85), 24);
|
||||
let wall_block = Fill::Brick(BlockKind::Rock, Rgb::new(158, 150, 121), 24);
|
||||
let structural_wood = Fill::Block(Block::new(BlockKind::Wood, Rgb::new(55, 25, 8)));
|
||||
|
||||
// Walls
|
||||
let inner = prim(Primitive::Aabb(Aabb {
|
||||
@ -66,10 +79,7 @@ impl Structure for House {
|
||||
min: self.bounds.min.with_z(self.alt - foundations),
|
||||
max: (self.bounds.max + 1).with_z(self.alt + roof),
|
||||
}));
|
||||
fill(
|
||||
outer,
|
||||
Fill::Brick(BlockKind::Rock, Rgb::new(80, 75, 85), 24),
|
||||
);
|
||||
fill(outer, wall_block);
|
||||
fill(inner, Fill::Block(Block::empty()));
|
||||
let walls = prim(Primitive::Xor(outer, inner));
|
||||
|
||||
@ -97,10 +107,7 @@ impl Structure for House {
|
||||
pillars_x = prim(Primitive::Or(pillars_x, pillar));
|
||||
}
|
||||
let pillars = prim(Primitive::And(pillars_x, pillars_y));
|
||||
fill(
|
||||
pillars,
|
||||
Fill::Block(Block::new(BlockKind::Wood, Rgb::new(55, 25, 8))),
|
||||
);
|
||||
fill(pillars, structural_wood);
|
||||
|
||||
// For each storey...
|
||||
for i in 0..self.levels + 1 {
|
||||
@ -147,13 +154,20 @@ impl Structure for House {
|
||||
}
|
||||
|
||||
// Floor
|
||||
let floor = prim(Primitive::Aabb(Aabb {
|
||||
min: (self.bounds.min + 1).with_z(self.alt + height),
|
||||
max: self.bounds.max.with_z(self.alt + height + 1),
|
||||
}));
|
||||
fill(
|
||||
prim(Primitive::Aabb(Aabb {
|
||||
min: (self.bounds.min + 1).with_z(self.alt + height),
|
||||
max: self.bounds.max.with_z(self.alt + height + 1),
|
||||
})),
|
||||
floor,
|
||||
Fill::Block(Block::new(BlockKind::Rock, Rgb::new(89, 44, 14))),
|
||||
);
|
||||
|
||||
let slice = prim(Primitive::Aabb(Aabb {
|
||||
min: self.bounds.min.with_z(self.alt + height),
|
||||
max: (self.bounds.max + 1).with_z(self.alt + height + 1),
|
||||
}));
|
||||
fill(prim(Primitive::AndNot(slice, floor)), structural_wood);
|
||||
}
|
||||
|
||||
let roof_lip = 2;
|
||||
@ -165,23 +179,32 @@ impl Structure for House {
|
||||
+ 1;
|
||||
|
||||
// Roof
|
||||
let roof_vol = prim(Primitive::Pyramid {
|
||||
aabb: Aabb {
|
||||
min: (self.bounds.min - roof_lip).with_z(self.alt + roof),
|
||||
max: (self.bounds.max + 1 + roof_lip).with_z(self.alt + roof + roof_height),
|
||||
},
|
||||
inset: self.roof_inset.map(|e| if e { roof_height } else { 0 }),
|
||||
});
|
||||
let eaves = prim(Primitive::Offset(roof_vol, -Vec3::unit_z()));
|
||||
let tiles = prim(Primitive::AndNot(roof_vol, eaves));
|
||||
fill(
|
||||
prim(Primitive::Pyramid {
|
||||
aabb: Aabb {
|
||||
min: (self.bounds.min - roof_lip).with_z(self.alt + roof),
|
||||
max: (self.bounds.max + 1 + roof_lip).with_z(self.alt + roof + roof_height),
|
||||
},
|
||||
inset: roof_height,
|
||||
}),
|
||||
prim(Primitive::And(tiles, can_spill)),
|
||||
Fill::Block(Block::new(BlockKind::Wood, self.roof_color)),
|
||||
);
|
||||
let roof_inner = prim(Primitive::Aabb(Aabb {
|
||||
min: self.bounds.min.with_z(self.alt + roof),
|
||||
max: (self.bounds.max + 1).with_z(self.alt + roof + roof_height),
|
||||
}));
|
||||
fill(prim(Primitive::And(eaves, roof_inner)), wall_block);
|
||||
|
||||
// Foundations
|
||||
let foundations = prim(Primitive::Aabb(Aabb {
|
||||
min: (self.bounds.min - 1).with_z(self.alt - foundations),
|
||||
max: (self.bounds.max + 2).with_z(self.alt + 1),
|
||||
}));
|
||||
fill(
|
||||
prim(Primitive::Aabb(Aabb {
|
||||
min: (self.bounds.min - 1).with_z(self.alt - foundations),
|
||||
max: (self.bounds.max + 2).with_z(self.alt + 1),
|
||||
})),
|
||||
prim(Primitive::And(foundations, can_spill)),
|
||||
Fill::Block(Block::new(BlockKind::Rock, Rgb::new(31, 33, 32))),
|
||||
);
|
||||
}
|
||||
|
@ -210,9 +210,16 @@ impl Tile {
|
||||
pub fn is_road(&self) -> bool { matches!(self.kind, TileKind::Road { .. }) }
|
||||
|
||||
pub fn is_obstacle(&self) -> bool {
|
||||
matches!(
|
||||
match self.kind {
|
||||
TileKind::Hazard(_) => true,
|
||||
_ => !self.is_void(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_void(&self) -> bool {
|
||||
!matches!(
|
||||
self.kind,
|
||||
TileKind::Hazard(_) | TileKind::Building | TileKind::Castle | TileKind::Wall(_)
|
||||
TileKind::Building | TileKind::Castle | TileKind::Wall(_)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user