2026-01-25 09:26:30 -06:00
|
|
|
extern crate alloc;
|
|
|
|
|
|
|
|
|
|
use alloc::vec;
|
|
|
|
|
use alloc::vec::Vec;
|
|
|
|
|
use libm::sqrtf;
|
|
|
|
|
|
2026-01-31 09:31:17 -06:00
|
|
|
use crate::bsp::{Bsp, BspBuilder, CellPortals, Edge, Face, Leaf, Node, Plane, Portal, Vertex};
|
|
|
|
|
use crate::math::{Vec3, Vec3Ext};
|
2026-01-25 09:26:30 -06:00
|
|
|
|
|
|
|
|
pub const CELL_SIZE: f32 = 64.0;
|
|
|
|
|
pub const WALL_HEIGHT: f32 = 128.0;
|
2026-01-31 09:31:17 -06:00
|
|
|
pub const TRIM_HEIGHT: f32 = 12.0;
|
2026-01-25 09:26:30 -06:00
|
|
|
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> {
|
2026-01-31 09:31:17 -06:00
|
|
|
bsp: &'a mut BspBuilder,
|
2026-01-25 09:26:30 -06:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-31 09:31:17 -06:00
|
|
|
fn compute_face_aabb(face: &mut Face, verts: &[Vertex], vert_idx: usize) {
|
2026-01-25 09:26:30 -06:00
|
|
|
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; }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-31 09:31:17 -06:00
|
|
|
fn build_bsp_node_grid(ctx: &mut BspBuildContext, x0: i32, y0: i32, x1: i32, y1: i32, depth: i32) -> i32 {
|
2026-01-25 09:26:30 -06:00
|
|
|
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;
|
|
|
|
|
|
2026-01-31 09:31:17 -06:00
|
|
|
ctx.bsp.planes[plane_idx as usize] = Plane {
|
2026-01-25 09:26:30 -06:00
|
|
|
normal: Vec3::new(1.0, 0.0, 0.0),
|
|
|
|
|
dist: split_pos,
|
2026-01-31 09:31:17 -06:00
|
|
|
type_: 0,
|
2026-01-25 09:26:30 -06:00
|
|
|
};
|
|
|
|
|
|
2026-01-31 09:31:17 -06:00
|
|
|
let child0 = build_bsp_node_grid(ctx, mid_x, y0, x1, y1, depth + 1);
|
|
|
|
|
let child1 = build_bsp_node_grid(ctx, x0, y0, mid_x, y1, depth + 1);
|
2026-01-25 09:26:30 -06:00
|
|
|
|
2026-01-31 09:31:17 -06:00
|
|
|
ctx.bsp.nodes[node_idx as usize] = Node {
|
2026-01-25 09:26:30 -06:00
|
|
|
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;
|
|
|
|
|
|
2026-01-31 09:31:17 -06:00
|
|
|
ctx.bsp.planes[plane_idx as usize] = Plane {
|
2026-01-25 09:26:30 -06:00
|
|
|
normal: Vec3::new(0.0, 0.0, 1.0),
|
|
|
|
|
dist: split_pos,
|
2026-01-31 09:31:17 -06:00
|
|
|
type_: 0,
|
2026-01-25 09:26:30 -06:00
|
|
|
};
|
|
|
|
|
|
2026-01-31 09:31:17 -06:00
|
|
|
let child0 = build_bsp_node_grid(ctx, x0, mid_y, x1, y1, depth + 1);
|
|
|
|
|
let child1 = build_bsp_node_grid(ctx, x0, y0, x1, mid_y, depth + 1);
|
2026-01-25 09:26:30 -06:00
|
|
|
|
2026-01-31 09:31:17 -06:00
|
|
|
ctx.bsp.nodes[node_idx as usize] = Node {
|
2026-01-25 09:26:30 -06:00
|
|
|
plane_id: plane_idx,
|
|
|
|
|
children: [child0, child1],
|
|
|
|
|
..Default::default()
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
node_idx as i32
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-31 09:31:17 -06:00
|
|
|
fn build_bsp_node(ctx: &mut BspBuildContext, x0: i32, y0: i32, x1: i32, y1: i32, depth: i32, floor_leaf_idx: i32) -> i32 {
|
|
|
|
|
let node_idx = ctx.node_count;
|
|
|
|
|
ctx.node_count += 1;
|
|
|
|
|
|
|
|
|
|
let plane_idx = ctx.plane_offset;
|
|
|
|
|
ctx.plane_offset += 1;
|
|
|
|
|
|
|
|
|
|
ctx.bsp.planes[plane_idx as usize] = Plane {
|
|
|
|
|
normal: Vec3::new(0.0, 1.0, 0.0),
|
|
|
|
|
dist: 0.0,
|
|
|
|
|
type_: 1,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let above_floor = build_bsp_node_grid(ctx, x0, y0, x1, y1, depth);
|
|
|
|
|
|
|
|
|
|
ctx.bsp.nodes[node_idx as usize] = Node {
|
|
|
|
|
plane_id: plane_idx,
|
|
|
|
|
children: [above_floor, -(floor_leaf_idx + 1)],
|
|
|
|
|
..Default::default()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
node_idx as i32
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn build_cell_portals(grid: &RoomGrid) -> Vec<CellPortals> {
|
2026-01-25 09:26:30 -06:00
|
|
|
let total_cells = (grid.width * grid.height) as usize;
|
2026-01-31 09:31:17 -06:00
|
|
|
let mut portals = vec![CellPortals::default(); total_cells];
|
2026-01-25 09:26:30 -06:00
|
|
|
|
|
|
|
|
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;
|
2026-01-31 09:31:17 -06:00
|
|
|
p.portals[idx] = Portal {
|
2026-01-25 09:26:30 -06:00
|
|
|
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;
|
2026-01-31 09:31:17 -06:00
|
|
|
p.portals[idx] = Portal {
|
2026-01-25 09:26:30 -06:00
|
|
|
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;
|
2026-01-31 09:31:17 -06:00
|
|
|
p.portals[idx] = Portal {
|
2026-01-25 09:26:30 -06:00
|
|
|
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;
|
2026-01-31 09:31:17 -06:00
|
|
|
p.portals[idx] = Portal {
|
2026-01-25 09:26:30 -06:00
|
|
|
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,
|
2026-01-31 09:31:17 -06:00
|
|
|
portals: &[CellPortals],
|
|
|
|
|
leafs: &[Leaf],
|
2026-01-25 09:26:30 -06:00
|
|
|
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,
|
2026-01-31 09:31:17 -06:00
|
|
|
portals: &[CellPortals],
|
|
|
|
|
leafs: &[Leaf],
|
2026-01-25 09:26:30 -06:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-31 09:31:17 -06:00
|
|
|
fn build_pvs_data(bsp: &mut BspBuilder, portals: &[CellPortals]) {
|
2026-01-25 09:26:30 -06:00
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-31 09:31:17 -06:00
|
|
|
fn compute_bsp_vertex_lighting(bsp: &mut BspBuilder, lights: &[LightSource], ambient: f32) {
|
2026-01-25 09:26:30 -06:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-31 09:31:17 -06:00
|
|
|
fn grid_to_bsp(bsp: &mut BspBuilder, grid: &RoomGrid) {
|
|
|
|
|
let mut wall_count = 0;
|
2026-01-25 09:26:30 -06:00
|
|
|
let mut floor_ceiling_count = 0;
|
|
|
|
|
|
|
|
|
|
for y in 0..grid.height {
|
|
|
|
|
for x in 0..grid.width {
|
|
|
|
|
if grid.get(x, y) == 0 {
|
2026-01-31 09:31:17 -06:00
|
|
|
if grid.get(x - 1, y) == 1 { wall_count += 1; }
|
|
|
|
|
if grid.get(x + 1, y) == 1 { wall_count += 1; }
|
|
|
|
|
if grid.get(x, y - 1) == 1 { wall_count += 1; }
|
|
|
|
|
if grid.get(x, y + 1) == 1 { wall_count += 1; }
|
2026-01-25 09:26:30 -06:00
|
|
|
floor_ceiling_count += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-31 09:31:17 -06:00
|
|
|
let face_count = wall_count * 2 + floor_ceiling_count;
|
2026-01-25 09:26:30 -06:00
|
|
|
let vertex_count = face_count * 4;
|
|
|
|
|
|
|
|
|
|
let total_cells = (grid.width * grid.height) as usize;
|
2026-01-31 09:31:17 -06:00
|
|
|
let max_nodes = 2 * total_cells + 1;
|
|
|
|
|
let total_planes = face_count + max_nodes + 1;
|
2026-01-25 09:26:30 -06:00
|
|
|
|
2026-01-31 09:31:17 -06:00
|
|
|
bsp.vertices = vec![Vertex::default(); vertex_count];
|
|
|
|
|
bsp.faces = vec![Face::default(); face_count];
|
|
|
|
|
bsp.planes = vec![Plane::default(); total_planes];
|
|
|
|
|
bsp.edges = vec![Edge::default(); vertex_count];
|
2026-01-25 09:26:30 -06:00
|
|
|
bsp.surfedges = vec![0i32; vertex_count];
|
2026-01-31 09:31:17 -06:00
|
|
|
bsp.nodes = vec![Node::default(); max_nodes];
|
2026-01-25 09:26:30 -06:00
|
|
|
|
|
|
|
|
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 {
|
2026-01-31 09:31:17 -06:00
|
|
|
bsp.vertices[vert_idx + 0].position = Vec3::new(fx, TRIM_HEIGHT, fy);
|
2026-01-25 09:26:30 -06:00
|
|
|
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);
|
2026-01-31 09:31:17 -06:00
|
|
|
bsp.vertices[vert_idx + 3].position = Vec3::new(fx, TRIM_HEIGHT, 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 = 1;
|
|
|
|
|
|
|
|
|
|
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[vert_idx + 0].position = Vec3::new(fx, 0.0, fy);
|
|
|
|
|
bsp.vertices[vert_idx + 1].position = Vec3::new(fx, TRIM_HEIGHT, fy);
|
|
|
|
|
bsp.vertices[vert_idx + 2].position = Vec3::new(fx, TRIM_HEIGHT, fy + CELL_SIZE);
|
2026-01-25 09:26:30 -06:00
|
|
|
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;
|
2026-01-31 09:31:17 -06:00
|
|
|
bsp.faces[face_idx].material_id = 3;
|
2026-01-25 09:26:30 -06:00
|
|
|
|
|
|
|
|
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 {
|
2026-01-31 09:31:17 -06:00
|
|
|
bsp.vertices[vert_idx + 0].position = Vec3::new(fx + CELL_SIZE, TRIM_HEIGHT, fy);
|
|
|
|
|
bsp.vertices[vert_idx + 1].position = Vec3::new(fx + CELL_SIZE, TRIM_HEIGHT, fy + CELL_SIZE);
|
2026-01-25 09:26:30 -06:00
|
|
|
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;
|
2026-01-31 09:31:17 -06:00
|
|
|
bsp.faces[face_idx].material_id = 1;
|
|
|
|
|
|
|
|
|
|
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[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, TRIM_HEIGHT, fy + CELL_SIZE);
|
|
|
|
|
bsp.vertices[vert_idx + 3].position = Vec3::new(fx + CELL_SIZE, TRIM_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 = 3;
|
2026-01-25 09:26:30 -06:00
|
|
|
|
|
|
|
|
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 {
|
2026-01-31 09:31:17 -06:00
|
|
|
bsp.vertices[vert_idx + 0].position = Vec3::new(fx, TRIM_HEIGHT, fy);
|
|
|
|
|
bsp.vertices[vert_idx + 1].position = Vec3::new(fx + CELL_SIZE, TRIM_HEIGHT, fy);
|
2026-01-25 09:26:30 -06:00
|
|
|
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;
|
2026-01-31 09:31:17 -06:00
|
|
|
bsp.faces[face_idx].material_id = 1;
|
|
|
|
|
|
|
|
|
|
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[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, TRIM_HEIGHT, fy);
|
|
|
|
|
bsp.vertices[vert_idx + 3].position = Vec3::new(fx, TRIM_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 = 3;
|
2026-01-25 09:26:30 -06:00
|
|
|
|
|
|
|
|
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 {
|
2026-01-31 09:31:17 -06:00
|
|
|
bsp.vertices[vert_idx + 0].position = Vec3::new(fx, TRIM_HEIGHT, fy + CELL_SIZE);
|
2026-01-25 09:26:30 -06:00
|
|
|
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);
|
2026-01-31 09:31:17 -06:00
|
|
|
bsp.vertices[vert_idx + 3].position = Vec3::new(fx + CELL_SIZE, TRIM_HEIGHT, 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 = 1;
|
|
|
|
|
|
|
|
|
|
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[vert_idx + 0].position = Vec3::new(fx, 0.0, fy + CELL_SIZE);
|
|
|
|
|
bsp.vertices[vert_idx + 1].position = Vec3::new(fx, TRIM_HEIGHT, fy + CELL_SIZE);
|
|
|
|
|
bsp.vertices[vert_idx + 2].position = Vec3::new(fx + CELL_SIZE, TRIM_HEIGHT, fy + CELL_SIZE);
|
2026-01-25 09:26:30 -06:00
|
|
|
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;
|
2026-01-31 09:31:17 -06:00
|
|
|
bsp.faces[face_idx].material_id = 3;
|
2026-01-25 09:26:30 -06:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
2026-01-31 09:31:17 -06:00
|
|
|
bsp.leafs = vec![Leaf::default(); total_cells + 1];
|
2026-01-25 09:26:30 -06:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-31 09:31:17 -06:00
|
|
|
let floor_leaf_idx = total_cells as i32;
|
|
|
|
|
let floor_leaf = &mut bsp.leafs[total_cells];
|
|
|
|
|
floor_leaf.contents = -1;
|
|
|
|
|
floor_leaf.mins = [0, i16::MIN, 0];
|
|
|
|
|
floor_leaf.maxs = [
|
|
|
|
|
(grid.width as f32 * CELL_SIZE) as i16,
|
|
|
|
|
0,
|
|
|
|
|
(grid.height as f32 * CELL_SIZE) as i16,
|
|
|
|
|
];
|
|
|
|
|
floor_leaf.visofs = -1;
|
|
|
|
|
|
2026-01-25 09:26:30 -06:00
|
|
|
let face_count_final = face_idx;
|
|
|
|
|
let mut ctx = BspBuildContext {
|
|
|
|
|
bsp,
|
|
|
|
|
grid,
|
|
|
|
|
node_count: 0,
|
|
|
|
|
plane_offset: face_count_final as u32,
|
|
|
|
|
};
|
|
|
|
|
|
2026-01-31 09:31:17 -06:00
|
|
|
build_bsp_node(&mut ctx, 0, 0, grid.width, grid.height, 0, floor_leaf_idx);
|
2026-01-25 09:26:30 -06:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-31 09:31:17 -06:00
|
|
|
let mut bsp = BspBuilder::new();
|
2026-01-25 09:26:30 -06:00
|
|
|
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);
|
|
|
|
|
|
2026-01-31 09:31:17 -06:00
|
|
|
bsp.into()
|
2026-01-25 09:26:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn generate(params: &ProcgenParams) -> Bsp {
|
|
|
|
|
generate_rooms(params)
|
|
|
|
|
}
|