fix(bsp): fill in exterior cells

This commit is contained in:
asrael 2026-03-07 07:19:40 -06:00
parent ab0d2a4b04
commit 1341b30920
8 changed files with 740 additions and 102 deletions

View file

@ -166,6 +166,10 @@ pub struct BspBuilder {
pub heightfield_ox: f32,
pub heightfield_oz: f32,
pub heightfield_cell_size: f32,
pub bounds_min_x: f32,
pub bounds_min_z: f32,
pub bounds_max_x: f32,
pub bounds_max_z: f32,
}
impl BspBuilder {
@ -225,6 +229,10 @@ impl From<BspBuilder> for Bsp {
heightfield_w: b.heightfield_w,
heightfield_h: b.heightfield_h,
visdata_size: visdata.len() as u32,
bounds_min_x: b.bounds_min_x,
bounds_min_z: b.bounds_min_z,
bounds_max_x: b.bounds_max_x,
bounds_max_z: b.bounds_max_z,
};
Self {

View file

@ -313,5 +313,11 @@ fn bsp_to_messages(bsp: &bsp::Bsp, cx: i32, cz: i32, version: u32) -> Vec<transp
data.extend_from_slice(&c_bsp.heightfield_cell_size.to_be_bytes());
}
let c_bsp = bsp.as_c_bsp();
data.extend_from_slice(&c_bsp.bounds_min_x.to_be_bytes());
data.extend_from_slice(&c_bsp.bounds_min_z.to_be_bytes());
data.extend_from_slice(&c_bsp.bounds_max_x.to_be_bytes());
data.extend_from_slice(&c_bsp.bounds_max_z.to_be_bytes());
transport::ChunkMessage::from_bsp(data, cx, cz, version)
}

View file

