better flat

This commit is contained in:
IsseW 2022-10-29 18:13:47 +02:00 committed by Isse
parent 6d2b39c254
commit 179c6dab8f
6 changed files with 195 additions and 145 deletions

View File

@ -189,10 +189,7 @@ impl Civs {
SiteKind::GiantTree => (12i32, 8.0),
SiteKind::Gnarling => (16i32, 10.0),
SiteKind::Citadel => (16i32, 0.0),
SiteKind::Bridge(start, end) => {
let e = (end - start).map(|e| e.abs()).reduce_max();
((e + 1) / 2, ((e + 1) / 2) as f32 / 2.0)
},
SiteKind::Bridge(_, _) => (0, 0.0),
};
let (raise, raise_dist, make_waypoint): (f32, i32, bool) = match &site.kind {
@ -1264,9 +1261,9 @@ fn walk_in_dir(
3.0 // + (1.0 - b_chunk.tree_density) * 20.0 // Prefer going through forests, for aesthetics
};
Some((a + dir, 1.0 + hill_cost + water_cost + wild_cost))
} else if dir.x == 0 || dir.y == 0 {
} else if NEIGHBORS.iter().all(|p| get_bridge(a + p).is_none()) && (dir.x == 0 || dir.y == 0) {
(4..=5).find_map(|i| {
loc_suitable_for_walking(sim, a + dir * i).then(|| (a + dir * i, i as f32 * 30.0))
loc_suitable_for_walking(sim, a + dir * i).then(|| (a + dir * i, 400.0 + (i - 4) as f32 * 10.0))
})
} else {
None
@ -1276,13 +1273,10 @@ fn walk_in_dir(
/// Return true if a position is suitable for walking on
fn loc_suitable_for_walking(sim: &WorldSim, loc: Vec2<i32>) -> bool {
if sim.get(loc).is_some() {
NEIGHBORS
.iter()
.find(|n| {
sim.get(loc + *n)
.map_or(false, |chunk| chunk.river.near_water())
})
.is_none()
!NEIGHBORS.iter().any(|n| {
sim.get(loc + *n)
.map_or(false, |chunk| chunk.river.near_water())
})
} else {
false
}
@ -1626,7 +1620,7 @@ impl Site {
}
pub fn is_castle(&self) -> bool { matches!(self.kind, SiteKind::Castle) }
pub fn is_bridge(&self) -> bool { matches!(self.kind, SiteKind::Bridge(_, _)) }
}

View File

@ -1,7 +1,8 @@
use crate::{
column::ColumnSample,
sim::{RiverKind, WorldSim},
CONFIG, IndexRef, site::SiteKind,
site::SiteKind,
IndexRef, CONFIG,
};
use common::{
terrain::{
@ -118,17 +119,21 @@ pub fn sample_pos(
sample.river.river_kind,
sample.river.spline_derivative,
sample.path.0.is_way(),
sample.sites.iter().any(|site| match &index.sites.get(*site).kind {
SiteKind::Bridge(bridge) => if let Some(plot) = bridge.wpos_tile(TerrainChunkSize::center_wpos(pos)).plot {
match bridge.plot(plot).kind {
crate::site2::PlotKind::Bridge(_) => true,
_ => false
}
} else {
false
},
_ => false,
}),
sample
.sites
.iter()
.any(|site| match &index.sites.get(*site).kind {
SiteKind::Bridge(bridge) => {
if let Some(plot) =
bridge.wpos_tile(TerrainChunkSize::center_wpos(pos)).plot
{
matches!(bridge.plot(plot).kind, crate::site2::PlotKind::Bridge(_))
} else {
false
}
},
_ => false,
}),
)
})
.unwrap_or((

View File

@ -15,7 +15,7 @@ use common::{
vol::ReadVol,
};
use num::cast::AsPrimitive;
use std::{cell::RefCell, sync::Arc, ops::RangeBounds};
use std::{cell::RefCell, ops::RangeBounds, sync::Arc};
use vek::*;
#[allow(dead_code)]
@ -386,8 +386,7 @@ impl Fill {
Self::contains_at(tree, *prim, pos.map2(*vec, i32::saturating_sub))
},
Primitive::Scale(prim, vec) => {
let center =
Self::get_bounds(tree, *prim).as_::<f32>().center();
let center = Self::get_bounds(tree, *prim).as_::<f32>().center();
let fpos = pos.as_::<f32>();
let spos = (center + ((fpos - center) / vec))
.map(|x| x.round())
@ -779,9 +778,8 @@ impl Painter {
self.prim(Primitive::Cylinder(aabb.made_valid()))
}
/// Returns a `PrimitiveRef` of the largest horizontal cylinder that fits in the
/// provided Aabb.
/// Returns a `PrimitiveRef` of the largest horizontal cylinder that fits in
/// the provided Aabb.
pub fn horizontal_cylinder(&self, aabb: Aabb<i32>, dir: Dir) -> PrimitiveRef {
let aabr = Aabr::from(aabb);
let length = dir.select(aabr.size());
@ -790,7 +788,8 @@ impl Painter {
min: (aabr.min - dir.abs().to_vec2() * height).with_z(aabb.min.z),
max: (dir.abs().select_with(aabr.min, aabr.max)).with_z(aabb.min.z + length),
};
self.cylinder(aabb).rotate_about((-dir.abs()).from_z_mat3(), aabr.min.with_z(aabb.min.z))
self.cylinder(aabb)
.rotate_about((-dir.abs()).from_z_mat3(), aabr.min.with_z(aabb.min.z))
}
/// Returns a `PrimitiveRef` of a cylinder using a radius check where a
@ -1072,40 +1071,21 @@ impl Painter {
/// |_____|/
/// ```
pub fn vault(&self, aabb: Aabb<i32>, dir: Dir) -> PrimitiveRef {
let aabr = Aabr::from(aabb);
let a = dir.select_aabr(aabr);
let b = (-dir).select_aabr(aabr);
let dmin = a.min(b);
let dmax = a.max(b);
let length = dmax - dmin;
let h = dir.orthogonal().select(Vec3::from(aabb.size()).xy());
let c = dir.rotated_cw().select_aabr(aabr);
let d = dir.rotated_ccw().select_aabr(aabr);
let omin = c.min(d);
let omax = c.max(d);
let diameter = omax - omin;
let radius = (diameter + 1) / 2;
let min = (a * dir.to_vec2() + c * dir.orthogonal().to_vec2()).with_z(aabb.max.z);
let mut prim = self.horizontal_cylinder(Aabb {
min: aabb.min.with_z(aabb.max.z - h),
max: aabb.max,
}, dir);
self.cylinder(
Aabb {
min,
max: (a * dir.to_vec2()
+ diameter * (-dir).to_vec2()
+ d * dir.orthogonal().to_vec2())
.with_z(aabb.max.z + length),
}
.made_valid(),
)
.rotate_about(dir.abs().from_z_mat3(), min)
.without(self.aabb(Aabb {
min: aabb.min.with_z(aabb.min.z - radius),
max: aabb.max.with_z(aabb.min.z),
}))
.union(self.aabb(Aabb {
if aabb.size().d < h {
prim = prim.intersect(self.aabb(aabb));
}
self.aabb(Aabb {
min: aabb.min,
max: aabb.max.with_z(aabb.max.z - radius),
}))
max: aabb.max.with_z(aabb.max.z - h / 2),
}).union(prim)
}
/// Place aabbs around another aabb in a symmetric and distributed manner.
@ -1293,7 +1273,9 @@ impl<'a> PrimitiveTransform for PrimitiveRef<'a> {
self.painter.prim(Primitive::rotate_about(self, rot, point))
}
fn scale(self, scale: Vec3<impl AsPrimitive<f32>>) -> Self { self.painter.prim(Primitive::scale(self, scale.as_())) }
fn scale(self, scale: Vec3<impl AsPrimitive<f32>>) -> Self {
self.painter.prim(Primitive::scale(self, scale.as_()))
}
fn repeat(self, offset: Vec3<i32>, count: u32) -> Self {
self.painter.prim(Primitive::repeat(self, offset, count))

View File

@ -1018,7 +1018,6 @@ impl Site {
max: start_tile.map2(end_tile, |a, b| a.max(b)) + 1 + orth * width,
};
let bridge = plot::Bridge::generate(land, index, &mut rng, &site, start_tile, end_tile);
let start_tile = site.wpos_tile_pos(bridge.start.xy());
@ -1044,7 +1043,7 @@ impl Site {
(-bridge.dir).select_aabr_with(start_aabr, aabr.center()),
2,
);
let plot = site.create_plot(Plot {
kind: PlotKind::Bridge(bridge),
root_tile: start_tile,

View File

@ -25,16 +25,20 @@ struct HeightenedViaduct {
}
impl HeightenedViaduct {
fn random(rng: &mut impl Rng) -> Self {
fn random(rng: &mut impl Rng, height: i32) -> Self {
let vault_spacing = *[3, 4, 5, 7].choose(rng).unwrap();
Self {
slope_inv: rng.gen_range(6..=8),
bridge_start_offset: rng.gen_range(5..=12),
vault_spacing: rng.gen_range(3..=4),
bridge_start_offset: rng.gen_range({
let min = (5 - height / 3).max(0);
min..=(12 - height).max(min)
}),
vault_spacing,
vault_size: *[(3, 16), (1, 4), (1, 4), (1, 4), (5, 32), (5, 32)]
.choose(rng)
.unwrap(),
side_vault_size: *[(4, 5), (7, 10), (7, 10), (13, 20)].choose(rng).unwrap(),
holes: rng.gen_bool(0.5),
holes: vault_spacing > 4 && rng.gen_bool(0.8),
}
}
}
@ -53,25 +57,32 @@ impl BridgeKind {
start: Vec3<i32>,
center: Vec3<i32>,
end: Vec3<i32>,
name: &str,
) -> BridgeKind {
let len = (start.xy() - end.xy()).map(|e| e.abs()).reduce_max();
let height = end.z - start.z;
let down = start.z - center.z;
(0..=3)
.filter_map(|bridge| match bridge {
0 if end.z - start.z > 17 => Some(BridgeKind::Tower(match rng.gen_range(0..=2) {
0 if height >= 16 => Some(BridgeKind::Tower(match rng.gen_range(0..=2) {
0 => RoofKind::Crenelated,
_ => RoofKind::Hipped,
})),
1 if len < 40 => Some(BridgeKind::Short),
2 if end.z - start.z < 5 && start.z - center.z < 16 => Some(
BridgeKind::HeightenedViaduct(HeightenedViaduct::random(rng)),
),
3 if end.z - start.z < 9 && start.z - center.z > 10 => Some(BridgeKind::HangBridge),
2 if height < 15 && down < 16 => Some(BridgeKind::HeightenedViaduct(
HeightenedViaduct::random(rng, height),
)),
3 if height < 9 && down > 10 => Some(BridgeKind::HangBridge),
_ => None,
})
.collect::<Vec<_>>()
.into_iter()
.choose(rng)
.unwrap_or(BridgeKind::Flat)
.unwrap_or_else(|| {
println!("Pick: {name}\n\tlen: {len}\n\theight: {height}\n\tdown: {down}\n");
BridgeKind::Flat
})
}
fn width(&self) -> i32 {
@ -190,59 +201,89 @@ fn render_short(bridge: &Bridge, painter: &Painter) {
fn render_flat(bridge: &Bridge, painter: &Painter) {
let rock = Fill::Block(Block::new(BlockKind::Rock, Rgb::gray(50)));
let light_rock = Fill::Block(Block::new(BlockKind::Rock, Rgb::gray(130)));
let dz = bridge.end.z - bridge.start.z;
let orthogonal = bridge.dir.orthogonal().to_vec2();
let orth_dir = bridge.dir.orthogonal();
let orthogonal = orth_dir.to_vec2();
let forward = bridge.dir.to_vec2();
let inset = 5;
let len = (bridge.end.xy() - bridge.start.xy())
.map(|e| e.abs())
.reduce_max();
let upset = bridge.end.z - bridge.start.z;
let height = bridge.end.z - bridge.start.z;
let size = tweak!(8);
let hole = painter
.cylinder(aabb(
bridge.center.with_z(bridge.end.z - 3 - bridge.width() * 2) - Vec2::broadcast(size),
bridge.center.with_z(bridge.end.z + 2) + Vec2::broadcast(size),
))
.rotate_about(
bridge.dir.orthogonal().from_z_mat3(),
bridge
.center
.as_()
.with_z(bridge.end.z as f32 - 1.5 - bridge.width() as f32),
let bridge_width = bridge.width();
let side = orthogonal * bridge_width;
let aabr = Aabr {
min: bridge.start.xy() - side,
max: bridge.end.xy() + side,
}
.made_valid();
let [ramp_aabr, aabr] = bridge.dir.split_aabr(aabr, height);
let ramp_prim = |ramp_aabr: Aabr<i32>, offset: i32| {
painter
.aabb(aabb(
ramp_aabr.min.with_z(bridge.start.z - 10 + offset),
ramp_aabr.max.with_z(bridge.start.z - 1 + offset),
))
.union(painter.ramp(
aabb(
ramp_aabr.min.with_z(bridge.start.z + offset),
ramp_aabr.max.with_z(bridge.end.z + offset),
),
height + 1,
bridge.dir,
))
};
ramp_prim(ramp_aabr, 1).fill(light_rock.clone());
let ramp_aabr = orth_dir.trim_aabr(ramp_aabr, 1);
ramp_prim(ramp_aabr, 5).clear();
ramp_prim(ramp_aabr, 0).fill(rock.clone());
let vault_width = 12;
let vault_offset = 5;
let bridge_thickness = 4;
let [vault, _] = bridge.dir.split_aabr(aabr, vault_width);
let len = bridge.dir.select(aabr.size());
let true_offset = vault_width + vault_offset;
let n = len / true_offset;
let p = len / n;
let holes = painter
.vault(
aabb(
vault.min.with_z(bridge.center.z - 20),
vault.max.with_z(bridge.end.z - bridge_thickness - 1),
),
orth_dir,
)
.scale(
bridge.dir.abs().to_vec3().as_()
* ((len as f32 - upset as f32 * tweak!(2.0)) / (size as f32 * 2.0) - 1.0)
+ 1.0,
);
.repeat((forward * p).with_z(0), n as u32);
painter
.ramp(
aabb(
bridge.start.with_z(bridge.start.z - inset)
- orthogonal * bridge.width()
- forward * inset,
bridge.start.with_z(bridge.end.z) + orthogonal * bridge.width() + forward * dz,
),
dz + inset + 1,
bridge.dir,
)
.union(
painter
.aabb(aabb(
bridge.start.with_z(bridge.end.z - 3 - size) - orthogonal * bridge.width()
+ forward * dz,
bridge.start.with_z(bridge.end.z)
+ orthogonal * bridge.width()
+ forward * forward * (bridge.end.xy() - bridge.start.xy()),
))
.without(hole),
)
.fill(rock);
.aabb(aabb(
aabr.min.with_z(bridge.center.z - 10),
aabr.max.with_z(bridge.end.z + 1),
))
.without(holes)
.fill(light_rock);
let aabr = orth_dir.trim_aabr(aabr, 1);
painter
.aabb(aabb(
aabr.min.with_z(bridge.end.z + 1),
aabr.max.with_z(bridge.end.z + 8),
)).clear();
painter
.aabb(aabb(
aabr.min.with_z(bridge.end.z),
aabr.max.with_z(bridge.end.z),
)).fill(rock);
}
fn render_heightened_viaduct(bridge: &Bridge, painter: &Painter, data: &HeightenedViaduct) {
@ -262,12 +303,6 @@ fn render_heightened_viaduct(bridge: &Bridge, painter: &Painter, data: &Heighten
let bridge_start_z = bridge.end.z + data.bridge_start_offset;
let bridge_top = bridge_start_z + len / slope_inv / 2;
// painter.ramp(aabb(
// bridge.start - side,
// (bridge.start.xy() + side + forward * (bridge_start_z -
// bridge.start.z)).with_z(bridge_start_z), ), bridge_start_z -
// bridge.start.z, bridge.dir).fill(rock);
let bridge_width = bridge.width();
let side = orthogonal * bridge_width;
@ -415,8 +450,8 @@ fn render_heightened_viaduct(bridge: &Bridge, painter: &Painter, data: &Heighten
}
}
bridge_prim(bridge_width).without(remove).fill(rock.clone());
b.translate(-Vec3::unit_z()).fill(light_rock.clone());
bridge_prim(bridge_width).without(remove).fill(rock);
b.translate(-Vec3::unit_z()).fill(light_rock);
br.translate(Vec3::unit_z() * 5)
.without(br.translate(-Vec3::unit_z()))
@ -625,7 +660,7 @@ fn render_tower(bridge: &Bridge, painter: &Painter, roof_kind: &RoofKind) {
(tower_aabr.min - 1).with_z(tower_end + 1),
(tower_aabr.max + 1).with_z(tower_end + 2 + tower_size),
))
.fill(wood.clone());
.fill(wood);
},
}
@ -650,14 +685,14 @@ fn render_tower(bridge: &Bridge, painter: &Painter, roof_kind: &RoofKind) {
painter
.vault(
aabb(
(start + offset - size).with_z(bridge.center.z - 10),
(start + offset + size).with_z(bridge.end.z - 3),
(start + offset / 2 - size).with_z(bridge.center.z - 10),
(start + offset / 2 + size).with_z(bridge.end.z - 3),
),
orth_dir,
)
.repeat(offset.with_z(0), n as u32),
)
.fill(rock.clone());
.fill(rock);
painter
.aabb(aabb(
@ -740,8 +775,10 @@ fn render_hang(bridge: &Bridge, painter: &Painter) {
let xsqr = (x * x) as f32;
let hsqr = (h * h) as f32;
let w = ((xsqr + (xsqr * (4.0 * hsqr + xsqr)).sqrt()) / 2.0).sqrt().ceil() + 1.0;
let w = ((xsqr + (xsqr * (4.0 * hsqr + xsqr)).sqrt()) / 2.0)
.sqrt()
.ceil()
+ 1.0;
let bottom = top - (h - (hsqr - hsqr * x as f32 / w).sqrt().ceil() as i32);
@ -756,7 +793,10 @@ fn render_hang(bridge: &Bridge, painter: &Painter) {
),
orth_dir,
)
.intersect(painter.aabb(aabb(aabr.min.with_z(bottom), aabr.max.with_z(bottom + h * 2))));
.intersect(painter.aabb(aabb(
aabr.min.with_z(bottom),
aabr.max.with_z(bottom + h * 2),
)));
cylinder.fill(wood.clone());
@ -773,14 +813,34 @@ fn render_hang(bridge: &Bridge, painter: &Painter) {
.translate(Vec3::unit_z())
.fill(Fill::Sprite(SpriteKind::Rope));
edges.translate(Vec3::unit_z() * 2).fill(wood.clone());
edges.translate(Vec3::unit_z() * 2).fill(wood);
let column_height = 3;
let column_range = top..=top + column_height;
painter.column(bridge.dir.select_aabr_with(ramp_f, ramp_f.min), column_range.clone()).fill(rock.clone());
painter.column(bridge.dir.select_aabr_with(ramp_f, ramp_f.max), column_range.clone()).fill(rock.clone());
painter.column((-bridge.dir).select_aabr_with(ramp_b, ramp_b.min), column_range.clone()).fill(rock.clone());
painter.column((-bridge.dir).select_aabr_with(ramp_b, ramp_b.max), column_range.clone()).fill(rock.clone());
painter
.column(
bridge.dir.select_aabr_with(ramp_f, ramp_f.min),
column_range.clone(),
)
.fill(rock.clone());
painter
.column(
bridge.dir.select_aabr_with(ramp_f, ramp_f.max),
column_range.clone(),
)
.fill(rock.clone());
painter
.column(
(-bridge.dir).select_aabr_with(ramp_b, ramp_b.min),
column_range.clone(),
)
.fill(rock.clone());
painter
.column(
(-bridge.dir).select_aabr_with(ramp_b, ramp_b.max),
column_range,
)
.fill(rock);
}
pub struct Bridge {
@ -852,7 +912,7 @@ impl Bridge {
let center = (start.xy() + end.xy()) / 2;
let center = center.with_z(land.get_alt_approx(center) as i32);
let bridge = BridgeKind::random(rng, start, center, end);
let bridge = BridgeKind::random(rng, start, center, end, site.name());
Self {
start,
end,

View File

@ -221,7 +221,10 @@ impl Dir {
}
}
pub fn split_aabr<T>(self, aabr: Aabr<T>, offset: T) -> [Aabr<T>; 2] where T: Copy + PartialOrd + Add<T, Output = T> + Sub<T, Output = T> {
pub fn split_aabr<T>(self, aabr: Aabr<T>, offset: T) -> [Aabr<T>; 2]
where
T: Copy + PartialOrd + Add<T, Output = T> + Sub<T, Output = T>,
{
match self {
Dir::X => aabr.split_at_x(aabr.min.x + offset),
Dir::Y => aabr.split_at_y(aabr.min.y + offset),
@ -236,6 +239,13 @@ impl Dir {
}
}
pub fn trim_aabr(self, aabr: Aabr<i32>, offset: i32) -> Aabr<i32> {
Aabr {
min: aabr.min + self.abs().to_vec2() * offset,
max: aabr.max - self.abs().to_vec2() * offset,
}
}
pub fn extend_aabr(self, aabr: Aabr<i32>, amount: i32) -> Aabr<i32> {
match self {
Dir::X => Aabr {