Skip to content

Commit

Permalink
Merge branch 'broken'
Browse files Browse the repository at this point in the history
  • Loading branch information
cmbasnett committed Apr 18, 2024
2 parents 5fad564 + 8a782a8 commit 753db59
Show file tree
Hide file tree
Showing 7 changed files with 580 additions and 396 deletions.
8 changes: 5 additions & 3 deletions src/brush.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
use crate::bsp::ECsgOper;
use crate::fpoly::EPolyFlags;
use crate::model::UModel;
use crate::math::FVector;
use crate::coords::{FCoords, FModelCoords};
use std::rc::Rc;
use std::cell::RefCell;

#[derive(Debug, PartialEq)]
pub struct ABrush {
pub model: Rc<RefCell<UModel>>,
pub model: UModel,
pub location: FVector,
pub pre_pivot: FVector,
pub poly_flags: EPolyFlags,
pub csg_operation: ECsgOper,
}

impl ABrush {
Expand Down
742 changes: 388 additions & 354 deletions src/bsp.rs

Large diffs are not rendered by default.

35 changes: 35 additions & 0 deletions src/csg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use crate::bsp::{bsp_brush_csg, ECsgOper};
use crate::fpoly::EPolyFlags;
use crate::model::UModel;
use crate::brush::ABrush;

struct ULevel {
model: UModel,
brushes: Vec<ABrush>,
}

impl ULevel {
pub fn new() -> Self {
ULevel {
model: UModel::new(),
brushes: Vec::new(),
}
}
}

fn csg_rebuild(level: &mut ULevel) {
// Empty the model out.
level.model.empty_model(true, true);

// Compose all structural brushes and portals.
for brush in &level.brushes {
if !brush.poly_flags.contains(EPolyFlags::Semisolid) || brush.csg_operation != ECsgOper::Add || brush.poly_flags.contains(EPolyFlags::Portal) {
// Treat portals as solids for cutting.
let mut poly_flags = brush.poly_flags;
if poly_flags.contains(EPolyFlags::Portal) {
poly_flags = (poly_flags & !EPolyFlags::Semisolid) | EPolyFlags::NotSolid;
bsp_brush_csg(brush, &mut level.model, poly_flags, brush.csg_operation, false);
}
}
}
}
8 changes: 7 additions & 1 deletion src/fpoly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ impl FPoly {
pub fn from_vertices(vertices: &[FVector]) -> Self {
let mut fpoly = FPoly::new();
_ = fpoly.vertices.try_extend_from_slice(vertices);
// fpoly.base = fpoly.vertices[0]; // TODO: the selection of the base vertex seems arbitrary.
// fpoly.base = fpoly.vertices[0]; // TODO: the selection of the base vertex seems arbitrary
_ = fpoly.calc_normal();
fpoly
}
Expand Down Expand Up @@ -202,6 +202,12 @@ impl FPoly {
self.vertices.reverse()
}

pub fn reversed(&self) -> FPoly {
let mut fpoly = self.clone();
fpoly.reverse();
fpoly
}

/// Fix up an editor poly by deleting vertices that are identical. Sets
/// vertex count to zero if it collapses. Returns number of vertices, 0 or >=3.
pub fn fix(&mut self) -> usize {
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub mod box_;
pub mod sphere;
pub mod coords;
pub mod brush;
pub mod csg;

use pyo3::prelude::*;
use crate::fpoly::FPoly;
Expand Down
41 changes: 31 additions & 10 deletions src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,23 @@ bitflags! {
/// same plane may reference the same poly.
#[derive(Debug, PartialEq)]
pub struct FBspSurf {
//pub material: Rc<UMaterial>,
/// Polygon flags.
pub poly_flags: EPolyFlags,
/// Polygon & texture base point index (where U,V==0,0).
pub base_point_index: usize,
/// Index to polygon normal.
pub normal_index: usize,
/// Texture U-vector index.
pub texture_u_index: usize,
/// Texture V-vector index.
pub texture_v_index: usize,
/// Editor brush polygon index.
pub brush_polygon_index: Option<usize>,
//pub actor: Rc<ABrush>,
pub node_indices: Vec<usize>, // TODO: what's this one??
/// Nodes which make up this surface
pub node_indices: Vec<usize>,
/// The plane this surface lies on.
pub plane: FPlane,
/// The number of units/lightmap texel on this surface.
pub light_map_scale: f32
}

Expand Down Expand Up @@ -99,7 +106,7 @@ pub const BSP_NODE_MAX_ZONES: usize = 64;
/// If iPlane==INDEX_NONE, a node has no coplanars. Otherwise iPlane
/// is an index to a coplanar polygon in the Bsp. All polygons that are iPlane
/// children can only have iPlane children themselves, not fronts or backs.
#[derive(Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq)]
pub struct FBspNode {
/// Plane the node falls into (X, Y, Z, W).
pub plane: FPlane,
Expand Down Expand Up @@ -134,8 +141,11 @@ pub struct FBspNode {
/// Leaf in back and front, INDEX_NONE = NOT A LEAF.
pub leaf_indices: [Option<usize>;2],

/// Index into array of sections.
pub section_index: Option<usize>,
/// The first vertex in the section which this node uses.
pub first_vertex_index: usize,
/// The lightmap that this node uses.
pub light_map_index: Option<usize>,
}

Expand Down Expand Up @@ -181,7 +191,9 @@ impl FBspNode {
/// other sides in the level which are cospatial with this side.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct FVert {
/// Index of vertex.
pub vertex_index: usize,
/// If shared, index of unique side. Otherwise INDEX_NONE.
pub side_index: Option<usize>,
}

Expand Down Expand Up @@ -216,24 +228,33 @@ pub struct FZoneProperties {

#[derive(Debug, PartialEq)]
pub struct UModel {
pub polys: Vec<FPoly>,

// BSP data.
pub vertices: Vec<FVert>,
pub points: Vec<FVector>,
pub vectors: Vec<FVector>,
pub nodes: Vec<FBspNode>,
pub surfaces: Vec<FBspSurf>,
pub polys: Vec<FPoly>,
pub bounds: Vec<FBox>,
pub leaf_hulls: Vec<usize>,
pub leaves: Vec<FLeaf>,
pub zones: Vec<FZoneProperties>,
pub bounding_sphere: FSphere,
pub bounding_box: FBox,

pub linked: bool,
pub is_root_outside: bool,
}

impl UModel {
pub fn new_from_polys(polys: &[FPoly]) -> UModel {
let mut model = UModel::new();
for poly in polys {
model.polys.push(poly.clone());
}
model
}

pub fn new() -> UModel {
UModel {
vertices: Vec::new(),
Expand All @@ -249,7 +270,7 @@ impl UModel {
bounding_sphere: FSphere::default(),
bounding_box: FBox::default(),
linked: false,
is_root_outside: true
is_root_outside: true, // TODO: what even is this?
}
}

Expand Down Expand Up @@ -335,8 +356,8 @@ impl UModel {

fn find_nearest_vertex_recursive(&self, source_point: FVector, dest_point: &mut FVector, mut min_radius: f32, node_index: Option<usize>, vertex_index: &mut usize) -> f32 {
let mut result_radius = -1.0f32;

let mut next_node_index = node_index;

while let Some(node_index) = next_node_index {
let node = &self.nodes[node_index];
let back_index = node.back_node_index;
Expand Down Expand Up @@ -410,8 +431,8 @@ impl UModel {
// Found an existing point.
vertex_index
} else {
let fast_rebuild = false;
add_thing(&mut self.points, v, thresh, fast_rebuild)
// No match found; add it slowly to find duplicates.
add_thing(&mut self.points, v, thresh, false)
}
}

Expand Down
141 changes: 113 additions & 28 deletions tests/bsp_tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use bdk_py::brush::ABrush;
use bdk_py::math::FVector;
use bdk_py::bsp::{EBspOptimization, ENodePlace, merge_coplanars, try_to_merge, bsp_add_node, find_best_split, bsp_brush_csg};
use bdk_py::bsp::{EBspOptimization, ENodePlace, merge_coplanars, try_to_merge, bsp_add_node, find_best_split, bsp_brush_csg, bsp_validate_brush};
use bdk_py::fpoly::{EPolyFlags, FPoly};
use bdk_py::model::{EBspNodeFlags, UModel};

Expand Down Expand Up @@ -78,18 +79,7 @@ fn try_to_merge_identical_triangles_test() {
}

/// Creates a unit cube with 6 polygons, with normals pointing outwards.
fn create_unit_cube_polys() -> [FPoly; 6] {
// Diagram of a unit cube:
//
// 7--------6
// /| /|
// / | / |
// 4--------5 |
// | 3-----|--2
// | / | /
// |/ |/
// 0--------1
//
fn create_cube_polys(min: FVector, extents: FVector) -> Vec<FPoly> {
let vertices = vec![
FVector::new(0.0, 0.0, 0.0),
FVector::new(1.0, 0.0, 0.0),
Expand All @@ -100,15 +90,42 @@ fn create_unit_cube_polys() -> [FPoly; 6] {
FVector::new(1.0, 1.0, 1.0),
FVector::new(0.0, 1.0, 1.0),
];
// TODO: not sure about the winding order of the vertices.
let polys: [FPoly; 6] = [
FPoly::from_vertices(&vec![vertices[0], vertices[1], vertices[2], vertices[3]]),
FPoly::from_vertices(&vec![vertices[4], vertices[7], vertices[6], vertices[5]]),
FPoly::from_vertices(&vec![vertices[0], vertices[1], vertices[5], vertices[4]]),
FPoly::from_vertices(&vec![vertices[3], vertices[7], vertices[6], vertices[2]]),
FPoly::from_vertices(&vec![vertices[0], vertices[4], vertices[7], vertices[3]]),
FPoly::from_vertices(&vec![vertices[1], vertices[2], vertices[6], vertices[5]]),

// Apply the min and extents to the vertices.
let vertices = vertices.iter().map(|v| {
FVector::new(
min.x + v.x * extents.x,
min.y + v.y * extents.y,
min.z + v.z * extents.z,
)
}).collect::<Vec<FVector>>();

// Diagram of a unit cube:
//
// 7--------6
// /| /|
// / | / |
// 4--------5 |
// | 3-----|--2
// | / | /
// |/ |/
// 0--------1
let mut poly_vertex_indices: [Vec<usize>; 6] = [
vec![0, 1, 2, 3],
vec![4, 7, 6, 5],
vec![0, 4, 5, 1],
vec![2, 6, 7, 3],
vec![3, 7, 4, 0],
vec![1, 5, 6, 2],
];
// // Reverse the order of all the faces to flip the normals.
for indices in poly_vertex_indices.iter_mut() {
indices.reverse();
}

let polys = poly_vertex_indices.iter().map(|indices| {
FPoly::from_vertices(&indices.iter().map(|i| vertices[*i]).collect::<Vec<FVector>>())
}).collect::<Vec<FPoly>>();

polys
}
Expand Down Expand Up @@ -230,11 +247,13 @@ fn find_best_split_test() {
#[test]
fn bsp_add_node_root_node() {
let mut model = UModel::new();
let poly = FPoly::from_vertices(&vec![
let mut poly = FPoly::from_vertices(&vec![
FVector::new(0.0, 0.0, 0.0),
FVector::new(1.0, 0.0, 0.0),
FVector::new(1.0, 1.0, 0.0),
]);
poly.link = Some(0); // TODO: this sucks. should we default it to 0?

bsp_add_node(&mut model,
0,
ENodePlace::Root,
Expand All @@ -245,16 +264,82 @@ fn bsp_add_node_root_node() {
assert!(model.nodes.len() == 1);
}

// #[test]
fn output_obj(model: &UModel) {
// Output the surfaces to an OBJ file for debugging.
for point in &model.points {
println!("v {} {} {}", point.x, point.y, point.z);
}

for node in &model.nodes {
if node.vertex_count == 0 {
continue;
}

let vertices = &model.vertices[node.vertex_pool_index..node.vertex_pool_index + node.vertex_count];
let point_indices = vertices.iter().map(|vertex| vertex.vertex_index).collect::<Vec<usize>>();
let face_indices = &point_indices.iter().map(|i| (i + 1).to_string()).collect::<Vec<String>>().join(" ");
println!("f {}", face_indices);
}
}

#[test]
fn bsp_brush_subtract_and_add_test() {
// Arrange
let mut model = UModel::new();

// Create the main subtraction brush.
let polys = create_cube_polys(FVector::new(0.0, 0.0, 0.0), FVector::new(1.0, 1.0, 1.0));
let mut subtraction_brush = ABrush {
model: UModel::new_from_polys(&polys),
location: FVector::new(0.0, 0.0, 0.0),
pre_pivot: FVector::new(0.0, 0.0, 0.0),
csg_operation: bdk_py::bsp::ECsgOper::Subtract,
poly_flags: EPolyFlags::empty(),
};
bsp_validate_brush(&mut subtraction_brush.model, false);

// Create a smaller additive brush inside the subtraction brush.
let polys = create_cube_polys(FVector::new(0.0, 0.0, 0.0), FVector::new(0.5, 0.5, 0.5));
let mut addition_brush = ABrush {
model: UModel::new_from_polys(&polys),
location: FVector::new(0.9, 0.5, 0.5),
pre_pivot: FVector::new(0.0, 0.0, 0.0),
csg_operation: bdk_py::bsp::ECsgOper::Add,
poly_flags: EPolyFlags::empty(),
};
bsp_validate_brush(&mut addition_brush.model, false);

// Act
model.is_root_outside = false;

bsp_brush_csg(&subtraction_brush, &mut model, EPolyFlags::empty(), bdk_py::bsp::ECsgOper::Subtract, false);

model.is_root_outside = true;

bsp_brush_csg(&addition_brush, &mut model, EPolyFlags::empty(), bdk_py::bsp::ECsgOper::Add, false);

output_obj(&model);
}

#[test]
fn bsp_brush_csg_subtract_test() {
// Arrange
let polys = create_unit_cube_polys();
let mut model = UModel::new();
let polys = create_cube_polys(FVector::new(0.0, 0.0, 0.0), FVector::new(1.0, 1.0, 1.0));
let mut brush = ABrush {
model: UModel::new_from_polys(&polys),
location: FVector::new(5.0, 0.0, 0.0),
pre_pivot: FVector::new(0.0, 0.0, 0.0),
csg_operation: bdk_py::bsp::ECsgOper::Subtract,
poly_flags: EPolyFlags::empty(),
};

bsp_validate_brush(&mut brush.model, false);

// Act
bsp_brush_csg(None, &mut model, EPolyFlags::empty(), bdk_py::bsp::ECsgOper::Subtract, true, true);
bsp_brush_csg(&brush, &mut model, EPolyFlags::empty(), bdk_py::bsp::ECsgOper::Subtract, false);

// We should end up with a model that has a single box with all the faces pointing inwards.
assert_eq!(model.nodes.len(), 1);
assert_eq!(model.polys.len(), 6);
// Assert
assert_eq!(model.nodes.len(), 6);
assert_eq!(model.surfaces.len(), 6);
}

0 comments on commit 753db59

Please sign in to comment.