pxl8/pxl8d/src/procgen.rs

807 lines
25 KiB
Rust
Raw Normal View History

2026-01-25 09:26:30 -06:00
extern crate alloc;
use alloc::vec;
use alloc::vec::Vec;
use libm::sqrtf;
use crate::bsp::{Bsp, BspVertex, BspEdge, BspFace, BspPlane, BspNode, BspLeaf, BspPortal, BspCellPortals};
use crate::math::Vec3;
pub const CELL_SIZE: f32 = 64.0;
pub const WALL_HEIGHT: f32 = 128.0;
pub const PVS_MAX_DEPTH: u32 = 64;
#[derive(Clone, Copy)]
pub struct LightSource {
pub position: Vec3,
pub intensity: f32,
pub radius: f32,
}
#[derive(Clone, Copy)]
pub struct ProcgenParams {
pub width: i32,
pub height: i32,
pub seed: u32,
pub min_room_size: i32,
pub max_room_size: i32,
pub num_rooms: i32,
}
impl Default for ProcgenParams {
fn default() -> Self {
Self {
width: 16,
height: 16,
seed: 12345,
min_room_size: 3,
max_room_size: 6,
num_rooms: 8,
}
}
}
struct RoomGrid {
cells: Vec<u8>,
width: i32,
height: i32,
}
impl RoomGrid {
fn new(width: i32, height: i32) -> Self {
Self {
cells: vec![0; (width * height) as usize],
width,
height,
}
}
fn get(&self, x: i32, y: i32) -> u8 {
if x < 0 || x >= self.width || y < 0 || y >= self.height {
return 1;
}
self.cells[(y * self.width + x) as usize]
}
fn set(&mut self, x: i32, y: i32, value: u8) {
if x >= 0 && x < self.width && y >= 0 && y < self.height {
self.cells[(y * self.width + x) as usize] = value;
}
}
fn fill(&mut self, value: u8) {
for cell in &mut self.cells {
*cell = value;
}
}
}
struct Rng {
state: u32,
}
impl Rng {
fn new(seed: u32) -> Self {
Self { state: seed }
}
fn next(&mut self) -> u32 {
self.state = self.state.wrapping_mul(1103515245).wrapping_add(12345);
(self.state >> 16) & 0x7FFF
}
}
#[derive(Clone, Copy)]
struct Bounds {
x: i32,
y: i32,
w: i32,
h: i32,
}
impl Bounds {
fn intersects(&self, other: &Bounds) -> bool {
!(self.x + self.w <= other.x || other.x + other.w <= self.x ||
self.y + self.h <= other.y || other.y + other.h <= self.y)
}
}
struct BspBuildContext<'a> {
bsp: &'a mut Bsp,
grid: &'a RoomGrid,
node_count: u32,
plane_offset: u32,
}
fn carve_corridor_h(grid: &mut RoomGrid, x1: i32, x2: i32, y: i32) {
let start = x1.min(x2);
let end = x1.max(x2);
for x in start..=end {
grid.set(x, y, 0);
grid.set(x, y - 1, 0);
grid.set(x, y + 1, 0);
}
}
fn carve_corridor_v(grid: &mut RoomGrid, y1: i32, y2: i32, x: i32) {
let start = y1.min(y2);
let end = y1.max(y2);
for y in start..=end {
grid.set(x, y, 0);
grid.set(x - 1, y, 0);
grid.set(x + 1, y, 0);
}
}
fn compute_face_aabb(face: &mut BspFace, verts: &[BspVertex], vert_idx: usize) {
face.aabb_min = Vec3::new(1e30, 1e30, 1e30);
face.aabb_max = Vec3::new(-1e30, -1e30, -1e30);
for i in 0..4 {
let v = verts[vert_idx + i].position;
if v.x < face.aabb_min.x { face.aabb_min.x = v.x; }
if v.x > face.aabb_max.x { face.aabb_max.x = v.x; }
if v.y < face.aabb_min.y { face.aabb_min.y = v.y; }
if v.y > face.aabb_max.y { face.aabb_max.y = v.y; }
if v.z < face.aabb_min.z { face.aabb_min.z = v.z; }
if v.z > face.aabb_max.z { face.aabb_max.z = v.z; }
}
}
fn build_bsp_node(ctx: &mut BspBuildContext, x0: i32, y0: i32, x1: i32, y1: i32, depth: i32) -> i32 {
if x1 - x0 == 1 && y1 - y0 == 1 {
let leaf_idx = y0 * ctx.grid.width + x0;
return -(leaf_idx + 1);
}
let node_idx = ctx.node_count;
ctx.node_count += 1;
let plane_idx = ctx.plane_offset;
ctx.plane_offset += 1;
if depth % 2 == 0 {
let mid_x = (x0 + x1) / 2;
let split_pos = mid_x as f32 * CELL_SIZE;
ctx.bsp.planes[plane_idx as usize] = BspPlane {
normal: Vec3::new(1.0, 0.0, 0.0),
dist: split_pos,
plane_type: 0,
};
let child0 = build_bsp_node(ctx, mid_x, y0, x1, y1, depth + 1);
let child1 = build_bsp_node(ctx, x0, y0, mid_x, y1, depth + 1);
ctx.bsp.nodes[node_idx as usize] = BspNode {
plane_id: plane_idx,
children: [child0, child1],
..Default::default()
};
} else {
let mid_y = (y0 + y1) / 2;
let split_pos = mid_y as f32 * CELL_SIZE;
ctx.bsp.planes[plane_idx as usize] = BspPlane {
normal: Vec3::new(0.0, 0.0, 1.0),
dist: split_pos,
plane_type: 0,
};
let child0 = build_bsp_node(ctx, x0, mid_y, x1, y1, depth + 1);
let child1 = build_bsp_node(ctx, x0, y0, x1, mid_y, depth + 1);
ctx.bsp.nodes[node_idx as usize] = BspNode {
plane_id: plane_idx,
children: [child0, child1],
..Default::default()
};
}
node_idx as i32
}
fn build_cell_portals(grid: &RoomGrid) -> Vec<BspCellPortals> {
let total_cells = (grid.width * grid.height) as usize;
let mut portals = vec![BspCellPortals::default(); total_cells];
for y in 0..grid.height {
for x in 0..grid.width {
if grid.get(x, y) != 0 {
continue;
}
let c = (y * grid.width + x) as usize;
let cx = x as f32 * CELL_SIZE;
let cz = y as f32 * CELL_SIZE;
if x > 0 && grid.get(x - 1, y) == 0 {
let p = &mut portals[c];
let idx = p.num_portals as usize;
p.portals[idx] = BspPortal {
x0: cx,
z0: cz,
x1: cx,
z1: cz + CELL_SIZE,
target_leaf: (y * grid.width + (x - 1)) as u32,
};
p.num_portals += 1;
}
if x < grid.width - 1 && grid.get(x + 1, y) == 0 {
let p = &mut portals[c];
let idx = p.num_portals as usize;
p.portals[idx] = BspPortal {
x0: cx + CELL_SIZE,
z0: cz + CELL_SIZE,
x1: cx + CELL_SIZE,
z1: cz,
target_leaf: (y * grid.width + (x + 1)) as u32,
};
p.num_portals += 1;
}
if y > 0 && grid.get(x, y - 1) == 0 {
let p = &mut portals[c];
let idx = p.num_portals as usize;
p.portals[idx] = BspPortal {
x0: cx + CELL_SIZE,
z0: cz,
x1: cx,
z1: cz,
target_leaf: ((y - 1) * grid.width + x) as u32,
};
p.num_portals += 1;
}
if y < grid.height - 1 && grid.get(x, y + 1) == 0 {
let p = &mut portals[c];
let idx = p.num_portals as usize;
p.portals[idx] = BspPortal {
x0: cx,
z0: cz + CELL_SIZE,
x1: cx + CELL_SIZE,
z1: cz + CELL_SIZE,
target_leaf: ((y + 1) * grid.width + x) as u32,
};
p.num_portals += 1;
}
}
}
portals
}
#[derive(Clone, Copy)]
struct FloodEntry {
leaf: u32,
depth: u32,
}
fn portal_flood_bfs(
start_leaf: u32,
portals: &[BspCellPortals],
leafs: &[BspLeaf],
pvs: &mut [u8],
num_leafs: u32,
) {
let pvs_bytes = ((num_leafs + 7) / 8) as usize;
let mut visited = vec![0u8; pvs_bytes];
let mut queue = Vec::with_capacity(num_leafs as usize);
pvs[(start_leaf >> 3) as usize] |= 1 << (start_leaf & 7);
visited[(start_leaf >> 3) as usize] |= 1 << (start_leaf & 7);
queue.push(FloodEntry { leaf: start_leaf, depth: 0 });
let mut head = 0;
while head < queue.len() {
let e = queue[head];
head += 1;
if e.depth >= PVS_MAX_DEPTH {
continue;
}
if leafs[e.leaf as usize].contents == -1 {
continue;
}
let cp = &portals[e.leaf as usize];
for i in 0..cp.num_portals {
let target = cp.portals[i as usize].target_leaf;
let byte = (target >> 3) as usize;
let bit = target & 7;
if visited[byte] & (1 << bit) != 0 {
continue;
}
visited[byte] |= 1 << bit;
if leafs[target as usize].contents == -1 {
continue;
}
pvs[byte] |= 1 << bit;
queue.push(FloodEntry { leaf: target, depth: e.depth + 1 });
}
}
}
fn compute_leaf_pvs(
start_leaf: u32,
portals: &[BspCellPortals],
leafs: &[BspLeaf],
num_leafs: u32,
) -> Vec<u8> {
let pvs_bytes = ((num_leafs + 7) / 8) as usize;
let mut pvs = vec![0u8; pvs_bytes];
portal_flood_bfs(start_leaf, portals, leafs, &mut pvs, num_leafs);
pvs
}
fn rle_compress_pvs(pvs: &[u8]) -> Vec<u8> {
let mut out = Vec::new();
let mut i = 0;
while i < pvs.len() {
if pvs[i] != 0 {
out.push(pvs[i]);
i += 1;
} else {
let mut count = 0u8;
while i < pvs.len() && pvs[i] == 0 && count < 255 {
count += 1;
i += 1;
}
out.push(0);
out.push(count);
}
}
out
}
fn build_pvs_data(bsp: &mut Bsp, portals: &[BspCellPortals]) {
let num_leafs = bsp.leafs.len() as u32;
let mut visdata = Vec::new();
for leaf in 0..num_leafs {
if bsp.leafs[leaf as usize].contents == -1 {
bsp.leafs[leaf as usize].visofs = -1;
continue;
}
let pvs = compute_leaf_pvs(leaf, portals, &bsp.leafs, num_leafs);
let compressed = rle_compress_pvs(&pvs);
bsp.leafs[leaf as usize].visofs = visdata.len() as i32;
visdata.extend_from_slice(&compressed);
}
bsp.visdata = visdata;
}
fn compute_vertex_light(
pos: Vec3,
normal: Vec3,
lights: &[LightSource],
ambient: f32,
) -> f32 {
let mut total = ambient;
for light in lights {
let to_light = Vec3::new(
light.position.x - pos.x,
light.position.y - pos.y,
light.position.z - pos.z,
);
let dist_sq = to_light.x * to_light.x + to_light.y * to_light.y + to_light.z * to_light.z;
let dist = sqrtf(dist_sq).max(1.0);
if dist > light.radius {
continue;
}
let inv_dist = 1.0 / dist;
let light_dir = Vec3::new(
to_light.x * inv_dist,
to_light.y * inv_dist,
to_light.z * inv_dist,
);
let ndotl = (normal.x * light_dir.x + normal.y * light_dir.y + normal.z * light_dir.z).max(0.0);
let attenuation = (1.0 - dist / light.radius).max(0.0);
let attenuation = attenuation * attenuation;
total += light.intensity * ndotl * attenuation;
}
total.min(1.0)
}
fn compute_bsp_vertex_lighting(bsp: &mut Bsp, lights: &[LightSource], ambient: f32) {
if bsp.vertices.is_empty() {
return;
}
bsp.vertex_lights = vec![0u32; bsp.vertices.len()];
for f in 0..bsp.faces.len() {
let face = &bsp.faces[f];
let normal = if (face.plane_id as usize) < bsp.planes.len() {
bsp.planes[face.plane_id as usize].normal
} else {
Vec3::new(0.0, 1.0, 0.0)
};
for e in 0..face.num_edges {
let surfedge_idx = (face.first_edge + e as u32) as usize;
if surfedge_idx >= bsp.surfedges.len() {
continue;
}
let edge_idx = bsp.surfedges[surfedge_idx];
let vert_idx = if edge_idx >= 0 {
let ei = edge_idx as usize;
if ei >= bsp.edges.len() { continue; }
bsp.edges[ei].vertex[0] as usize
} else {
let ei = (-edge_idx) as usize;
if ei >= bsp.edges.len() { continue; }
bsp.edges[ei].vertex[1] as usize
};
if vert_idx >= bsp.vertices.len() {
continue;
}
let pos = bsp.vertices[vert_idx].position;
let light = compute_vertex_light(pos, normal, lights, ambient);
let light_byte = (light * 255.0) as u8;
bsp.vertex_lights[vert_idx] = ((light_byte as u32) << 24) | 0x00FFFFFF;
}
}
}
fn grid_to_bsp(bsp: &mut Bsp, grid: &RoomGrid) {
let mut face_count = 0;
let mut floor_ceiling_count = 0;
for y in 0..grid.height {
for x in 0..grid.width {
if grid.get(x, y) == 0 {
if grid.get(x - 1, y) == 1 { face_count += 1; }
if grid.get(x + 1, y) == 1 { face_count += 1; }
if grid.get(x, y - 1) == 1 { face_count += 1; }
if grid.get(x, y + 1) == 1 { face_count += 1; }
floor_ceiling_count += 1;
}
}
}
face_count += floor_ceiling_count;
let vertex_count = face_count * 4;
let total_cells = (grid.width * grid.height) as usize;
let max_nodes = 2 * total_cells;
let total_planes = face_count + max_nodes;
bsp.vertices = vec![BspVertex::default(); vertex_count];
bsp.faces = vec![BspFace::default(); face_count];
bsp.planes = vec![BspPlane::default(); total_planes];
bsp.edges = vec![BspEdge::default(); vertex_count];
bsp.surfedges = vec![0i32; vertex_count];
bsp.nodes = vec![BspNode::default(); max_nodes];
let mut face_cell = vec![0u32; face_count];
let mut vert_idx = 0usize;
let mut face_idx = 0usize;
let mut edge_idx = 0usize;
for y in 0..grid.height {
for x in 0..grid.width {
if grid.get(x, y) == 0 {
let fx = x as f32 * CELL_SIZE;
let fy = y as f32 * CELL_SIZE;
let cell_idx = (y * grid.width + x) as u32;
if grid.get(x - 1, y) == 1 {
bsp.vertices[vert_idx + 0].position = Vec3::new(fx, 0.0, fy);
bsp.vertices[vert_idx + 1].position = Vec3::new(fx, WALL_HEIGHT, fy);
bsp.vertices[vert_idx + 2].position = Vec3::new(fx, WALL_HEIGHT, fy + CELL_SIZE);
bsp.vertices[vert_idx + 3].position = Vec3::new(fx, 0.0, fy + CELL_SIZE);
bsp.planes[face_idx].normal = Vec3::new(1.0, 0.0, 0.0);
bsp.planes[face_idx].dist = fx;
bsp.faces[face_idx].plane_id = face_idx as u16;
bsp.faces[face_idx].num_edges = 4;
bsp.faces[face_idx].first_edge = edge_idx as u32;
bsp.faces[face_idx].material_id = 0;
for i in 0..4 {
bsp.edges[edge_idx + i].vertex[0] = (vert_idx + i) as u16;
bsp.edges[edge_idx + i].vertex[1] = (vert_idx + ((i + 1) % 4)) as u16;
bsp.surfedges[edge_idx + i] = (edge_idx + i) as i32;
}
compute_face_aabb(&mut bsp.faces[face_idx], &bsp.vertices, vert_idx);
face_cell[face_idx] = cell_idx;
vert_idx += 4;
edge_idx += 4;
face_idx += 1;
}
if grid.get(x + 1, y) == 1 {
bsp.vertices[vert_idx + 0].position = Vec3::new(fx + CELL_SIZE, 0.0, fy);
bsp.vertices[vert_idx + 1].position = Vec3::new(fx + CELL_SIZE, 0.0, fy + CELL_SIZE);
bsp.vertices[vert_idx + 2].position = Vec3::new(fx + CELL_SIZE, WALL_HEIGHT, fy + CELL_SIZE);
bsp.vertices[vert_idx + 3].position = Vec3::new(fx + CELL_SIZE, WALL_HEIGHT, fy);
bsp.planes[face_idx].normal = Vec3::new(-1.0, 0.0, 0.0);
bsp.planes[face_idx].dist = -(fx + CELL_SIZE);
bsp.faces[face_idx].plane_id = face_idx as u16;
bsp.faces[face_idx].num_edges = 4;
bsp.faces[face_idx].first_edge = edge_idx as u32;
bsp.faces[face_idx].material_id = 0;
for i in 0..4 {
bsp.edges[edge_idx + i].vertex[0] = (vert_idx + i) as u16;
bsp.edges[edge_idx + i].vertex[1] = (vert_idx + ((i + 1) % 4)) as u16;
bsp.surfedges[edge_idx + i] = (edge_idx + i) as i32;
}
compute_face_aabb(&mut bsp.faces[face_idx], &bsp.vertices, vert_idx);
face_cell[face_idx] = cell_idx;
vert_idx += 4;
edge_idx += 4;
face_idx += 1;
}
if grid.get(x, y - 1) == 1 {
bsp.vertices[vert_idx + 0].position = Vec3::new(fx, 0.0, fy);
bsp.vertices[vert_idx + 1].position = Vec3::new(fx + CELL_SIZE, 0.0, fy);
bsp.vertices[vert_idx + 2].position = Vec3::new(fx + CELL_SIZE, WALL_HEIGHT, fy);
bsp.vertices[vert_idx + 3].position = Vec3::new(fx, WALL_HEIGHT, fy);
bsp.planes[face_idx].normal = Vec3::new(0.0, 0.0, 1.0);
bsp.planes[face_idx].dist = fy;
bsp.faces[face_idx].plane_id = face_idx as u16;
bsp.faces[face_idx].num_edges = 4;
bsp.faces[face_idx].first_edge = edge_idx as u32;
bsp.faces[face_idx].material_id = 0;
for i in 0..4 {
bsp.edges[edge_idx + i].vertex[0] = (vert_idx + i) as u16;
bsp.edges[edge_idx + i].vertex[1] = (vert_idx + ((i + 1) % 4)) as u16;
bsp.surfedges[edge_idx + i] = (edge_idx + i) as i32;
}
compute_face_aabb(&mut bsp.faces[face_idx], &bsp.vertices, vert_idx);
face_cell[face_idx] = cell_idx;
vert_idx += 4;
edge_idx += 4;
face_idx += 1;
}
if grid.get(x, y + 1) == 1 {
bsp.vertices[vert_idx + 0].position = Vec3::new(fx, 0.0, fy + CELL_SIZE);
bsp.vertices[vert_idx + 1].position = Vec3::new(fx, WALL_HEIGHT, fy + CELL_SIZE);
bsp.vertices[vert_idx + 2].position = Vec3::new(fx + CELL_SIZE, WALL_HEIGHT, fy + CELL_SIZE);
bsp.vertices[vert_idx + 3].position = Vec3::new(fx + CELL_SIZE, 0.0, fy + CELL_SIZE);
bsp.planes[face_idx].normal = Vec3::new(0.0, 0.0, -1.0);
bsp.planes[face_idx].dist = -(fy + CELL_SIZE);
bsp.faces[face_idx].plane_id = face_idx as u16;
bsp.faces[face_idx].num_edges = 4;
bsp.faces[face_idx].first_edge = edge_idx as u32;
bsp.faces[face_idx].material_id = 0;
for i in 0..4 {
bsp.edges[edge_idx + i].vertex[0] = (vert_idx + i) as u16;
bsp.edges[edge_idx + i].vertex[1] = (vert_idx + ((i + 1) % 4)) as u16;
bsp.surfedges[edge_idx + i] = (edge_idx + i) as i32;
}
compute_face_aabb(&mut bsp.faces[face_idx], &bsp.vertices, vert_idx);
face_cell[face_idx] = cell_idx;
vert_idx += 4;
edge_idx += 4;
face_idx += 1;
}
}
}
}
for y in 0..grid.height {
for x in 0..grid.width {
if grid.get(x, y) == 0 {
let fx = x as f32 * CELL_SIZE;
let fy = y as f32 * CELL_SIZE;
let cell_idx = (y * grid.width + x) as u32;
bsp.vertices[vert_idx + 0].position = Vec3::new(fx, 0.0, fy);
bsp.vertices[vert_idx + 1].position = Vec3::new(fx, 0.0, fy + CELL_SIZE);
bsp.vertices[vert_idx + 2].position = Vec3::new(fx + CELL_SIZE, 0.0, fy + CELL_SIZE);
bsp.vertices[vert_idx + 3].position = Vec3::new(fx + CELL_SIZE, 0.0, fy);
bsp.planes[face_idx].normal = Vec3::new(0.0, 1.0, 0.0);
bsp.planes[face_idx].dist = 0.0;
bsp.faces[face_idx].plane_id = face_idx as u16;
bsp.faces[face_idx].num_edges = 4;
bsp.faces[face_idx].first_edge = edge_idx as u32;
bsp.faces[face_idx].material_id = 0;
for i in 0..4 {
bsp.edges[edge_idx + i].vertex[0] = (vert_idx + i) as u16;
bsp.edges[edge_idx + i].vertex[1] = (vert_idx + ((i + 1) % 4)) as u16;
bsp.surfedges[edge_idx + i] = (edge_idx + i) as i32;
}
compute_face_aabb(&mut bsp.faces[face_idx], &bsp.vertices, vert_idx);
face_cell[face_idx] = cell_idx;
vert_idx += 4;
edge_idx += 4;
face_idx += 1;
}
}
}
bsp.vertices.truncate(vert_idx);
bsp.faces.truncate(face_idx);
bsp.edges.truncate(edge_idx);
bsp.surfedges.truncate(edge_idx);
bsp.leafs = vec![BspLeaf::default(); total_cells];
bsp.marksurfaces = vec![0u16; face_idx];
let mut faces_per_cell = vec![0u32; total_cells];
let mut cell_offset = vec![0u32; total_cells];
let mut cell_cursor = vec![0u32; total_cells];
for i in 0..face_idx {
faces_per_cell[face_cell[i] as usize] += 1;
}
let mut offset = 0u32;
for c in 0..total_cells {
cell_offset[c] = offset;
offset += faces_per_cell[c];
}
for i in 0..face_idx {
let c = face_cell[i] as usize;
bsp.marksurfaces[(cell_offset[c] + cell_cursor[c]) as usize] = i as u16;
cell_cursor[c] += 1;
}
for y in 0..grid.height {
for x in 0..grid.width {
let c = (y * grid.width + x) as usize;
let leaf = &mut bsp.leafs[c];
let fx = x as f32 * CELL_SIZE;
let fz = y as f32 * CELL_SIZE;
leaf.mins[0] = fx as i16;
leaf.mins[1] = 0;
leaf.mins[2] = fz as i16;
leaf.maxs[0] = (fx + CELL_SIZE) as i16;
leaf.maxs[1] = WALL_HEIGHT as i16;
leaf.maxs[2] = (fz + CELL_SIZE) as i16;
if grid.get(x, y) == 0 {
leaf.contents = -2;
leaf.first_marksurface = cell_offset[c] as u16;
leaf.num_marksurfaces = faces_per_cell[c] as u16;
} else {
leaf.contents = -1;
leaf.first_marksurface = 0;
leaf.num_marksurfaces = 0;
}
leaf.visofs = -1;
}
}
let face_count_final = face_idx;
let mut ctx = BspBuildContext {
bsp,
grid,
node_count: 0,
plane_offset: face_count_final as u32,
};
build_bsp_node(&mut ctx, 0, 0, grid.width, grid.height, 0);
let node_count = ctx.node_count as usize;
let plane_count = ctx.plane_offset as usize;
ctx.bsp.nodes.truncate(node_count);
ctx.bsp.planes.truncate(plane_count);
let portals = build_cell_portals(grid);
build_pvs_data(ctx.bsp, &portals);
ctx.bsp.cell_portals = portals;
}
pub fn generate_rooms(params: &ProcgenParams) -> Bsp {
let mut rng = Rng::new(params.seed);
let mut grid = RoomGrid::new(params.width, params.height);
grid.fill(1);
let mut rooms: Vec<Bounds> = Vec::new();
let max_attempts = params.num_rooms * 10;
for _ in 0..max_attempts {
if rooms.len() >= params.num_rooms as usize {
break;
}
let w = params.min_room_size + (rng.next() % (params.max_room_size - params.min_room_size + 1) as u32) as i32;
let h = params.min_room_size + (rng.next() % (params.max_room_size - params.min_room_size + 1) as u32) as i32;
let x = 1 + (rng.next() % (params.width - w - 2) as u32) as i32;
let y = 1 + (rng.next() % (params.height - h - 2) as u32) as i32;
let new_room = Bounds { x, y, w, h };
let overlaps = rooms.iter().any(|r| new_room.intersects(r));
if !overlaps {
for ry in y..(y + h) {
for rx in x..(x + w) {
grid.set(rx, ry, 0);
}
}
if !rooms.is_empty() {
let prev = rooms.last().unwrap();
let new_cx = x + w / 2;
let new_cy = y + h / 2;
let prev_cx = prev.x + prev.w / 2;
let prev_cy = prev.y + prev.h / 2;
if rng.next() % 2 == 0 {
carve_corridor_h(&mut grid, prev_cx, new_cx, prev_cy);
carve_corridor_v(&mut grid, prev_cy, new_cy, new_cx);
} else {
carve_corridor_v(&mut grid, prev_cy, new_cy, prev_cx);
carve_corridor_h(&mut grid, prev_cx, new_cx, new_cy);
}
}
rooms.push(new_room);
}
}
let mut bsp = Bsp::new();
grid_to_bsp(&mut bsp, &grid);
let light_height = 80.0;
let lights: Vec<LightSource> = rooms.iter().map(|room| {
let cx = (room.x as f32 + room.w as f32 / 2.0) * CELL_SIZE;
let cy = (room.y as f32 + room.h as f32 / 2.0) * CELL_SIZE;
LightSource {
position: Vec3::new(cx, light_height, cy),
intensity: 0.8,
radius: 300.0,
}
}).collect();
compute_bsp_vertex_lighting(&mut bsp, &lights, 0.1);
bsp
}
pub fn generate(params: &ProcgenParams) -> Bsp {
generate_rooms(params)
}