I made a small improvement to the corridor curve sampling to create an equidistant sampling.
let t_samples = 100;
// determine length of path
let path_len = {
let mut l = 0.0;
let mut prev_point = control_a;
for t in 1..=t_samples {
let t = t as f32 / t_samples as f32;
let point = corridor.sample(t);
l += point.pos.dist(&prev_point);
prev_point = point.pos;
}
l
};
// determine spacing based on a desired spacing
// E.g. if we want 1.0 spacing but the curve is 1.1 long,
// a naive approach would result in a 0.1 segment at the end.
// Instead we want a 1.1 segement or a 0.55 segment
let desired_spacing = 1.0;
let spacing = {
let needed_steps = path_len / desired_spacing;
let low = needed_steps.floor().max(1.0);
let high = needed_steps.ceil();
let low_step = path_len / low;
let high_step = path_len / high;
if (low_step - desired_spacing).abs() <= (high_step - desired_spacing).abs() {
low_step
} else {
high_step
}
};
let mut dist = 0.0;
let mut prev_dist_point = control_a;
for t in 1..t_samples {
let t = t as f32 / t_samples as f32;
let point = corridor.sample(t);
dist += point.pos.dist(&prev_dist_point);
prev_dist_point = point.pos;
if dist >= spacing {
// create sample point
}
}
I've added the possibility that new rooms can also be connected to existing corridors.
Additionally added round rooms to the generation. They use my previously implemented Delauney triangulation. I guess there are better ways to generate them but these are just placeholders for the moment.
After some time doing Advent of Code I've returned to my "game" project. The grid based approach to create maps was too restrictive and didn't take full advantage of the free geometry generation.
I've reworked this to allow arbitrarily shaped rooms in the future. Rooms are added to the layout one by one. Each new room is connected to the existing ones with a corridor defined by a cubic Bézier curve. Each "wall edge" of a room is treated a potential exit.
Next steps will be refining the corridor generation. At the moment I connect the two closest edge of new and existing rooms with control points placed at a fixed distance along the walls normals. This leads to misshaped corridors if the room are too close or at a bad angle.
Implemented edge flip for corridors. For each cell which only has horizontal or vertical neighbors the edge between two cells is flipped. This works great but there are some edge cases that still need to be handled.
Geometry generation is now in an okay state. Next improvements are significantly harder and have almost no visual effect. For example merging the "pillar" geometry (green) correctly. At the moment these are just overlapping tubes.
Decided to work on the room layouts instead and started with a classical grid dungeon layout. Corridors still look incorrect and need an edge flip to align the flow of the ceiling correctly.
Added walls to the room and removed the pillars in the room itself. These can still be placed in the data structure but my random generator isn't using them at the moment. The geometry is now mostly done for my purposes. The next step will be tooling to create and edit rooms manually.
I've found a simple way to create UVs for my ceiling geometry, built from Bezier triangles.
For each triangle I compute a LookAt matrix with the normal of the triangle as the view direction. For the up vector I use one edge of the triangle. The UV coordinates can then be calculated by applying this matrix to each sampled points and discarding the Z component.
let mut ps = [
self.room.surface_point(*face, 0.0, 0.0, 1.0),
self.room.surface_point(*face, 1.0, 0.0, 0.0),
self.room.surface_point(*face, 0.0, 1.0, 0.0),
];
ps.sort_by(|x, y| x.y.partial_cmp(&y.y).unwrap());
let v1 = ps[1] - ps[0];
let v2 = ps[2] - ps[0];
let n1 = v1.cross(&v2);
let lt = Matrix3::look_to_rh(n1.normalize().into(), v1.normalize().into());
UV coordinates on pillars and floor working. I've tinkered with UVs for the ceiling surfaces but have not found a solution yet. There are always discontinuities. The ceiling is created with quadratic Bezier triangles.