Compare commits
20 Commits
luau-md-de
...
trj-lifeti
| Author | SHA1 | Date | |
|---|---|---|---|
|
b4e1678b48
|
|||
|
170e2b9bce
|
|||
| 3e0fc54852 | |||
|
acea52646a
|
|||
|
7220506fd5
|
|||
|
8f94234ddc
|
|||
|
36143b8b69
|
|||
|
3893b2f44f
|
|||
|
d62ff68baa
|
|||
|
2331bef281
|
|||
| 31b52f7c34 | |||
|
b12c495a33
|
|||
|
8329eadb18
|
|||
|
4b2f93be66
|
|||
|
469ab48156
|
|||
|
c2650adf54
|
|||
| cdafbb4077 | |||
|
087e95b1f7
|
|||
|
e46a51319f
|
|||
|
a3b0306430
|
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -3904,7 +3904,6 @@ dependencies = [
|
||||
"arrayvec",
|
||||
"glam",
|
||||
"id",
|
||||
"mlua",
|
||||
"strafesnet_common",
|
||||
]
|
||||
|
||||
|
||||
@@ -909,7 +909,7 @@ impl GraphicsState{
|
||||
|
||||
// update rotation
|
||||
let camera_uniforms=self.camera.to_uniform_data(
|
||||
frame_state.body.extrapolated_position(frame_state.time).map(Into::<f32>::into).to_array().into(),
|
||||
frame_state.trajectory.extrapolated_position(frame_state.time).map(Into::<f32>::into).to_array().into(),
|
||||
frame_state.camera.simulate_move_angles(glam::IVec2::ZERO)
|
||||
);
|
||||
self.staging_belt
|
||||
|
||||
@@ -7,7 +7,6 @@ edition = "2024"
|
||||
arrayvec = "0.7.6"
|
||||
glam = "0.30.0"
|
||||
id = { version = "0.1.0", registry = "strafesnet" }
|
||||
mlua = { version = "0.11.5", features = ["luau"] }
|
||||
strafesnet_common = { path = "../../lib/common", registry = "strafesnet" }
|
||||
|
||||
[lints]
|
||||
|
||||
@@ -2,12 +2,19 @@ use strafesnet_common::aabb;
|
||||
use strafesnet_common::integer::{self,vec3,Time,Planar64,Planar64Vec3};
|
||||
#[derive(Clone,Copy,Debug,Hash)]
|
||||
pub struct Body<T>{
|
||||
pub position:Planar64Vec3,//I64 where 2^32 = 1 u
|
||||
pub velocity:Planar64Vec3,//I64 where 2^32 = 1 u/s
|
||||
pub acceleration:Planar64Vec3,//I64 where 2^32 = 1 u/s/s
|
||||
pub time:Time<T>,//nanoseconds x xxxxD!
|
||||
pub position:Planar64Vec3,
|
||||
pub velocity:Planar64Vec3,
|
||||
pub time:Time<T>,
|
||||
}
|
||||
impl<T> std::ops::Neg for Body<T>{
|
||||
#[derive(Clone,Copy,Debug,Hash)]
|
||||
pub struct Trajectory<'a,T>{
|
||||
pub position:Planar64Vec3,
|
||||
pub velocity:Planar64Vec3,
|
||||
pub acceleration:Planar64Vec3,
|
||||
pub time:Time<T>,
|
||||
lifetime:core::marker::PhantomData<&'a Body<T>>,
|
||||
}
|
||||
impl<T> std::ops::Neg for Trajectory<'_,T>{
|
||||
type Output=Self;
|
||||
fn neg(self)->Self::Output{
|
||||
Self{
|
||||
@@ -15,23 +22,52 @@ impl<T> std::ops::Neg for Body<T>{
|
||||
velocity:-self.velocity,
|
||||
acceleration:self.acceleration,
|
||||
time:-self.time,
|
||||
lifetime:core::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T:Copy> std::ops::Neg for &Body<T>{
|
||||
type Output=Body<T>;
|
||||
impl<'a,T:Copy> std::ops::Neg for &Trajectory<'a,T>{
|
||||
type Output=Trajectory<'a,T>;
|
||||
fn neg(self)->Self::Output{
|
||||
Body{
|
||||
Trajectory{
|
||||
position:self.position,
|
||||
velocity:-self.velocity,
|
||||
acceleration:self.acceleration,
|
||||
time:-self.time,
|
||||
lifetime:core::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Body<T>
|
||||
where Time<T>:Copy,
|
||||
{
|
||||
pub const ZERO:Self=Self::new(vec3::zero(),vec3::zero(),Time::ZERO);
|
||||
pub const fn new(position:Planar64Vec3,velocity:Planar64Vec3,time:Time<T>)->Self{
|
||||
Self{
|
||||
position,
|
||||
velocity,
|
||||
time,
|
||||
}
|
||||
}
|
||||
pub const fn with_acceleration<'a>(&'a self,acceleration:Planar64Vec3)->Trajectory<'a,T>{
|
||||
let Body{
|
||||
position,
|
||||
velocity,
|
||||
time,
|
||||
}=*self;
|
||||
Trajectory{
|
||||
position,
|
||||
velocity,
|
||||
acceleration,
|
||||
time,
|
||||
lifetime:core::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Trajectory<'_,T>
|
||||
where Time<T>:Copy,
|
||||
{
|
||||
pub const ZERO:Self=Self::new(vec3::zero(),vec3::zero(),vec3::zero(),Time::ZERO);
|
||||
pub const fn new(position:Planar64Vec3,velocity:Planar64Vec3,acceleration:Planar64Vec3,time:Time<T>)->Self{
|
||||
@@ -40,15 +76,17 @@ impl<T> Body<T>
|
||||
velocity,
|
||||
acceleration,
|
||||
time,
|
||||
lifetime:core::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
pub const fn relative_to<'a>(&'a self,body0:&'a Body<T>)->VirtualBody<'a,T>{
|
||||
pub fn relative_to(&self,trj0:&Self,time:Time<T>)->Self{
|
||||
//(p0,v0,a0,t0)
|
||||
//(p1,v1,a1,t1)
|
||||
VirtualBody{
|
||||
body0,
|
||||
body1:self,
|
||||
}
|
||||
Trajectory::new(
|
||||
self.extrapolated_position(time)-trj0.extrapolated_position(time),
|
||||
self.extrapolated_velocity(time)-trj0.extrapolated_velocity(time),
|
||||
self.acceleration-trj0.acceleration,
|
||||
time)
|
||||
}
|
||||
pub fn extrapolated_position(&self,time:Time<T>)->Planar64Vec3{
|
||||
let dt=time-self.time;
|
||||
@@ -60,10 +98,12 @@ impl<T> Body<T>
|
||||
let dt=time-self.time;
|
||||
self.velocity+(self.acceleration*dt).map(|elem|elem.divide().clamp_1())
|
||||
}
|
||||
pub fn advance_time(&mut self,time:Time<T>){
|
||||
self.position=self.extrapolated_position(time);
|
||||
self.velocity=self.extrapolated_velocity(time);
|
||||
self.time=time;
|
||||
pub fn extrapolated_body(&self,time:Time<T>)->Body<T>{
|
||||
Body::new(
|
||||
self.extrapolated_position(time),
|
||||
self.extrapolated_velocity(time),
|
||||
time,
|
||||
)
|
||||
}
|
||||
pub fn extrapolated_position_ratio_dt<Num,Den,N1,D1,N2,N3,D2,N4,T1>(&self,dt:integer::Ratio<Num,Den>)->Planar64Vec3
|
||||
where
|
||||
@@ -101,10 +141,12 @@ impl<T> Body<T>
|
||||
// a*dt + v
|
||||
self.acceleration.map(|elem|(dt*elem).divide().clamp())+self.velocity
|
||||
}
|
||||
pub fn advance_time_ratio_dt(&mut self,dt:crate::model::GigaTime){
|
||||
self.position=self.extrapolated_position_ratio_dt(dt);
|
||||
self.velocity=self.extrapolated_velocity_ratio_dt(dt);
|
||||
self.time+=dt.into();
|
||||
pub fn extrapolated_body_ratio_dt(&self,dt:crate::model::GigaTime)->Body<T>{
|
||||
Body::new(
|
||||
self.extrapolated_position_ratio_dt(dt),
|
||||
self.extrapolated_velocity_ratio_dt(dt),
|
||||
self.time+dt.into(),
|
||||
)
|
||||
}
|
||||
pub fn infinity_dir(&self)->Option<Planar64Vec3>{
|
||||
if self.velocity==vec3::zero(){
|
||||
@@ -144,28 +186,12 @@ impl<T> Body<T>
|
||||
|
||||
}
|
||||
impl<T> std::fmt::Display for Body<T>{
|
||||
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||
write!(f,"p({}) v({}) t({})",self.position,self.velocity,self.time)
|
||||
}
|
||||
}
|
||||
impl<T> std::fmt::Display for Trajectory<'_,T>{
|
||||
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||
write!(f,"p({}) v({}) a({}) t({})",self.position,self.velocity,self.acceleration,self.time)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VirtualBody<'a,T>{
|
||||
body0:&'a Body<T>,
|
||||
body1:&'a Body<T>,
|
||||
}
|
||||
impl<T> VirtualBody<'_,T>
|
||||
where Time<T>:Copy,
|
||||
{
|
||||
pub fn extrapolated_position(&self,time:Time<T>)->Planar64Vec3{
|
||||
self.body1.extrapolated_position(time)-self.body0.extrapolated_position(time)
|
||||
}
|
||||
pub fn extrapolated_velocity(&self,time:Time<T>)->Planar64Vec3{
|
||||
self.body1.extrapolated_velocity(time)-self.body0.extrapolated_velocity(time)
|
||||
}
|
||||
pub fn acceleration(&self)->Planar64Vec3{
|
||||
self.body1.acceleration-self.body0.acceleration
|
||||
}
|
||||
pub fn body(&self,time:Time<T>)->Body<T>{
|
||||
Body::new(self.extrapolated_position(time),self.extrapolated_velocity(time),self.acceleration(),time)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::model::{into_giga_time,GigaTime,FEV,MeshQuery,DirectedEdge};
|
||||
use strafesnet_common::integer::{Fixed,Ratio,vec3::Vector3};
|
||||
use crate::physics::{Time,Body};
|
||||
use crate::physics::{Time,Trajectory};
|
||||
|
||||
use core::ops::Bound;
|
||||
|
||||
@@ -74,7 +74,7 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
|
||||
<F as core::ops::Mul<Fixed<1,32>>>::Output:core::iter::Sum,
|
||||
M::Offset:core::ops::Sub<<F as std::ops::Mul<Fixed<1,32>>>::Output>,
|
||||
{
|
||||
fn next_transition(&self,mesh:&M,body:&Body,lower_bound:Bound<GigaTime>,mut upper_bound:Bound<GigaTime>)->Transition<M>{
|
||||
fn next_transition(&self,mesh:&M,trajectory:&Trajectory,lower_bound:Bound<GigaTime>,mut upper_bound:Bound<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;
|
||||
@@ -86,8 +86,8 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
|
||||
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 low(&lower_bound,&dt)&&upp(&dt,&upper_bound)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
|
||||
for dt in Fixed::<4,128>::zeroes2((n.dot(trajectory.position)-d)*2,n.dot(trajectory.velocity)*2,n.dot(trajectory.acceleration)){
|
||||
if low(&lower_bound,&dt)&&upp(&dt,&upper_bound)&&n.dot(trajectory.extrapolated_velocity_ratio_dt(dt)).is_negative(){
|
||||
upper_bound=Bound::Included(dt);
|
||||
best_transition=Transition::Hit(face_id,dt);
|
||||
break;
|
||||
@@ -101,8 +101,8 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
|
||||
//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 low(&lower_bound,&dt)&&upp(&dt,&upper_bound)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
|
||||
for dt in Fixed::<4,128>::zeroes2(n.dot(trajectory.position*2-(mesh.vert(v0)+mesh.vert(v1))).wrap_4(),n.dot(trajectory.velocity).wrap_4()*2,n.dot(trajectory.acceleration).wrap_4()){
|
||||
if low(&lower_bound,&dt)&&upp(&dt,&upper_bound)&&n.dot(trajectory.extrapolated_velocity_ratio_dt(dt)).is_negative(){
|
||||
upper_bound=Bound::Included(dt);
|
||||
best_transition=Transition::Next(FEV::Edge(directed_edge_id.as_undirected()),dt);
|
||||
break;
|
||||
@@ -117,15 +117,15 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
|
||||
let &[ev0,ev1]=edge_verts.as_ref();
|
||||
let (v0,v1)=(mesh.vert(ev0),mesh.vert(ev1));
|
||||
let edge_n=v1-v0;
|
||||
let delta_pos=body.position*2-(v0+v1);
|
||||
let delta_pos=trajectory.position*2-(v0+v1);
|
||||
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 low(&lower_bound,&dt)&&upp(&dt,&upper_bound)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
|
||||
for dt in Fixed::<4,128>::zeroes2(n.dot(delta_pos).wrap_4(),n.dot(trajectory.velocity).wrap_4()*2,n.dot(trajectory.acceleration).wrap_4()){
|
||||
if low(&lower_bound,&dt)&&upp(&dt,&upper_bound)&&n.dot(trajectory.extrapolated_velocity_ratio_dt(dt)).is_negative(){
|
||||
upper_bound=Bound::Included(dt);
|
||||
best_transition=Transition::Next(FEV::Face(edge_face_id),dt);
|
||||
break;
|
||||
@@ -136,8 +136,8 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
|
||||
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 low(&lower_bound,&dt)&&upp(&dt,&upper_bound)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
|
||||
for dt in Fixed::<2,64>::zeroes2((n.dot(trajectory.position-mesh.vert(vert_id)))*2,n.dot(trajectory.velocity)*2,n.dot(trajectory.acceleration)){
|
||||
if low(&lower_bound,&dt)&&upp(&dt,&upper_bound)&&n.dot(trajectory.extrapolated_velocity_ratio_dt(dt)).is_negative(){
|
||||
let dt=Ratio::new(dt.num.widen_4(),dt.den.widen_4());
|
||||
upper_bound=Bound::Included(dt);
|
||||
best_transition=Transition::Next(FEV::Vert(vert_id),dt);
|
||||
@@ -152,8 +152,8 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
|
||||
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 low(&lower_bound,&dt)&&upp(&dt,&upper_bound)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
|
||||
for dt in Fixed::<2,64>::zeroes2((n.dot(trajectory.position-mesh.vert(vert_id)))*2,n.dot(trajectory.velocity)*2,n.dot(trajectory.acceleration)){
|
||||
if low(&lower_bound,&dt)&&upp(&dt,&upper_bound)&&n.dot(trajectory.extrapolated_velocity_ratio_dt(dt)).is_negative(){
|
||||
let dt=Ratio::new(dt.num.widen_4(),dt.den.widen_4());
|
||||
upper_bound=Bound::Included(dt);
|
||||
best_transition=Transition::Next(FEV::Edge(directed_edge_id.as_undirected()),dt);
|
||||
@@ -166,11 +166,11 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
|
||||
}
|
||||
best_transition
|
||||
}
|
||||
pub fn crawl(mut self,mesh:&M,relative_body:&Body,lower_bound:Bound<&Time>,upper_bound:Bound<&Time>)->CrawlResult<M>{
|
||||
let mut lower_bound=lower_bound.map(|&t|into_giga_time(t,relative_body.time));
|
||||
let upper_bound=upper_bound.map(|&t|into_giga_time(t,relative_body.time));
|
||||
pub fn crawl(mut self,mesh:&M,trajectory:&Trajectory,lower_bound:Bound<&Time>,upper_bound:Bound<&Time>)->CrawlResult<M>{
|
||||
let mut lower_bound=lower_bound.map(|&t|into_giga_time(t,trajectory.time));
|
||||
let upper_bound=upper_bound.map(|&t|into_giga_time(t,trajectory.time));
|
||||
for _ in 0..20{
|
||||
match self.next_transition(mesh,relative_body,lower_bound,upper_bound){
|
||||
match self.next_transition(mesh,trajectory,lower_bound,upper_bound){
|
||||
Transition::Miss=>return CrawlResult::Miss(self),
|
||||
Transition::Next(next_fev,next_time)=>(self,lower_bound)=(next_fev,Bound::Included(next_time)),
|
||||
Transition::Hit(face,time)=>return CrawlResult::Hit(face,time),
|
||||
|
||||
@@ -3,7 +3,6 @@ mod face_crawler;
|
||||
mod model;
|
||||
mod push_solve;
|
||||
mod minimum_difference;
|
||||
mod minimum_difference_lua;
|
||||
|
||||
pub mod physics;
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::model::{MinkowskiMesh,MinkowskiVert};
|
||||
// written by Trey Reynolds in 2021
|
||||
|
||||
type Simplex<const N:usize,Vert>=[Vert;N];
|
||||
#[derive(Clone,Copy,Debug)]
|
||||
#[derive(Clone,Copy)]
|
||||
enum Simplex1_3<Vert>{
|
||||
Simplex1(Simplex<1,Vert>),
|
||||
Simplex2(Simplex<2,Vert>),
|
||||
@@ -136,15 +136,12 @@ fn reduce1<M:MeshQuery>(
|
||||
mesh:&M,
|
||||
point:Planar64Vec3,
|
||||
)->Reduced<M::Vert>{
|
||||
println!("reduce1");
|
||||
// --debug.profilebegin("reduceSimplex0")
|
||||
// local a = a1 - a0
|
||||
let p0=mesh.vert(v0);
|
||||
|
||||
println!("p0={p0}");
|
||||
// local p = -a
|
||||
let p=-(p0+point);
|
||||
println!("p={p}");
|
||||
|
||||
// local direction = p
|
||||
let mut dir=p;
|
||||
@@ -168,7 +165,6 @@ fn reduce2<M:MeshQuery>(
|
||||
mesh:&M,
|
||||
point:Planar64Vec3,
|
||||
)->Reduced<M::Vert>{
|
||||
println!("reduce2");
|
||||
// --debug.profilebegin("reduceSimplex1")
|
||||
// local a = a1 - a0
|
||||
// local b = b1 - b0
|
||||
@@ -226,7 +222,6 @@ fn reduce3<M:MeshQuery>(
|
||||
mesh:&M,
|
||||
point:Planar64Vec3,
|
||||
)->Reduced<M::Vert>{
|
||||
println!("reduce3");
|
||||
// --debug.profilebegin("reduceSimplex2")
|
||||
// local a = a1 - a0
|
||||
// local b = b1 - b0
|
||||
@@ -336,7 +331,6 @@ fn reduce4<M:MeshQuery>(
|
||||
mesh:&M,
|
||||
point:Planar64Vec3,
|
||||
)->Reduce<M::Vert>{
|
||||
println!("reduce4");
|
||||
// --debug.profilebegin("reduceSimplex3")
|
||||
// local a = a1 - a0
|
||||
// local b = b1 - b0
|
||||
@@ -355,10 +349,6 @@ fn reduce4<M:MeshQuery>(
|
||||
let mut u=p1-p0;
|
||||
let mut v=p2-p0;
|
||||
let w=p3-p0;
|
||||
println!("p={p}");
|
||||
println!("u={u}");
|
||||
println!("v={v}");
|
||||
println!("w={w}");
|
||||
|
||||
// local uv = u:Cross(v)
|
||||
// local vw = v:Cross(w)
|
||||
@@ -429,28 +419,20 @@ fn reduce4<M:MeshQuery>(
|
||||
let pv=p.cross(v);
|
||||
let uv_up=uv.dot(up);
|
||||
let uv_pv=uv.dot(pv);
|
||||
println!("up={up}");
|
||||
println!("pv={pv}");
|
||||
println!("uv_up={uv_up}");
|
||||
println!("uv_pv={uv_pv}");
|
||||
|
||||
// if uv_up >= 0 and uv_pv >= 0 then
|
||||
if !uv_up.is_negative()&&!uv_pv.is_negative(){
|
||||
// local direction = uvw < 0 and uv or -uv
|
||||
// return direction, a0, a1, b0, b1, c0, c1
|
||||
if uv_w.is_negative(){
|
||||
println!("a");
|
||||
return Reduce::Reduced(Reduced{
|
||||
dir:narrow_dir2(uv),
|
||||
simplex:Simplex1_3::Simplex3([v0,v1,v2]),
|
||||
});
|
||||
let dir=if uv_w.is_negative(){
|
||||
narrow_dir2(uv)
|
||||
}else{
|
||||
println!("b");
|
||||
return Reduce::Reduced(Reduced{
|
||||
dir:narrow_dir2(-uv),
|
||||
simplex:Simplex1_3::Simplex3([v0,v1,v2]),
|
||||
});
|
||||
}
|
||||
narrow_dir2(-uv)
|
||||
};
|
||||
return Reduce::Reduced(Reduced{
|
||||
dir,
|
||||
simplex:Simplex1_3::Simplex3([v0,v1,v2]),
|
||||
});
|
||||
}
|
||||
|
||||
// local u_u = u:Dot(u)
|
||||
@@ -482,22 +464,17 @@ fn reduce4<M:MeshQuery>(
|
||||
if direction==vec3::zero(){
|
||||
// direction = uvw < 0 and uv or -uv
|
||||
// return direction, a0, a1, b0, b1
|
||||
if uv_w.is_negative(){
|
||||
println!("c");
|
||||
return Reduce::Reduced(Reduced{
|
||||
dir:narrow_dir2(uv),
|
||||
simplex:Simplex1_3::Simplex2([v0,v1]),
|
||||
});
|
||||
let dir=if uv_w.is_negative(){
|
||||
narrow_dir2(uv)
|
||||
}else{
|
||||
println!("d");
|
||||
return Reduce::Reduced(Reduced{
|
||||
dir:narrow_dir2(-uv),
|
||||
simplex:Simplex1_3::Simplex2([v0,v1]),
|
||||
});
|
||||
}
|
||||
narrow_dir2(-uv)
|
||||
};
|
||||
return Reduce::Reduced(Reduced{
|
||||
dir,
|
||||
simplex:Simplex1_3::Simplex2([v0,v1]),
|
||||
});
|
||||
}
|
||||
|
||||
println!("e");
|
||||
// return direction, a0, a1, b0, b1
|
||||
return Reduce::Reduced(Reduced{
|
||||
dir:narrow_dir3(direction),
|
||||
@@ -510,22 +487,17 @@ fn reduce4<M:MeshQuery>(
|
||||
// if direction.magnitude == 0 then
|
||||
if dir==vec3::zero(){
|
||||
// direction = uvw < 0 and uv or -uv
|
||||
if uv_w.is_negative(){
|
||||
println!("f");
|
||||
return Reduce::Reduced(Reduced{
|
||||
dir:narrow_dir2(uv),
|
||||
simplex:Simplex1_3::Simplex1([v0]),
|
||||
});
|
||||
let dir=if uv_w.is_negative(){
|
||||
narrow_dir2(uv)
|
||||
}else{
|
||||
println!("g");
|
||||
return Reduce::Reduced(Reduced{
|
||||
dir:narrow_dir2(-uv),
|
||||
simplex:Simplex1_3::Simplex1([v0]),
|
||||
});
|
||||
}
|
||||
narrow_dir2(-uv)
|
||||
};
|
||||
return Reduce::Reduced(Reduced{
|
||||
dir,
|
||||
simplex:Simplex1_3::Simplex1([v0]),
|
||||
});
|
||||
}
|
||||
|
||||
println!("h");
|
||||
// return direction, a0, a1
|
||||
Reduce::Reduced(Reduced{
|
||||
dir,
|
||||
@@ -686,7 +658,7 @@ impl<M:MeshQuery,C:Contains> EVFinder<'_,M,C>{
|
||||
}
|
||||
}
|
||||
}
|
||||
/// 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
|
||||
/// This function hops along parallel vertices until it finds the EV which contains the closest point to `point`.
|
||||
fn crawl_to_closest_ev<M:MeshQuery>(mesh:&M,simplex:Simplex<2,M::Vert>,point:Planar64Vec3)->EV<M>{
|
||||
// naively start at the closest vertex
|
||||
// the closest vertex is not necessarily the one with the fewest boundary hops
|
||||
@@ -704,7 +676,7 @@ fn crawl_to_closest_ev<M:MeshQuery>(mesh:&M,simplex:Simplex<2,M::Vert>,point:Pla
|
||||
finder.crawl_boundaries(vert_id,point)
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// This function hops along connected vertices until it finds the FEV which contains the closest point to `point`.
|
||||
fn crawl_to_closest_fev<'a>(mesh:&MinkowskiMesh<'a>,simplex:Simplex<3,MinkowskiVert>,point:Planar64Vec3)->FEV::<MinkowskiMesh<'a>>{
|
||||
// naively start at the closest vertex
|
||||
// the closest vertex is not necessarily the one with the fewest boundary hops
|
||||
@@ -752,30 +724,18 @@ fn crawl_to_closest_fev<'a>(mesh:&MinkowskiMesh<'a>,simplex:Simplex<3,MinkowskiV
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InfiniteLoop;
|
||||
pub fn closest_fev_not_inside<'a>(mesh:&MinkowskiMesh<'a>,point:Planar64Vec3)->Result<Option<FEV<MinkowskiMesh<'a>>>,InfiniteLoop>{
|
||||
println!("=== LUA ===");
|
||||
let (hits,_details)=crate::minimum_difference_lua::minimum_difference_details(mesh,point).unwrap();
|
||||
println!("=== RUST ===");
|
||||
let closest_fev_not_inside=closest_fev_not_inside_inner(mesh,point).unwrap();
|
||||
assert_eq!(hits,closest_fev_not_inside.is_none(),"algorithms disagree");
|
||||
Ok(closest_fev_not_inside)
|
||||
}
|
||||
|
||||
pub fn closest_fev_not_inside_inner<'a>(mesh:&MinkowskiMesh<'a>,point:Planar64Vec3)->Result<Option<FEV<MinkowskiMesh<'a>>>,InfiniteLoop>{
|
||||
pub fn closest_fev_not_inside<'a>(mesh:&MinkowskiMesh<'a>,point:Planar64Vec3)->Option<FEV<MinkowskiMesh<'a>>>{
|
||||
const ENABLE_FAST_FAIL:bool=false;
|
||||
// TODO: remove mesh negation
|
||||
minimum_difference::<ENABLE_FAST_FAIL,_,_>(&-mesh,point,
|
||||
// on_exact
|
||||
|is_intersecting,simplex|{
|
||||
println!("on_exact simplex={simplex:?}");
|
||||
if is_intersecting{
|
||||
return Ok(None);
|
||||
return None;
|
||||
}
|
||||
// Convert simplex to FEV
|
||||
// Vertices must be inverted since the mesh is inverted
|
||||
Ok(Some(match simplex{
|
||||
Some(match simplex{
|
||||
Simplex1_3::Simplex1([v0])=>FEV::Vert(-v0),
|
||||
Simplex1_3::Simplex2([v0,v1])=>{
|
||||
// invert
|
||||
@@ -790,18 +750,17 @@ pub fn closest_fev_not_inside_inner<'a>(mesh:&MinkowskiMesh<'a>,point:Planar64Ve
|
||||
// return E or V in edge cases but I don't think that will break the face crawler
|
||||
crawl_to_closest_fev(mesh,[v0,v1,v2],point)
|
||||
},
|
||||
}))
|
||||
})
|
||||
},
|
||||
// on_escape
|
||||
|_simplex|{
|
||||
// intersection is guaranteed at this point
|
||||
// local norm, dist, u0, u1, v0, v1, w0, w1 = expand(queryP, queryQ, a0, a1, b0, b1, c0, c1, d0, d1, 1e-5)
|
||||
// let simplex=refine_to_exact(mesh,simplex);
|
||||
Ok(None)
|
||||
None
|
||||
},
|
||||
// fast_fail value is irrelevant and will never be returned!
|
||||
||unreachable!(),
|
||||
||Err(InfiniteLoop),
|
||||
||unreachable!()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -819,9 +778,7 @@ pub fn contains_point(mesh:&MinkowskiMesh<'_>,point:Planar64Vec3)->bool{
|
||||
true
|
||||
},
|
||||
// fast_fail value
|
||||
||false,
|
||||
// infinite loop
|
||||
||false,
|
||||
||false
|
||||
)
|
||||
}
|
||||
|
||||
@@ -836,7 +793,6 @@ fn minimum_difference<const ENABLE_FAST_FAIL:bool,T,M:MeshQuery>(
|
||||
on_exact:impl FnOnce(bool,Simplex1_3<M::Vert>)->T,
|
||||
on_escape:impl FnOnce(Simplex<4,M::Vert>)->T,
|
||||
on_fast_fail:impl FnOnce()->T,
|
||||
on_infinite_loop:impl FnOnce()->T,
|
||||
)->T{
|
||||
// local initialAxis = queryQ() - queryP()
|
||||
// local new_point_p = queryP(initialAxis)
|
||||
@@ -847,7 +803,6 @@ fn minimum_difference<const ENABLE_FAST_FAIL:bool,T,M:MeshQuery>(
|
||||
if initial_axis==vec3::zero(){
|
||||
initial_axis=choose_any_direction();
|
||||
}
|
||||
println!("initial_axis={initial_axis}");
|
||||
let last_point=mesh.farthest_vert(-initial_axis);
|
||||
// this represents the 'a' value in the commented code
|
||||
let mut last_pos=mesh.vert(last_point);
|
||||
@@ -855,9 +810,7 @@ fn minimum_difference<const ENABLE_FAST_FAIL:bool,T,M:MeshQuery>(
|
||||
|
||||
// exitRadius = testIntersection and 0 or exitRadius or 1/0
|
||||
// for _ = 1, 100 do
|
||||
for _ in 0..100{
|
||||
println!("direction={direction}");
|
||||
|
||||
loop{
|
||||
// new_point_p = queryP(-direction)
|
||||
// new_point_q = queryQ(direction)
|
||||
// local next_point = new_point_q - new_point_p
|
||||
@@ -865,11 +818,7 @@ fn minimum_difference<const ENABLE_FAST_FAIL:bool,T,M:MeshQuery>(
|
||||
let next_pos=mesh.vert(next_point);
|
||||
|
||||
// if -direction:Dot(next_point) > (exitRadius + radiusP + radiusQ)*direction.magnitude then
|
||||
let d=direction.dot(next_pos+point);
|
||||
let fast_fail=d.is_negative();
|
||||
println!("ENABLE_FAST_FAIL={ENABLE_FAST_FAIL} fast_fail={fast_fail} next_point={} dot={d}",next_pos+point);
|
||||
if ENABLE_FAST_FAIL&&fast_fail{
|
||||
println!("on_fast_fail");
|
||||
if ENABLE_FAST_FAIL&&direction.dot(next_pos+point).is_negative(){
|
||||
return on_fast_fail();
|
||||
}
|
||||
|
||||
@@ -878,11 +827,8 @@ fn minimum_difference<const ENABLE_FAST_FAIL:bool,T,M:MeshQuery>(
|
||||
// if
|
||||
// direction:Dot(next_point - a) <= 0 or
|
||||
// absDet(next_point, a, b, c) < 1e-6
|
||||
let cond1=!direction.dot(next_pos-last_pos).is_positive();
|
||||
let cond2=simplex_big.det_is_zero(mesh);
|
||||
println!("cond1={cond1} cond2={cond2}");
|
||||
if cond1||cond2{
|
||||
println!("on_exact");
|
||||
if !direction.dot(next_pos-last_pos).is_positive()
|
||||
||simplex_big.det_is_zero(mesh){
|
||||
// Found enough information to compute the exact closest point.
|
||||
// local norm = direction.unit
|
||||
// local dist = a:Dot(norm)
|
||||
@@ -895,7 +841,6 @@ fn minimum_difference<const ENABLE_FAST_FAIL:bool,T,M:MeshQuery>(
|
||||
match simplex_big.reduce(mesh,point){
|
||||
// if a and b and c and d then
|
||||
Reduce::Escape(simplex)=>{
|
||||
println!("on_escape");
|
||||
// Enough information to conclude that the meshes are intersecting.
|
||||
// Topology information is computed if needed.
|
||||
return on_escape(simplex);
|
||||
@@ -909,7 +854,6 @@ fn minimum_difference<const ENABLE_FAST_FAIL:bool,T,M:MeshQuery>(
|
||||
// next loop this will be a
|
||||
last_pos=next_pos;
|
||||
}
|
||||
on_infinite_loop()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -931,8 +875,7 @@ mod test{
|
||||
true
|
||||
},
|
||||
// fast_fail value
|
||||
||false,
|
||||
||false,
|
||||
||false
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
use mlua::{Lua,FromLuaMulti,IntoLuaMulti,Function,Result as LuaResult,Vector};
|
||||
use strafesnet_common::integer::{Planar64,Planar64Vec3,FixedFromFloatError};
|
||||
|
||||
use crate::model::{MeshQuery,MinkowskiMesh};
|
||||
|
||||
pub fn contains_point(
|
||||
mesh:&MinkowskiMesh,
|
||||
point:Planar64Vec3,
|
||||
)->LuaResult<bool>{
|
||||
Ok(minimum_difference(mesh,point,true)?.hits)
|
||||
}
|
||||
pub fn minimum_difference_details(
|
||||
mesh:&MinkowskiMesh,
|
||||
point:Planar64Vec3,
|
||||
)->LuaResult<(bool,Option<Details>)>{
|
||||
let md=minimum_difference(mesh,point,false)?;
|
||||
Ok((md.hits,md.details))
|
||||
}
|
||||
fn p64v3(v:Vector)->Result<Planar64Vec3,FixedFromFloatError>{
|
||||
Ok(Planar64Vec3::new([
|
||||
v.x().try_into()?,
|
||||
v.y().try_into()?,
|
||||
v.z().try_into()?,
|
||||
]))
|
||||
}
|
||||
fn vec(v:Planar64Vec3)->Vector{
|
||||
Vector::new(v.x.into(),v.y.into(),v.z.into())
|
||||
}
|
||||
struct MinimumDifference{
|
||||
hits:bool,
|
||||
details:Option<Details>
|
||||
}
|
||||
pub struct Details{
|
||||
pub distance:Planar64,
|
||||
pub p_pos:Planar64Vec3,
|
||||
pub p_norm:Planar64Vec3,
|
||||
pub q_pos:Planar64Vec3,
|
||||
pub q_norm:Planar64Vec3,
|
||||
}
|
||||
impl FromLuaMulti for MinimumDifference{
|
||||
fn from_lua_multi(mut values:mlua::MultiValue,_lua:&Lua)->LuaResult<Self>{
|
||||
match values.make_contiguous(){
|
||||
&mut [
|
||||
mlua::Value::Boolean(hits),
|
||||
mlua::Value::Nil,
|
||||
mlua::Value::Nil,
|
||||
mlua::Value::Nil,
|
||||
mlua::Value::Nil,
|
||||
mlua::Value::Nil,
|
||||
]=>Ok(Self{hits,details:None}),
|
||||
&mut [
|
||||
mlua::Value::Boolean(hits),
|
||||
mlua::Value::Number(distance),
|
||||
mlua::Value::Vector(p_pos),
|
||||
mlua::Value::Vector(p_norm),
|
||||
mlua::Value::Vector(q_pos),
|
||||
mlua::Value::Vector(q_norm),
|
||||
]=>Ok(Self{
|
||||
hits,
|
||||
details:Some(Details{
|
||||
distance:distance.try_into().unwrap(),
|
||||
p_pos:p64v3(p_pos).unwrap(),
|
||||
p_norm:p64v3(p_norm).unwrap(),
|
||||
q_pos:p64v3(q_pos).unwrap(),
|
||||
q_norm:p64v3(q_norm).unwrap(),
|
||||
}),
|
||||
}),
|
||||
&mut [
|
||||
mlua::Value::Boolean(hits),
|
||||
mlua::Value::Integer(distance),
|
||||
mlua::Value::Vector(p_pos),
|
||||
mlua::Value::Vector(p_norm),
|
||||
mlua::Value::Vector(q_pos),
|
||||
mlua::Value::Vector(q_norm),
|
||||
]=>Ok(Self{
|
||||
hits,
|
||||
details:Some(Details{
|
||||
distance:distance.into(),
|
||||
p_pos:p64v3(p_pos).unwrap(),
|
||||
p_norm:p64v3(p_norm).unwrap(),
|
||||
q_pos:p64v3(q_pos).unwrap(),
|
||||
q_norm:p64v3(q_norm).unwrap(),
|
||||
}),
|
||||
}),
|
||||
values=>Err(mlua::Error::runtime(format!("Invalid return values: {values:?}"))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Args{
|
||||
query_p:Function,
|
||||
radius_p:f64,
|
||||
query_q:Function,
|
||||
radius_q:f64,
|
||||
test_intersection:bool,
|
||||
}
|
||||
impl Args{
|
||||
fn new(
|
||||
lua:&Lua,
|
||||
mesh:&'static MinkowskiMesh<'static>,
|
||||
point:Planar64Vec3,
|
||||
test_intersection:bool,
|
||||
)->LuaResult<Self>{
|
||||
let radius_p=0.0;
|
||||
let radius_q=0.0;
|
||||
// Query the farthest point on the mesh in the given direction.
|
||||
let query_p=lua.create_function(move|_,dir:Option<Vector>|{
|
||||
let Some(dir)=dir else{
|
||||
return Ok(vec(mesh.mesh0.hint_point()));
|
||||
};
|
||||
let dir=p64v3(dir).unwrap();
|
||||
let vert_id=mesh.mesh0.farthest_vert(dir);
|
||||
let dir=mesh.mesh0.vert(vert_id);
|
||||
Ok(vec(dir))
|
||||
})?;
|
||||
// query_q is different since it includes the test point offset.
|
||||
let query_q=lua.create_function(move|_,dir:Option<Vector>|{
|
||||
let Some(dir)=dir else{
|
||||
return Ok(vec(mesh.mesh1.hint_point()+point));
|
||||
};
|
||||
let dir=p64v3(dir).unwrap();
|
||||
let vert_id=mesh.mesh1.farthest_vert(dir);
|
||||
let dir=mesh.mesh1.vert(vert_id)+point;
|
||||
Ok(vec(dir))
|
||||
})?;
|
||||
Ok(Args{
|
||||
query_p,
|
||||
radius_p,
|
||||
query_q,
|
||||
radius_q,
|
||||
test_intersection,
|
||||
})
|
||||
}
|
||||
}
|
||||
impl IntoLuaMulti for Args{
|
||||
fn into_lua_multi(self,lua:&Lua)->LuaResult<mlua::MultiValue>{
|
||||
use mlua::IntoLua;
|
||||
Ok(mlua::MultiValue::from_vec(vec![
|
||||
self.query_p.into_lua(lua)?,
|
||||
self.radius_p.into_lua(lua)?,
|
||||
self.query_q.into_lua(lua)?,
|
||||
self.radius_q.into_lua(lua)?,
|
||||
mlua::Value::Nil,
|
||||
self.test_intersection.into_lua(lua)?,
|
||||
]))
|
||||
}
|
||||
}
|
||||
|
||||
fn minimum_difference(
|
||||
mesh:&MinkowskiMesh,
|
||||
point:Planar64Vec3,
|
||||
test_intersection:bool,
|
||||
)->LuaResult<MinimumDifference>{
|
||||
let ctx=init_lua()?;
|
||||
// SAFETY: mesh lifetime must outlive args usages
|
||||
let mesh=unsafe{core::mem::transmute(mesh)};
|
||||
let args=Args::new(&ctx.lua,mesh,point,test_intersection)?;
|
||||
ctx.f.call(args)
|
||||
}
|
||||
|
||||
struct Ctx{
|
||||
lua:Lua,
|
||||
f:Function,
|
||||
}
|
||||
fn init_lua()->LuaResult<Ctx>{
|
||||
static SOURCE:std::sync::LazyLock<String>=std::sync::LazyLock::new(||std::fs::read_to_string("Trey-MinimumDifference.lua").unwrap());
|
||||
let lua=Lua::new();
|
||||
lua.sandbox(true)?;
|
||||
let lib_f=lua.load(SOURCE.as_str()).set_name("Trey-MinimumDifference").into_function()?;
|
||||
let lib:mlua::Table=lib_f.call(())?;
|
||||
let f=lib.raw_get("difference")?;
|
||||
Ok(Ctx{lua,f})
|
||||
}
|
||||
@@ -5,7 +5,7 @@ use strafesnet_common::model::{self,MeshId,PolygonIter};
|
||||
use strafesnet_common::integer::{self,vec3,Fixed,Planar64,Planar64Vec3,Ratio};
|
||||
use strafesnet_common::physics::Time;
|
||||
|
||||
type Body=crate::body::Body<strafesnet_common::physics::TimeInner>;
|
||||
type Trajectory<'a>=crate::body::Trajectory<'a,strafesnet_common::physics::TimeInner>;
|
||||
|
||||
struct AsRefHelper<T>(T);
|
||||
impl<T> AsRef<T> for AsRefHelper<T>{
|
||||
@@ -16,14 +16,14 @@ impl<T> AsRef<T> for AsRefHelper<T>{
|
||||
|
||||
pub trait UndirectedEdge{
|
||||
type DirectedEdge:Copy+DirectedEdge;
|
||||
fn as_directed(&self,parity:bool)->Self::DirectedEdge;
|
||||
fn as_directed(self,parity:bool)->Self::DirectedEdge;
|
||||
}
|
||||
pub trait DirectedEdge{
|
||||
pub trait DirectedEdge:Copy{
|
||||
type UndirectedEdge:Copy+std::fmt::Debug+UndirectedEdge;
|
||||
fn as_undirected(&self)->Self::UndirectedEdge;
|
||||
fn as_undirected(self)->Self::UndirectedEdge;
|
||||
fn parity(&self)->bool;
|
||||
//this is stupid but may work fine
|
||||
fn reverse(&self)-><<Self as DirectedEdge>::UndirectedEdge as UndirectedEdge>::DirectedEdge{
|
||||
fn reverse(self)-><<Self as DirectedEdge>::UndirectedEdge as UndirectedEdge>::DirectedEdge{
|
||||
self.as_undirected().as_directed(!self.parity())
|
||||
}
|
||||
}
|
||||
@@ -45,13 +45,13 @@ pub struct SubmeshFaceId(u32);
|
||||
|
||||
impl UndirectedEdge for SubmeshEdgeId{
|
||||
type DirectedEdge=SubmeshDirectedEdgeId;
|
||||
fn as_directed(&self,parity:bool)->SubmeshDirectedEdgeId{
|
||||
fn as_directed(self,parity:bool)->SubmeshDirectedEdgeId{
|
||||
SubmeshDirectedEdgeId(self.0|((parity as u32)<<(u32::BITS-1)))
|
||||
}
|
||||
}
|
||||
impl DirectedEdge for SubmeshDirectedEdgeId{
|
||||
type UndirectedEdge=SubmeshEdgeId;
|
||||
fn as_undirected(&self)->SubmeshEdgeId{
|
||||
fn as_undirected(self)->SubmeshEdgeId{
|
||||
SubmeshEdgeId(self.0&!(1<<(u32::BITS-1)))
|
||||
}
|
||||
fn parity(&self)->bool{
|
||||
@@ -597,7 +597,7 @@ impl core::ops::Neg for MinkowskiVert{
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Clone,Copy,Debug,Eq,PartialEq)]
|
||||
#[derive(Clone,Copy,Debug)]
|
||||
pub enum MinkowskiEdge{
|
||||
VertEdge(SubmeshVertId,SubmeshEdgeId),
|
||||
EdgeVert(SubmeshEdgeId,SubmeshVertId),
|
||||
@@ -605,14 +605,14 @@ pub enum MinkowskiEdge{
|
||||
}
|
||||
impl UndirectedEdge for MinkowskiEdge{
|
||||
type DirectedEdge=MinkowskiDirectedEdge;
|
||||
fn as_directed(&self,parity:bool)->Self::DirectedEdge{
|
||||
fn as_directed(self,parity:bool)->Self::DirectedEdge{
|
||||
match self{
|
||||
MinkowskiEdge::VertEdge(v0,e1)=>MinkowskiDirectedEdge::VertEdge(*v0,e1.as_directed(parity)),
|
||||
MinkowskiEdge::EdgeVert(e0,v1)=>MinkowskiDirectedEdge::EdgeVert(e0.as_directed(parity),*v1),
|
||||
MinkowskiEdge::VertEdge(v0,e1)=>MinkowskiDirectedEdge::VertEdge(v0,e1.as_directed(parity)),
|
||||
MinkowskiEdge::EdgeVert(e0,v1)=>MinkowskiDirectedEdge::EdgeVert(e0.as_directed(parity),v1),
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Clone,Copy,Debug,Eq,PartialEq)]
|
||||
#[derive(Clone,Copy,Debug)]
|
||||
pub enum MinkowskiDirectedEdge{
|
||||
VertEdge(SubmeshVertId,SubmeshDirectedEdgeId),
|
||||
EdgeVert(SubmeshDirectedEdgeId,SubmeshVertId),
|
||||
@@ -620,10 +620,10 @@ pub enum MinkowskiDirectedEdge{
|
||||
}
|
||||
impl DirectedEdge for MinkowskiDirectedEdge{
|
||||
type UndirectedEdge=MinkowskiEdge;
|
||||
fn as_undirected(&self)->Self::UndirectedEdge{
|
||||
fn as_undirected(self)->Self::UndirectedEdge{
|
||||
match self{
|
||||
MinkowskiDirectedEdge::VertEdge(v0,e1)=>MinkowskiEdge::VertEdge(*v0,e1.as_undirected()),
|
||||
MinkowskiDirectedEdge::EdgeVert(e0,v1)=>MinkowskiEdge::EdgeVert(e0.as_undirected(),*v1),
|
||||
MinkowskiDirectedEdge::VertEdge(v0,e1)=>MinkowskiEdge::VertEdge(v0,e1.as_undirected()),
|
||||
MinkowskiDirectedEdge::EdgeVert(e0,v1)=>MinkowskiEdge::EdgeVert(e0.as_undirected(),v1),
|
||||
}
|
||||
}
|
||||
fn parity(&self)->bool{
|
||||
@@ -645,8 +645,8 @@ pub enum MinkowskiFace{
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MinkowskiMesh<'a>{
|
||||
pub mesh0:TransformedMesh<'a>,
|
||||
pub mesh1:TransformedMesh<'a>,
|
||||
mesh0:TransformedMesh<'a>,
|
||||
mesh1:TransformedMesh<'a>,
|
||||
}
|
||||
|
||||
pub type GigaTime=Ratio<Fixed<4,128>,Fixed<4,128>>;
|
||||
@@ -670,39 +670,40 @@ impl MinkowskiMesh<'_>{
|
||||
mesh1,
|
||||
}
|
||||
}
|
||||
pub fn predict_collision_in(&self,relative_body:&Body,range:impl RangeBounds<Time>)->Result<Option<(MinkowskiFace,GigaTime)>,crate::minimum_difference::InfiniteLoop>{
|
||||
let Some(fev)=crate::minimum_difference::closest_fev_not_inside(self,relative_body.position)?else{
|
||||
return Ok(None);
|
||||
pub fn predict_collision_in(&self,trajectory:&Trajectory,range:impl RangeBounds<Time>)->Option<(MinkowskiFace,GigaTime)>{
|
||||
let start_position=match range.start_bound(){
|
||||
Bound::Included(time)=>trajectory.extrapolated_position(*time),
|
||||
Bound::Excluded(time)=>trajectory.extrapolated_position(*time),
|
||||
Bound::Unbounded=>trajectory.position,
|
||||
};
|
||||
let fev=crate::minimum_difference::closest_fev_not_inside(self,start_position)?;
|
||||
//continue forwards along the body parabola
|
||||
Ok(fev.crawl(self,relative_body,range.start_bound(),range.end_bound()).hit())
|
||||
fev.crawl(self,trajectory,range.start_bound(),range.end_bound()).hit()
|
||||
}
|
||||
pub fn predict_collision_out(&self,relative_body:&Body,range:impl RangeBounds<Time>)->Result<Option<(MinkowskiFace,GigaTime)>,crate::minimum_difference::InfiniteLoop>{
|
||||
pub fn predict_collision_out(&self,trajectory:&Trajectory,range:impl RangeBounds<Time>)->Option<(MinkowskiFace,GigaTime)>{
|
||||
let (lower_bound,upper_bound)=(range.start_bound(),range.end_bound());
|
||||
// TODO: handle unbounded collision using infinity fev
|
||||
let time=match upper_bound{
|
||||
Bound::Included(&time)=>time,
|
||||
Bound::Excluded(&time)=>time,
|
||||
Bound::Unbounded=>unimplemented!("unbounded collision out"),
|
||||
};
|
||||
let Some(fev)=crate::minimum_difference::closest_fev_not_inside(self,relative_body.extrapolated_position(time))?else{
|
||||
return Ok(None);
|
||||
let start_position=match upper_bound{
|
||||
Bound::Included(time)=>trajectory.extrapolated_position(*time),
|
||||
Bound::Excluded(time)=>trajectory.extrapolated_position(*time),
|
||||
Bound::Unbounded=>trajectory.position,
|
||||
};
|
||||
let fev=crate::minimum_difference::closest_fev_not_inside(self,start_position)?;
|
||||
// swap and negate bounds to do a time inversion
|
||||
let (lower_bound,upper_bound)=(upper_bound.map(|&t|-t),lower_bound.map(|&t|-t));
|
||||
let infinity_body=-relative_body;
|
||||
let time_reversed_trajectory=-trajectory;
|
||||
//continue backwards along the body parabola
|
||||
Ok(fev.crawl(self,&infinity_body,lower_bound.as_ref(),upper_bound.as_ref()).hit()
|
||||
fev.crawl(self,&time_reversed_trajectory,lower_bound.as_ref(),upper_bound.as_ref()).hit()
|
||||
//no need to test -time<time_limit because of the first step
|
||||
.map(|(face,time)|(face,-time)))
|
||||
.map(|(face,time)|(face,-time))
|
||||
}
|
||||
pub fn predict_collision_face_out(&self,relative_body:&Body,range:impl RangeBounds<Time>,contact_face_id:MinkowskiFace)->Option<(MinkowskiDirectedEdge,GigaTime)>{
|
||||
pub fn predict_collision_face_out(&self,trajectory:&Trajectory,range:impl RangeBounds<Time>,contact_face_id:MinkowskiFace)->Option<(MinkowskiDirectedEdge,GigaTime)>{
|
||||
// TODO: make better
|
||||
use crate::face_crawler::{low,upp};
|
||||
//no algorithm needed, there is only one state and two cases (Edge,None)
|
||||
//determine when it passes an edge ("sliding off" case)
|
||||
let start_time=range.start_bound().map(|&t|(t-relative_body.time).to_ratio());
|
||||
let mut best_time=range.end_bound().map(|&t|into_giga_time(t,relative_body.time));
|
||||
let start_time=range.start_bound().map(|&t|(t-trajectory.time).to_ratio());
|
||||
let mut best_time=range.end_bound().map(|&t|into_giga_time(t,trajectory.time));
|
||||
let mut best_edge=None;
|
||||
let face_n=self.face_nd(contact_face_id).0;
|
||||
for &directed_edge_id in self.face_edges(contact_face_id).as_ref(){
|
||||
@@ -714,8 +715,8 @@ impl MinkowskiMesh<'_>{
|
||||
//WARNING! d outside of *2
|
||||
//WARNING: truncated precision
|
||||
//wrap for speed
|
||||
for dt in Fixed::<4,128>::zeroes2(((n.dot(relative_body.position))*2-d).wrap_4(),n.dot(relative_body.velocity).wrap_4()*2,n.dot(relative_body.acceleration).wrap_4()){
|
||||
if low(&start_time,&dt)&&upp(&dt,&best_time)&&n.dot(relative_body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
|
||||
for dt in Fixed::<4,128>::zeroes2(((n.dot(trajectory.position))*2-d).wrap_4(),n.dot(trajectory.velocity).wrap_4()*2,n.dot(trajectory.acceleration).wrap_4()){
|
||||
if low(&start_time,&dt)&&upp(&dt,&best_time)&&n.dot(trajectory.extrapolated_velocity_ratio_dt(dt)).is_negative(){
|
||||
best_time=Bound::Included(dt);
|
||||
best_edge=Some((directed_edge_id,dt));
|
||||
break;
|
||||
|
||||
@@ -15,6 +15,7 @@ pub use strafesnet_common::physics::{Time,TimeInner};
|
||||
use gameplay::ModeState;
|
||||
|
||||
pub type Body=crate::body::Body<TimeInner>;
|
||||
pub type Trajectory<'a>=crate::body::Trajectory<'a,TimeInner>;
|
||||
type MouseState=strafesnet_common::mouse::MouseState<TimeInner>;
|
||||
|
||||
//external influence
|
||||
@@ -28,6 +29,7 @@ pub enum InternalInstruction{
|
||||
CollisionStart(Collision,model_physics::GigaTime),
|
||||
CollisionEnd(Collision,model_physics::GigaTime),
|
||||
StrafeTick,
|
||||
// TODO: add GigaTime to ReachWalkTargetVelocity
|
||||
ReachWalkTargetVelocity,
|
||||
// Water,
|
||||
}
|
||||
@@ -498,16 +500,20 @@ enum MoveState{
|
||||
}
|
||||
impl MoveState{
|
||||
//call this after state.move_state is changed
|
||||
fn apply_enum(&self,body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){
|
||||
fn acceleration(&self,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState)->Planar64Vec3{
|
||||
match self{
|
||||
MoveState::Fly=>body.acceleration=vec3::zero(),
|
||||
MoveState::Air=>{
|
||||
//calculate base acceleration
|
||||
let a=touching.base_acceleration(models,style,camera,input_state);
|
||||
//set_acceleration clips according to contacts
|
||||
set_acceleration(body,touching,models,hitbox_mesh,a);
|
||||
MoveState::Fly=>vec3::zero(),
|
||||
MoveState::Air
|
||||
|MoveState::Water
|
||||
=>{
|
||||
// calculate base acceleration
|
||||
let base_acceleration=touching.base_acceleration(models,style,camera,input_state);
|
||||
// constrain_acceleration clips according to contacts
|
||||
touching.constrain_acceleration(models,hitbox_mesh,base_acceleration)
|
||||
},
|
||||
_=>(),
|
||||
MoveState::Walk(walk_state)
|
||||
|MoveState::Ladder(walk_state)
|
||||
=>walk_state.target.acceleration(),
|
||||
}
|
||||
}
|
||||
//function to coerce &mut self into &self
|
||||
@@ -521,13 +527,9 @@ impl MoveState{
|
||||
//set_velocity clips velocity according to current touching state
|
||||
set_velocity(body,touching,models,hitbox_mesh,v);
|
||||
},
|
||||
MoveState::Walk(walk_state)
|
||||
|MoveState::Ladder(walk_state)
|
||||
=>{
|
||||
//accelerate towards walk target or do nothing
|
||||
let a=walk_state.target.acceleration();
|
||||
set_acceleration(body,touching,models,hitbox_mesh,a);
|
||||
},
|
||||
MoveState::Walk(_walk_state)
|
||||
|MoveState::Ladder(_walk_state)
|
||||
=>(),
|
||||
}
|
||||
}
|
||||
/// changes the move state
|
||||
@@ -590,12 +592,10 @@ impl MoveState{
|
||||
}
|
||||
//lmao idk this is convenient
|
||||
fn apply_enum_and_input_and_body(&mut self,body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){
|
||||
self.apply_enum(body,touching,models,hitbox_mesh,style,camera,input_state);
|
||||
self.apply_input(body,touching,models,hitbox_mesh,style,camera,input_state);
|
||||
self.apply_to_body(body,touching,models,hitbox_mesh,style,camera,input_state);
|
||||
}
|
||||
fn apply_enum_and_body(&mut self,body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){
|
||||
self.apply_enum(body,touching,models,hitbox_mesh,style,camera,input_state);
|
||||
self.apply_to_body(body,touching,models,hitbox_mesh,style,camera,input_state);
|
||||
}
|
||||
fn apply_input_and_body(&mut self,body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){
|
||||
@@ -815,7 +815,7 @@ impl TouchingState{
|
||||
normal:n,
|
||||
}
|
||||
}).collect();
|
||||
crate::push_solve::push_solve(&contacts,velocity)
|
||||
crate::push_solve::push_solve(&contacts,velocity).0
|
||||
}
|
||||
fn constrain_acceleration(&self,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,acceleration:Planar64Vec3)->Planar64Vec3{
|
||||
let contacts:Vec<_>=self.contacts.iter().map(|(convex_mesh_id,face_id)|{
|
||||
@@ -826,18 +826,17 @@ impl TouchingState{
|
||||
normal:n,
|
||||
}
|
||||
}).collect();
|
||||
crate::push_solve::push_solve(&contacts,acceleration)
|
||||
crate::push_solve::push_solve(&contacts,acceleration).0
|
||||
}
|
||||
fn predict_collision_end(&self,collector:&mut instruction::InstructionCollector<InternalInstruction,Time>,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,body:&Body,start_time:Time)->Result<(),crate::minimum_difference::InfiniteLoop>{
|
||||
fn predict_collision_end(&self,collector:&mut instruction::InstructionCollector<InternalInstruction,Time>,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,trajectory:&Trajectory,start_time:Time){
|
||||
// let relative_body=body.relative_to(&Body::ZERO);
|
||||
let relative_body=body;
|
||||
for (convex_mesh_id,face_id) in &self.contacts{
|
||||
//detect face slide off
|
||||
let model_mesh=models.contact_mesh(convex_mesh_id);
|
||||
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh());
|
||||
collector.collect(minkowski.predict_collision_face_out(&relative_body,start_time..collector.time(),*face_id).map(|(_face,time)|{
|
||||
collector.collect(minkowski.predict_collision_face_out(&trajectory,start_time..collector.time(),*face_id).map(|(_face,time)|{
|
||||
TimedInstruction{
|
||||
time:relative_body.time+time.into(),
|
||||
time:trajectory.time+time.into(),
|
||||
instruction:InternalInstruction::CollisionEnd(
|
||||
Collision::Contact(ContactCollision{face_id:*face_id,convex_mesh_id:*convex_mesh_id}),
|
||||
time
|
||||
@@ -849,9 +848,9 @@ impl TouchingState{
|
||||
//detect model collision in reverse
|
||||
let model_mesh=models.intersect_mesh(convex_mesh_id);
|
||||
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh());
|
||||
collector.collect(minkowski.predict_collision_out(&relative_body,start_time..collector.time())?.map(|(_face,time)|{
|
||||
collector.collect(minkowski.predict_collision_out(&trajectory,start_time..collector.time()).map(|(_face,time)|{
|
||||
TimedInstruction{
|
||||
time:relative_body.time+time.into(),
|
||||
time:trajectory.time+time.into(),
|
||||
instruction:InternalInstruction::CollisionEnd(
|
||||
Collision::Intersect(IntersectCollision{convex_mesh_id:*convex_mesh_id}),
|
||||
time
|
||||
@@ -859,7 +858,6 @@ impl TouchingState{
|
||||
}
|
||||
}));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -887,7 +885,7 @@ pub struct PhysicsState{
|
||||
impl Default for PhysicsState{
|
||||
fn default()->Self{
|
||||
Self{
|
||||
body:Body::new(vec3::int(0,50,0),vec3::int(0,0,0),vec3::int(0,-100,0),Time::ZERO),
|
||||
body:Body::new(vec3::int(0,50,0),vec3::int(0,0,0),Time::ZERO),
|
||||
time:Time::ZERO,
|
||||
style:StyleModifiers::default(),
|
||||
touching:TouchingState::default(),
|
||||
@@ -911,11 +909,14 @@ impl PhysicsState{
|
||||
pub const fn body(&self)->&Body{
|
||||
&self.body
|
||||
}
|
||||
pub fn camera_body(&self)->Body{
|
||||
Body{
|
||||
position:self.body.position+self.style.camera_offset,
|
||||
..self.body
|
||||
}
|
||||
pub fn camera_trajectory(&self,data:&PhysicsData)->Trajectory<'static>{
|
||||
let acceleration=self.acceleration(data);
|
||||
Trajectory::new(
|
||||
self.body.position+self.style.camera_offset,
|
||||
self.body.velocity,
|
||||
acceleration,
|
||||
self.body.time,
|
||||
)
|
||||
}
|
||||
pub const fn camera(&self)->PhysicsCamera{
|
||||
self.camera
|
||||
@@ -947,6 +948,9 @@ impl PhysicsState{
|
||||
fn apply_input_and_body(&mut self,data:&PhysicsData){
|
||||
self.move_state.apply_input_and_body(&mut self.body,&self.touching,&data.models,&data.hitbox_mesh,&self.style,&self.camera,&self.input_state);
|
||||
}
|
||||
fn acceleration(&self,data:&PhysicsData)->Planar64Vec3{
|
||||
self.move_state.acceleration(&self.touching,&data.models,&data.hitbox_mesh,&self.style,&self.camera,&self.input_state)
|
||||
}
|
||||
//state mutated on collision:
|
||||
//Accelerator
|
||||
//stair step-up
|
||||
@@ -1199,15 +1203,15 @@ impl<'a> PhysicsContext<'a>{
|
||||
|
||||
collector.collect(state.next_move_instruction());
|
||||
|
||||
let trajectory=state.body.with_acceleration(state.acceleration(data));
|
||||
//check for collision ends
|
||||
state.touching.predict_collision_end(&mut collector,&data.models,&data.hitbox_mesh,&state.body,state.time).unwrap();
|
||||
state.touching.predict_collision_end(&mut collector,&data.models,&data.hitbox_mesh,&trajectory,state.time);
|
||||
//check for collision starts
|
||||
let mut aabb=aabb::Aabb::default();
|
||||
state.body.grow_aabb(&mut aabb,state.time,collector.time());
|
||||
trajectory.grow_aabb(&mut aabb,state.time,collector.time());
|
||||
aabb.inflate(data.hitbox_mesh.halfsize);
|
||||
//relative to moving platforms
|
||||
//let relative_body=state.body.relative_to(&Body::ZERO);
|
||||
let relative_body=&state.body;
|
||||
data.bvh.sample_aabb(&aabb,&mut |convex_mesh_id|{
|
||||
if state.touching.contains(convex_mesh_id){
|
||||
return;
|
||||
@@ -1215,17 +1219,17 @@ impl<'a> PhysicsContext<'a>{
|
||||
//no checks are needed because of the time limits.
|
||||
let model_mesh=data.models.mesh(*convex_mesh_id);
|
||||
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,data.hitbox_mesh.transformed_mesh());
|
||||
let Ok(collision)=minkowski.predict_collision_in(relative_body,state.time..collector.time())else{
|
||||
println!("Infinite loop! body={relative_body}");
|
||||
return;
|
||||
};
|
||||
collector.collect(collision.map(|(face,dt)|TimedInstruction{
|
||||
time:relative_body.time+dt.into(),
|
||||
instruction:InternalInstruction::CollisionStart(
|
||||
Collision::new(*convex_mesh_id,face),
|
||||
dt
|
||||
collector.collect(minkowski.predict_collision_in(&trajectory,state.time..collector.time())
|
||||
.map(|(face,dt)|
|
||||
TimedInstruction{
|
||||
time:trajectory.time+dt.into(),
|
||||
instruction:InternalInstruction::CollisionStart(
|
||||
Collision::new(*convex_mesh_id,face),
|
||||
dt
|
||||
)
|
||||
}
|
||||
)
|
||||
}));
|
||||
);
|
||||
});
|
||||
collector.take()
|
||||
}
|
||||
@@ -1336,24 +1340,6 @@ fn set_velocity_cull(body:&mut Body,touching:&mut TouchingState,models:&PhysicsM
|
||||
fn set_velocity(body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,v:Planar64Vec3){
|
||||
body.velocity=touching.constrain_velocity(models,hitbox_mesh,v);
|
||||
}
|
||||
#[expect(dead_code)]
|
||||
fn set_acceleration_cull(body:&mut Body,touching:&mut TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,a:Planar64Vec3)->bool{
|
||||
//This is not correct but is better than what I have
|
||||
let mut culled=false;
|
||||
touching.contacts.retain(|convex_mesh_id,face_id|{
|
||||
let n=contact_normal(models,hitbox_mesh,convex_mesh_id,*face_id);
|
||||
let r=n.dot(a).is_positive();
|
||||
if r{
|
||||
culled=true;
|
||||
}
|
||||
!r
|
||||
});
|
||||
set_acceleration(body,touching,models,hitbox_mesh,a);
|
||||
culled
|
||||
}
|
||||
fn set_acceleration(body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,a:Planar64Vec3){
|
||||
body.acceleration=touching.constrain_acceleration(models,hitbox_mesh,a);
|
||||
}
|
||||
|
||||
fn teleport(
|
||||
point:Planar64Vec3,
|
||||
@@ -1372,7 +1358,6 @@ fn teleport(
|
||||
time:Time,
|
||||
){
|
||||
set_position(point,move_state,body,touching,run,mode_state,mode,models,hitbox_mesh,bvh,style,camera,input_state,time);
|
||||
set_acceleration(body,touching,models,hitbox_mesh,style.gravity);
|
||||
}
|
||||
enum TeleportToSpawnError{
|
||||
NoModel,
|
||||
@@ -1670,7 +1655,20 @@ fn collision_start_intersect(
|
||||
},
|
||||
Some(gameplay_modes::Zone::Finish)=>{
|
||||
match run.finish(time){
|
||||
Ok(())=>println!("@@@@ Finished run time={}",run.time(time)),
|
||||
Ok(())=>{
|
||||
let time=run.time(time);
|
||||
let h=time.get()/(Time::ONE_SECOND.get()*60*60);
|
||||
let m=(time.get()/(Time::ONE_SECOND.get()*60)).rem_euclid(60);
|
||||
let s=(time.get()/(Time::ONE_SECOND.get())).rem_euclid(60);
|
||||
let ms=(time.get()/(Time::ONE_MILLISECOND.get())).rem_euclid(1000);
|
||||
let us=(time.get()/(Time::ONE_MICROSECOND.get())).rem_euclid(1000);
|
||||
let ns=(time.get()/(Time::ONE_NANOSECOND.get())).rem_euclid(1000);
|
||||
if h==0{
|
||||
println!("@@@@ Finished run time={m:02}:{s:02}.{ms:03}_{us:03}_{ns:03}");
|
||||
}else{
|
||||
println!("@@@@ Finished run time={h}:{m:02}:{s:02}.{ms:03}_{us:03}_{ns:03}");
|
||||
}
|
||||
},
|
||||
Err(e)=>println!("@@@@ Run Finish error:{e:?}"),
|
||||
}
|
||||
},
|
||||
@@ -1744,99 +1742,99 @@ fn collision_end_intersect(
|
||||
}
|
||||
fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedInstruction<InternalInstruction,Time>){
|
||||
state.time=ins.time;
|
||||
let (should_advance_body,goober_time)=match ins.instruction{
|
||||
match ins.instruction{
|
||||
// collisions advance the body precisely
|
||||
InternalInstruction::CollisionStart(_,dt)
|
||||
|InternalInstruction::CollisionEnd(_,dt)=>(true,Some(dt)),
|
||||
InternalInstruction::StrafeTick
|
||||
|InternalInstruction::ReachWalkTargetVelocity=>(true,None),
|
||||
};
|
||||
if should_advance_body{
|
||||
match goober_time{
|
||||
Some(dt)=>state.body.advance_time_ratio_dt(dt),
|
||||
None=>state.body.advance_time(state.time),
|
||||
}
|
||||
|InternalInstruction::CollisionEnd(_,dt)=>{
|
||||
state.body=state.body.with_acceleration(state.acceleration(data)).extrapolated_body_ratio_dt(dt);
|
||||
},
|
||||
// this advances imprecisely
|
||||
InternalInstruction::ReachWalkTargetVelocity=>state.body=state.body.with_acceleration(state.acceleration(data)).extrapolated_body(state.time),
|
||||
// strafe tick decides for itself whether to advance the body.
|
||||
InternalInstruction::StrafeTick=>(),
|
||||
}
|
||||
match ins.instruction{
|
||||
InternalInstruction::CollisionStart(collision,_)=>{
|
||||
let mode=data.modes.get_mode(state.mode_state.get_mode_id());
|
||||
match collision{
|
||||
Collision::Contact(contact)=>collision_start_contact(
|
||||
&mut state.move_state,&mut state.body,&mut state.mode_state,&mut state.touching,&mut state.run,
|
||||
mode,
|
||||
&data.models,&data.hitbox_mesh,&data.bvh,&state.style,&state.camera,&state.input_state,
|
||||
data.models.contact_attr(contact.convex_mesh_id.model_id),
|
||||
contact,
|
||||
state.time,
|
||||
),
|
||||
Collision::Intersect(intersect)=>collision_start_intersect(
|
||||
&mut state.move_state,&mut state.body,&mut state.mode_state,&mut state.touching,
|
||||
mode,
|
||||
&mut state.run,&data.models,&data.hitbox_mesh,&data.bvh,&state.style,&state.camera,&state.input_state,
|
||||
data.models.intersect_attr(intersect.convex_mesh_id.model_id),
|
||||
intersect,
|
||||
state.time,
|
||||
),
|
||||
}
|
||||
},
|
||||
InternalInstruction::CollisionEnd(collision,_)=>match collision{
|
||||
Collision::Contact(contact)=>collision_end_contact(
|
||||
&mut state.move_state,&mut state.body,&mut state.touching,&data.models,&data.hitbox_mesh,&state.style,&state.camera,&state.input_state,
|
||||
match ins.instruction{
|
||||
InternalInstruction::CollisionStart(collision,_)=>{
|
||||
let mode=data.modes.get_mode(state.mode_state.get_mode_id());
|
||||
match collision{
|
||||
Collision::Contact(contact)=>collision_start_contact(
|
||||
&mut state.move_state,&mut state.body,&mut state.mode_state,&mut state.touching,&mut state.run,
|
||||
mode,
|
||||
&data.models,&data.hitbox_mesh,&data.bvh,&state.style,&state.camera,&state.input_state,
|
||||
data.models.contact_attr(contact.convex_mesh_id.model_id),
|
||||
&contact.convex_mesh_id
|
||||
contact,
|
||||
state.time,
|
||||
),
|
||||
Collision::Intersect(intersect)=>collision_end_intersect(
|
||||
&mut state.move_state,&mut state.body,&mut state.touching,&data.models,&data.hitbox_mesh,&state.style,&state.camera,&state.input_state,
|
||||
data.modes.get_mode(state.mode_state.get_mode_id()),
|
||||
&mut state.run,
|
||||
Collision::Intersect(intersect)=>collision_start_intersect(
|
||||
&mut state.move_state,&mut state.body,&mut state.mode_state,&mut state.touching,
|
||||
mode,
|
||||
&mut state.run,&data.models,&data.hitbox_mesh,&data.bvh,&state.style,&state.camera,&state.input_state,
|
||||
data.models.intersect_attr(intersect.convex_mesh_id.model_id),
|
||||
&intersect.convex_mesh_id,
|
||||
state.time
|
||||
intersect,
|
||||
state.time,
|
||||
),
|
||||
},
|
||||
InternalInstruction::StrafeTick=>{
|
||||
//TODO make this less huge
|
||||
if let Some(strafe_settings)=&state.style.strafe{
|
||||
let controls=state.input_state.controls;
|
||||
if strafe_settings.activates(controls){
|
||||
let masked_controls=strafe_settings.mask(controls);
|
||||
let control_dir=state.style.get_control_dir(masked_controls);
|
||||
if control_dir!=vec3::zero(){
|
||||
let camera_mat=state.camera.simulate_move_rotation_y(state.input_state.lerp_delta(state.time).x);
|
||||
if let Some(ticked_velocity)=strafe_settings.tick_velocity(state.body.velocity,(camera_mat*control_dir).with_length(Planar64::ONE).divide().wrap_1()){
|
||||
//this is wrong but will work ig
|
||||
//need to note which push planes activate in push solve and keep those
|
||||
state.cull_velocity(data,ticked_velocity);
|
||||
}
|
||||
}
|
||||
},
|
||||
InternalInstruction::CollisionEnd(collision,_)=>match collision{
|
||||
Collision::Contact(contact)=>collision_end_contact(
|
||||
&mut state.move_state,&mut state.body,&mut state.touching,&data.models,&data.hitbox_mesh,&state.style,&state.camera,&state.input_state,
|
||||
data.models.contact_attr(contact.convex_mesh_id.model_id),
|
||||
&contact.convex_mesh_id
|
||||
),
|
||||
Collision::Intersect(intersect)=>collision_end_intersect(
|
||||
&mut state.move_state,&mut state.body,&mut state.touching,&data.models,&data.hitbox_mesh,&state.style,&state.camera,&state.input_state,
|
||||
data.modes.get_mode(state.mode_state.get_mode_id()),
|
||||
&mut state.run,
|
||||
data.models.intersect_attr(intersect.convex_mesh_id.model_id),
|
||||
&intersect.convex_mesh_id,
|
||||
state.time
|
||||
),
|
||||
},
|
||||
InternalInstruction::StrafeTick=>{
|
||||
//TODO make this less huge
|
||||
if let Some(strafe_settings)=&state.style.strafe{
|
||||
let controls=state.input_state.controls;
|
||||
if strafe_settings.activates(controls){
|
||||
let masked_controls=strafe_settings.mask(controls);
|
||||
let control_dir=state.style.get_control_dir(masked_controls);
|
||||
if control_dir!=vec3::zero(){
|
||||
// manually advance time
|
||||
state.body=state.body.with_acceleration(state.acceleration(data)).extrapolated_body(state.time);
|
||||
let camera_mat=state.camera.simulate_move_rotation_y(state.input_state.lerp_delta(state.time).x);
|
||||
if let Some(ticked_velocity)=strafe_settings.tick_velocity(state.body.velocity,(camera_mat*control_dir).with_length(Planar64::ONE).divide().wrap_1()){
|
||||
//this is wrong but will work ig
|
||||
//need to note which push planes activate in push solve and keep those
|
||||
state.cull_velocity(data,ticked_velocity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
InternalInstruction::ReachWalkTargetVelocity=>{
|
||||
match &mut state.move_state{
|
||||
MoveState::Air
|
||||
|MoveState::Water
|
||||
|MoveState::Fly
|
||||
=>println!("ReachWalkTargetVelocity fired for non-walking MoveState"),
|
||||
MoveState::Walk(walk_state)|MoveState::Ladder(walk_state)=>{
|
||||
//velocity is already handled by advance_time
|
||||
//we know that the acceleration is precisely zero because the walk target is known to be reachable
|
||||
//which means that gravity can be fully cancelled
|
||||
//ignore moving platforms for now
|
||||
let target=core::mem::replace(&mut walk_state.target,TransientAcceleration::Reached);
|
||||
set_acceleration(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,vec3::zero());
|
||||
// check what the target was to see if it was invalid
|
||||
match target{
|
||||
//you are not supposed to reach a walk target which is already reached!
|
||||
TransientAcceleration::Reached=>println!("Invalid walk target: Reached"),
|
||||
TransientAcceleration::Reachable{..}=>(),
|
||||
//you are not supposed to reach an unreachable walk target!
|
||||
TransientAcceleration::Unreachable{..}=>println!("Invalid walk target: Unreachable"),
|
||||
}
|
||||
}
|
||||
InternalInstruction::ReachWalkTargetVelocity=>{
|
||||
match &mut state.move_state{
|
||||
MoveState::Air
|
||||
|MoveState::Water
|
||||
|MoveState::Fly
|
||||
=>println!("ReachWalkTargetVelocity fired for non-walking MoveState"),
|
||||
MoveState::Walk(walk_state)|MoveState::Ladder(walk_state)=>{
|
||||
//velocity is already handled by extrapolated_body
|
||||
//we know that the acceleration is precisely zero because the walk target is known to be reachable
|
||||
//which means that gravity can be fully cancelled
|
||||
//ignore moving platforms for now
|
||||
let target=core::mem::replace(&mut walk_state.target,TransientAcceleration::Reached);
|
||||
// check what the target was to see if it was invalid
|
||||
match target{
|
||||
//you are not supposed to reach a walk target which is already reached!
|
||||
TransientAcceleration::Reached=>println!("Invalid walk target: Reached"),
|
||||
TransientAcceleration::Reachable{..}=>(),
|
||||
//you are not supposed to reach an unreachable walk target!
|
||||
TransientAcceleration::Unreachable{..}=>println!("Invalid walk target: Unreachable"),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedInstruction<Instruction,Time>){
|
||||
state.time=ins.time;
|
||||
@@ -1862,7 +1860,7 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
|
||||
Instruction::Misc(MiscInstruction::PracticeFly)=>true,
|
||||
};
|
||||
if should_advance_body{
|
||||
state.body.advance_time(state.time);
|
||||
state.body=state.body.with_acceleration(state.acceleration(data)).extrapolated_body(state.time);
|
||||
}
|
||||
|
||||
let mut b_refresh_walk_target=true;
|
||||
@@ -1898,6 +1896,9 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
|
||||
state.input_state.set_control(Controls::Zoom,s);
|
||||
b_refresh_walk_target=false;
|
||||
},
|
||||
Instruction::SetControl(SetControlInstruction::SetSprint(s))=>{
|
||||
state.input_state.set_control(Controls::Sprint,s);
|
||||
},
|
||||
Instruction::Mode(ModeInstruction::Reset)=>{
|
||||
//totally reset physics state
|
||||
state.reset_to_default();
|
||||
@@ -1967,16 +1968,16 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
|
||||
mod test{
|
||||
use strafesnet_common::integer::{vec3::{self,int as int3},mat3};
|
||||
use super::*;
|
||||
fn test_collision_axis_aligned(relative_body:Body,expected_collision_time:Option<Time>){
|
||||
fn test_collision_axis_aligned(relative_body:Trajectory,expected_collision_time:Option<Time>){
|
||||
let h0=HitboxMesh::new(PhysicsMesh::unit_cube(),integer::Planar64Affine3::new(mat3::from_diagonal(int3(5,1,5)>>1),vec3::zero()));
|
||||
let h1=StyleModifiers::roblox_bhop().calculate_mesh();
|
||||
let hitbox_mesh=h1.transformed_mesh();
|
||||
let platform_mesh=h0.transformed_mesh();
|
||||
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(platform_mesh,hitbox_mesh);
|
||||
let collision=minkowski.predict_collision_in(&relative_body,Time::ZERO..Time::from_secs(10)).unwrap();
|
||||
let collision=minkowski.predict_collision_in(&relative_body,..);
|
||||
assert_eq!(collision.map(|tup|relative_body.time+tup.1.into()),expected_collision_time,"Incorrect time of collision");
|
||||
}
|
||||
fn test_collision_rotated(relative_body:Body,expected_collision_time:Option<Time>){
|
||||
fn test_collision_rotated(relative_body:Trajectory,expected_collision_time:Option<Time>){
|
||||
let h0=HitboxMesh::new(PhysicsMesh::unit_cube(),
|
||||
integer::Planar64Affine3::new(
|
||||
Planar64Mat3::from_cols([
|
||||
@@ -1991,16 +1992,16 @@ mod test{
|
||||
let hitbox_mesh=h1.transformed_mesh();
|
||||
let platform_mesh=h0.transformed_mesh();
|
||||
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(platform_mesh,hitbox_mesh);
|
||||
let collision=minkowski.predict_collision_in(&relative_body,Time::ZERO..Time::from_secs(10)).unwrap();
|
||||
let collision=minkowski.predict_collision_in(&relative_body,..);
|
||||
assert_eq!(collision.map(|tup|relative_body.time+tup.1.into()),expected_collision_time,"Incorrect time of collision");
|
||||
}
|
||||
fn test_collision(relative_body:Body,expected_collision_time:Option<Time>){
|
||||
fn test_collision(relative_body:Trajectory,expected_collision_time:Option<Time>){
|
||||
test_collision_axis_aligned(relative_body,expected_collision_time);
|
||||
test_collision_rotated(relative_body,expected_collision_time);
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_degenerate_straight_down(){
|
||||
test_collision(Body::new(
|
||||
test_collision(Trajectory::new(
|
||||
int3(0,5,0),
|
||||
int3(0,-1,0),
|
||||
vec3::zero(),
|
||||
@@ -2009,7 +2010,7 @@ mod test{
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_small_mv(){
|
||||
test_collision(Body::new(
|
||||
test_collision(Trajectory::new(
|
||||
int3(0,5,0),
|
||||
int3(0,-1,0)+(vec3::X>>32),
|
||||
vec3::zero(),
|
||||
@@ -2018,7 +2019,7 @@ mod test{
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_degenerate_east(){
|
||||
test_collision(Body::new(
|
||||
test_collision(Trajectory::new(
|
||||
int3(3,5,0),
|
||||
int3(0,-1,0),
|
||||
vec3::zero(),
|
||||
@@ -2027,7 +2028,7 @@ mod test{
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_degenerate_south(){
|
||||
test_collision(Body::new(
|
||||
test_collision(Trajectory::new(
|
||||
int3(0,5,3),
|
||||
int3(0,-1,0),
|
||||
vec3::zero(),
|
||||
@@ -2036,7 +2037,7 @@ mod test{
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_degenerate_west(){
|
||||
test_collision(Body::new(
|
||||
test_collision(Trajectory::new(
|
||||
int3(-3,5,0),
|
||||
int3(0,-1,0),
|
||||
vec3::zero(),
|
||||
@@ -2045,7 +2046,7 @@ mod test{
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_degenerate_north(){
|
||||
test_collision(Body::new(
|
||||
test_collision(Trajectory::new(
|
||||
int3(0,5,-3),
|
||||
int3(0,-1,0),
|
||||
vec3::zero(),
|
||||
@@ -2054,115 +2055,115 @@ mod test{
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_parabola_edge_east_from_west(){
|
||||
test_collision(Body::new(
|
||||
test_collision(Trajectory::new(
|
||||
int3(3,3,0),
|
||||
int3(100,-1,0),
|
||||
int3(0,-1,0),
|
||||
Time::ZERO
|
||||
).relative_to(&Body::ZERO).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
).relative_to(&Trajectory::ZERO,Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_parabola_edge_south_from_north(){
|
||||
test_collision(Body::new(
|
||||
test_collision(Trajectory::new(
|
||||
int3(0,3,3),
|
||||
int3(0,-1,100),
|
||||
int3(0,-1,0),
|
||||
Time::ZERO
|
||||
).relative_to(&Body::ZERO).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
).relative_to(&Trajectory::ZERO,Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_parabola_edge_west_from_east(){
|
||||
test_collision(Body::new(
|
||||
test_collision(Trajectory::new(
|
||||
int3(-3,3,0),
|
||||
int3(-100,-1,0),
|
||||
int3(0,-1,0),
|
||||
Time::ZERO
|
||||
).relative_to(&Body::ZERO).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
).relative_to(&Trajectory::ZERO,Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_parabola_edge_north_from_south(){
|
||||
test_collision(Body::new(
|
||||
test_collision(Trajectory::new(
|
||||
int3(0,3,-3),
|
||||
int3(0,-1,-100),
|
||||
int3(0,-1,0),
|
||||
Time::ZERO
|
||||
).relative_to(&Body::ZERO).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
).relative_to(&Trajectory::ZERO,Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_parabola_edge_north_from_ne(){
|
||||
test_collision(Body::new(
|
||||
test_collision(Trajectory::new(
|
||||
int3(0,6,-7)>>1,
|
||||
int3(-10,-1,1),
|
||||
int3(0,-1,0),
|
||||
Time::ZERO
|
||||
).relative_to(&Body::ZERO).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
).relative_to(&Trajectory::ZERO,Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_parabola_edge_north_from_nw(){
|
||||
test_collision(Body::new(
|
||||
test_collision(Trajectory::new(
|
||||
int3(0,6,-7)>>1,
|
||||
int3(10,-1,1),
|
||||
int3(0,-1,0),
|
||||
Time::ZERO
|
||||
).relative_to(&Body::ZERO).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
).relative_to(&Trajectory::ZERO,Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_parabola_edge_east_from_se(){
|
||||
test_collision(Body::new(
|
||||
test_collision(Trajectory::new(
|
||||
int3(7,6,0)>>1,
|
||||
int3(-1,-1,-10),
|
||||
int3(0,-1,0),
|
||||
Time::ZERO
|
||||
).relative_to(&Body::ZERO).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
).relative_to(&Trajectory::ZERO,Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_parabola_edge_east_from_ne(){
|
||||
test_collision(Body::new(
|
||||
test_collision(Trajectory::new(
|
||||
int3(7,6,0)>>1,
|
||||
int3(-1,-1,10),
|
||||
int3(0,-1,0),
|
||||
Time::ZERO
|
||||
).relative_to(&Body::ZERO).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
).relative_to(&Trajectory::ZERO,Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_parabola_edge_south_from_se(){
|
||||
test_collision(Body::new(
|
||||
test_collision(Trajectory::new(
|
||||
int3(0,6,7)>>1,
|
||||
int3(-10,-1,-1),
|
||||
int3(0,-1,0),
|
||||
Time::ZERO
|
||||
).relative_to(&Body::ZERO).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
).relative_to(&Trajectory::ZERO,Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_parabola_edge_south_from_sw(){
|
||||
test_collision(Body::new(
|
||||
test_collision(Trajectory::new(
|
||||
int3(0,6,7)>>1,
|
||||
int3(10,-1,-1),
|
||||
int3(0,-1,0),
|
||||
Time::ZERO
|
||||
).relative_to(&Body::ZERO).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
).relative_to(&Trajectory::ZERO,Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_parabola_edge_west_from_se(){
|
||||
test_collision(Body::new(
|
||||
test_collision(Trajectory::new(
|
||||
int3(-7,6,0)>>1,
|
||||
int3(1,-1,-10),
|
||||
int3(0,-1,0),
|
||||
Time::ZERO
|
||||
).relative_to(&Body::ZERO).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
).relative_to(&Trajectory::ZERO,Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_parabola_edge_west_from_ne(){
|
||||
test_collision(Body::new(
|
||||
test_collision(Trajectory::new(
|
||||
int3(-7,6,0)>>1,
|
||||
int3(1,-1,10),
|
||||
int3(0,-1,0),
|
||||
Time::ZERO
|
||||
).relative_to(&Body::ZERO).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
).relative_to(&Trajectory::ZERO,Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_oblique(){
|
||||
test_collision(Body::new(
|
||||
test_collision(Trajectory::new(
|
||||
int3(0,5,0),
|
||||
int3(1,-64,2)>>6,// /64
|
||||
vec3::zero(),
|
||||
@@ -2171,7 +2172,7 @@ mod test{
|
||||
}
|
||||
#[test]
|
||||
fn zoom_hit_nothing(){
|
||||
test_collision(Body::new(
|
||||
test_collision(Trajectory::new(
|
||||
int3(0,10,0),
|
||||
int3(1,0,0),
|
||||
int3(0,1,0),
|
||||
@@ -2180,7 +2181,7 @@ mod test{
|
||||
}
|
||||
#[test]
|
||||
fn already_inside_hit_nothing(){
|
||||
test_collision(Body::new(
|
||||
test_collision(Trajectory::new(
|
||||
vec3::zero(),
|
||||
int3(1,0,0),
|
||||
int3(0,1,0),
|
||||
@@ -2190,7 +2191,7 @@ mod test{
|
||||
// overlap edges by 1 epsilon
|
||||
#[test]
|
||||
fn almost_miss_north(){
|
||||
test_collision_axis_aligned(Body::new(
|
||||
test_collision_axis_aligned(Trajectory::new(
|
||||
(int3(0,10,-7)>>1)+vec3::raw_xyz(0,0,1),
|
||||
int3(0,-1,0),
|
||||
vec3::zero(),
|
||||
@@ -2199,7 +2200,7 @@ mod test{
|
||||
}
|
||||
#[test]
|
||||
fn almost_miss_east(){
|
||||
test_collision_axis_aligned(Body::new(
|
||||
test_collision_axis_aligned(Trajectory::new(
|
||||
(int3(7,10,0)>>1)+vec3::raw_xyz(-1,0,0),
|
||||
int3(0,-1,0),
|
||||
vec3::zero(),
|
||||
@@ -2208,7 +2209,7 @@ mod test{
|
||||
}
|
||||
#[test]
|
||||
fn almost_miss_south(){
|
||||
test_collision_axis_aligned(Body::new(
|
||||
test_collision_axis_aligned(Trajectory::new(
|
||||
(int3(0,10,7)>>1)+vec3::raw_xyz(0,0,-1),
|
||||
int3(0,-1,0),
|
||||
vec3::zero(),
|
||||
@@ -2217,7 +2218,7 @@ mod test{
|
||||
}
|
||||
#[test]
|
||||
fn almost_miss_west(){
|
||||
test_collision_axis_aligned(Body::new(
|
||||
test_collision_axis_aligned(Trajectory::new(
|
||||
(int3(-7,10,0)>>1)+vec3::raw_xyz(1,0,0),
|
||||
int3(0,-1,0),
|
||||
vec3::zero(),
|
||||
@@ -2227,7 +2228,7 @@ mod test{
|
||||
// exactly miss edges
|
||||
#[test]
|
||||
fn exact_miss_north(){
|
||||
test_collision_axis_aligned(Body::new(
|
||||
test_collision_axis_aligned(Trajectory::new(
|
||||
int3(0,10,-7)>>1,
|
||||
int3(0,-1,0),
|
||||
vec3::zero(),
|
||||
@@ -2236,7 +2237,7 @@ mod test{
|
||||
}
|
||||
#[test]
|
||||
fn exact_miss_east(){
|
||||
test_collision_axis_aligned(Body::new(
|
||||
test_collision_axis_aligned(Trajectory::new(
|
||||
int3(7,10,0)>>1,
|
||||
int3(0,-1,0),
|
||||
vec3::zero(),
|
||||
@@ -2245,7 +2246,7 @@ mod test{
|
||||
}
|
||||
#[test]
|
||||
fn exact_miss_south(){
|
||||
test_collision_axis_aligned(Body::new(
|
||||
test_collision_axis_aligned(Trajectory::new(
|
||||
int3(0,10,7)>>1,
|
||||
int3(0,-1,0),
|
||||
vec3::zero(),
|
||||
@@ -2254,7 +2255,7 @@ mod test{
|
||||
}
|
||||
#[test]
|
||||
fn exact_miss_west(){
|
||||
test_collision_axis_aligned(Body::new(
|
||||
test_collision_axis_aligned(Trajectory::new(
|
||||
int3(-7,10,0)>>1,
|
||||
int3(0,-1,0),
|
||||
vec3::zero(),
|
||||
@@ -2264,7 +2265,7 @@ mod test{
|
||||
// miss edges by 1 epsilon
|
||||
#[test]
|
||||
fn narrow_miss_north(){
|
||||
test_collision_axis_aligned(Body::new(
|
||||
test_collision_axis_aligned(Trajectory::new(
|
||||
(int3(0,10,-7)>>1)-vec3::raw_xyz(0,0,1),
|
||||
int3(0,-1,0),
|
||||
vec3::zero(),
|
||||
@@ -2273,7 +2274,7 @@ mod test{
|
||||
}
|
||||
#[test]
|
||||
fn narrow_miss_east(){
|
||||
test_collision_axis_aligned(Body::new(
|
||||
test_collision_axis_aligned(Trajectory::new(
|
||||
(int3(7,10,0)>>1)-vec3::raw_xyz(-1,0,0),
|
||||
int3(0,-1,0),
|
||||
vec3::zero(),
|
||||
@@ -2282,7 +2283,7 @@ mod test{
|
||||
}
|
||||
#[test]
|
||||
fn narrow_miss_south(){
|
||||
test_collision_axis_aligned(Body::new(
|
||||
test_collision_axis_aligned(Trajectory::new(
|
||||
(int3(0,10,7)>>1)-vec3::raw_xyz(0,0,-1),
|
||||
int3(0,-1,0),
|
||||
vec3::zero(),
|
||||
@@ -2291,7 +2292,7 @@ mod test{
|
||||
}
|
||||
#[test]
|
||||
fn narrow_miss_west(){
|
||||
test_collision_axis_aligned(Body::new(
|
||||
test_collision_axis_aligned(Trajectory::new(
|
||||
(int3(-7,10,0)>>1)-vec3::raw_xyz(1,0,0),
|
||||
int3(0,-1,0),
|
||||
vec3::zero(),
|
||||
|
||||
@@ -15,6 +15,7 @@ type Conts<'a>=arrayvec::ArrayVec<&'a Contact,4>;
|
||||
const RATIO_ZERO:Ratio<Fixed<1,32>,Fixed<1,32>>=Ratio::new(Fixed::ZERO,Fixed::EPSILON);
|
||||
|
||||
/// Information about a contact restriction
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct Contact{
|
||||
pub position:Planar64Vec3,
|
||||
pub velocity:Planar64Vec3,
|
||||
@@ -281,16 +282,16 @@ fn get_first_touch<'a>(contacts:&'a [Contact],ray:&Ray,conts:&Conts)->Option<(Ra
|
||||
.min_by_key(|&(t,_)|t)
|
||||
}
|
||||
|
||||
pub fn push_solve(contacts:&[Contact],point:Planar64Vec3)->Planar64Vec3{
|
||||
pub fn push_solve(contacts:&[Contact],point:Planar64Vec3)->(Planar64Vec3,Conts<'_>){
|
||||
let (mut ray,mut conts)=get_best_push_ray_and_conts_0(point);
|
||||
loop{
|
||||
let (next_t,next_cont)=match get_first_touch(contacts,&ray,&conts){
|
||||
Some((t,cont))=>(t,cont),
|
||||
None=>return ray.origin,
|
||||
None=>return (ray.origin,conts),
|
||||
};
|
||||
|
||||
if RATIO_ZERO.le_ratio(next_t){
|
||||
return ray.origin;
|
||||
return (ray.origin,conts);
|
||||
}
|
||||
|
||||
//push_front
|
||||
@@ -306,7 +307,7 @@ pub fn push_solve(contacts:&[Contact],point:Planar64Vec3)->Planar64Vec3{
|
||||
let meet_point=ray.extrapolate(next_t);
|
||||
match get_best_push_ray_and_conts(meet_point,conts.as_slice()){
|
||||
Some((new_ray,new_conts))=>(ray,conts)=(new_ray,new_conts),
|
||||
None=>return meet_point,
|
||||
None=>return (meet_point,conts),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -323,9 +324,8 @@ mod tests{
|
||||
normal:vec3::Y,
|
||||
}
|
||||
];
|
||||
assert_eq!(
|
||||
vec3::zero(),
|
||||
push_solve(&contacts,vec3::NEG_Y)
|
||||
);
|
||||
let (point,conts)=push_solve(&contacts,vec3::NEG_Y);
|
||||
assert_eq!(point,vec3::zero());
|
||||
assert_eq!(conts.as_slice(),[&contacts[0]].as_slice());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,13 +52,12 @@ pub enum SessionControlInstruction{
|
||||
pub enum SessionPlaybackInstruction{
|
||||
SkipForward,
|
||||
SkipBack,
|
||||
TogglePaused,
|
||||
DecreaseTimescale,
|
||||
IncreaseTimescale,
|
||||
}
|
||||
|
||||
pub struct FrameState{
|
||||
pub body:physics::Body,
|
||||
pub trajectory:physics::Trajectory<'static>,
|
||||
pub camera:physics::PhysicsCamera,
|
||||
pub time:PhysicsTime,
|
||||
}
|
||||
@@ -77,9 +76,9 @@ impl Simulation{
|
||||
physics,
|
||||
}
|
||||
}
|
||||
pub fn get_frame_state(&self,time:SessionTime)->FrameState{
|
||||
pub fn get_frame_state(&self,time:SessionTime,data:&PhysicsData)->FrameState{
|
||||
FrameState{
|
||||
body:self.physics.camera_body(),
|
||||
trajectory:self.physics.camera_trajectory(data),
|
||||
camera:self.physics.camera(),
|
||||
time:self.timer.time(time),
|
||||
}
|
||||
@@ -188,9 +187,9 @@ impl Session{
|
||||
}
|
||||
pub fn get_frame_state(&self,time:SessionTime)->Option<FrameState>{
|
||||
match &self.view_state{
|
||||
ViewState::Play=>Some(self.simulation.get_frame_state(time)),
|
||||
ViewState::Play=>Some(self.simulation.get_frame_state(time,&self.geometry_shared)),
|
||||
ViewState::Replay(bot_id)=>self.replays.get(bot_id).map(|replay|
|
||||
replay.simulation.get_frame_state(time)
|
||||
replay.simulation.get_frame_state(time,&self.geometry_shared)
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -239,12 +238,16 @@ impl InstructionConsumer<Instruction<'_>> for Session{
|
||||
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Mode(ModeInstruction::Reset));
|
||||
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Misc(MiscInstruction::SetSensitivity(self.user_settings().calculate_sensitivity())));
|
||||
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Mode(ModeInstruction::Restart(mode_id)));
|
||||
// TODO: think about this harder. This works around a bug where you fall infinitely when you reset.
|
||||
self.simulation.timer.set_time(ins.time,PhysicsTime::ZERO);
|
||||
},
|
||||
Instruction::Input(SessionInputInstruction::Mode(ImplicitModeInstruction::ResetAndSpawn(mode_id,spawn_id)))=>{
|
||||
self.clear_recording();
|
||||
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Mode(ModeInstruction::Reset));
|
||||
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Misc(MiscInstruction::SetSensitivity(self.user_settings().calculate_sensitivity())));
|
||||
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Mode(ModeInstruction::Spawn(mode_id,spawn_id)));
|
||||
// TODO: think about this harder. This works around a bug where you fall infinitely when you reset.
|
||||
self.simulation.timer.set_time(ins.time,PhysicsTime::ZERO);
|
||||
},
|
||||
Instruction::Input(SessionInputInstruction::Misc(misc_instruction))=>{
|
||||
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Misc(misc_instruction));
|
||||
@@ -253,7 +256,14 @@ impl InstructionConsumer<Instruction<'_>> for Session{
|
||||
// don't flush the buffered instructions in the mouse interpolator
|
||||
// until the mouse is confirmed to be not moving at a later time
|
||||
// what if they pause for 5ms lmao
|
||||
_=self.simulation.timer.set_paused(ins.time,paused);
|
||||
match &self.view_state{
|
||||
ViewState::Play=>{
|
||||
_=self.simulation.timer.set_paused(ins.time,paused);
|
||||
},
|
||||
ViewState::Replay(bot_id)=>if let Some(replay)=self.replays.get_mut(bot_id){
|
||||
_=replay.simulation.timer.set_paused(ins.time,paused);
|
||||
},
|
||||
}
|
||||
},
|
||||
Instruction::Control(SessionControlInstruction::CopyRecordingIntoReplayAndSpectate)=> if let ViewState::Play=self.view_state{
|
||||
// Bind: B
|
||||
@@ -374,14 +384,6 @@ impl InstructionConsumer<Instruction<'_>> for Session{
|
||||
},
|
||||
}
|
||||
},
|
||||
Instruction::Playback(SessionPlaybackInstruction::TogglePaused)=>{
|
||||
match &self.view_state{
|
||||
ViewState::Play=>(),
|
||||
ViewState::Replay(bot_id)=>if let Some(replay)=self.replays.get_mut(bot_id){
|
||||
_=replay.simulation.timer.set_paused(ins.time,!replay.simulation.timer.is_paused());
|
||||
},
|
||||
}
|
||||
}
|
||||
Instruction::ChangeMap(complete_map)=>{
|
||||
self.clear_recording();
|
||||
self.change_map(complete_map);
|
||||
|
||||
@@ -71,16 +71,16 @@ fn segment_determinism(bot:strafesnet_snf::bot::Segment,physics_data:&PhysicsDat
|
||||
for (i,ins) in bot.instructions.into_iter().enumerate(){
|
||||
let state_deterministic=physics_deterministic.clone();
|
||||
let state_filtered=physics_filtered.clone();
|
||||
PhysicsContext::run_input_instruction(&mut physics_deterministic,&physics_data,ins.clone());
|
||||
PhysicsContext::run_input_instruction(&mut physics_deterministic,physics_data,ins.clone());
|
||||
match ins{
|
||||
strafesnet_common::instruction::TimedInstruction{instruction:strafesnet_common::physics::Instruction::Idle,..}=>(),
|
||||
other=>{
|
||||
non_idle_count+=1;
|
||||
// run
|
||||
PhysicsContext::run_input_instruction(&mut physics_filtered,&physics_data,other.clone());
|
||||
PhysicsContext::run_input_instruction(&mut physics_filtered,physics_data,other.clone());
|
||||
// check if position matches
|
||||
let b0=physics_deterministic.camera_body();
|
||||
let b1=physics_filtered.camera_body();
|
||||
let b0=physics_deterministic.camera_trajectory(physics_data);
|
||||
let b1=physics_filtered.camera_trajectory(physics_data);
|
||||
if b0.position!=b1.position{
|
||||
let nanoseconds=start.elapsed().as_nanos() as u64;
|
||||
println!("desync at instruction #{}",i);
|
||||
|
||||
@@ -73,7 +73,6 @@ fn simultaneous_collision(){
|
||||
let body=strafesnet_physics::physics::Body::new(
|
||||
(vec3::int(5+2,0,0)>>1)+vec3::int(1,1,0),
|
||||
vec3::int(-1,-1,0),
|
||||
vec3::int(0,0,0),
|
||||
Time::ZERO,
|
||||
);
|
||||
let mut physics=PhysicsState::new_with_body(body);
|
||||
@@ -88,7 +87,6 @@ fn simultaneous_collision(){
|
||||
let body=physics.body();
|
||||
assert_eq!(body.position,vec3::int(5,0,0));
|
||||
assert_eq!(body.velocity,vec3::int(0,0,0));
|
||||
assert_eq!(body.acceleration,vec3::int(0,0,0));
|
||||
assert_eq!(body.time,Time::from_secs(1));
|
||||
}
|
||||
#[test]
|
||||
@@ -97,7 +95,6 @@ fn bug_3(){
|
||||
let body=strafesnet_physics::physics::Body::new(
|
||||
(vec3::int(5+2,0,0)>>1)+vec3::int(1,2,0),
|
||||
vec3::int(-1,-1,0),
|
||||
vec3::int(0,0,0),
|
||||
Time::ZERO,
|
||||
);
|
||||
let mut physics=PhysicsState::new_with_body(body);
|
||||
@@ -112,43 +109,5 @@ fn bug_3(){
|
||||
let body=physics.body();
|
||||
assert_eq!(body.position,vec3::int(5+2,0,0)>>1);
|
||||
assert_eq!(body.velocity,vec3::int(0,0,0));
|
||||
assert_eq!(body.acceleration,vec3::int(0,0,0));
|
||||
assert_eq!(body.time,Time::from_secs(2));
|
||||
}
|
||||
|
||||
fn test_scene_cylinder()->PhysicsData{
|
||||
let mut builder=TestSceneBuilder::new();
|
||||
let cube_face_description=CubeFaceDescription::new(Default::default(),RenderConfigId::new(0));
|
||||
let mesh=builder.push_mesh(strafesnet_rbx_loader::primitives::unit_cylinder(cube_face_description));
|
||||
// place one 5x5x5 cylinder.
|
||||
builder.push_mesh_instance(mesh,Planar64Affine3::new(
|
||||
mat3::from_diagonal(vec3::int(5,5,5)>>1),
|
||||
vec3::int(0,-5,0)
|
||||
));
|
||||
builder.build()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_minimum_difference(){
|
||||
let physics_data=test_scene_cylinder();
|
||||
let body=strafesnet_physics::physics::Body::new(
|
||||
vec3::int(4,1,4)>>1,
|
||||
vec3::int(-1,-1,-2),
|
||||
vec3::int(0,-100,0),
|
||||
Time::ZERO,
|
||||
);
|
||||
let mut physics=PhysicsState::new_with_body(body);
|
||||
physics.style_mut().gravity=vec3::zero();
|
||||
let mut phys_iter=PhysicsContext::iter_internal(&mut physics,&physics_data,Time::from_secs(3))
|
||||
.filter(|ins|!matches!(ins.instruction,InternalInstruction::StrafeTick));
|
||||
// touch side of part at 0,0,0
|
||||
assert_eq!(phys_iter.next().unwrap().time,Time::from_secs(1));
|
||||
// touch top of part at 5,-5,0
|
||||
assert_eq!(phys_iter.next().unwrap().time,Time::from_secs(2));
|
||||
assert!(phys_iter.next().is_none());
|
||||
let body=physics.body();
|
||||
assert_eq!(body.position,vec3::int(5+2,0,0)>>1);
|
||||
assert_eq!(body.velocity,vec3::int(0,0,0));
|
||||
assert_eq!(body.acceleration,vec3::int(0,0,0));
|
||||
assert_eq!(body.time,Time::from_secs(2));
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ fn physics_bug_2()->Result<(),ReplayError>{
|
||||
let body=strafesnet_physics::physics::Body::new(
|
||||
vec3::raw_xyz(555690659654,1490485868773,1277783839382),
|
||||
vec3::int(0,0,0),
|
||||
vec3::int(0,-100,0),
|
||||
Time::ZERO,
|
||||
);
|
||||
let mut physics=PhysicsState::new_with_body(body);
|
||||
@@ -60,45 +59,12 @@ fn physics_bug_3()->Result<(),ReplayError>{
|
||||
// vec3::raw_xyz(0,-96915585363,1265),
|
||||
// vec3::raw_xyz(0,-429496729600,0),
|
||||
// corner setup before wall hits
|
||||
vec3::raw_xyz(-1392580080675,3325402529458,-2444727738679),
|
||||
vec3::raw_xyz(-30259028820,-22950929553,-71141663007),
|
||||
vec3::raw_xyz(0,-429496729600,0),
|
||||
Time::ZERO,
|
||||
);
|
||||
let mut physics=PhysicsState::new_with_body(body);
|
||||
// wait one second to activate the bug
|
||||
PhysicsContext::run_input_instruction(&mut physics,&physics_data,strafesnet_common::instruction::TimedInstruction{
|
||||
time:Time::from_millis(500),
|
||||
instruction:strafesnet_common::physics::Instruction::Idle,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Infinite loop! body=p(-1796.657, 677.618, 36.959) v(3.158, -53.650, -34.435) a(-0.000, -71.276, -45.248) t(288s+440000000ns)
|
||||
// Infinite loop! body=p(-2382.440, 160.150, -379.151) v(53.632, 35.779, 44.904) a(0.000, -100.000, 0.000) t(306s+675758543ns)
|
||||
// Infinite loop! body=p(-1798.724, 731.459, 68.784) v(-17.389, 0.000, -78.087) a(0.000, 0.000, 0.000) t(284s+006980061ns)
|
||||
// Infinite loop! body=p(-1796.657, 677.618, 36.959) v(3.158, -53.650, -34.435) a(-0.000, -71.276, -45.248) t(288s+440000000ns)
|
||||
// Infinite loop! body=p(-1797.504, 738.529, 74.864) v(-3.653, 0.000, -79.917) a(0.000, 0.000, 0.000) t(282s+709871336ns)
|
||||
// Infinite loop! body=p(-1797.569, 735.449, 71.859) v(23.726, -76.309, -3.747) a(0.000, 0.000, 0.000) t(283s+325193187ns)
|
||||
|
||||
#[test]
|
||||
fn physics_md_infinite_loop()->Result<(),ReplayError>{
|
||||
println!("loading map file..");
|
||||
let data=read_entire_file("../tools/bhop_maps/5692113331.snfm")?;
|
||||
let map=strafesnet_snf::read_map(data)?.into_complete_map()?;
|
||||
|
||||
// create recording
|
||||
println!("generating models..");
|
||||
let physics_data=PhysicsData::new(&map);
|
||||
println!("simulating...");
|
||||
|
||||
//teleport to bug
|
||||
use strafesnet_common::integer::{vec3,Time};
|
||||
let body=strafesnet_physics::physics::Body::new(
|
||||
vec3::try_from_f32_array([-1796.657, 677.618, 36.959]).unwrap(),
|
||||
vec3::try_from_f32_array([3.158, -53.650, -34.435]).unwrap(),
|
||||
vec3::int(0,-100,0),
|
||||
// vec3::raw_xyz(-1392580080675,3325402529458,-2444727738679),
|
||||
// vec3::raw_xyz(-30259028820,-22950929553,-71141663007),
|
||||
// vec3::raw_xyz(0,-429496729600,0),
|
||||
// Actual bug 3 repro
|
||||
vec3::raw_xyz(-2505538624455,3357963283914,557275711118),
|
||||
vec3::raw_xyz(204188283920,-282280474198,166172785440),
|
||||
Time::ZERO,
|
||||
);
|
||||
let mut physics=PhysicsState::new_with_body(body);
|
||||
|
||||
@@ -36,6 +36,7 @@ pub enum SetControlInstruction{
|
||||
SetMoveForward(bool),
|
||||
SetJump(bool),
|
||||
SetZoom(bool),
|
||||
SetSprint(bool),
|
||||
}
|
||||
#[derive(Clone,Debug)]
|
||||
pub enum ModeInstruction{
|
||||
|
||||
@@ -12,7 +12,7 @@ authors = ["Rhys Lloyd <krakow20@gmail.com>"]
|
||||
[dependencies]
|
||||
bytemuck = "1.14.3"
|
||||
glam = "0.30.0"
|
||||
regex = { version = "1.11.3", default-features = false }
|
||||
regex = { version = "1.11.3", default-features = false, features = ["unicode-perl"] }
|
||||
rbx_mesh = "0.5.0"
|
||||
rbxassetid = { version = "0.1.0", path = "../rbxassetid", registry = "strafesnet" }
|
||||
roblox_emulator = { version = "0.5.1", path = "../roblox_emulator", default-features = false, registry = "strafesnet" }
|
||||
|
||||
@@ -88,6 +88,11 @@ pub enum Instruction{
|
||||
PracticeFly,
|
||||
#[brw(magic=14u8)]
|
||||
SetSensitivity(super::integer::Ratio64Vec2),
|
||||
#[brw(magic=15u8)]
|
||||
SetSprint(
|
||||
#[br(map=bool_from_u8)]
|
||||
#[bw(map=bool_into_u8)]
|
||||
bool),
|
||||
#[brw(magic=255u8)]
|
||||
Idle,
|
||||
}
|
||||
@@ -116,6 +121,7 @@ impl TryInto<strafesnet_common::physics::Instruction> for Instruction{
|
||||
Instruction::SetMoveForward(state)=>strafesnet_common::physics::Instruction::SetControl(strafesnet_common::physics::SetControlInstruction::SetMoveForward(state.into())),
|
||||
Instruction::SetJump(state)=>strafesnet_common::physics::Instruction::SetControl(strafesnet_common::physics::SetControlInstruction::SetJump(state.into())),
|
||||
Instruction::SetZoom(state)=>strafesnet_common::physics::Instruction::SetControl(strafesnet_common::physics::SetControlInstruction::SetZoom(state.into())),
|
||||
Instruction::SetSprint(state)=>strafesnet_common::physics::Instruction::SetControl(strafesnet_common::physics::SetControlInstruction::SetSprint(state.into())),
|
||||
Instruction::Reset=>strafesnet_common::physics::Instruction::Mode(strafesnet_common::physics::ModeInstruction::Reset),
|
||||
Instruction::Restart(mode_id)=>strafesnet_common::physics::Instruction::Mode(strafesnet_common::physics::ModeInstruction::Restart(strafesnet_common::gameplay_modes::ModeId::new(mode_id))),
|
||||
Instruction::Spawn(mode_id,stage_id)=>strafesnet_common::physics::Instruction::Mode(strafesnet_common::physics::ModeInstruction::Spawn(
|
||||
@@ -142,6 +148,7 @@ impl TryFrom<strafesnet_common::physics::Instruction> for Instruction{
|
||||
strafesnet_common::physics::Instruction::SetControl(strafesnet_common::physics::SetControlInstruction::SetMoveForward(state))=>Ok(Instruction::SetMoveForward(state.into())),
|
||||
strafesnet_common::physics::Instruction::SetControl(strafesnet_common::physics::SetControlInstruction::SetJump(state))=>Ok(Instruction::SetJump(state.into())),
|
||||
strafesnet_common::physics::Instruction::SetControl(strafesnet_common::physics::SetControlInstruction::SetZoom(state))=>Ok(Instruction::SetZoom(state.into())),
|
||||
strafesnet_common::physics::Instruction::SetControl(strafesnet_common::physics::SetControlInstruction::SetSprint(state))=>Ok(Instruction::SetSprint(state.into())),
|
||||
strafesnet_common::physics::Instruction::Mode(strafesnet_common::physics::ModeInstruction::Reset)=>Ok(Instruction::Reset),
|
||||
strafesnet_common::physics::Instruction::Mode(strafesnet_common::physics::ModeInstruction::Restart(mode_id))=>Ok(Instruction::Restart(mode_id.get())),
|
||||
strafesnet_common::physics::Instruction::Mode(strafesnet_common::physics::ModeInstruction::Spawn(mode_id,stage_id))=>Ok(Instruction::Spawn(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,62 +0,0 @@
|
||||
# Blender MTL File: 'teslacyberv3.0.blend'
|
||||
# Material Count: 6
|
||||
|
||||
newmtl Material
|
||||
Ns 65.476285
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.411568 0.411568 0.411568
|
||||
Ks 0.614679 0.614679 0.614679
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 36.750000
|
||||
d 1.000000
|
||||
illum 3
|
||||
|
||||
newmtl Материал
|
||||
Ns 323.999994
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.800000 0.800000 0.800000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl Материал.001
|
||||
Ns 900.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.026240 0.026240 0.026240
|
||||
Ks 0.000000 0.000000 0.000000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.450000
|
||||
d 1.000000
|
||||
illum 1
|
||||
|
||||
newmtl Материал.002
|
||||
Ns 0.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.031837 0.032429 0.029425
|
||||
Ks 0.169725 0.169725 0.169725
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 0.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
|
||||
newmtl Материал.003
|
||||
Ns 900.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.023585 0.083235 0.095923
|
||||
Ks 1.000000 1.000000 1.000000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 45.049999
|
||||
d 1.000000
|
||||
illum 3
|
||||
|
||||
newmtl Материал.004
|
||||
Ns 323.999994
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.800000 0.800000 0.800000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 2
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,7 @@ pub enum Instruction{
|
||||
struct WindowContext<'a>{
|
||||
manual_mouse_lock:bool,
|
||||
mouse_pos:glam::DVec2,
|
||||
simulation_paused:bool,
|
||||
screen_size:glam::UVec2,
|
||||
window:&'a winit::window::Window,
|
||||
physics_thread:crate::compat_worker::QNWorker<'a,TimedInstruction<PhysicsWorkerInstruction,SessionTime>>,
|
||||
@@ -24,6 +25,35 @@ impl WindowContext<'_>{
|
||||
fn get_middle_of_screen(&self)->winit::dpi::PhysicalPosition<u32>{
|
||||
winit::dpi::PhysicalPosition::new(self.screen_size.x/2,self.screen_size.y/2)
|
||||
}
|
||||
fn free_mouse(&mut self){
|
||||
self.manual_mouse_lock=false;
|
||||
match self.window.set_cursor_position(self.get_middle_of_screen()){
|
||||
Ok(())=>(),
|
||||
Err(e)=>println!("Could not set cursor position: {:?}",e),
|
||||
}
|
||||
match self.window.set_cursor_grab(winit::window::CursorGrabMode::None){
|
||||
Ok(())=>(),
|
||||
Err(e)=>println!("Could not release cursor: {:?}",e),
|
||||
}
|
||||
self.window.set_cursor_visible(true);
|
||||
}
|
||||
fn lock_mouse(&mut self){
|
||||
//if cursor is outside window don't lock but apparently there's no get pos function
|
||||
//let pos=window.get_cursor_pos();
|
||||
match self.window.set_cursor_grab(winit::window::CursorGrabMode::Locked){
|
||||
Ok(())=>(),
|
||||
Err(_)=>{
|
||||
match self.window.set_cursor_grab(winit::window::CursorGrabMode::Confined){
|
||||
Ok(())=>(),
|
||||
Err(e)=>{
|
||||
self.manual_mouse_lock=true;
|
||||
println!("Could not confine cursor: {:?}",e)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
self.window.set_cursor_visible(false);
|
||||
}
|
||||
fn window_event(&mut self,time:SessionTime,event:winit::event::WindowEvent){
|
||||
match event{
|
||||
winit::event::WindowEvent::DroppedFile(path)=>{
|
||||
@@ -34,6 +64,10 @@ impl WindowContext<'_>{
|
||||
}
|
||||
},
|
||||
winit::event::WindowEvent::Focused(state)=>{
|
||||
// don't unpause if manually paused
|
||||
if self.simulation_paused{
|
||||
return;
|
||||
}
|
||||
//pause unpause
|
||||
self.physics_thread.send(TimedInstruction{
|
||||
time,
|
||||
@@ -46,35 +80,8 @@ impl WindowContext<'_>{
|
||||
..
|
||||
}=>{
|
||||
match (logical_key,state){
|
||||
(winit::keyboard::Key::Named(winit::keyboard::NamedKey::Tab),winit::event::ElementState::Pressed)=>{
|
||||
self.manual_mouse_lock=false;
|
||||
match self.window.set_cursor_position(self.get_middle_of_screen()){
|
||||
Ok(())=>(),
|
||||
Err(e)=>println!("Could not set cursor position: {:?}",e),
|
||||
}
|
||||
match self.window.set_cursor_grab(winit::window::CursorGrabMode::None){
|
||||
Ok(())=>(),
|
||||
Err(e)=>println!("Could not release cursor: {:?}",e),
|
||||
}
|
||||
self.window.set_cursor_visible(state.is_pressed());
|
||||
},
|
||||
(winit::keyboard::Key::Named(winit::keyboard::NamedKey::Tab),winit::event::ElementState::Released)=>{
|
||||
//if cursor is outside window don't lock but apparently there's no get pos function
|
||||
//let pos=window.get_cursor_pos();
|
||||
match self.window.set_cursor_grab(winit::window::CursorGrabMode::Locked){
|
||||
Ok(())=>(),
|
||||
Err(_)=>{
|
||||
match self.window.set_cursor_grab(winit::window::CursorGrabMode::Confined){
|
||||
Ok(())=>(),
|
||||
Err(e)=>{
|
||||
self.manual_mouse_lock=true;
|
||||
println!("Could not confine cursor: {:?}",e)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
self.window.set_cursor_visible(state.is_pressed());
|
||||
},
|
||||
(winit::keyboard::Key::Named(winit::keyboard::NamedKey::Tab),winit::event::ElementState::Pressed)=>self.free_mouse(),
|
||||
(winit::keyboard::Key::Named(winit::keyboard::NamedKey::Tab),winit::event::ElementState::Released)=>self.lock_mouse(),
|
||||
(winit::keyboard::Key::Named(winit::keyboard::NamedKey::F11),winit::event::ElementState::Pressed)=>{
|
||||
if self.window.fullscreen().is_some(){
|
||||
self.window.set_fullscreen(None);
|
||||
@@ -131,8 +138,18 @@ impl WindowContext<'_>{
|
||||
|
||||
if let Some(session_instruction)=match keycode{
|
||||
winit::keyboard::Key::Named(winit::keyboard::NamedKey::Space)=>input_ctrl!(SetJump,s),
|
||||
winit::keyboard::Key::Named(winit::keyboard::NamedKey::Shift)=>input_ctrl!(SetSprint,s),
|
||||
// TODO: bind system so playback pausing can use spacebar
|
||||
winit::keyboard::Key::Named(winit::keyboard::NamedKey::Enter)=>session_playback!(TogglePaused,s),
|
||||
winit::keyboard::Key::Named(winit::keyboard::NamedKey::Enter)=>if s{
|
||||
let paused=!self.simulation_paused;
|
||||
self.simulation_paused=paused;
|
||||
if paused{
|
||||
self.free_mouse();
|
||||
}else{
|
||||
self.lock_mouse();
|
||||
}
|
||||
Some(SessionInstructionSubset::Control(SessionControlInstruction::SetPaused(paused)))
|
||||
}else{None},
|
||||
winit::keyboard::Key::Named(winit::keyboard::NamedKey::ArrowUp)=>session_playback!(IncreaseTimescale,s),
|
||||
winit::keyboard::Key::Named(winit::keyboard::NamedKey::ArrowDown)=>session_playback!(DecreaseTimescale,s),
|
||||
winit::keyboard::Key::Named(winit::keyboard::NamedKey::ArrowLeft)=>session_playback!(SkipBack,s),
|
||||
@@ -241,6 +258,7 @@ pub fn worker<'a>(
|
||||
let mut window_context=WindowContext{
|
||||
manual_mouse_lock:false,
|
||||
mouse_pos:glam::DVec2::ZERO,
|
||||
simulation_paused:false,
|
||||
//make sure to update this!!!!!
|
||||
screen_size,
|
||||
window,
|
||||
|
||||
Reference in New Issue
Block a user