THE ALGORITHM

This commit is contained in:
Quaternions 2025-02-05 10:41:55 -08:00
parent 0b63e06ad2
commit 067b5eee3c

@ -34,6 +34,7 @@ pub enum BrushToMeshError{
InvalidFaceCount{
count:usize,
},
InvalidPlanes,
}
impl std::fmt::Display for BrushToMeshError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
@ -43,6 +44,7 @@ impl std::fmt::Display for BrushToMeshError{
impl core::error::Error for BrushToMeshError{}
fn planes_to_faces(face_list:Vec<Face>)->Option<Faces>{
let mut faces=Vec::new();
// for each face, determine one edge at a time until you complete the face
let mut dedup=std::collections::HashSet::new();
'face: for face0 in &face_list{
@ -136,33 +138,58 @@ fn planes_to_faces(face_list:Vec<Face>)->Option<Faces>{
}
// === follow edges around face ===
// Note that we chose face2 so that the faces create a particular winding order.
// Note that we chose face2 such that the 3 faces create a particular winding order.
// If we choose a consistent face to follow (face1, face2) it will always wind with a consistent chirality
// keep looping until we meet this face again
let face1=face1;
let mut face=Vec::new();
loop{
// push point onto vertices
face.push(intersection.divide().fix_1());
// the measure
let edge_dir=face0.normal.cross(face1.normal);
// the dot product to beat
let d_intersection=edge_dir.dot(intersection.num)/intersection.den;
for new_face in &face_list{
if core::ptr::eq(face0,new_face){
continue;
// find the next face moving clockwise around face0
let (new_face,new_intersection,_)=face_list.iter().filter_map(|new_face|{
// ignore faces we've seen before
if core::ptr::eq(face0,new_face)
|core::ptr::eq(face2,new_face){
return None;
}
if core::ptr::eq(face1,new_face){
continue;
}
if core::ptr::eq(face2,new_face){
continue;
}
if let Some(new_intersection)=solve3(new_face,face1,face2){
let new_intersection=solve3(face0,face2,new_face)?;
// the d value must be larger
let d_new_intersection=edge_dir.dot(new_intersection.num)/new_intersection.den;
if d_new_intersection.le_ratio(d_intersection){
return None;
}
Some((new_face,new_intersection,d_new_intersection))
}).min_by_key(|&(_,_,d)|d)?;
// we looped back around to face1, we're done!
if core::ptr::eq(face1,new_face){
break;
}
face2=new_face;
intersection=new_intersection;
}
faces.push(face);
}
None
if faces.is_empty(){
None
}else{
Some(Faces{
faces,
})
}
}
pub fn brush_to_mesh(bsp:&vbsp::Bsp,brush:&vbsp::Brush)->Result<model::Mesh,BrushToMeshError>{
@ -181,7 +208,7 @@ pub fn brush_to_mesh(bsp:&vbsp::Bsp,brush:&vbsp::Brush)->Result<model::Mesh,Brus
return Err(BrushToMeshError::InvalidFaceCount{count:face_list.len()});
}
let faces=planes_to_faces(face_list)?;
let faces=planes_to_faces(face_list).ok_or(BrushToMeshError::InvalidPlanes)?;
// generate the mesh
let mut polygon_list=Vec::new();