diff --git a/lib/bsp_loader/src/brush.rs b/lib/bsp_loader/src/brush.rs index d06c4a8..873b2b4 100644 --- a/lib/bsp_loader/src/brush.rs +++ b/lib/bsp_loader/src/brush.rs @@ -1,5 +1,5 @@ -use strafesnet_common::integer::Planar64; -use strafesnet_common::{model,integer}; +use strafesnet_common::integer::{self,Planar64,Planar64Vec3}; +use strafesnet_common::model::{self,VertexId}; use strafesnet_common::integer::{vec3::Vector3,Fixed,Ratio}; use crate::{valve_transform_normal,valve_transform_dist}; @@ -212,6 +212,33 @@ impl std::fmt::Display for BrushToMeshError{ } impl core::error::Error for BrushToMeshError{} +fn subdivide_max_area(tris:&mut Vec<Vec<VertexId>>,cw_verts:&[(VertexId,Planar64Vec3)],i0:usize,i2:usize,id0:VertexId,id2:VertexId,v0:Planar64Vec3,v2:Planar64Vec3){ + if i0+1==i2{ + return; + } + let mut best_i1=i0+1; + if i0+2<i2{ + let mut best_area={ + let (_,v1)=cw_verts[best_i1.rem_euclid(cw_verts.len())]; + (v2-v0).cross(v1-v0).length_squared() + }; + for i1 in i0+2..=i2-1{ + let (_,v1)=cw_verts[i1.rem_euclid(cw_verts.len())]; + let area=(v2-v0).cross(v1-v0).length_squared(); + if best_area<area{ + best_i1=i1; + best_area=area; + } + } + } + let i1=best_i1; + let (id1,v1)=cw_verts[i1.rem_euclid(cw_verts.len())]; + // draw max area first + tris.push(vec![id0,id1,id2]); + subdivide_max_area(tris,cw_verts,i0,i1,id0,id1,v0,v1); + subdivide_max_area(tris,cw_verts,i1,i2,id1,id2,v1,v2); +} + pub fn faces_to_mesh(faces:Vec<Vec<integer::Planar64Vec3>>)->model::Mesh{ // generate the mesh let mut mb=model::MeshBuilder::new(); @@ -220,16 +247,34 @@ pub fn faces_to_mesh(faces:Vec<Vec<integer::Planar64Vec3>>)->model::Mesh{ // normals are ignored by physics let normal=mb.acquire_normal_id(integer::vec3::ZERO); - let polygon_list=faces.into_iter().map(|face|{ - face.into_iter().map(|pos|{ - let pos=mb.acquire_pos_id(pos); - mb.acquire_vertex_id(model::IndexedVertex{ + let polygon_list=faces.into_iter().flat_map(|face|{ + let cw_verts=face.into_iter().map(|position|{ + let pos=mb.acquire_pos_id(position); + (mb.acquire_vertex_id(model::IndexedVertex{ pos, tex, normal, color, - }) - }).collect() + }),position) + }).collect::<Vec<_>>(); + + // scan and select maximum area triangle O(n^3) + let len=cw_verts.len(); + let cw_verts=cw_verts.as_slice(); + let ((i0,i1,i2),(v0,v1,v2))=cw_verts[..len-2].iter().enumerate().flat_map(|(i0,&(_,v0))| + cw_verts[i0+1..len-1].iter().enumerate().flat_map(move|(i1,&(_,v1))| + cw_verts[i0+i1+2..].iter().enumerate().map(move|(i2,&(_,v2))|((i0,i0+i1+1,i0+i1+i2+2),(v0,v1,v2))) + ) + ).max_by_key(|&(_,(v0,v1,v2))|(v2-v0).cross(v1-v0).length_squared()).unwrap(); + // scan and select more maximum area triangles n * O(n) + let mut tris=Vec::with_capacity(len-2); + // da big one + let (id0,id1,id2)=(cw_verts[i0].0,cw_verts[i1].0,cw_verts[i2].0); + tris.push(vec![id0,id1,id2]); + subdivide_max_area(&mut tris,cw_verts,i0,i1,id0,id1,v0,v1); + subdivide_max_area(&mut tris,cw_verts,i1,i2,id1,id2,v1,v2); + subdivide_max_area(&mut tris,cw_verts,i2,i0+len,id2,id0,v2,v0); + tris }).collect(); let polygon_groups=vec![model::PolygonGroup::PolygonList(model::PolygonList::new(polygon_list))];