mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Gnarling watch towers.
This commit is contained in:
parent
90cedfc95d
commit
cf73da2955
@ -114,6 +114,18 @@ impl Primitive {
|
||||
pub fn repeat(a: impl Into<Id<Primitive>>, offset: Vec3<i32>, count: i32) -> Self {
|
||||
Self::Repeat(a.into(), offset, count)
|
||||
}
|
||||
|
||||
pub fn cylinder(origin: Vec3<i32>, radius: f32, height: f32) -> Self {
|
||||
let min = origin - Vec2::broadcast(radius.round() as i32);
|
||||
let max = origin + Vec2::broadcast(radius.round() as i32).with_z(height.round() as i32);
|
||||
Primitive::Cylinder(Aabb { min, max })
|
||||
}
|
||||
|
||||
pub fn sphere(origin: Vec3<i32>, radius: f32) -> Self {
|
||||
let min = origin - Vec3::broadcast(radius.round() as i32);
|
||||
let max = origin + Vec3::broadcast(radius.round() as i32);
|
||||
Primitive::Sphere(Aabb { min, max })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -9,7 +9,10 @@ pub struct GnarlingFortification {
|
||||
seed: u32,
|
||||
origin: Vec2<i32>,
|
||||
radius: i32,
|
||||
ordered_wall_points: Vec<Vec2<i32>>,
|
||||
wall_radius: i32,
|
||||
// Vec2 is relative position of wall relative to site origin, bool indicates whether it is a
|
||||
// corner, and thus whether a tower gets constructed
|
||||
ordered_wall_points: Vec<(Vec2<i32>, bool)>,
|
||||
}
|
||||
|
||||
impl GnarlingFortification {
|
||||
@ -18,27 +21,31 @@ impl GnarlingFortification {
|
||||
let seed = rng.gen();
|
||||
let origin = wpos;
|
||||
|
||||
let radius = {
|
||||
let wall_radius = {
|
||||
let unit_size = rng.gen_range(10..20);
|
||||
let num_units = rng.gen_range(5..10);
|
||||
let variation = rng.gen_range(0..50);
|
||||
unit_size * num_units + variation
|
||||
};
|
||||
|
||||
let num_points = (radius / 15).max(4);
|
||||
let radius = wall_radius + 25;
|
||||
|
||||
let num_points = (wall_radius / 15).max(4);
|
||||
let ordered_wall_points = (0..num_points)
|
||||
.into_iter()
|
||||
.map(|a| {
|
||||
let angle = a as f32 / num_points as f32 * core::f32::consts::TAU;
|
||||
Vec2::new(angle.cos(), angle.sin()).map(|a| (a * radius as f32) as i32)
|
||||
Vec2::new(angle.cos(), angle.sin()).map(|a| (a * wall_radius as f32) as i32)
|
||||
})
|
||||
.map(|point| {
|
||||
point.map(|a| {
|
||||
let variation = radius / 5;
|
||||
let variation = wall_radius / 5;
|
||||
a + rng.gen_range(-variation..=variation)
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
// This adds additional points for the wall on the line between two points,
|
||||
// allowing the wall to better handle slopes
|
||||
let ordered_wall_points = ordered_wall_points
|
||||
.iter()
|
||||
.enumerate()
|
||||
@ -50,7 +57,7 @@ impl GnarlingFortification {
|
||||
};
|
||||
let mid_point_1 = point + (next_point - point) / 3;
|
||||
let mid_point_2 = point + (next_point - point) * 2 / 3;
|
||||
[*point, mid_point_1, mid_point_2]
|
||||
[(*point, true), (mid_point_1, false), (mid_point_2, false)]
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
@ -59,6 +66,7 @@ impl GnarlingFortification {
|
||||
seed,
|
||||
origin,
|
||||
radius,
|
||||
wall_radius,
|
||||
ordered_wall_points,
|
||||
}
|
||||
}
|
||||
@ -71,9 +79,9 @@ impl GnarlingFortification {
|
||||
impl Structure for GnarlingFortification {
|
||||
fn render(&self, _site: &Site, land: &Land, painter: &Painter) {
|
||||
// Create outer wall
|
||||
for (i, point) in self.ordered_wall_points.iter().enumerate() {
|
||||
for (i, (point, _is_tower)) in self.ordered_wall_points.iter().enumerate() {
|
||||
// Other point of wall segment
|
||||
let next_point = if let Some(point) = self.ordered_wall_points.get(i + 1) {
|
||||
let (next_point, _is_tower) = if let Some(point) = self.ordered_wall_points.get(i + 1) {
|
||||
*point
|
||||
} else {
|
||||
self.ordered_wall_points[0]
|
||||
@ -91,35 +99,34 @@ impl Structure for GnarlingFortification {
|
||||
.as_()
|
||||
.with_z(land.get_alt_approx(end_wpos) - wall_depth);
|
||||
|
||||
let wall_base_segment = LineSegment3 { start, end };
|
||||
let wall_base_thickness = 3.0;
|
||||
let wall_base_height = 3.0;
|
||||
|
||||
painter.fill(
|
||||
painter.prim(Primitive::SegmentPrism(
|
||||
wall_base_segment,
|
||||
painter
|
||||
.segment_prism(
|
||||
start,
|
||||
end,
|
||||
wall_base_thickness,
|
||||
wall_base_height + wall_depth as f32,
|
||||
)),
|
||||
Fill::Block(Block::new(BlockKind::Wood, Rgb::new(55, 25, 8))),
|
||||
);
|
||||
)
|
||||
.fill(Fill::Block(Block::new(
|
||||
BlockKind::Wood,
|
||||
Rgb::new(55, 25, 8),
|
||||
)));
|
||||
|
||||
// Middle of wall
|
||||
let start = start_wpos.as_().with_z(land.get_alt_approx(start_wpos));
|
||||
let end = end_wpos.as_().with_z(land.get_alt_approx(end_wpos));
|
||||
|
||||
let wall_mid_segment = LineSegment3 { start, end };
|
||||
let wall_mid_thickness = 1.0;
|
||||
let wall_mid_height = 5.0 + wall_base_height;
|
||||
|
||||
painter.fill(
|
||||
painter.prim(Primitive::SegmentPrism(
|
||||
wall_mid_segment,
|
||||
wall_mid_thickness,
|
||||
wall_mid_height,
|
||||
)),
|
||||
Fill::Block(Block::new(BlockKind::Wood, Rgb::new(55, 25, 8))),
|
||||
);
|
||||
painter
|
||||
.segment_prism(start, end, wall_mid_thickness, wall_mid_height)
|
||||
.fill(Fill::Block(Block::new(
|
||||
BlockKind::Wood,
|
||||
Rgb::new(55, 25, 8),
|
||||
)));
|
||||
|
||||
// Top of wall
|
||||
let start = start_wpos
|
||||
@ -129,47 +136,153 @@ impl Structure for GnarlingFortification {
|
||||
.as_()
|
||||
.with_z(land.get_alt_approx(end_wpos) + wall_mid_height);
|
||||
|
||||
let wall_top_segment = LineSegment3 { start, end };
|
||||
let wall_top_thickness = 2.0;
|
||||
let wall_top_height = 1.0;
|
||||
|
||||
painter.fill(
|
||||
painter.prim(Primitive::SegmentPrism(
|
||||
wall_top_segment,
|
||||
wall_top_thickness,
|
||||
wall_top_height,
|
||||
)),
|
||||
Fill::Block(Block::new(BlockKind::Wood, Rgb::new(55, 25, 8))),
|
||||
);
|
||||
painter
|
||||
.segment_prism(start, end, wall_top_thickness, wall_top_height)
|
||||
.fill(Fill::Block(Block::new(
|
||||
BlockKind::Wood,
|
||||
Rgb::new(55, 25, 8),
|
||||
)));
|
||||
|
||||
// Wall parapets
|
||||
let parapet_z_offset = 1.0;
|
||||
|
||||
let start = Vec3::new(
|
||||
point.x as f32 * (self.radius as f32 + 1.0) / (self.radius as f32)
|
||||
point.x as f32 * (self.wall_radius as f32 + 1.0) / (self.wall_radius as f32)
|
||||
+ self.origin.x as f32,
|
||||
point.y as f32 * (self.radius as f32 + 1.0) / (self.radius as f32)
|
||||
point.y as f32 * (self.wall_radius as f32 + 1.0) / (self.wall_radius as f32)
|
||||
+ self.origin.y as f32,
|
||||
land.get_alt_approx(start_wpos) + wall_mid_height + wall_top_height - 1.0,
|
||||
land.get_alt_approx(start_wpos) + wall_mid_height + wall_top_height
|
||||
- parapet_z_offset,
|
||||
);
|
||||
let end = Vec3::new(
|
||||
next_point.x as f32 * (self.radius as f32 + 1.0) / (self.radius as f32)
|
||||
next_point.x as f32 * (self.wall_radius as f32 + 1.0) / (self.wall_radius as f32)
|
||||
+ self.origin.x as f32,
|
||||
next_point.y as f32 * (self.radius as f32 + 1.0) / (self.radius as f32)
|
||||
next_point.y as f32 * (self.wall_radius as f32 + 1.0) / (self.wall_radius as f32)
|
||||
+ self.origin.y as f32,
|
||||
land.get_alt_approx(end_wpos) + wall_mid_height + wall_top_height - 1.0,
|
||||
land.get_alt_approx(end_wpos) + wall_mid_height + wall_top_height
|
||||
- parapet_z_offset,
|
||||
);
|
||||
|
||||
let wall_par_segment = LineSegment3 { start, end };
|
||||
let wall_par_thickness = tweak!(0.8);
|
||||
let wall_par_height = 1.0;
|
||||
|
||||
painter.fill(
|
||||
painter.prim(Primitive::SegmentPrism(
|
||||
wall_par_segment,
|
||||
painter
|
||||
.segment_prism(
|
||||
start,
|
||||
end,
|
||||
wall_par_thickness,
|
||||
wall_par_height + 1.0,
|
||||
)),
|
||||
Fill::Block(Block::new(BlockKind::Wood, Rgb::new(55, 25, 8))),
|
||||
);
|
||||
wall_par_height + parapet_z_offset as f32,
|
||||
)
|
||||
.fill(Fill::Block(Block::new(
|
||||
BlockKind::Wood,
|
||||
Rgb::new(55, 25, 8),
|
||||
)));
|
||||
}
|
||||
|
||||
// Create towers
|
||||
self.ordered_wall_points
|
||||
.iter()
|
||||
.filter_map(|(point, is_tower)| is_tower.then_some(point))
|
||||
.for_each(|point| {
|
||||
let wpos = point + self.origin;
|
||||
|
||||
// Tower base
|
||||
let tower_depth = 3;
|
||||
let tower_base_pos = wpos.with_z(land.get_alt_approx(wpos) as i32 - tower_depth);
|
||||
let tower_radius = 5.;
|
||||
let tower_height = 20.0;
|
||||
|
||||
painter
|
||||
.prim(Primitive::cylinder(
|
||||
tower_base_pos,
|
||||
tower_radius,
|
||||
tower_depth as f32 + tower_height,
|
||||
))
|
||||
.fill(Fill::Block(Block::new(
|
||||
BlockKind::Wood,
|
||||
Rgb::new(55, 25, 8),
|
||||
)));
|
||||
|
||||
// Tower cylinder
|
||||
let tower_floor_pos = wpos.with_z(land.get_alt_approx(wpos) as i32);
|
||||
|
||||
painter
|
||||
.prim(Primitive::cylinder(
|
||||
tower_floor_pos,
|
||||
tower_radius - 1.0,
|
||||
tower_height,
|
||||
))
|
||||
.fill(Fill::Block(Block::empty()));
|
||||
|
||||
// Tower top floor
|
||||
let top_floor_z = (land.get_alt_approx(wpos) + tower_height - 2.0) as i32;
|
||||
let tower_top_floor_pos = wpos.with_z(top_floor_z);
|
||||
|
||||
painter
|
||||
.prim(Primitive::cylinder(tower_top_floor_pos, tower_radius, 1.0))
|
||||
.fill(Fill::Block(Block::new(
|
||||
BlockKind::Wood,
|
||||
Rgb::new(55, 25, 8),
|
||||
)));
|
||||
|
||||
// Tower roof poles
|
||||
let roof_pole_height = 5;
|
||||
let relative_pole_positions = [
|
||||
Vec2::new(-4, -4),
|
||||
Vec2::new(-4, 3),
|
||||
Vec2::new(3, -4),
|
||||
Vec2::new(3, 3),
|
||||
];
|
||||
relative_pole_positions
|
||||
.iter()
|
||||
.map(|rpos| wpos + rpos)
|
||||
.for_each(|pole_pos| {
|
||||
painter
|
||||
.line(
|
||||
pole_pos.with_z(top_floor_z),
|
||||
pole_pos.with_z(top_floor_z + roof_pole_height),
|
||||
1.,
|
||||
)
|
||||
.fill(Fill::Block(Block::new(
|
||||
BlockKind::Wood,
|
||||
Rgb::new(55, 25, 8),
|
||||
)));
|
||||
});
|
||||
|
||||
// Tower roof
|
||||
let roof_sphere_radius = 10;
|
||||
let roof_radius = tower_radius + 1.0;
|
||||
let roof_height = 3;
|
||||
|
||||
let roof_cyl = painter.prim(Primitive::cylinder(
|
||||
wpos.with_z(top_floor_z + roof_pole_height),
|
||||
roof_radius,
|
||||
roof_height as f32,
|
||||
));
|
||||
|
||||
painter
|
||||
.prim(Primitive::sphere(
|
||||
wpos.with_z(
|
||||
top_floor_z + roof_pole_height + roof_height - roof_sphere_radius,
|
||||
),
|
||||
roof_sphere_radius as f32,
|
||||
))
|
||||
.intersect(roof_cyl)
|
||||
.fill(Fill::Block(Block::new(
|
||||
BlockKind::Wood,
|
||||
Rgb::new(55, 25, 8),
|
||||
)));
|
||||
// TODO: There is probably a better way of getting intersection
|
||||
// of 2 primitives than this? let roof_sphere =
|
||||
// painter.prim(Primitive::sphere(wpos.with_z(top_floor_z +
|
||||
// roof_pole_height + roof_height - roof_sphere_radius),
|
||||
// roof_sphere_radius as f32)); painter
|
||||
// .prim(roof_sphere.intersect(roof_cyl))
|
||||
// .fill(Fill::Block(Block::new(BlockKind::Wood,
|
||||
// Rgb::new(55, 25, 8))));
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user