simple hang bridge

This commit is contained in:
IsseW 2022-10-28 19:13:29 +02:00 committed by Isse
parent df8db713ea
commit 6d2b39c254
3 changed files with 253 additions and 70 deletions

View File

@ -15,7 +15,7 @@ use common::{
vol::ReadVol,
};
use num::cast::AsPrimitive;
use std::{cell::RefCell, sync::Arc};
use std::{cell::RefCell, sync::Arc, ops::RangeBounds};
use vek::*;
#[allow(dead_code)]
@ -289,13 +289,10 @@ impl Fill {
- ((pos.z - aabb.min.z) as f32 + 0.5) / (aabb.max.z - aabb.min.z) as f32
},
Primitive::Cylinder(aabb) => {
let fpos = pos.as_::<f32>().xy() - aabb.as_::<f32>().center().xy();
let size = Vec3::from(aabb.size().as_::<f32>()).xy();
(aabb.min.z..aabb.max.z).contains(&pos.z)
&& (pos
.xy()
.as_()
.distance_squared(aabb.as_().center().xy() - 0.5)
as f32)
< (aabb.size().w.min(aabb.size().h) as f32 / 2.0).powi(2)
&& (2.0 * fpos / size).magnitude_squared() <= 1.0
},
Primitive::Cone(aabb) => {
(aabb.min.z..aabb.max.z).contains(&pos.z)
@ -390,9 +387,9 @@ impl Fill {
},
Primitive::Scale(prim, vec) => {
let center =
Self::get_bounds(tree, *prim).center().as_::<f32>() - Vec3::broadcast(0.5);
Self::get_bounds(tree, *prim).as_::<f32>().center();
let fpos = pos.as_::<f32>();
let spos = (center + ((center - fpos) / vec))
let spos = (center + ((fpos - center) / vec))
.map(|x| x.round())
.as_::<i32>();
Self::contains_at(tree, *prim, spos)
@ -782,6 +779,20 @@ impl Painter {
self.prim(Primitive::Cylinder(aabb.made_valid()))
}
/// 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());
let height = aabb.max.z - aabb.min.z;
let aabb = Aabb {
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))
}
/// Returns a `PrimitiveRef` of a cylinder using a radius check where a
/// radius and origin are parameters instead of a bounding box.
pub fn cylinder_with_radius(
@ -1169,6 +1180,21 @@ impl Painter {
prim
}
pub fn column(&self, point: Vec2<i32>, range: impl RangeBounds<i32>) -> PrimitiveRef {
self.aabb(Aabb {
min: point.with_z(match range.start_bound() {
std::ops::Bound::Included(n) => *n,
std::ops::Bound::Excluded(n) => n + 1,
std::ops::Bound::Unbounded => i32::MIN,
}),
max: (point + 1).with_z(match range.end_bound() {
std::ops::Bound::Included(n) => n + 1,
std::ops::Bound::Excluded(n) => *n,
std::ops::Bound::Unbounded => i32::MAX,
}),
})
}
/// The area that the canvas is currently rendering.
pub fn render_aabr(&self) -> Aabr<i32> { self.render_area }
@ -1250,7 +1276,7 @@ pub trait PrimitiveTransform {
/// Scales the primitive along each axis by the x, y, and z components of
/// the `scale` vector respectively.
#[must_use]
fn scale(self, scale: Vec3<f32>) -> Self;
fn scale(self, scale: Vec3<impl AsPrimitive<f32>>) -> Self;
/// Returns a `PrimitiveRef` of the primitive in addition to the same
/// primitive translated by `offset` and repeated `count` times, each time
/// translated by an additional offset.
@ -1267,7 +1293,7 @@ impl<'a> PrimitiveTransform for PrimitiveRef<'a> {
self.painter.prim(Primitive::rotate_about(self, rot, point))
}
fn scale(self, scale: Vec3<f32>) -> Self { self.painter.prim(Primitive::scale(self, scale)) }
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))
@ -1289,7 +1315,7 @@ impl<'a, const N: usize> PrimitiveTransform for [PrimitiveRef<'a>; N] {
self
}
fn scale(mut self, scale: Vec3<f32>) -> Self {
fn scale(mut self, scale: Vec3<impl AsPrimitive<f32>>) -> Self {
for prim in &mut self {
*prim = prim.scale(scale);
}

View File

@ -1024,7 +1024,7 @@ impl Site {
let start_tile = site.wpos_tile_pos(bridge.start.xy());
let end_tile = site.wpos_tile_pos(bridge.end.xy());
let width = (bridge.width + TILE_SIZE as i32 / 2) / TILE_SIZE as i32;
let width = (bridge.width() + TILE_SIZE as i32 / 2) / TILE_SIZE as i32;
let aabr = Aabr {
min: start_tile.map2(end_tile, |a, b| a.min(b)) - orth * width,
max: start_tile.map2(end_tile, |a, b| a.max(b)) + 1 + orth * width,

View File

@ -1,6 +1,10 @@
use super::*;
use crate::{site2::gen::PrimitiveTransform, Land};
use common::terrain::{BiomeKind, Block, BlockKind};
use common::{
generation::EntityInfo,
terrain::{BiomeKind, Block, BlockKind},
};
use num::integer::Roots;
use rand::prelude::*;
use vek::*;
@ -26,7 +30,9 @@ impl HeightenedViaduct {
slope_inv: rng.gen_range(6..=8),
bridge_start_offset: rng.gen_range(5..=12),
vault_spacing: rng.gen_range(3..=4),
vault_size: *[(3, 16), (1, 4), (1, 4), (1, 4), (5, 32), (5, 32)].choose(rng).unwrap(),
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),
}
@ -38,6 +44,42 @@ enum BridgeKind {
Tower(RoofKind),
Short,
HeightenedViaduct(HeightenedViaduct),
HangBridge,
}
impl BridgeKind {
fn random(
rng: &mut impl Rng,
start: Vec3<i32>,
center: Vec3<i32>,
end: Vec3<i32>,
) -> BridgeKind {
let len = (start.xy() - end.xy()).map(|e| e.abs()).reduce_max();
(0..=3)
.filter_map(|bridge| match bridge {
0 if end.z - start.z > 17 => 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),
_ => None,
})
.collect::<Vec<_>>()
.into_iter()
.choose(rng)
.unwrap_or(BridgeKind::Flat)
}
fn width(&self) -> i32 {
match self {
BridgeKind::HangBridge => 2,
_ => 8,
}
}
}
fn aabb(min: Vec3<i32>, max: Vec3<i32>) -> Aabb<i32> {
@ -162,7 +204,7 @@ fn render_flat(bridge: &Bridge, painter: &Painter) {
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 - 3 - bridge.width() * 2) - Vec2::broadcast(size),
bridge.center.with_z(bridge.end.z + 2) + Vec2::broadcast(size),
))
.rotate_about(
@ -170,7 +212,7 @@ fn render_flat(bridge: &Bridge, painter: &Painter) {
bridge
.center
.as_()
.with_z(bridge.end.z as f32 - 1.5 - bridge.width as f32),
.with_z(bridge.end.z as f32 - 1.5 - bridge.width() as f32),
)
.scale(
bridge.dir.abs().to_vec3().as_()
@ -182,9 +224,9 @@ fn render_flat(bridge: &Bridge, painter: &Painter) {
.ramp(
aabb(
bridge.start.with_z(bridge.start.z - inset)
- orthogonal * bridge.width
- orthogonal * bridge.width()
- forward * inset,
bridge.start.with_z(bridge.end.z) + orthogonal * bridge.width + forward * dz,
bridge.start.with_z(bridge.end.z) + orthogonal * bridge.width() + forward * dz,
),
dz + inset + 1,
bridge.dir,
@ -192,10 +234,10 @@ fn render_flat(bridge: &Bridge, painter: &Painter) {
.union(
painter
.aabb(aabb(
bridge.start.with_z(bridge.end.z - 3 - size) - orthogonal * bridge.width
bridge.start.with_z(bridge.end.z - 3 - size) - orthogonal * bridge.width()
+ forward * dz,
bridge.start.with_z(bridge.end.z)
+ orthogonal * bridge.width
+ orthogonal * bridge.width()
+ forward * forward * (bridge.end.xy() - bridge.start.xy()),
))
.without(hole),
@ -226,7 +268,7 @@ fn render_heightened_viaduct(bridge: &Bridge, painter: &Painter, data: &Heighten
// bridge.start.z)).with_z(bridge_start_z), ), bridge_start_z -
// bridge.start.z, bridge.dir).fill(rock);
let bridge_width = bridge.width;
let bridge_width = bridge.width();
let side = orthogonal * bridge_width;
let aabr = Aabr {
@ -317,6 +359,14 @@ fn render_heightened_viaduct(bridge: &Bridge, painter: &Painter, data: &Heighten
orth_dir,
);
let mut rng = thread_rng();
if rng.gen_bool(0.1) {
painter.spawn(
EntityInfo::at(c.with_z(bridge.center.z).as_())
.with_asset_expect("common.entity.wild.aggressive.swamp_troll", &mut rng),
);
}
if side_vault * 2 + side_vault_offset < len / 2 + 5 {
remove = remove.union(
painter
@ -339,35 +389,52 @@ fn render_heightened_viaduct(bridge: &Bridge, painter: &Painter, data: &Heighten
),
),
);
if data.holes {
remove = remove.union(
painter.vault(aabb(
(c - side + forward * (vault_size + 1)).with_z(side_vault_top - 4),
(c + side + forward * (vault_size + spacing)).with_z(side_vault_top + 2),
), orth_dir).union(
painter.vault(aabb(
(c - side - forward * (vault_size + 1)).with_z(side_vault_top - 4),
(c + side - forward * (vault_size + spacing)).with_z(side_vault_top + 2),
), orth_dir)
)
painter
.vault(
aabb(
(c - side + forward * (vault_size + 1)).with_z(side_vault_top - 4),
(c + side + forward * (vault_size + spacing))
.with_z(side_vault_top + 2),
),
orth_dir,
)
.union(
painter.vault(
aabb(
(c - side - forward * (vault_size + 1)).with_z(side_vault_top - 4),
(c + side - forward * (vault_size + spacing))
.with_z(side_vault_top + 2),
),
orth_dir,
),
),
);
}
}
bridge_prim(bridge_width)
.without(remove)
.fill(rock.clone());
bridge_prim(bridge_width).without(remove).fill(rock.clone());
b.translate(-Vec3::unit_z()).fill(light_rock.clone());
br.translate(Vec3::unit_z() * 5)
.without(br.translate(-Vec3::unit_z()))
.clear();
let place_lights = |center: Vec3<i32>| {
painter.sprite(orth_dir.select_aabr_with(bridge_aabr, center.xy()).with_z(center.z), SpriteKind::FireBowlGround);
painter.sprite((-orth_dir).select_aabr_with(bridge_aabr, center.xy()).with_z(center.z), SpriteKind::FireBowlGround);
painter.sprite(
orth_dir
.select_aabr_with(bridge_aabr, center.xy())
.with_z(center.z),
SpriteKind::FireBowlGround,
);
painter.sprite(
(-orth_dir)
.select_aabr_with(bridge_aabr, center.xy())
.with_z(center.z),
SpriteKind::FireBowlGround,
);
};
place_lights(bridge_aabr.center().with_z(bridge_top + 1));
@ -377,10 +444,20 @@ fn render_heightened_viaduct(bridge: &Bridge, painter: &Painter, data: &Heighten
let place_lights = |i: i32| {
let offset = i * light_spacing;
let z = bridge_start_z + 1 + (offset + if len / 2 % 2 == 0 { 4 } else { 3 }) / (slope_inv - 1);
place_lights((bridge.dir.select_aabr_with(bridge_aabr, bridge_aabr.center()) - forward * offset).with_z(z));
place_lights(((-bridge.dir).select_aabr_with(bridge_aabr, bridge_aabr.center()) + forward * offset).with_z(z));
let z =
bridge_start_z + 1 + (offset + if len / 2 % 2 == 0 { 4 } else { 3 }) / (slope_inv - 1);
place_lights(
(bridge
.dir
.select_aabr_with(bridge_aabr, bridge_aabr.center())
- forward * offset)
.with_z(z),
);
place_lights(
((-bridge.dir).select_aabr_with(bridge_aabr, bridge_aabr.center()) + forward * offset)
.with_z(z),
);
};
for i in 0..num_lights {
place_lights(i);
@ -603,10 +680,112 @@ fn render_tower(bridge: &Bridge, painter: &Painter, roof_kind: &RoofKind) {
}
}
fn render_hang(bridge: &Bridge, painter: &Painter) {
let orth_dir = bridge.dir.orthogonal();
let orthogonal = orth_dir.to_vec2();
let forward = bridge.dir.to_vec2();
let rock = Fill::Block(Block::new(BlockKind::Rock, Rgb::gray(50)));
let wood = Fill::Block(Block::new(BlockKind::Wood, Rgb::new(133, 94, 66)));
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 top_offset = 4;
let top = bridge.end.z + top_offset;
let [ramp_f, aabr] = bridge.dir.split_aabr(aabr, top - bridge.start.z + 1);
painter
.aabb(aabb(
ramp_f.min.with_z(bridge.start.z - 10),
ramp_f.max.with_z(bridge.start.z),
))
.fill(rock.clone());
painter
.ramp(
aabb(ramp_f.min.with_z(bridge.start.z), ramp_f.max.with_z(top)),
top - bridge.start.z + 1,
bridge.dir,
)
.fill(rock.clone());
let [ramp_b, aabr] = (-bridge.dir).split_aabr(aabr, top_offset + 1);
painter
.aabb(aabb(
ramp_b.min.with_z(bridge.end.z - 10),
ramp_b.max.with_z(bridge.end.z),
))
.fill(rock.clone());
painter
.ramp(
aabb(ramp_b.min.with_z(bridge.end.z), ramp_b.max.with_z(top)),
top - bridge.end.z,
-bridge.dir,
)
.fill(rock.clone());
let len = bridge.dir.select(aabr.size());
let h = 3 * len.sqrt() / 4;
let x = len / 2;
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 bottom = top - (h - (hsqr - hsqr * x as f32 / w).sqrt().ceil() as i32);
let w = w as i32;
let c = aabr.center();
let cylinder = painter
.horizontal_cylinder(
aabb(
(c - forward * w - side).with_z(bottom),
(c + forward * w + side).with_z(bottom + h * 2),
),
orth_dir,
)
.intersect(painter.aabb(aabb(aabr.min.with_z(bottom), aabr.max.with_z(bottom + h * 2))));
cylinder.fill(wood.clone());
cylinder.translate(Vec3::unit_z()).clear();
let edges = cylinder
.without(cylinder.translate(Vec3::unit_z()))
.without(painter.aabb(aabb(
(c - forward * w - orthogonal * (bridge_width - 1)).with_z(bottom),
(c + forward * w + orthogonal * (bridge_width - 1)).with_z(bottom + h * 2),
)));
edges
.translate(Vec3::unit_z())
.fill(Fill::Sprite(SpriteKind::Rope));
edges.translate(Vec3::unit_z() * 2).fill(wood.clone());
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());
}
pub struct Bridge {
pub(crate) start: Vec3<i32>,
pub(crate) end: Vec3<i32>,
pub(crate) width: i32,
pub(crate) dir: Dir,
center: Vec3<i32>,
kind: BridgeKind,
@ -658,17 +837,6 @@ impl Bridge {
} else {
break test_start.with_z(last_alt);
}
/*
let alt = land.get_alt_approx(test_start + dir);
let is_water = land.get_chunk_wpos(test_start + dir * 5).map_or(false, |chunk| chunk.river.river_kind.is_some());
if is_water || last_alt - alt as i32 > 1 || start.z - alt as i32 > 5 {
break test_start.with_z(last_alt);
}
last_alt = alt as i32;
test_start += dir;
*/
}
};
@ -684,32 +852,20 @@ impl Bridge {
let center = (start.xy() + end.xy()) / 2;
let center = center.with_z(land.get_alt_approx(center) as i32);
let len = (start.xy() - end.xy()).map(|e| e.abs()).reduce_max();
let bridge = BridgeKind::random(rng, start, center, end);
Self {
start,
end,
center,
dir: Dir::from_vector(end.xy() - start.xy()),
kind: if end.z - start.z > 17 {
BridgeKind::Tower(match rng.gen_range(0..=2) {
0 => RoofKind::Crenelated,
_ => RoofKind::Hipped,
})
} else if len < 40 {
BridgeKind::Short
} else if end.z - start.z < 5 && start.z - center.z < 16 {
BridgeKind::HeightenedViaduct(HeightenedViaduct::random(rng))
} else {
BridgeKind::Flat
},
width: 8,
kind: bridge,
biome: land
.get_chunk_wpos(center.xy())
.map_or(BiomeKind::Void, |chunk| chunk.get_biome()),
}
}
pub fn width(&self) -> i32 { self.kind.width() }
}
impl Structure for Bridge {
@ -719,6 +875,7 @@ impl Structure for Bridge {
BridgeKind::Tower(roof) => render_tower(self, painter, roof),
BridgeKind::Short => render_short(self, painter),
BridgeKind::HeightenedViaduct(data) => render_heightened_viaduct(self, painter, data),
BridgeKind::HangBridge => render_hang(self, painter),
}
}
}