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{ InvalidFaceCount{
count:usize, count:usize,
}, },
InvalidPlanes,
} }
impl std::fmt::Display for BrushToMeshError{ impl std::fmt::Display for BrushToMeshError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ 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{} impl core::error::Error for BrushToMeshError{}
fn planes_to_faces(face_list:Vec<Face>)->Option<Faces>{ 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 // for each face, determine one edge at a time until you complete the face
let mut dedup=std::collections::HashSet::new(); let mut dedup=std::collections::HashSet::new();
'face: for face0 in &face_list{ 'face: for face0 in &face_list{
@ -136,33 +138,58 @@ fn planes_to_faces(face_list:Vec<Face>)->Option<Faces>{
} }
// === follow edges around face === // === 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 // 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{ loop{
// push point onto vertices
face.push(intersection.divide().fix_1());
// the measure // the measure
let edge_dir=face0.normal.cross(face1.normal); let edge_dir=face0.normal.cross(face1.normal);
// the dot product to beat // the dot product to beat
let d_intersection=edge_dir.dot(intersection.num)/intersection.den; let d_intersection=edge_dir.dot(intersection.num)/intersection.den;
for new_face in &face_list{ // find the next face moving clockwise around face0
if core::ptr::eq(face0,new_face){ let (new_face,new_intersection,_)=face_list.iter().filter_map(|new_face|{
continue; // 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){ let new_intersection=solve3(face0,face2,new_face)?;
continue;
} // the d value must be larger
if core::ptr::eq(face2,new_face){ let d_new_intersection=edge_dir.dot(new_intersection.num)/new_intersection.den;
continue; if d_new_intersection.le_ratio(d_intersection){
} return None;
if let Some(new_intersection)=solve3(new_face,face1,face2){
} }
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>{ 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()}); 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 // generate the mesh
let mut polygon_list=Vec::new(); let mut polygon_list=Vec::new();