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, raw_vertices: Vec, vertices: Vec, 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 { self.vertices.clone() } pub fn get_intersections(&self) -> Vec { 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 = 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; } }