CSG-Forge/csg/src/brush.rs

151 lines
4.0 KiB
Rust

use glam::Vec3;
use crate::{
aabb::Aabb,
math,
plane::{Plane, PlaneIntersection},
};
#[derive(Default, Debug, Clone)]
pub struct MaterialId(pub String);
#[derive(Debug, Clone, Copy)]
pub struct TextureProjection {
pub u: Plane,
pub v: Plane,
}
impl Default for TextureProjection {
fn default() -> Self {
Self {
u: Plane {
normal: glam::vec3(1.0, 0.0, 0.0),
offset: 0.0,
},
v: Plane {
normal: glam::vec3(0.0, 1.0, 0.0),
offset: 0.0,
},
}
}
}
#[derive(Default, Debug, Clone)]
pub struct BrushPlane {
pub plane: Plane,
pub material: MaterialId,
pub tex_projection: TextureProjection,
}
pub struct Brush {
pub planes: Vec<BrushPlane>,
raw_vertices: Vec<PlaneIntersection>,
vertices: Vec<Vec3>,
aabb: Aabb,
}
impl Brush {
pub fn new(planes: &[BrushPlane]) -> Self {
Self {
planes: planes.to_vec(),
raw_vertices: vec![],
vertices: vec![],
aabb: Aabb::new(),
}
}
pub fn rebuild(&mut self) {
if self.planes.len() < 4 {
panic!("Invalid number of planes. Minimum 4 planes required.");
}
self.calculate_raw_vertices();
self.merge_vertices();
self.aabb = Aabb::from_positions(&self.vertices);
}
pub fn aabb_intersects_with(&self, other: &Brush) -> bool {
self.aabb.intersects(other.get_aabb())
}
pub fn planes_intersect_with(&self, other: &Brush) -> bool {
// Check our vertices against their planes
for v in &self.vertices {
let mut iter = other.planes.iter();
if iter.all(|bp| bp.plane.point_in_halfspace(*v)) {
return true;
}
}
// Check their vertices against our planes
for v in &other.vertices {
let mut iter = self.planes.iter();
if iter.all(|bp| bp.plane.point_in_halfspace(*v)) {
return true;
}
}
false
}
pub fn get_vertices(&self) -> Vec<Vec3> {
self.vertices.clone()
}
pub fn get_intersections(&self) -> Vec<PlaneIntersection> {
self.raw_vertices.clone()
}
pub fn get_aabb(&self) -> &Aabb {
&self.aabb
}
fn calculate_raw_vertices(&mut self) {
// Test all combinations of brush planes for intersections
// I experimented with Itertools::(tuple_)combinations instead of this nested loop
// but it was about 2x slower
// TODO: Experiment more
let mut intersections = vec![];
let len = self.planes.len();
for i in 0..(len - 2) {
for j in (i + 1)..(len - 1) {
for k in (j + 1)..len {
if let Some(intersection) = PlaneIntersection::from_planes(
self.planes[i].plane,
self.planes[j].plane,
self.planes[k].plane,
) {
intersections.push(intersection)
}
}
}
}
// Validate intersections against other brush planes
// Intersections only produce vertices if they're in the half-space of every
// plane in the brush.
// TODO: No need to check against planes that are part of the intersection
let mut raw_vertices = vec![];
for intersection in &intersections {
let mut iter = self.planes.iter();
if iter.all(|bplane| intersection.in_halfspace_mat(&bplane.plane)) {
raw_vertices.push(*intersection);
}
}
self.raw_vertices = raw_vertices;
}
fn merge_vertices(&mut self) {
let mut vs: Vec<Vec3> = vec![];
for intersection in &self.raw_vertices {
let point = intersection.get_intersection_point();
if !vs.iter().any(|v| v.abs_diff_eq(point, math::EPSILON)) {
vs.push(point);
}
}
self.vertices = vs;
}
}