use crate::model::{GigaTime,FEV,MeshQuery,DirectedEdge}; use strafesnet_common::integer::{Fixed,Ratio,vec3::Vector3}; use crate::physics::{Time,Body}; enum Transition<M:MeshQuery>{ Miss, Next(FEV<M>,GigaTime), Hit(M::Face,GigaTime), } pub enum CrawlResult<M:MeshQuery>{ Miss(FEV<M>), Hit(M::Face,GigaTime), } impl<M:MeshQuery> CrawlResult<M>{ pub fn hit(self)->Option<(M::Face,GigaTime)>{ match self{ CrawlResult::Miss(_)=>None, CrawlResult::Hit(face,time)=>Some((face,time)), } } pub fn miss(self)->Option<FEV<M>>{ match self{ CrawlResult::Miss(fev)=>Some(fev), CrawlResult::Hit(_,_)=>None, } } } impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M> where // This is hardcoded for MinkowskiMesh lol M::Face:Copy, M::Edge:Copy, M::Vert:Copy, F:core::ops::Mul<Fixed<1,32>,Output=Fixed<4,128>>, <F as core::ops::Mul<Fixed<1,32>>>::Output:core::iter::Sum, <M as MeshQuery>::Offset:core::ops::Sub<<F as std::ops::Mul<Fixed<1,32>>>::Output>, { fn next_transition(&self,body_time:GigaTime,mesh:&M,body:&Body,mut best_time:GigaTime)->Transition<M>{ //conflicting derivative means it crosses in the wrong direction. //if the transition time is equal to an already tested transition, do not replace the current best. let mut best_transition=Transition::Miss; match self{ &FEV::Face(face_id)=>{ //test own face collision time, ignoring roots with zero or conflicting derivative //n=face.normal d=face.dot //n.a t^2+n.v t+n.p-d==0 let (n,d)=mesh.face_nd(face_id); //TODO: use higher precision d value? //use the mesh transform translation instead of baking it into the d value. for dt in Fixed::<4,128>::zeroes2((n.dot(body.position)-d)*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){ if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){ best_time=dt; best_transition=Transition::Hit(face_id,dt); break; } } //test each edge collision time, ignoring roots with zero or conflicting derivative for &directed_edge_id in mesh.face_edges(face_id).as_ref(){ let edge_n=mesh.directed_edge_n(directed_edge_id); let n=n.cross(edge_n); let &[v0,v1]=mesh.edge_verts(directed_edge_id.as_undirected()).as_ref(); //WARNING: d is moved out of the *2 block because of adding two vertices! //WARNING: precision is swept under the rug! //wrap for speed for dt in Fixed::<4,128>::zeroes2(n.dot(body.position*2-(mesh.vert(v0)+mesh.vert(v1))).wrap_4(),n.dot(body.velocity).wrap_4()*2,n.dot(body.acceleration).wrap_4()){ if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){ best_time=dt; best_transition=Transition::Next(FEV::Edge(directed_edge_id.as_undirected()),dt); break; } } } //if none: }, &FEV::Edge(edge_id)=>{ //test each face collision time, ignoring roots with zero or conflicting derivative let edge_n=mesh.edge_n(edge_id); let edge_verts=mesh.edge_verts(edge_id); let &[ev0,ev1]=edge_verts.as_ref(); let delta_pos=body.position*2-(mesh.vert(ev0)+mesh.vert(ev1)); for (i,&edge_face_id) in mesh.edge_faces(edge_id).as_ref().iter().enumerate(){ let face_n=mesh.face_nd(edge_face_id).0; //edge_n gets parity from the order of edge_faces let n=face_n.cross(edge_n)*((i as i64)*2-1); //WARNING yada yada d *2 //wrap for speed for dt in Fixed::<4,128>::zeroes2(n.dot(delta_pos).wrap_4(),n.dot(body.velocity).wrap_4()*2,n.dot(body.acceleration).wrap_4()){ if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){ best_time=dt; best_transition=Transition::Next(FEV::Face(edge_face_id),dt); break; } } } //test each vertex collision time, ignoring roots with zero or conflicting derivative for (i,&vert_id) in edge_verts.as_ref().iter().enumerate(){ //vertex normal gets parity from vert index let n=edge_n*(1-2*(i as i64)); for dt in Fixed::<2,64>::zeroes2((n.dot(body.position-mesh.vert(vert_id)))*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){ if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){ let dt=Ratio::new(dt.num.widen_4(),dt.den.widen_4()); best_time=dt; best_transition=Transition::Next(FEV::Vert(vert_id),dt); break; } } } //if none: }, &FEV::Vert(vert_id)=>{ //test each edge collision time, ignoring roots with zero or conflicting derivative for &directed_edge_id in mesh.vert_edges(vert_id).as_ref(){ //edge is directed away from vertex, but we want the dot product to turn out negative let n=-mesh.directed_edge_n(directed_edge_id); for dt in Fixed::<2,64>::zeroes2((n.dot(body.position-mesh.vert(vert_id)))*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){ if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){ let dt=Ratio::new(dt.num.widen_4(),dt.den.widen_4()); best_time=dt; best_transition=Transition::Next(FEV::Edge(directed_edge_id.as_undirected()),dt); break; } } } //if none: }, } best_transition } pub fn crawl(mut self,mesh:&M,relative_body:&Body,start_time:Time,time_limit:Time)->CrawlResult<M>{ let mut body_time={ let r=(start_time-relative_body.time).to_ratio(); Ratio::new(r.num.widen_4(),r.den.widen_4()) }; let time_limit={ let r=(time_limit-relative_body.time).to_ratio(); Ratio::new(r.num.widen_4(),r.den.widen_4()) }; for _ in 0..20{ match self.next_transition(body_time,mesh,relative_body,time_limit){ Transition::Miss=>return CrawlResult::Miss(self), Transition::Next(next_fev,next_time)=>(self,body_time)=(next_fev,next_time), Transition::Hit(face,time)=>return CrawlResult::Hit(face,time), } } //TODO: fix all bugs //println!("Too many iterations! Using default behaviour instead of crashing..."); CrawlResult::Miss(self) } }