@ -199,6 +199,45 @@ fn carve_entries(grid: &mut RoomGrid, entries: &[ChunkEntry]) {
}
}
fn shrink_grid(grid: &RoomGrid, entries: &[ChunkEntry]) -> (RoomGrid, i32, i32) {
let (mut min_x, mut min_z, mut max_x, mut max_z) = (grid.width, grid.height, 0i32, 0i32);
for z in 0..grid.height {
for x in 0..grid.width {
if grid.get(x, z) == 0 {
if x < min_x { min_x = x; }
if x > max_x { max_x = x; }
if z < min_z { min_z = z; }
if z > max_z { max_z = z; }
}
}
}
min_x = (min_x - 1).max(0);
min_z = (min_z - 1).max(0);
max_x = (max_x + 1).min(grid.width - 1);
max_z = (max_z + 1).min(grid.height - 1);
for e in entries {
match e.edge {
ChunkEdge::West => min_x = 0,
ChunkEdge::East => max_x = grid.width - 1,
ChunkEdge::North => min_z = 0,
ChunkEdge::South => max_z = grid.height - 1,
}
}
let tw = max_x - min_x + 1;
let th = max_z - min_z + 1;
let mut tight = RoomGrid::new(tw, th);
for z in 0..th {
for x in 0..tw {
tight.set(x, z, grid.get(x + min_x, z + min_z));
}
}
(tight, min_x, min_z)
}
fn compute_face_aabb(face: &mut Face, verts: &[Vertex], vert_idx: usize) {
face.aabb_min = Vec3::new(1e30, 1e30, 1e30);
face.aabb_max = Vec3::new(-1e30, -1e30, -1e30);
@ -364,6 +403,8 @@ fn build_cell_portals(grid: &RoomGrid, origin_x: f32, origin_z: f32) -> Vec<Cell
}
}
portals
}
@ -790,6 +831,14 @@ struct BspMaterials {
const DUNGEON_MATS: BspMaterials = BspMaterials { floor: 0, wall: 1, trim: 3 };
const COURTYARD_MATS: BspMaterials = BspMaterials { floor: 4, wall: 5, trim: 6 };
fn is_adj_wall(grid: &RoomGrid, x: i32, y: i32) -> bool {
if x < 0 || x >= grid.width || y < 0 || y >= grid.height { return false; }
grid.get(x, y) == 1 && (
grid.get(x - 1, y) == 0 || grid.get(x + 1, y) == 0 ||
grid.get(x, y - 1) == 0 || grid.get(x, y + 1) == 0
)
}
fn grid_to_bsp(bsp: &mut BspBuilder, grid: &RoomGrid, doorways: &[Doorway], entries: &[ChunkEntry], mats: &BspMaterials, origin_x: f32, origin_z: f32) {
let mut wall_count = 0;
let mut floor_ceiling_count = 0;
@ -831,16 +880,29 @@ fn grid_to_bsp(bsp: &mut BspBuilder, grid: &RoomGrid, doorways: &[Doorway], entr
}
}
let mut solid_floor_count = 0;
let mut filler_floor_count = 0;
let mut ext_wall_count = 0;
let mut wall_top_count = 0;
for y in 0..grid.height {
for x in 0..grid.width {
if grid.get(x, y) == 1 {
solid_floor_count += 1;
let adj_room = grid.get(x-1,y)==0 || grid.get(x+1,y)==0
|| grid.get(x,y-1)==0 || grid.get(x,y+1)==0;
if adj_room {
wall_top_count += 1;
if grid.get(x-1,y) != 0 && !is_adj_wall(grid, x-1, y) { ext_wall_count += 1; }
if grid.get(x+1,y) != 0 && !is_adj_wall(grid, x+1, y) { ext_wall_count += 1; }
if grid.get(x,y-1) != 0 && !is_adj_wall(grid, x, y-1) { ext_wall_count += 1; }
if grid.get(x,y+1) != 0 && !is_adj_wall(grid, x, y+1) { ext_wall_count += 1; }
} else {
filler_floor_count += 1;
}
}
}
}
let face_count = wall_count * 2 + floor_ceiling_count + doorway_extra + partition_faces + solid_floor_count;
let face_count = wall_count * 2 + floor_ceiling_count + doorway_extra + partition_faces
+ filler_floor_count + ext_wall_count * 2 + wall_top_count;
let vertex_count = face_count * 4;
let total_cells = (grid.width * grid.height) as usize;
@ -1126,12 +1188,74 @@ fn grid_to_bsp(bsp: &mut BspBuilder, grid: &RoomGrid, doorways: &[Doorway], entr
for x in 0..grid.width {
if grid.get(x, y) == 1 {
let fx = origin_x + x as f32 * CELL_SIZE;
let fy = origin_z + y as f32 * CELL_SIZE;
let fz = origin_z + y as f32 * CELL_SIZE;
let cell_idx = (y * grid.width + x) as u32;
emit_quad(bsp, &mut face_cell, &mut vert_idx, &mut face_idx, &mut edge_idx,
[Vec3::new(fx, 0.0, fy), Vec3::new(fx, 0.0, fy + CELL_SIZE),
Vec3::new(fx + CELL_SIZE, 0.0, fy + CELL_SIZE), Vec3::new(fx + CELL_SIZE, 0.0, fy)],
up, 0.0, 7, cell_idx);
let adj_room = grid.get(x-1,y)==0 || grid.get(x+1,y)==0
|| grid.get(x,y-1)==0 || grid.get(x,y+1)==0;
if adj_room {
let room_ci = if grid.get(x-1,y)==0 { ((y)*grid.width+(x-1)) as u32 }
else if grid.get(x+1,y)==0 { ((y)*grid.width+(x+1)) as u32 }
else if grid.get(x,y-1)==0 { ((y-1)*grid.width+(x)) as u32 }
else { ((y+1)*grid.width+(x)) as u32 };
emit_quad(bsp, &mut face_cell, &mut vert_idx, &mut face_idx, &mut edge_idx,
[Vec3::new(fx, WALL_HEIGHT, fz), Vec3::new(fx + CELL_SIZE, WALL_HEIGHT, fz),
Vec3::new(fx + CELL_SIZE, WALL_HEIGHT, fz + CELL_SIZE), Vec3::new(fx, WALL_HEIGHT, fz + CELL_SIZE)],
up, WALL_HEIGHT, mats.wall, room_ci);
if grid.get(x-1,y) != 0 && !is_adj_wall(grid, x-1, y) {
let n = Vec3::new(-1.0, 0.0, 0.0);
emit_quad(bsp, &mut face_cell, &mut vert_idx, &mut face_idx, &mut edge_idx,
[Vec3::new(fx, TRIM_HEIGHT, fz + CELL_SIZE), Vec3::new(fx, TRIM_HEIGHT, fz),
Vec3::new(fx, WALL_HEIGHT, fz), Vec3::new(fx, WALL_HEIGHT, fz + CELL_SIZE)],
n, -fx, mats.wall, room_ci);
emit_quad(bsp, &mut face_cell, &mut vert_idx, &mut face_idx, &mut edge_idx,
[Vec3::new(fx, 0.0, fz + CELL_SIZE), Vec3::new(fx, 0.0, fz),
Vec3::new(fx, TRIM_HEIGHT, fz), Vec3::new(fx, TRIM_HEIGHT, fz + CELL_SIZE)],
n, -fx, mats.trim, room_ci);
}
if grid.get(x+1,y) != 0 && !is_adj_wall(grid, x+1, y) {
let wx = fx + CELL_SIZE;
let n = Vec3::new(1.0, 0.0, 0.0);
emit_quad(bsp, &mut face_cell, &mut vert_idx, &mut face_idx, &mut edge_idx,
[Vec3::new(wx, TRIM_HEIGHT, fz), Vec3::new(wx, TRIM_HEIGHT, fz + CELL_SIZE),
Vec3::new(wx, WALL_HEIGHT, fz + CELL_SIZE), Vec3::new(wx, WALL_HEIGHT, fz)],
n, wx, mats.wall, room_ci);
emit_quad(bsp, &mut face_cell, &mut vert_idx, &mut face_idx, &mut edge_idx,
[Vec3::new(wx, 0.0, fz), Vec3::new(wx, 0.0, fz + CELL_SIZE),
Vec3::new(wx, TRIM_HEIGHT, fz + CELL_SIZE), Vec3::new(wx, TRIM_HEIGHT, fz)],
n, wx, mats.trim, room_ci);
}
if grid.get(x,y-1) != 0 && !is_adj_wall(grid, x, y-1) {
let n = Vec3::new(0.0, 0.0, -1.0);
emit_quad(bsp, &mut face_cell, &mut vert_idx, &mut face_idx, &mut edge_idx,
[Vec3::new(fx + CELL_SIZE, TRIM_HEIGHT, fz), Vec3::new(fx, TRIM_HEIGHT, fz),
Vec3::new(fx, WALL_HEIGHT, fz), Vec3::new(fx + CELL_SIZE, WALL_HEIGHT, fz)],
n, -fz, mats.wall, room_ci);
emit_quad(bsp, &mut face_cell, &mut vert_idx, &mut face_idx, &mut edge_idx,
[Vec3::new(fx + CELL_SIZE, 0.0, fz), Vec3::new(fx, 0.0, fz),
Vec3::new(fx, TRIM_HEIGHT, fz), Vec3::new(fx + CELL_SIZE, TRIM_HEIGHT, fz)],
n, -fz, mats.trim, room_ci);
}
if grid.get(x,y+1) != 0 && !is_adj_wall(grid, x, y+1) {
let wz = fz + CELL_SIZE;
let n = Vec3::new(0.0, 0.0, 1.0);
emit_quad(bsp, &mut face_cell, &mut vert_idx, &mut face_idx, &mut edge_idx,
[Vec3::new(fx, TRIM_HEIGHT, wz), Vec3::new(fx + CELL_SIZE, TRIM_HEIGHT, wz),
Vec3::new(fx + CELL_SIZE, WALL_HEIGHT, wz), Vec3::new(fx, WALL_HEIGHT, wz)],
n, wz, mats.wall, room_ci);
emit_quad(bsp, &mut face_cell, &mut vert_idx, &mut face_idx, &mut edge_idx,
[Vec3::new(fx, 0.0, wz), Vec3::new(fx + CELL_SIZE, 0.0, wz),
Vec3::new(fx + CELL_SIZE, TRIM_HEIGHT, wz), Vec3::new(fx, TRIM_HEIGHT, wz)],
n, wz, mats.trim, room_ci);
}
} else {
emit_quad(bsp, &mut face_cell, &mut vert_idx, &mut face_idx, &mut edge_idx,
[Vec3::new(fx, 0.0, fz), Vec3::new(fx, 0.0, fz + CELL_SIZE),
Vec3::new(fx + CELL_SIZE, 0.0, fz + CELL_SIZE), Vec3::new(fx + CELL_SIZE, 0.0, fz)],
up, 0.0, 7, cell_idx);
}
}
}
}
@ -1181,13 +1305,16 @@ fn grid_to_bsp(bsp: &mut BspBuilder, grid: &RoomGrid, doorways: &[Doorway], entr
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 = cell_offset[c] as u16;
leaf.num_marksurfaces = faces_per_cell[c] as u16;
let adjacent_to_room =
grid.get(x - 1, y) == 0 ||
grid.get(x + 1, y) == 0 ||
grid.get(x, y - 1) == 0 ||
grid.get(x, y + 1) == 0;
leaf.contents = if adjacent_to_room { -1 } else { 0 };
}
leaf.first_marksurface = cell_offset[c] as u16;
leaf.num_marksurfaces = faces_per_cell[c] as u16;
leaf.visofs = -1;
}
}
@ -1274,10 +1401,10 @@ fn grid_to_bsp_exterior(bsp: &mut BspBuilder, grid: &RoomGrid, origin_x: f32, or
let wx = origin_x + vx as f32 * CELL_SIZE;
let wz = origin_z + vz as f32 * CELL_SIZE;
let h = unsafe {
crate::pxl8::pxl8_fbm(wx * 0.005, wz * 0.005, world_seed + 5000, 4)
crate::pxl8::pxl8_fbm(wx * 0.002, wz * 0.002, world_seed + 5000, 4)
};
let blend = edge_blend(wx, wz, world_seed);
corner_heights[vz as usize * vw + vx as usize] = h * 64.0 * blend;
corner_heights[vz as usize * vw + vx as usize] = h * 128.0 * blend;
}
}
@ -1286,6 +1413,28 @@ fn grid_to_bsp_exterior(bsp: &mut BspBuilder, grid: &RoomGrid, origin_x: f32, or
if border_south { border_face_count += grid.width; }
if border_west { border_face_count += grid.height; }
if border_east { border_face_count += grid.height; }
if border_north && border_west { border_face_count += 1; }
if border_north && border_east { border_face_count += 1; }
if border_south && border_west { border_face_count += 1; }
if border_south && border_east { border_face_count += 1; }
let bg_north = if border_north { generate_building_grid(cx, cz - 1, world_seed) } else { None };
let bg_south = if border_south { generate_building_grid(cx, cz + 1, world_seed) } else { None };
let bg_west = if border_west { generate_building_grid(cx - 1, cz, world_seed) } else { None };
let bg_east = if border_east { generate_building_grid(cx + 1, cz, world_seed) } else { None };
let mut building_face_count = 0usize;
for bg in [&bg_north, &bg_south, &bg_west, &bg_east] {
if let Some(g) = bg {
for y in 0..g.height {
for x in 0..g.width {
if g.get(x, y) != 0 && !is_adj_wall(g, x, y) {
building_face_count += 1;
}
}
}
}
}
let mut face_count = 0;
for y in 0..grid.height {
@ -1295,7 +1444,7 @@ fn grid_to_bsp_exterior(bsp: &mut BspBuilder, grid: &RoomGrid, origin_x: f32, or
}
}
}
face_count += border_face_count as usize;
face_count += border_face_count as usize + building_face_count;
let vertex_count = face_count * 4;
let max_nodes = 2 * total_cells + 1;
@ -1400,6 +1549,124 @@ fn grid_to_bsp_exterior(bsp: &mut BspBuilder, grid: &RoomGrid, origin_x: f32, or
}
}
if border_north && border_west {
let fx_outer = origin_x - CELL_SIZE;
let fx_inner = origin_x;
let fz_outer = origin_z - CELL_SIZE;
let fz_inner = origin_z;
let h = corner_heights[0];
let cell_idx = 0u32;
emit_quad(bsp, &mut face_cell, &mut vert_idx, &mut face_idx, &mut edge_idx,
[Vec3::new(fx_outer, 0.0, fz_outer), Vec3::new(fx_inner, 0.0, fz_outer),
Vec3::new(fx_inner, h, fz_inner), Vec3::new(fx_outer, 0.0, fz_inner)],
up, 0.0, 7, cell_idx);
}
if border_north && border_east {
let last_col = grid.width as usize;
let fx_inner = origin_x + grid.width as f32 * CELL_SIZE;
let fx_outer = fx_inner + CELL_SIZE;
let fz_outer = origin_z - CELL_SIZE;
let fz_inner = origin_z;
let h = corner_heights[last_col];
let cell_idx = (grid.width - 1) as u32;
emit_quad(bsp, &mut face_cell, &mut vert_idx, &mut face_idx, &mut edge_idx,
[Vec3::new(fx_inner, 0.0, fz_outer), Vec3::new(fx_outer, 0.0, fz_outer),
Vec3::new(fx_outer, 0.0, fz_inner), Vec3::new(fx_inner, h, fz_inner)],
up, 0.0, 7, cell_idx);
}
if border_south && border_west {
let last_row = grid.height as usize;
let fx_outer = origin_x - CELL_SIZE;
let fx_inner = origin_x;
let fz_inner = origin_z + grid.height as f32 * CELL_SIZE;
let fz_outer = fz_inner + CELL_SIZE;
let h = corner_heights[last_row * vw];
let cell_idx = ((grid.height - 1) * grid.width) as u32;
emit_quad(bsp, &mut face_cell, &mut vert_idx, &mut face_idx, &mut edge_idx,
[Vec3::new(fx_outer, 0.0, fz_inner), Vec3::new(fx_inner, h, fz_inner),
Vec3::new(fx_inner, 0.0, fz_outer), Vec3::new(fx_outer, 0.0, fz_outer)],
up, 0.0, 7, cell_idx);
}
if border_south && border_east {
let last_row = grid.height as usize;
let last_col = grid.width as usize;
let fx_inner = origin_x + grid.width as f32 * CELL_SIZE;
let fx_outer = fx_inner + CELL_SIZE;
let fz_inner = origin_z + grid.height as f32 * CELL_SIZE;
let fz_outer = fz_inner + CELL_SIZE;
let h = corner_heights[last_row * vw + last_col];
let cell_idx = ((grid.height - 1) * grid.width + grid.width - 1) as u32;
emit_quad(bsp, &mut face_cell, &mut vert_idx, &mut face_idx, &mut edge_idx,
[Vec3::new(fx_inner, h, fz_inner), Vec3::new(fx_outer, 0.0, fz_inner),
Vec3::new(fx_outer, 0.0, fz_outer), Vec3::new(fx_inner, 0.0, fz_outer)],
up, 0.0, 7, cell_idx);
}
if let Some(ref bg) = bg_north {
let bg_oz = (cz - 1) as f32 * chunk_size;
for by in 0..bg.height {
for bx in 0..bg.width {
if bg.get(bx, by) != 0 && !is_adj_wall(bg, bx, by) {
let fx = origin_x + bx as f32 * CELL_SIZE;
let fz = bg_oz + by as f32 * CELL_SIZE;
let cell_idx = bx as u32;
emit_quad(bsp, &mut face_cell, &mut vert_idx, &mut face_idx, &mut edge_idx,
[Vec3::new(fx, 0.0, fz), Vec3::new(fx + CELL_SIZE, 0.0, fz),
Vec3::new(fx + CELL_SIZE, 0.0, fz + CELL_SIZE), Vec3::new(fx, 0.0, fz + CELL_SIZE)],
up, 0.0, 7, cell_idx);
}
}
}
}
if let Some(ref bg) = bg_south {
let bg_oz = (cz + 1) as f32 * chunk_size;
for by in 0..bg.height {
for bx in 0..bg.width {
if bg.get(bx, by) != 0 && !is_adj_wall(bg, bx, by) {
let fx = origin_x + bx as f32 * CELL_SIZE;
let fz = bg_oz + by as f32 * CELL_SIZE;
let cell_idx = ((grid.height - 1) * grid.width + bx) as u32;
emit_quad(bsp, &mut face_cell, &mut vert_idx, &mut face_idx, &mut edge_idx,
[Vec3::new(fx, 0.0, fz), Vec3::new(fx + CELL_SIZE, 0.0, fz),
Vec3::new(fx + CELL_SIZE, 0.0, fz + CELL_SIZE), Vec3::new(fx, 0.0, fz + CELL_SIZE)],
up, 0.0, 7, cell_idx);
}
}
}
}
if let Some(ref bg) = bg_west {
let bg_ox = (cx - 1) as f32 * chunk_size;
for by in 0..bg.height {
for bx in 0..bg.width {
if bg.get(bx, by) != 0 && !is_adj_wall(bg, bx, by) {
let fx = bg_ox + bx as f32 * CELL_SIZE;
let fz = origin_z + by as f32 * CELL_SIZE;
let cell_idx = (by * grid.width) as u32;
emit_quad(bsp, &mut face_cell, &mut vert_idx, &mut face_idx, &mut edge_idx,
[Vec3::new(fx, 0.0, fz), Vec3::new(fx + CELL_SIZE, 0.0, fz),
Vec3::new(fx + CELL_SIZE, 0.0, fz + CELL_SIZE), Vec3::new(fx, 0.0, fz + CELL_SIZE)],
up, 0.0, 7, cell_idx);
}
}
}
}
if let Some(ref bg) = bg_east {
let bg_ox = (cx + 1) as f32 * chunk_size;
for by in 0..bg.height {
for bx in 0..bg.width {
if bg.get(bx, by) != 0 && !is_adj_wall(bg, bx, by) {
let fx = bg_ox + bx as f32 * CELL_SIZE;
let fz = origin_z + by as f32 * CELL_SIZE;
let cell_idx = (by * grid.width + grid.width - 1) as u32;
emit_quad(bsp, &mut face_cell, &mut vert_idx, &mut face_idx, &mut edge_idx,
[Vec3::new(fx, 0.0, fz), Vec3::new(fx + CELL_SIZE, 0.0, fz),
Vec3::new(fx + CELL_SIZE, 0.0, fz + CELL_SIZE), Vec3::new(fx, 0.0, fz + CELL_SIZE)],
up, 0.0, 7, cell_idx);
}
}
}
}
bsp.vertices.truncate(vert_idx);
bsp.faces.truncate(face_idx);
bsp.edges.truncate(edge_idx);
@ -1450,31 +1717,35 @@ fn grid_to_bsp_exterior(bsp: &mut BspBuilder, grid: &RoomGrid, origin_x: f32, or
}
if border_north {
let ext = if bg_north.is_some() { chunk_size } else { CELL_SIZE };
for x in 0..grid.width {
let c = (0 * grid.width + x) as usize;
let fz = origin_z - CELL_SIZE;
bsp.leafs[c].mins[2] = fz as i16;
bsp.leafs[c].mins[2] = (origin_z - ext) as i16;
if bg_north.is_some() { bsp.leafs[c].mins[1] = -64; }
}
}
if border_south {
let ext = if bg_south.is_some() { chunk_size } else { CELL_SIZE };
for x in 0..grid.width {
let c = ((grid.height - 1) * grid.width + x) as usize;
let fz = origin_z + (grid.height + 1) as f32 * CELL_SIZE;
bsp.leafs[c].maxs[2] = fz as i16;
bsp.leafs[c].maxs[2] = (origin_z + grid.height as f32 * CELL_SIZE + ext) as i16;
if bg_south.is_some() { bsp.leafs[c].mins[1] = -64; }
}
}
if border_west {
let ext = if bg_west.is_some() { chunk_size } else { CELL_SIZE };
for y in 0..grid.height {
let c = (y * grid.width + 0) as usize;
let fx = origin_x - CELL_SIZE;
bsp.leafs[c].mins[0] = fx as i16;
bsp.leafs[c].mins[0] = (origin_x - ext) as i16;
if bg_west.is_some() { bsp.leafs[c].mins[1] = -64; }
}
}
if border_east {
let ext = if bg_east.is_some() { chunk_size } else { CELL_SIZE };
for y in 0..grid.height {
let c = (y * grid.width + grid.width - 1) as usize;
let fx = origin_x + (grid.width + 1) as f32 * CELL_SIZE;
bsp.leafs[c].maxs[0] = fx as i16;
bsp.leafs[c].maxs[0] = (origin_x + grid.width as f32 * CELL_SIZE + ext) as i16;
if bg_east.is_some() { bsp.leafs[c].mins[1] = -64; }
}
}
@ -1508,7 +1779,7 @@ fn grid_to_bsp_exterior(bsp: &mut BspBuilder, grid: &RoomGrid, origin_x: f32, or
ctx.bsp.heightfield_cell_size = CELL_SIZE;
}
pub fn generate_rooms(params: &ProcgenParams, doorways: &[Doorway], entries: &[ChunkEntry]) -> Bsp {
fn generate_dungeon_grid(params: &ProcgenParams, doorways: &[Doorway], entries: &[ChunkEntry]) -> (RoomGrid, Vec<Bounds>) {
let mut rng = Rng::new(params.seed);
let mut grid = RoomGrid::new(params.width, params.height);
@ -1574,8 +1845,49 @@ pub fn generate_rooms(params: &ProcgenParams, doorways: &[Doorway], entries: &[C
carve_entries(&mut grid, entries);
(grid, rooms)
}
pub fn generate_rooms(params: &ProcgenParams, doorways: &[Doorway], entries: &[ChunkEntry], world_seed: u64) -> Bsp {
let (grid, rooms) = generate_dungeon_grid(params, doorways, entries);
let (tight, x_off, z_off) = shrink_grid(&grid, entries);
let tight_origin_x = params.origin_x + x_off as f32 * CELL_SIZE;
let tight_origin_z = params.origin_z + z_off as f32 * CELL_SIZE;
let tight_entries: Vec<ChunkEntry> = entries.iter().map(|e| {
let cell = match e.edge {
ChunkEdge::West | ChunkEdge::East => e.cell - z_off,
ChunkEdge::North | ChunkEdge::South => e.cell - x_off,
};
ChunkEntry { edge: e.edge, cell }
}).collect();
let mut bsp = BspBuilder::new();
grid_to_bsp(&mut bsp, &grid, doorways, entries, &DUNGEON_MATS, params.origin_x, params.origin_z);
grid_to_bsp(&mut bsp, &tight, doorways, &tight_entries, &DUNGEON_MATS, tight_origin_x, tight_origin_z);
bsp.bounds_min_x = tight_origin_x;
bsp.bounds_min_z = tight_origin_z;
bsp.bounds_max_x = tight_origin_x + tight.width as f32 * CELL_SIZE;
bsp.bounds_max_z = tight_origin_z + tight.height as f32 * CELL_SIZE;
let vw = 17usize;
let vh = 17usize;
bsp.heightfield = vec![0.0f32; vw * vh];
for vz in 0..vh as i32 {
for vx in 0..vw as i32 {
let wx = params.origin_x + vx as f32 * CELL_SIZE;
let wz = params.origin_z + vz as f32 * CELL_SIZE;
let h = unsafe { crate::pxl8::pxl8_fbm(wx * 0.002, wz * 0.002, world_seed + 5000, 4) };
let blend = edge_blend(wx, wz, world_seed);
bsp.heightfield[vz as usize * vw + vx as usize] = h * 128.0 * blend;
}
}
bsp.heightfield_w = 17;
bsp.heightfield_h = 17;
bsp.heightfield_ox = params.origin_x;
bsp.heightfield_oz = params.origin_z;
bsp.heightfield_cell_size = CELL_SIZE;
let light_height = 80.0;
@ -1603,7 +1915,7 @@ pub fn generate_rooms(params: &ProcgenParams, doorways: &[Doorway], entries: &[C
bsp.into()
}
pub fn generate_courtyard(params: &ProcgenParams, entries: &[ChunkEntry]) -> Bsp {
fn generate_courtyard_grid(params: &ProcgenParams, entries: &[ChunkEntry]) -> (RoomGrid, Vec<Bounds>) {
let mut rng = Rng::new(params.seed);
let mut grid = RoomGrid::new(params.width, params.height);
grid.fill(1);
@ -1701,8 +2013,49 @@ pub fn generate_courtyard(params: &ProcgenParams, entries: &[ChunkEntry]) -> Bsp
carve_entries(&mut grid, entries);
(grid, rooms)
}
pub fn generate_courtyard(params: &ProcgenParams, entries: &[ChunkEntry], world_seed: u64) -> Bsp {
let (grid, rooms) = generate_courtyard_grid(params, entries);
let (tight, x_off, z_off) = shrink_grid(&grid, entries);
let tight_origin_x = params.origin_x + x_off as f32 * CELL_SIZE;
let tight_origin_z = params.origin_z + z_off as f32 * CELL_SIZE;
let tight_entries: Vec<ChunkEntry> = entries.iter().map(|e| {
let cell = match e.edge {
ChunkEdge::West | ChunkEdge::East => e.cell - z_off,
ChunkEdge::North | ChunkEdge::South => e.cell - x_off,
};
ChunkEntry { edge: e.edge, cell }
}).collect();
let mut bsp = BspBuilder::new();
grid_to_bsp(&mut bsp, &grid, &[], entries, &COURTYARD_MATS, params.origin_x, params.origin_z);
grid_to_bsp(&mut bsp, &tight, &[], &tight_entries, &COURTYARD_MATS, tight_origin_x, tight_origin_z);
bsp.bounds_min_x = tight_origin_x;
bsp.bounds_min_z = tight_origin_z;
bsp.bounds_max_x = tight_origin_x + tight.width as f32 * CELL_SIZE;
bsp.bounds_max_z = tight_origin_z + tight.height as f32 * CELL_SIZE;
let vw = 17usize;
let vh = 17usize;
bsp.heightfield = vec![0.0f32; vw * vh];
for vz in 0..vh as i32 {
for vx in 0..vw as i32 {
let wx = params.origin_x + vx as f32 * CELL_SIZE;
let wz = params.origin_z + vz as f32 * CELL_SIZE;
let h = unsafe { crate::pxl8::pxl8_fbm(wx * 0.002, wz * 0.002, world_seed + 5000, 4) };
let blend = edge_blend(wx, wz, world_seed);
bsp.heightfield[vz as usize * vw + vx as usize] = h * 128.0 * blend;
}
}
bsp.heightfield_w = 17;
bsp.heightfield_h = 17;
bsp.heightfield_ox = params.origin_x;
bsp.heightfield_oz = params.origin_z;
bsp.heightfield_cell_size = CELL_SIZE;
let light_height = 80.0;
@ -1763,7 +2116,11 @@ pub fn generate_exterior(params: &ProcgenParams, world_seed: u64) -> Bsp {
let mut bsp = BspBuilder::new();
grid_to_bsp_exterior(&mut bsp, &grid, params.origin_x, params.origin_z, world_seed);
let lights = vec![LightSource {
let chunk_size = 16.0 * CELL_SIZE;
let cx = libm::floorf(params.origin_x / chunk_size) as i32;
let cz = libm::floorf(params.origin_z / chunk_size) as i32;
let mut lights = vec![LightSource {
position: Vec3::new(
params.origin_x + params.width as f32 * CELL_SIZE * 0.5,
200.0,
@ -1772,17 +2129,33 @@ pub fn generate_exterior(params: &ProcgenParams, world_seed: u64) -> Bsp {
intensity: 2.0,
radius: 2000.0,
}];
for &(dx, dz) in &[(0, -1), (0, 1), (-1, 0), (1, 0)] {
if select_biome(cx + dx, cz + dz, world_seed) != Biome::Exterior {
lights.push(LightSource {
position: Vec3::new(
(cx + dx) as f32 * chunk_size + chunk_size * 0.5,
200.0,
(cz + dz) as f32 * chunk_size + chunk_size * 0.5,
),
intensity: 2.0,
radius: 2000.0,
});
}
}
compute_bsp_vertex_lighting(&mut bsp, &lights);
bsp.into()
}
pub fn generate_chunk(cx: i32, cz: i32, world_seed: u64) -> Bsp {
fn generate_building_grid(cx: i32, cz: i32, world_seed: u64) -> Option<RoomGrid> {
let biome = select_biome(cx, cz, world_seed);
if biome == Biome::Exterior { return None; }
let seed = chunk_seed(world_seed, cx, cz);
let origin_x = cx as f32 * 16.0 * CELL_SIZE;
let origin_z = cz as f32 * 16.0 * CELL_SIZE;
let params = ProcgenParams {
seed,
origin_x,
@ -1790,6 +2163,18 @@ pub fn generate_chunk(cx: i32, cz: i32, world_seed: u64) -> Bsp {
..Default::default()
};
let (doorways, entries) = get_chunk_config(cx, cz);
let (grid, _) = match biome {
Biome::Dungeon => generate_dungeon_grid(&params, &doorways, &entries),
Biome::Courtyard => generate_courtyard_grid(&params, &entries),
_ => return None,
};
Some(grid)
}
fn get_chunk_config(cx: i32, cz: i32) -> (Vec<Doorway>, Vec<ChunkEntry>) {
let mut doorways = Vec::new();
let mut entries = Vec::new();
@ -1820,9 +2205,27 @@ pub fn generate_chunk(cx: i32, cz: i32, world_seed: u64) -> Bsp {
entries.push(ChunkEntry { edge: ChunkEdge::South, cell: 8 });
}
(doorways, entries)
}
pub fn generate_chunk(cx: i32, cz: i32, world_seed: u64) -> Bsp {
let biome = select_biome(cx, cz, world_seed);
let seed = chunk_seed(world_seed, cx, cz);
let origin_x = cx as f32 * 16.0 * CELL_SIZE;
let origin_z = cz as f32 * 16.0 * CELL_SIZE;
let params = ProcgenParams {
seed,
origin_x,
origin_z,
..Default::default()
};
let (doorways, entries) = get_chunk_config(cx, cz);
match biome {
Biome::Dungeon => generate_rooms(&params, &doorways, &entries),
Biome::Courtyard => generate_courtyard(&params, &entries),
Biome::Dungeon => generate_rooms(&params, &doorways, &entries, world_seed),
Biome::Courtyard => generate_courtyard(&params, &entries, world_seed),
Biome::Exterior => generate_exterior(&params, world_seed),
}
}