From 7b53d7d595e4217ac3a61ced48c9a2c837f84fdf Mon Sep 17 00:00:00 2001 From: Rhys Lloyd Date: Thu, 27 Nov 2025 11:52:16 -0800 Subject: [PATCH] use new algorithm --- engine/physics/src/face_crawler.rs | 6 - engine/physics/src/minimum_difference.rs | 41 ------- engine/physics/src/model.rs | 143 ++--------------------- 3 files changed, 8 insertions(+), 182 deletions(-) diff --git a/engine/physics/src/face_crawler.rs b/engine/physics/src/face_crawler.rs index a012fd17..44cfb914 100644 --- a/engine/physics/src/face_crawler.rs +++ b/engine/physics/src/face_crawler.rs @@ -21,12 +21,6 @@ impl CrawlResult{ CrawlResult::Hit(face,time)=>Some((face,time)), } } - pub fn miss(self)->Option>{ - match self{ - CrawlResult::Miss(fev)=>Some(fev), - CrawlResult::Hit(_,_)=>None, - } - } } // TODO: move predict_collision_face_out algorithm in here or something diff --git a/engine/physics/src/minimum_difference.rs b/engine/physics/src/minimum_difference.rs index e947f354..05a1181f 100644 --- a/engine/physics/src/minimum_difference.rs +++ b/engine/physics/src/minimum_difference.rs @@ -501,47 +501,6 @@ impl Simplex2_4{ } } -// local function expand( -// queryP, queryQ, -// vertA0, vertA1, -// vertB0, vertB1, -// vertC0, vertC1, -// vertD0, vertD1, -// accuracy -// ) -fn refine_to_exact(mesh:&MinkowskiMesh,simplex:Simplex<4>)->Simplex2_4{ - unimplemented!() -} - -/// Intermediate data structure containing a partially complete calculation. -/// Sometimes you only care about the topology, and not about the -/// exact point of intersection details. -pub struct Topology{ - simplex:Simplex2_4, -} -impl Topology{ - /// Returns None if the point is intersecting the mesh. - pub fn closest_point_details(self,mesh:&MinkowskiMesh)->Option
{ - // NOTE: if hits is true, this if statement necessarily evaluates to true. - // i.e. hits implies this statement - // if -dist <= exitRadius + radiusP + radiusQ then - // local posP, posQ = decompose(-point, a0, a1, b0, b1, c0, c1) - // return hits, -dist - radiusP - radiusQ, - // posP - radiusP*norm, -norm, - // posQ + radiusQ*norm, norm - // end - // return false - unimplemented!() - } -} -pub struct Details{ - // distance:Planar64, - // p_pos:Planar64Vec3, - // p_norm:Planar64Vec3, - // q_pos:Planar64Vec3, - // q_norm:Planar64Vec3, -} - pub fn contains_point(mesh:&MinkowskiMesh,point:Planar64Vec3)->bool{ const ENABLE_FAST_FAIL:bool=true; // TODO: remove mesh negation diff --git a/engine/physics/src/model.rs b/engine/physics/src/model.rs index 2f443c44..d2a6198b 100644 --- a/engine/physics/src/model.rs +++ b/engine/physics/src/model.rs @@ -632,17 +632,6 @@ pub struct MinkowskiMesh<'a>{ mesh1:TransformedMesh<'a>, } -//infinity fev algorithm state transition -#[derive(Debug)] -enum Transition{ - Done,//found closest vert, no edges are better - Vert(MinkowskiVert),//transition to vert -} -enum EV{ - Vert(MinkowskiVert), - Edge(MinkowskiEdge), -} - pub type GigaTime=Ratio,Fixed<4,128>>; pub fn into_giga_time(time:Time,relative_to:Time)->GigaTime{ let r=(time-relative_to).to_ratio(); @@ -667,137 +656,21 @@ impl MinkowskiMesh<'_>{ pub fn farthest_vert(&self,dir:Planar64Vec3)->MinkowskiVert{ MinkowskiVert::VertVert(self.mesh0.farthest_vert(dir),self.mesh1.farthest_vert(-dir)) } - fn next_transition_vert(&self,vert_id:MinkowskiVert,best_distance_squared:&mut Fixed<2,64>,infinity_dir:Planar64Vec3,point:Planar64Vec3)->Transition{ - let mut best_transition=Transition::Done; - for &directed_edge_id in self.vert_edges(vert_id).as_ref(){ - let edge_n=self.directed_edge_n(directed_edge_id); - //is boundary uncrossable by a crawl from infinity - let edge_verts=self.edge_verts(directed_edge_id.as_undirected()); - //select opposite vertex - let test_vert_id=edge_verts.as_ref()[directed_edge_id.parity() as usize]; - //test if it's closer - let diff=point-self.vert(test_vert_id); - if edge_n.dot(infinity_dir).is_zero(){ - let distance_squared=diff.dot(diff); - if distance_squared<*best_distance_squared{ - best_transition=Transition::Vert(test_vert_id); - *best_distance_squared=distance_squared; - } - } - } - best_transition - } - fn final_ev(&self,vert_id:MinkowskiVert,best_distance_squared:&mut Fixed<2,64>,infinity_dir:Planar64Vec3,point:Planar64Vec3)->EV{ - let mut best_transition=EV::Vert(vert_id); - let diff=point-self.vert(vert_id); - for &directed_edge_id in self.vert_edges(vert_id).as_ref(){ - let edge_n=self.directed_edge_n(directed_edge_id); - //is boundary uncrossable by a crawl from infinity - //check if time of collision is outside Time::MIN..Time::MAX - if edge_n.dot(infinity_dir).is_zero(){ - let d=edge_n.dot(diff); - //test the edge - let edge_nn=edge_n.dot(edge_n); - if !d.is_negative()&&d<=edge_nn{ - let distance_squared={ - let c=diff.cross(edge_n); - //wrap for speed - (c.dot(c)/edge_nn).divide().wrap_2() - }; - if distance_squared<=*best_distance_squared{ - best_transition=EV::Edge(directed_edge_id.as_undirected()); - *best_distance_squared=distance_squared; - } - } - } - } - best_transition - } - fn crawl_boundaries(&self,mut vert_id:MinkowskiVert,infinity_dir:Planar64Vec3,point:Planar64Vec3)->EV{ - let mut best_distance_squared={ - let diff=point-self.vert(vert_id); - diff.dot(diff) - }; - loop{ - match self.next_transition_vert(vert_id,&mut best_distance_squared,infinity_dir,point){ - Transition::Done=>return self.final_ev(vert_id,&mut best_distance_squared,infinity_dir,point), - Transition::Vert(new_vert_id)=>vert_id=new_vert_id, - } - } - } - /// This function drops a vertex down to an edge or a face if the path from infinity did not cross any vertex-edge boundaries but the point is supposed to have already crossed a boundary down from a vertex - fn infinity_fev(&self,infinity_dir:Planar64Vec3,point:Planar64Vec3)->FEV::>{ - //start on any vertex - //cross uncrossable vertex-edge boundaries until you find the closest vertex or edge - //cross edge-face boundary if it's uncrossable - match self.crawl_boundaries(self.farthest_vert(infinity_dir),infinity_dir,point){ - //if a vert is returned, it is the closest point to the infinity point - EV::Vert(vert_id)=>FEV::Vert(vert_id), - EV::Edge(edge_id)=>{ - //cross to face if the boundary is not crossable and we are on the wrong side - let edge_n=self.edge_n(edge_id); - // point is multiplied by two because vert_sum sums two vertices. - let delta_pos=point*2-{ - let &[v0,v1]=self.edge_verts(edge_id).as_ref(); - self.vert(v0)+self.vert(v1) - }; - for (i,&face_id) in self.edge_faces(edge_id).as_ref().iter().enumerate(){ - let face_n=self.face_nd(face_id).0; - //edge-face boundary nd, n facing out of the face towards the edge - let boundary_n=face_n.cross(edge_n)*(i as i64*2-1); - let boundary_d=boundary_n.dot(delta_pos); - //check if time of collision is outside Time::MIN..Time::MAX - //infinity_dir can always be treated as a velocity - if !boundary_d.is_positive()&&boundary_n.dot(infinity_dir).is_zero(){ - //both faces cannot pass this condition, return early if one does. - return FEV::Face(face_id); - } - } - FEV::Edge(edge_id) - }, - } - } - // TODO: fundamentally improve this algorithm. - // All it needs to do is find the closest point on the mesh - // and return the FEV which the point resides on. - // - // What it actually does is use the above functions to trace a ray in from infinity, - // crawling the closest point along the mesh surface until the ray reaches - // the starting point to discover the final FEV. - // - // The actual collision prediction probably does a single test - // and then immediately returns with 0 FEV transitions on average, - // because of the strict time_limit constraint. - // - // Most of the calculation time is just calculating the starting point - // for the "actual" crawling algorithm below (predict_collision_{in|out}). - fn closest_fev_not_inside(&self,mut infinity_body:Body,start_time:Bound<&Time>)->Option>>{ - infinity_body.infinity_dir().and_then(|dir|{ - let infinity_fev=self.infinity_fev(-dir,infinity_body.position); - //a line is simpler to solve than a parabola - infinity_body.velocity=dir; - infinity_body.acceleration=vec3::zero(); - //crawl in from negative infinity along a tangent line to get the closest fev - infinity_fev.crawl(self,&infinity_body,Bound::Unbounded,start_time).miss() - }) - } pub fn predict_collision_in(&self,relative_body:&Body,range:impl RangeBounds