stream world data from pxl8d to pxl8
This commit is contained in:
parent
39ee0fefb7
commit
a71a9840b2
55 changed files with 5290 additions and 2131 deletions
806
pxl8d/src/procgen.rs
Normal file
806
pxl8d/src/procgen.rs
Normal file
|
|
@ -0,0 +1,806 @@
|
|||
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)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue