Compare commits
17 Commits
5b26304e95
...
2370fbea54
Author | SHA1 | Date | |
---|---|---|---|
2370fbea54 | |||
33972b0c2c | |||
f50670db57 | |||
559a38b607 | |||
751ccf57ca | |||
5bfd340d87 | |||
2d3efdc2e1 | |||
d217966f77 | |||
328c38ca38 | |||
4c05c01b6e | |||
b20f573f1d | |||
21e29aa63e | |||
48870b8a76 | |||
020c704968 | |||
8f63699d45 | |||
6071e23be1 | |||
46d6c31957 |
@ -1,26 +1,20 @@
|
||||
use crate::physics::Body;
|
||||
use crate::model_physics::{FEV,MeshQuery};
|
||||
use crate::integer::{Time,Planar64,Planar64Vec3};
|
||||
use crate::integer::{Time,Planar64};
|
||||
use crate::zeroes::zeroes2;
|
||||
|
||||
struct State<FEV>{
|
||||
fev:FEV,
|
||||
time:Time,
|
||||
}
|
||||
|
||||
enum Transition<F,E,V>{
|
||||
Miss,
|
||||
Next(FEV<F,E,V>,Time),
|
||||
Hit(F,Time),
|
||||
}
|
||||
|
||||
impl<F:Copy,E:Copy,V:Copy> State<FEV<F,E,V>>{
|
||||
fn next_transition(&self,mesh:&impl MeshQuery<F,E,V>,body:&Body,time_limit:Time)->Transition<F,E,V>{
|
||||
pub fn next_transition_body<F:Copy,E:Copy,V:Copy>(fev:&FEV<F,E,V>,time:Time,mesh:&impl MeshQuery<F,E,V>,body:&Body,time_limit:Time)->Transition<F,E,V>{
|
||||
//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_time=time_limit;
|
||||
let mut best_transtition=Transition::Miss;
|
||||
match &self.fev{
|
||||
match fev{
|
||||
&FEV::<F,E,V>::Face(face_id)=>{
|
||||
//test own face collision time, ignoring roots with zero or conflicting derivative
|
||||
//n=face.normal d=face.dot
|
||||
@ -28,17 +22,22 @@ impl<F:Copy,E:Copy,V:Copy> State<FEV<F,E,V>>{
|
||||
let (n,d)=mesh.face_nd(face_id);
|
||||
for t in zeroes2((n.dot(body.position)-d)*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
|
||||
let t=body.time+Time::from(t);
|
||||
if self.time<t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{
|
||||
if time<t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{
|
||||
best_time=t;
|
||||
best_transtition=Transition::Hit(face_id,t);
|
||||
}
|
||||
}
|
||||
//test each edge collision time, ignoring roots with zero or conflicting derivative
|
||||
for &(edge_id,test_face_id) in mesh.face_edges(face_id).iter(){
|
||||
let (n,d)=mesh.face_nd(test_face_id);
|
||||
for &edge_id in mesh.face_edges(face_id).iter(){
|
||||
//this needs to be directed edge!
|
||||
todo!();
|
||||
let edge_n=mesh.edge_n(edge_id);
|
||||
let n=n.cross(edge_n);
|
||||
//picking a vert randomly is terrible
|
||||
let d=n.dot(mesh.vert(mesh.edge_verts(edge_id)[0]));
|
||||
for t in zeroes2((n.dot(body.position)-d)*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
|
||||
let t=body.time+Time::from(t);
|
||||
if self.time<t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{
|
||||
if time<t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{
|
||||
best_time=t;
|
||||
best_transtition=Transition::Next(FEV::<F,E,V>::Edge(edge_id),t);
|
||||
break;
|
||||
@ -49,11 +48,14 @@ impl<F:Copy,E:Copy,V:Copy> State<FEV<F,E,V>>{
|
||||
},
|
||||
&FEV::<F,E,V>::Edge(edge_id)=>{
|
||||
//test each face collision time, ignoring roots with zero or conflicting derivative
|
||||
let edge_n=mesh.edge_n(edge_id);
|
||||
for &test_face_id in mesh.edge_faces(edge_id).iter(){
|
||||
let (n,d)=mesh.face_nd(test_face_id);
|
||||
let face_n=mesh.face_nd(test_face_id).0;
|
||||
let n=edge_n.cross(face_n);
|
||||
let d=n.dot(mesh.vert(mesh.edge_verts(edge_id)[0]));
|
||||
for t in zeroes2((n.dot(body.position)-d)*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
|
||||
let t=body.time+Time::from(t);
|
||||
if self.time<t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{
|
||||
if time<t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{
|
||||
best_time=t;
|
||||
best_transtition=Transition::Next(FEV::<F,E,V>::Face(test_face_id),t);
|
||||
break;
|
||||
@ -66,7 +68,7 @@ impl<F:Copy,E:Copy,V:Copy> State<FEV<F,E,V>>{
|
||||
let d=n.dot(mesh.vert(vert_id));
|
||||
for t in zeroes2((n.dot(body.position)-d)*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
|
||||
let t=body.time+Time::from(t);
|
||||
if self.time<t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{
|
||||
if time<t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{
|
||||
best_time=t;
|
||||
best_transtition=Transition::Next(FEV::<F,E,V>::Vert(vert_id),t);
|
||||
break;
|
||||
@ -82,7 +84,7 @@ impl<F:Copy,E:Copy,V:Copy> State<FEV<F,E,V>>{
|
||||
let d=n.dot(mesh.vert(vert_id));
|
||||
for t in zeroes2((n.dot(body.position)-d)*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
|
||||
let t=body.time+Time::from(t);
|
||||
if self.time<t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{
|
||||
if time<t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{
|
||||
best_time=t;
|
||||
best_transtition=Transition::Next(FEV::<F,E,V>::Edge(edge_id),t);
|
||||
break;
|
||||
@ -94,27 +96,13 @@ impl<F:Copy,E:Copy,V:Copy> State<FEV<F,E,V>>{
|
||||
}
|
||||
best_transtition
|
||||
}
|
||||
}
|
||||
|
||||
pub fn predict_collision<F:Copy,E:Copy,V:Copy>(mesh:&impl MeshQuery<F,E,V>,relative_body:&Body,time_limit:Time)->Option<(F,Time)>{
|
||||
let mut state=State{
|
||||
fev:mesh.closest_fev(relative_body.position),
|
||||
time:relative_body.time,
|
||||
};
|
||||
//it would be possible to write down the point of closest approach...
|
||||
pub fn crawl_fev_body<F:Copy,E:Copy,V:Copy>(mut fev:FEV<F,E,V>,mesh:&impl MeshQuery<F,E,V>,relative_body:&Body,time_limit:Time)->Option<(F,Time)>{
|
||||
let mut time=relative_body.time;
|
||||
loop{
|
||||
match state.next_transition(mesh,relative_body,time_limit){
|
||||
match next_transition_body(&fev,time,mesh,relative_body,time_limit){
|
||||
Transition::Miss=>return None,
|
||||
Transition::Next(fev,time)=>(state.fev,state.time)=(fev,time),
|
||||
Transition::Next(next_fev,next_time)=>(fev,time)=(next_fev,next_time),
|
||||
Transition::Hit(face,time)=>return Some((face,time)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn predict_collision_end<F:Copy,E:Copy,V:Copy>(mesh:&impl MeshQuery<F,E,V>,relative_body:&Body,time_limit:Time,ignore_face_id:F)->Option<(F,Time)>{
|
||||
//imagine the mesh without the collision face
|
||||
//no algorithm needed, there is only one state and three cases (Face,Edge,None)
|
||||
//determine when it passes an edge ("sliding off" case) or if it leaves the surface directly
|
||||
//the state can be constructed from the ContactCollision directly
|
||||
None
|
||||
}
|
||||
|
@ -415,6 +415,7 @@ impl Planar64{
|
||||
pub const ZERO:Self=Self(0);
|
||||
pub const ONE:Self=Self(1<<32);
|
||||
pub const MAX:Self=Self(i64::MAX);
|
||||
pub const MIN:Self=Self(i64::MIN);
|
||||
#[inline]
|
||||
pub const fn int(num:i32)->Self{
|
||||
Self(Self::ONE.0*num as i64)
|
||||
|
@ -43,7 +43,7 @@ impl Face{
|
||||
}
|
||||
struct Vert(Planar64Vec3);
|
||||
struct FaceRefs{
|
||||
edges:Vec<(EdgeId,FaceId)>,
|
||||
edges:Vec<EdgeId>,
|
||||
//verts:Vec<VertId>,
|
||||
}
|
||||
struct EdgeRefs{
|
||||
@ -94,15 +94,16 @@ struct EdgePool{
|
||||
edge_id_from_guy:std::collections::HashMap<EdgeIdGuy,usize>,
|
||||
}
|
||||
impl EdgePool{
|
||||
fn push(&mut self,edge_id_guy:EdgeIdGuy)->(&mut EdgeRefGuy,EdgeId,bool){
|
||||
if let Some(&edge_id)=self.edge_id_from_guy.get(&edge_id_guy){
|
||||
(&mut unsafe{self.edge_guys.get_unchecked_mut(edge_id)}.1,EdgeId(edge_id),true)
|
||||
fn push(&mut self,edge_id_guy:EdgeIdGuy)->(&mut EdgeRefGuy,EdgeId){
|
||||
let edge_id=if let Some(&edge_id)=self.edge_id_from_guy.get(&edge_id_guy){
|
||||
edge_id
|
||||
}else{
|
||||
let edge_id=self.edge_guys.len();
|
||||
self.edge_guys.push((edge_id_guy.clone(),EdgeRefGuy::new()));
|
||||
self.edge_id_from_guy.insert(edge_id_guy,edge_id);
|
||||
(&mut unsafe{self.edge_guys.get_unchecked_mut(edge_id)}.1,EdgeId(edge_id),false)
|
||||
}
|
||||
edge_id
|
||||
};
|
||||
(&mut unsafe{self.edge_guys.get_unchecked_mut(edge_id)}.1,EdgeId(edge_id))
|
||||
}
|
||||
}
|
||||
impl From<&crate::model::IndexedModel> for PhysicsMesh{
|
||||
@ -131,12 +132,10 @@ impl From<&crate::model::IndexedModel> for PhysicsMesh{
|
||||
);
|
||||
//get/create edge and push face into it
|
||||
let (edge_id_guy,is_sorted)=EdgeIdGuy::new(VertId(vert0_id),VertId(vert1_id));
|
||||
let (edge_ref_guy,edge_id,exists)=edge_pool.push(edge_id_guy);
|
||||
if exists{
|
||||
edge_ref_guy.push(1,face_id);
|
||||
}else{
|
||||
edge_ref_guy.push(0,face_id);
|
||||
}
|
||||
let (edge_ref_guy,edge_id)=edge_pool.push(edge_id_guy);
|
||||
//polygon vertices as assumed to be listed clockwise
|
||||
//populate the edge face on the left or right depending on how the edge vertices got sorted
|
||||
edge_ref_guy.push(is_sorted as usize,face_id);
|
||||
//index edges & face into vertices
|
||||
{
|
||||
let vert_ref_guy=unsafe{vert_ref_guys.get_unchecked_mut(vert0_id)};
|
||||
@ -161,19 +160,8 @@ impl From<&crate::model::IndexedModel> for PhysicsMesh{
|
||||
Self{
|
||||
faces,
|
||||
verts,
|
||||
face_topology:face_ref_guys.into_iter().enumerate().map(|(i,face_ref_guy)|{
|
||||
let face_id=FaceId(i);
|
||||
FaceRefs{edges:face_ref_guy.0.into_iter().map(|edge_id|{
|
||||
//get the edge face that's not this face
|
||||
let edge_faces=&edge_pool.edge_guys[edge_id.0].1.0;
|
||||
if edge_faces[0]==face_id{
|
||||
(edge_id,edge_faces[1])
|
||||
}else if edge_faces[1]==face_id{
|
||||
(edge_id,edge_faces[0])
|
||||
}else{
|
||||
panic!("edge does not contain face edge_faces={:?} face={:?}",edge_faces,face_id)
|
||||
}
|
||||
}).collect()}
|
||||
face_topology:face_ref_guys.into_iter().map(|face_ref_guy|{
|
||||
FaceRefs{edges:face_ref_guy.0}
|
||||
}).collect(),
|
||||
edge_topology:edge_pool.edge_guys.into_iter().map(|(edge_id_guy,edge_ref_guy)|
|
||||
EdgeRefs{faces:edge_ref_guy.0,verts:edge_id_guy.0}
|
||||
@ -189,14 +177,13 @@ impl From<&crate::model::IndexedModel> for PhysicsMesh{
|
||||
}
|
||||
|
||||
pub trait MeshQuery<FACE:Clone,EDGE:Clone,VERT:Clone>{
|
||||
fn closest_fev(&self,point:Planar64Vec3)->FEV<FACE,EDGE,VERT>;
|
||||
fn edge_n(&self,edge_id:EDGE)->Planar64Vec3{
|
||||
let verts=self.edge_verts(edge_id);
|
||||
self.vert(verts[1].clone())-self.vert(verts[0].clone())
|
||||
}
|
||||
fn vert(&self,vert_id:VERT)->Planar64Vec3;
|
||||
fn face_nd(&self,face_id:FACE)->(Planar64Vec3,Planar64);
|
||||
fn face_edges(&self,face_id:FACE)->Cow<Vec<(EDGE,FACE)>>;
|
||||
fn face_edges(&self,face_id:FACE)->Cow<Vec<EDGE>>;
|
||||
fn edge_faces(&self,edge_id:EDGE)->Cow<[FACE;2]>;
|
||||
fn edge_verts(&self,edge_id:EDGE)->Cow<[VERT;2]>;
|
||||
fn vert_edges(&self,vert_id:VERT)->Cow<Vec<EDGE>>;
|
||||
@ -206,29 +193,6 @@ impl PhysicsMesh{
|
||||
pub fn verts<'a>(&'a self)->impl Iterator<Item=Planar64Vec3>+'a{
|
||||
self.verts.iter().map(|Vert(pos)|*pos)
|
||||
}
|
||||
pub fn brute(&self,body:&crate::physics::Body,time_limit:crate::integer::Time)->Option<(FaceId,crate::integer::Time)>{
|
||||
//check each face
|
||||
let mut best_time=time_limit;
|
||||
let mut best_face=None;
|
||||
for (i,face) in self.faces.iter().enumerate(){
|
||||
let face_id=FaceId(i);
|
||||
let (n,d)=face.nd();
|
||||
for t in crate::zeroes::zeroes2((n.dot(body.position)-d)*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
|
||||
let t=body.time+crate::integer::Time::from(t);
|
||||
if body.time<t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{
|
||||
let p=body.extrapolated_position(t);
|
||||
if self.face_edges(face_id).iter().all(|&(_,face_id)|{
|
||||
let (n,d)=self.face_nd(face_id);
|
||||
n.dot(p)<=d
|
||||
}){
|
||||
best_time=t;
|
||||
best_face=Some(face_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
best_face.map(|f|(f,best_time))
|
||||
}
|
||||
fn vert_directed_edges(&self,vert_id:VertId)->Cow<Vec<DirectedEdgeId>>{
|
||||
Cow::Borrowed(&self.vert_topology[vert_id.0].edges)
|
||||
}
|
||||
@ -238,48 +202,6 @@ impl PhysicsMesh{
|
||||
}
|
||||
}
|
||||
impl MeshQuery<FaceId,EdgeId,VertId> for PhysicsMesh{
|
||||
fn closest_fev(&self,point:Planar64Vec3)->FEV<FaceId,EdgeId,VertId>{
|
||||
//TODO: put some genius code right here
|
||||
|
||||
//brute force for now
|
||||
let mut best_distance_squared=Planar64::MAX;
|
||||
//make something up as default ret
|
||||
//hopefully empty meshes don't make their way through here
|
||||
let mut best_fev=FEV::<FaceId,EdgeId,VertId>::Vert(VertId(0));
|
||||
//check each vert
|
||||
for (i,v) in self.verts.iter().enumerate(){
|
||||
let d=(v.0-point).dot(v.0-point);
|
||||
if d<best_distance_squared{
|
||||
best_distance_squared=d;
|
||||
best_fev=FEV::<FaceId,EdgeId,VertId>::Vert(VertId(i));
|
||||
}
|
||||
}
|
||||
//check each edge
|
||||
for (i,e) in self.edge_topology.iter().enumerate(){
|
||||
let v0=self.vert(e.verts[0]);
|
||||
let v1=self.vert(e.verts[1]);
|
||||
let n=v1-v0;
|
||||
//n.cross(point-v0)=sin(t)*n*dis
|
||||
let d=n.dot(point-v0);
|
||||
if d<n.dot(v1)&&n.dot(v0)<d{
|
||||
let c=n.cross(point-v0);
|
||||
let edge_distance_squared=c.dot(c)/n.dot(n);
|
||||
if edge_distance_squared<best_distance_squared{
|
||||
best_distance_squared=edge_distance_squared;
|
||||
best_fev=FEV::<FaceId,EdgeId,VertId>::Edge(EdgeId(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
let face_dots:Vec<Planar64>=self.faces.iter().map(|f|f.normal.dot(point)).collect();
|
||||
//check each face
|
||||
for (i,f) in self.face_topology.iter().enumerate(){
|
||||
if face_dots[i]<best_distance_squared&&f.edges.iter().all(|&(_,face_id)|face_dots[face_id.0]<=Planar64::ZERO){
|
||||
best_distance_squared=face_dots[i];
|
||||
best_fev=FEV::<FaceId,EdgeId,VertId>::Face(FaceId(i));
|
||||
}
|
||||
}
|
||||
best_fev
|
||||
}
|
||||
fn face_nd(&self,face_id:FaceId)->(Planar64Vec3,Planar64){
|
||||
(self.faces[face_id.0].normal,self.faces[face_id.0].dot)
|
||||
}
|
||||
@ -287,7 +209,7 @@ impl MeshQuery<FaceId,EdgeId,VertId> for PhysicsMesh{
|
||||
fn vert(&self,vert_id:VertId)->Planar64Vec3{
|
||||
self.verts[vert_id.0].0
|
||||
}
|
||||
fn face_edges(&self,face_id:FaceId)->Cow<Vec<(EdgeId,FaceId)>>{
|
||||
fn face_edges(&self,face_id:FaceId)->Cow<Vec<EdgeId>>{
|
||||
Cow::Borrowed(&self.face_topology[face_id.0].edges)
|
||||
}
|
||||
fn edge_faces(&self,edge_id:EdgeId)->Cow<[FaceId;2]>{
|
||||
@ -322,67 +244,18 @@ impl TransformedMesh<'_>{
|
||||
normal_transform,
|
||||
}
|
||||
}
|
||||
pub fn brute_in(&self,body:&crate::physics::Body,time_limit:crate::integer::Time)->Option<(FaceId,crate::integer::Time)>{
|
||||
//check each face
|
||||
let mut best_time=time_limit;
|
||||
let mut best_face=None;
|
||||
for i in 0..self.mesh.faces.len(){
|
||||
let face_id=FaceId(i);
|
||||
let (n,d)=self.face_nd(face_id);
|
||||
for t in crate::zeroes::zeroes2((n.dot(body.position)-d)*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
|
||||
let t=body.time+crate::integer::Time::from(t);
|
||||
if body.time<t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{
|
||||
let p=body.extrapolated_position(t);
|
||||
if self.face_edges(face_id).iter().all(|&(_,face_id)|{
|
||||
let (n,d)=self.face_nd(face_id);
|
||||
n.dot(p)<=d
|
||||
}){
|
||||
best_time=t;
|
||||
best_face=Some(face_id);
|
||||
}
|
||||
}
|
||||
fn farthest_vert(&self,dir:Planar64Vec3)->VertId{
|
||||
let best_dot=Planar64::MIN;
|
||||
let best_vert;
|
||||
for (i,vert) in self.mesh.verts.iter().enumerate(){
|
||||
let p=self.transform.transform_point3(vert.0);
|
||||
let d=dir.dot(p);
|
||||
if best_dot<d{
|
||||
best_dot=d;
|
||||
best_vert=VertId(i);
|
||||
}
|
||||
}
|
||||
best_face.map(|f|(f,best_time))
|
||||
}
|
||||
pub fn brute_out(&self,body:&crate::physics::Body,time_limit:crate::integer::Time)->Option<(FaceId,crate::integer::Time)>{
|
||||
//check each face
|
||||
let mut best_time=time_limit;
|
||||
let mut best_face=None;
|
||||
for i in 0..self.mesh.faces.len(){
|
||||
let face_id=FaceId(i);
|
||||
let (n,d)=self.face_nd(face_id);
|
||||
for t in crate::zeroes::zeroes2((n.dot(body.position)-d)*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
|
||||
let t=body.time+crate::integer::Time::from(t);
|
||||
if body.time<t&&t<best_time&&n.dot(body.extrapolated_velocity(t))>Planar64::ZERO{
|
||||
let p=body.extrapolated_position(t);
|
||||
if self.face_edges(face_id).iter().all(|&(_,test_face_id)|{
|
||||
let (n,d)=self.face_nd(test_face_id);
|
||||
n.dot(p)<=d
|
||||
}){
|
||||
best_time=t;
|
||||
best_face=Some(face_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
best_face.map(|f|(f,best_time))
|
||||
}
|
||||
pub fn brute_out_face(&self,body:&crate::physics::Body,time_limit:crate::integer::Time,face_id:FaceId)->Option<(FaceId,crate::integer::Time)>{
|
||||
//check each face
|
||||
let mut best_time=time_limit;
|
||||
let mut best_face=None;
|
||||
for &(_,test_face_id) in self.mesh.face_edges(face_id).iter(){
|
||||
let (n,d)=self.face_nd(test_face_id);
|
||||
for t in crate::zeroes::zeroes2((n.dot(body.position)-d)*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
|
||||
let t=body.time+crate::integer::Time::from(t);
|
||||
if body.time<t&&t<best_time&&n.dot(body.extrapolated_velocity(t))>Planar64::ZERO{
|
||||
best_time=t;
|
||||
best_face=Some(test_face_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
best_face.map(|f|(f,best_time))
|
||||
best_vert
|
||||
}
|
||||
#[inline]
|
||||
fn vert_directed_edges(&self,vert_id:VertId)->Cow<Vec<DirectedEdgeId>>{
|
||||
@ -394,49 +267,6 @@ impl TransformedMesh<'_>{
|
||||
}
|
||||
}
|
||||
impl MeshQuery<FaceId,EdgeId,VertId> for TransformedMesh<'_>{
|
||||
fn closest_fev(&self,point:Planar64Vec3)->FEV<FaceId,EdgeId,VertId>{
|
||||
//TODO: put some genius code right here
|
||||
|
||||
//brute force for now
|
||||
let mut best_distance_squared=Planar64::MAX;
|
||||
//make something up as default ret
|
||||
//hopefully empty meshes don't make their way through here
|
||||
let mut best_fev=FEV::<FaceId,EdgeId,VertId>::Vert(VertId(0));
|
||||
//check each vert
|
||||
for i in 0..self.mesh.verts.len(){
|
||||
let v=self.vert(VertId(i));
|
||||
let d=(v-point).dot(v-point);
|
||||
if d<best_distance_squared{
|
||||
best_distance_squared=d;
|
||||
best_fev=FEV::<FaceId,EdgeId,VertId>::Vert(VertId(i));
|
||||
}
|
||||
}
|
||||
//check each edge
|
||||
for (i,e) in self.mesh.edge_topology.iter().enumerate(){
|
||||
let v0=self.vert(e.verts[0]);
|
||||
let v1=self.vert(e.verts[1]);
|
||||
let n=v1-v0;
|
||||
//n.cross(point-v0)=sin(t)*n*dis
|
||||
let d=n.dot(point-v0);
|
||||
if d<n.dot(v1)&&n.dot(v0)<d{
|
||||
let c=n.cross(point-v0);
|
||||
let edge_distance_squared=c.dot(c)/n.dot(n);
|
||||
if edge_distance_squared<best_distance_squared{
|
||||
best_distance_squared=edge_distance_squared;
|
||||
best_fev=FEV::<FaceId,EdgeId,VertId>::Edge(EdgeId(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
let face_dots:Vec<Planar64>=self.mesh.faces.iter().map(|f|(*self.normal_transform*f.normal).dot(point)).collect();
|
||||
//check each face
|
||||
for (i,f) in self.mesh.face_topology.iter().enumerate(){
|
||||
if face_dots[i]<best_distance_squared&&f.edges.iter().all(|&(_,face_id)|face_dots[face_id.0]<=Planar64::ZERO){
|
||||
best_distance_squared=face_dots[i];
|
||||
best_fev=FEV::<FaceId,EdgeId,VertId>::Face(FaceId(i));
|
||||
}
|
||||
}
|
||||
best_fev
|
||||
}
|
||||
fn face_nd(&self,face_id:FaceId)->(Planar64Vec3,Planar64){
|
||||
let (n,d)=self.mesh.face_nd(face_id);
|
||||
let transformed_n=*self.normal_transform*n;
|
||||
@ -447,7 +277,7 @@ impl MeshQuery<FaceId,EdgeId,VertId> for TransformedMesh<'_>{
|
||||
self.transform.transform_point3(self.mesh.vert(vert_id))
|
||||
}
|
||||
#[inline]
|
||||
fn face_edges(&self,face_id:FaceId)->Cow<Vec<(EdgeId,FaceId)>>{
|
||||
fn face_edges(&self,face_id:FaceId)->Cow<Vec<EdgeId>>{
|
||||
self.mesh.face_edges(face_id)
|
||||
}
|
||||
#[inline]
|
||||
@ -480,12 +310,16 @@ enum MinkowskiVert{
|
||||
enum MinkowskiEdge{
|
||||
VertEdge(VertId,EdgeId),
|
||||
EdgeVert(EdgeId,VertId),
|
||||
//EdgeEdge when edges are parallel
|
||||
}
|
||||
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
|
||||
pub enum MinkowskiFace{
|
||||
FaceVert(FaceId,VertId),
|
||||
EdgeEdge(EdgeId,EdgeId),
|
||||
VertFace(VertId,FaceId),
|
||||
EdgeEdge(EdgeId,EdgeId),
|
||||
FaceVert(FaceId,VertId),
|
||||
//EdgeFace
|
||||
//FaceEdge
|
||||
//FaceFace
|
||||
}
|
||||
|
||||
pub struct MinkowskiMesh<'a>{
|
||||
@ -500,17 +334,48 @@ impl MinkowskiMesh<'_>{
|
||||
mesh1,
|
||||
}
|
||||
}
|
||||
fn farthest_vert(&self,dir:Planar64Vec3)->MinkowskiVert{
|
||||
MinkowskiVert::VertVert(self.mesh0.farthest_vert(dir),self.mesh1.farthest_vert(-dir))
|
||||
}
|
||||
fn closest_fev(&self,point:Planar64Vec3)->FEV<MinkowskiFace,MinkowskiEdge,MinkowskiVert>{
|
||||
//put some genius code right here instead of this
|
||||
//assume that point is outside the mesh and nonzero
|
||||
//find vertex on mesh0 farthest in point direction
|
||||
let fev=FEV::<MinkowskiFace,MinkowskiEdge,MinkowskiVert>::Vert(self.farthest_vert(point));
|
||||
crate::face_crawler::crawl_fev_dot(fev,self,point)
|
||||
}
|
||||
pub fn predict_collision_in(&self,relative_body:&crate::physics::Body,time_limit:crate::integer::Time)->Option<(MinkowskiFace,crate::integer::Time)>{
|
||||
crate::face_crawler::crawl_fev_body(self.closest_fev(relative_body.position),self,relative_body,time_limit)
|
||||
}
|
||||
pub fn predict_collision_face_out(&self,relative_body:&crate::physics::Body,time_limit:crate::integer::Time,contact_face_id:MinkowskiFace)->Option<(MinkowskiEdge,crate::integer::Time)>{
|
||||
//no algorithm needed, there is only one state and two cases (Edge,None)
|
||||
//determine when it passes an edge ("sliding off" case)
|
||||
let mut best_time=time_limit;
|
||||
let mut best_edge=None;
|
||||
let face_n=self.face_nd(contact_face_id).0;
|
||||
for &edge_id in self.face_edges(contact_face_id).iter(){
|
||||
let edge_n=self.edge_n(edge_id);
|
||||
let n=face_n.cross(edge_n);
|
||||
//picking a vert randomly is terrible
|
||||
let d=n.dot(self.vert(self.edge_verts(edge_id)[0]));
|
||||
for t in crate::zeroes::zeroes2((n.dot(relative_body.position)-d)*2,n.dot(relative_body.velocity)*2,n.dot(relative_body.acceleration)){
|
||||
let t=relative_body.time+crate::integer::Time::from(t);
|
||||
if relative_body.time<t&&t<best_time&&n.dot(relative_body.extrapolated_velocity(t))<Planar64::ZERO{
|
||||
best_time=t;
|
||||
best_edge=Some(edge_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
best_edge.map(|e|(e,best_time))
|
||||
}
|
||||
}
|
||||
impl MeshQuery<MinkowskiFace,MinkowskiEdge,MinkowskiVert> for MinkowskiMesh<'_>{
|
||||
fn closest_fev(&self,point:Planar64Vec3)->FEV<MinkowskiFace,MinkowskiEdge,MinkowskiVert>{
|
||||
//put some genius code right here
|
||||
todo!()
|
||||
}
|
||||
fn face_nd(&self,face_id:MinkowskiFace)->(Planar64Vec3,Planar64){
|
||||
match face_id{
|
||||
MinkowskiFace::FaceVert(f0,v1)=>{
|
||||
let (n,d)=self.mesh0.face_nd(f0);
|
||||
(n,d+n.dot(self.mesh1.vert(v1)))
|
||||
MinkowskiFace::VertFace(v0,f1)=>{
|
||||
let (n,d)=self.mesh1.face_nd(f1);
|
||||
(-n,d-n.dot(self.mesh0.vert(v0)))
|
||||
},
|
||||
MinkowskiFace::EdgeEdge(e0,e1)=>{
|
||||
let edge0_n=self.mesh0.edge_n(e0);
|
||||
@ -523,9 +388,9 @@ impl MeshQuery<MinkowskiFace,MinkowskiEdge,MinkowskiVert> for MinkowskiMesh<'_>{
|
||||
let sign=e0d.signum_i64();
|
||||
(n*(sign*2),(e0d-e1d)*sign)
|
||||
},
|
||||
MinkowskiFace::VertFace(v0,f1)=>{
|
||||
let (n,d)=self.mesh1.face_nd(f1);
|
||||
(-n,d-n.dot(self.mesh0.vert(v0)))
|
||||
MinkowskiFace::FaceVert(f0,v1)=>{
|
||||
let (n,d)=self.mesh0.face_nd(f0);
|
||||
(n,d+n.dot(self.mesh1.vert(v1)))
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -536,106 +401,79 @@ impl MeshQuery<MinkowskiFace,MinkowskiEdge,MinkowskiVert> for MinkowskiMesh<'_>{
|
||||
},
|
||||
}
|
||||
}
|
||||
fn face_edges(&self,face_id:MinkowskiFace)->Cow<Vec<(MinkowskiEdge,MinkowskiFace)>>{
|
||||
fn face_edges(&self,face_id:MinkowskiFace)->Cow<Vec<MinkowskiEdge>>{
|
||||
match face_id{
|
||||
MinkowskiFace::FaceVert(f0,v1)=>{
|
||||
let face0_n=self.mesh0.face_nd(f0).0;
|
||||
Cow::Owned(self.mesh0.face_edges(f0).iter().map(|&(edge_id0,edge_face_id0)|{
|
||||
//compare v1 edges
|
||||
//candidate edges have negative dot with edge_face_id0 normal
|
||||
//choose the edge with the smallest edgedir dot with f0 normal
|
||||
//MinkowskiFace::EdgeEdge(edge_id0,edge_id1)
|
||||
//if there is no candidate edges
|
||||
//MinkowskiFace::FaceVert(edge_face_id0,v1)
|
||||
(MinkowskiEdge::EdgeVert(edge_id0,v1),{
|
||||
let mut best_edge=None;
|
||||
let mut best_d=Planar64::MAX;
|
||||
let edge_face0_n=self.mesh0.face_nd(edge_face_id0).0;
|
||||
let v1e=self.mesh1.vert_directed_edges(v1);
|
||||
for &directed_edge_id1 in v1e.iter(){
|
||||
let edge1_n=self.mesh1.directed_edge_n(directed_edge_id1);
|
||||
if edge_face0_n.dot(edge1_n)<Planar64::ZERO{
|
||||
let d=face0_n.dot(edge1_n);
|
||||
if d<best_d{
|
||||
best_d=d;
|
||||
best_edge=Some(directed_edge_id1)
|
||||
}
|
||||
}
|
||||
}
|
||||
best_edge.map_or(
|
||||
MinkowskiFace::FaceVert(edge_face_id0,v1),
|
||||
|directed_edge_id1|MinkowskiFace::EdgeEdge(edge_id0,directed_edge_id1.as_edge_id())
|
||||
)
|
||||
})
|
||||
MinkowskiFace::VertFace(v0,f1)=>{
|
||||
Cow::Owned(self.mesh1.face_edges(f1).iter().map(|&edge_id1|{
|
||||
MinkowskiEdge::VertEdge(v0,edge_id1)
|
||||
}).collect())
|
||||
},
|
||||
MinkowskiFace::EdgeEdge(e0,e1)=>{
|
||||
/*
|
||||
let e0v=self.mesh0.edge_verts(e0);
|
||||
let e1v=self.mesh1.edge_verts(e1);
|
||||
let [r0,r1]=e0v.map(|vert_id0|{
|
||||
//sort e1 ends by e0 edge dir to get v1
|
||||
//find face normal formulation without cross products
|
||||
let v1=if 0<(e0.v1-e0.v0).dot(e1.v1-e1.v0){
|
||||
e1.v0
|
||||
}else{
|
||||
e1.v1
|
||||
};
|
||||
(MinkowskiEdge::VertEdge(vert_id0,e1),MinkowskiFace::FaceVert(face_id0,v1))
|
||||
});
|
||||
let [r2,r3]=e1v.map(|vert_id1|{
|
||||
//sort e0 ends by e1 edge dir to get v0
|
||||
let v0=if 0<(e0.v1-e0.v0).dot(e1.v1-e1.v0){
|
||||
e0.v0
|
||||
}else{
|
||||
e0.v1
|
||||
};
|
||||
(MinkowskiEdge::EdgeVert(e0,vert_id1),MinkowskiFace::VertFace(v0,face_id1))
|
||||
});
|
||||
Cow::Owned(vec![r0,r1,r2,r3])
|
||||
*/
|
||||
todo!()
|
||||
//could sort this if ordered edges are needed
|
||||
Cow::Owned(vec![
|
||||
MinkowskiEdge::VertEdge(e0v[0],e1),
|
||||
MinkowskiEdge::VertEdge(e0v[1],e1),
|
||||
MinkowskiEdge::EdgeVert(e0,e1v[0]),
|
||||
MinkowskiEdge::EdgeVert(e0,e1v[1]),
|
||||
])
|
||||
},
|
||||
MinkowskiFace::VertFace(v0,f1)=>{
|
||||
let face1_n=self.mesh1.face_nd(f1).0;
|
||||
Cow::Owned(self.mesh1.face_edges(f1).iter().map(|&(edge_id1,edge_face_id1)|{
|
||||
//same as above
|
||||
(MinkowskiEdge::VertEdge(v0,edge_id1),{
|
||||
let mut best_edge=None;
|
||||
let mut best_d=Planar64::MAX;
|
||||
let edge_face1_n=self.mesh1.face_nd(edge_face_id1).0;
|
||||
let v0e=self.mesh0.vert_directed_edges(v0);
|
||||
for &directed_edge_id0 in v0e.iter(){
|
||||
let edge0_n=self.mesh0.directed_edge_n(directed_edge_id0);
|
||||
if edge_face1_n.dot(edge0_n)<Planar64::ZERO{
|
||||
let d=face1_n.dot(edge0_n);
|
||||
if d<best_d{
|
||||
best_d=d;
|
||||
best_edge=Some(directed_edge_id0)
|
||||
}
|
||||
}
|
||||
}
|
||||
best_edge.map_or(
|
||||
MinkowskiFace::VertFace(v0,edge_face_id1),
|
||||
|directed_edge_id0|MinkowskiFace::EdgeEdge(directed_edge_id0.as_edge_id(),edge_id1)
|
||||
)
|
||||
})
|
||||
MinkowskiFace::FaceVert(f0,v1)=>{
|
||||
Cow::Owned(self.mesh0.face_edges(f0).iter().map(|&edge_id0|{
|
||||
MinkowskiEdge::EdgeVert(edge_id0,v1)
|
||||
}).collect())
|
||||
},
|
||||
}
|
||||
}
|
||||
fn edge_faces(&self,edge_id:MinkowskiEdge)->Cow<[MinkowskiFace;2]>{
|
||||
//WRONG!!!!!!!!!!!! MORE CASES!!!!!!!!!!!!
|
||||
match edge_id{
|
||||
MinkowskiEdge::VertEdge(v0,e1)=>{
|
||||
//also need to check v0 edges to see if they overtake the face
|
||||
Cow::Owned(self.mesh1.edge_faces(e1).map(|face_id1|{
|
||||
MinkowskiFace::VertFace(v0,face_id1)
|
||||
let e1f=self.mesh1.edge_faces(e1);
|
||||
Cow::Owned([(e1f[0],e1f[1]),(e1f[1],e1f[0])].map(|(edge_face_id1,other_edge_face_id1)|{
|
||||
let mut best_edge=None;
|
||||
let mut best_d=Planar64::MAX;
|
||||
let edge_face1_n=self.mesh1.face_nd(edge_face_id1).0;
|
||||
let other_edge_face1_n=self.mesh1.face_nd(other_edge_face_id1).0;
|
||||
let v0e=self.mesh0.vert_directed_edges(v0);
|
||||
for &directed_edge_id0 in v0e.iter(){
|
||||
let edge0_n=self.mesh0.directed_edge_n(directed_edge_id0);
|
||||
if edge_face1_n.dot(edge0_n)<Planar64::ZERO{
|
||||
let d=other_edge_face1_n.dot(edge0_n);
|
||||
if d<best_d{
|
||||
best_d=d;
|
||||
best_edge=Some(directed_edge_id0);
|
||||
}
|
||||
}
|
||||
}
|
||||
best_edge.map_or(
|
||||
MinkowskiFace::VertFace(v0,edge_face_id1),
|
||||
|directed_edge_id0|MinkowskiFace::EdgeEdge(directed_edge_id0.as_edge_id(),e1)
|
||||
)
|
||||
}))
|
||||
},
|
||||
MinkowskiEdge::EdgeVert(e0,v1)=>{
|
||||
Cow::Owned(self.mesh0.edge_faces(e0).map(|face_id0|{
|
||||
MinkowskiFace::FaceVert(face_id0,v1)
|
||||
let e0f=self.mesh0.edge_faces(e0);
|
||||
Cow::Owned([(e0f[0],e0f[1]),(e0f[1],e0f[0])].map(|(edge_face_id0,other_edge_face_id0)|{
|
||||
let mut best_edge=None;
|
||||
let mut best_d=Planar64::MAX;
|
||||
let edge_face0_n=self.mesh0.face_nd(edge_face_id0).0;
|
||||
let other_edge_face0_n=self.mesh0.face_nd(other_edge_face_id0).0;
|
||||
let v1e=self.mesh1.vert_directed_edges(v1);
|
||||
for &directed_edge_id1 in v1e.iter(){
|
||||
let edge1_n=self.mesh1.directed_edge_n(directed_edge_id1);
|
||||
if edge_face0_n.dot(edge1_n)<Planar64::ZERO{
|
||||
let d=other_edge_face0_n.dot(edge1_n);
|
||||
if d<best_d{
|
||||
best_d=d;
|
||||
best_edge=Some(directed_edge_id1);
|
||||
}
|
||||
}
|
||||
}
|
||||
best_edge.map_or(
|
||||
MinkowskiFace::FaceVert(edge_face_id0,v1),
|
||||
|directed_edge_id1|MinkowskiFace::EdgeEdge(e0,directed_edge_id1.as_edge_id())
|
||||
)
|
||||
}))
|
||||
},
|
||||
}
|
||||
|
@ -731,8 +731,7 @@ impl PhysicsModel{
|
||||
|
||||
#[derive(Debug,Clone,Eq,Hash,PartialEq)]
|
||||
struct ContactCollision{
|
||||
//face_id:crate::model_physics::MinkowskiFace,
|
||||
face_id:crate::model_physics::FaceId,
|
||||
face_id:crate::model_physics::MinkowskiFace,
|
||||
model_id:usize,//using id to avoid lifetimes
|
||||
}
|
||||
#[derive(Debug,Clone,Eq,Hash,PartialEq)]
|
||||
@ -751,7 +750,7 @@ impl Collision{
|
||||
|&Collision::Intersect(IntersectCollision{model_id})=>model_id,
|
||||
}
|
||||
}
|
||||
fn face_id(&self)->Option<crate::model_physics::FaceId>{
|
||||
fn face_id(&self)->Option<crate::model_physics::MinkowskiFace>{
|
||||
match self{
|
||||
&Collision::Contact(ContactCollision{model_id:_,face_id})=>Some(face_id),
|
||||
&Collision::Intersect(IntersectCollision{model_id:_})=>None,
|
||||
@ -790,9 +789,7 @@ impl TouchingState{
|
||||
match models.attr(contact.model_id){
|
||||
PhysicsCollisionAttributes::Contact{contacting,general}=>{
|
||||
match &general.accelerator{
|
||||
Some(accelerator)=>{
|
||||
a+=accelerator.acceleration;
|
||||
},
|
||||
Some(accelerator)=>a+=accelerator.acceleration,
|
||||
None=>(),
|
||||
}
|
||||
},
|
||||
@ -803,9 +800,7 @@ impl TouchingState{
|
||||
match models.attr(intersect.model_id){
|
||||
PhysicsCollisionAttributes::Intersect{intersecting,general}=>{
|
||||
match &general.accelerator{
|
||||
Some(accelerator)=>{
|
||||
a+=accelerator.acceleration;
|
||||
},
|
||||
Some(accelerator)=>a+=accelerator.acceleration,
|
||||
None=>(),
|
||||
}
|
||||
},
|
||||
@ -877,10 +872,12 @@ impl TouchingState{
|
||||
self.constrain_acceleration(models,&mut a);
|
||||
(move_state,a)
|
||||
}
|
||||
fn predict_collision_end(&self,collector:&mut crate::instruction::InstructionCollector<PhysicsInstruction>,models:&PhysicsModels,body:&Body,time:Time){
|
||||
fn predict_collision_end(&self,collector:&mut crate::instruction::InstructionCollector<PhysicsInstruction>,models:&PhysicsModels,style_mesh:&TransformedMesh,body:&Body,time:Time){
|
||||
let relative_body=VirtualBody::relative(&Body::default(),body).body(time);
|
||||
for contact in &self.contacts{
|
||||
//detect face slide off
|
||||
collector.collect(models.mesh(contact.model_id).brute_out_face(body,collector.time(),contact.face_id).map(|(face,time)|{
|
||||
let minkowski=crate::model_physics::MinkowskiMesh::minkowski_sum(&style_mesh,&models.mesh(contact.model_id));
|
||||
collector.collect(minkowski.predict_collision_face_out(&relative_body,collector.time(),contact.face_id).map(|(face,time)|{
|
||||
TimedInstruction{
|
||||
time,
|
||||
instruction:PhysicsInstruction::CollisionEnd(
|
||||
@ -889,10 +886,10 @@ impl TouchingState{
|
||||
}
|
||||
}));
|
||||
}
|
||||
let relative_body=VirtualBody::relative(&Body::default(),body).body(time);
|
||||
for intersect in &self.intersects{
|
||||
//detect model collision in reverse
|
||||
collector.collect(models.mesh(intersect.model_id).brute_out(&relative_body,collector.time()).map(|(face,time)|{
|
||||
let minkowski=crate::model_physics::MinkowskiMesh::minkowski_sum(&style_mesh,&models.mesh(intersect.model_id));
|
||||
collector.collect(minkowski.predict_collision_out(&relative_body,collector.time()).map(|(face,time)|{
|
||||
TimedInstruction{
|
||||
time,
|
||||
instruction:PhysicsInstruction::CollisionEnd(
|
||||
@ -1180,8 +1177,9 @@ impl crate::instruction::InstructionEmitter<PhysicsInstruction> for PhysicsState
|
||||
|
||||
collector.collect(self.next_move_instruction());
|
||||
|
||||
let style_mesh=self.style.mesh();
|
||||
//check for collision ends
|
||||
self.touching.predict_collision_end(&mut collector,&self.models,&self.body,self.time);
|
||||
self.touching.predict_collision_end(&mut collector,&self.models,&style_mesh,&self.body,self.time);
|
||||
//check for collision starts
|
||||
let mut aabb=crate::aabb::Aabb::default();
|
||||
aabb.grow(self.body.extrapolated_position(self.time));
|
||||
@ -1191,9 +1189,8 @@ impl crate::instruction::InstructionEmitter<PhysicsInstruction> for PhysicsState
|
||||
let relative_body=VirtualBody::relative(&Body::default(),&self.body).body(self.time);
|
||||
self.bvh.the_tester(&aabb,&mut |id|{
|
||||
//no checks are needed because of the time limits.
|
||||
//let minkowski=crate::model_physics::MinkowskiMesh::minkowski_sum(self.style.mesh,&self.models.mesh(id));
|
||||
//collector.collect(crate::face_crawler::predict_collision(&minkowski,&relative_body,collector.time()).map(|(face,time)|{
|
||||
collector.collect(self.models.mesh(id).brute_in(&relative_body,collector.time()).map(|(face,time)|{
|
||||
let minkowski=crate::model_physics::MinkowskiMesh::minkowski_sum(&style_mesh,&self.models.mesh(id));
|
||||
collector.collect(minkowski.predict_collision_in(&relative_body,collector.time()).map(|(face,time)|{
|
||||
TimedInstruction{time,instruction:PhysicsInstruction::CollisionStart(match self.models.attr(id){
|
||||
PhysicsCollisionAttributes::Contact{contacting:_,general:_}=>Collision::Contact(ContactCollision{model_id:id,face_id:face}),
|
||||
PhysicsCollisionAttributes::Intersect{intersecting:_,general:_}=>Collision::Intersect(IntersectCollision{model_id:id}),
|
||||
@ -1544,10 +1541,7 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
|
||||
PhysicsInputInstruction::Idle => {refresh_walk_target=false;},//literally idle!
|
||||
}
|
||||
if refresh_walk_target{
|
||||
let mut a=self.refresh_walk_target();
|
||||
if let Some(rocket_force)=self.style.rocket_force{
|
||||
a+=self.style.get_propulsion_control_dir(&self.camera,self.controls,&self.next_mouse,self.time)*rocket_force;
|
||||
}
|
||||
let a=self.refresh_walk_target();
|
||||
if set_acceleration_cull(&mut self.body,&mut self.touching,&self.models,a){
|
||||
(self.move_state,self.body.acceleration)=self.touching.get_move_state(&self.body,&self.models,&self.style,&self.camera,self.controls,&self.next_mouse,self.time);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user