fixup strafe client for strongly typed time
This commit is contained in:
parent
004e0d3776
commit
2f7597146e
160
strafe-client/src/body.rs
Normal file
160
strafe-client/src/body.rs
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
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!
|
||||||
|
}
|
||||||
|
impl<T> std::ops::Neg for Body<T>{
|
||||||
|
type Output=Self;
|
||||||
|
fn neg(self)->Self::Output{
|
||||||
|
Self{
|
||||||
|
position:self.position,
|
||||||
|
velocity:-self.velocity,
|
||||||
|
acceleration:self.acceleration,
|
||||||
|
time:-self.time,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Body<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{
|
||||||
|
Self{
|
||||||
|
position,
|
||||||
|
velocity,
|
||||||
|
acceleration,
|
||||||
|
time,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn extrapolated_position(&self,time:Time<T>)->Planar64Vec3{
|
||||||
|
let dt=time-self.time;
|
||||||
|
self.position
|
||||||
|
+(self.velocity*dt).map(|elem|elem.divide().fix_1())
|
||||||
|
+self.acceleration.map(|elem|(dt*dt*elem/2).divide().fix_1())
|
||||||
|
}
|
||||||
|
pub fn extrapolated_velocity(&self,time:Time<T>)->Planar64Vec3{
|
||||||
|
let dt=time-self.time;
|
||||||
|
self.velocity+(self.acceleration*dt).map(|elem|elem.divide().fix_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_position_ratio_dt<Num,Den,N1,D1,N2,N3,D2,N4,T1>(&self,dt:integer::Ratio<Num,Den>)->Planar64Vec3
|
||||||
|
where
|
||||||
|
// Why?
|
||||||
|
// All of this can be removed with const generics because the type can be specified as
|
||||||
|
// Ratio<Fixed<N,NF>,Fixed<D,DF>>
|
||||||
|
// which is known to implement all the necessary traits
|
||||||
|
Num:Copy,
|
||||||
|
Den:Copy+core::ops::Mul<i64,Output=D1>,
|
||||||
|
D1:Copy,
|
||||||
|
Num:core::ops::Mul<Planar64,Output=N1>,
|
||||||
|
Planar64:core::ops::Mul<D1,Output=N2>,
|
||||||
|
N1:core::ops::Add<N2,Output=N3>,
|
||||||
|
Num:core::ops::Mul<N3,Output=N4>,
|
||||||
|
Den:core::ops::Mul<D1,Output=D2>,
|
||||||
|
D2:Copy,
|
||||||
|
Planar64:core::ops::Mul<D2,Output=N4>,
|
||||||
|
N4:integer::Divide<D2,Output=T1>,
|
||||||
|
T1:integer::Fix<Planar64>,
|
||||||
|
{
|
||||||
|
// a*dt^2/2 + v*dt + p
|
||||||
|
// (a*dt/2+v)*dt+p
|
||||||
|
(self.acceleration.map(|elem|dt*elem/2)+self.velocity).map(|elem|dt.mul_ratio(elem))
|
||||||
|
.map(|elem|elem.divide().fix())+self.position
|
||||||
|
}
|
||||||
|
pub fn extrapolated_velocity_ratio_dt<Num,Den,N1,T1>(&self,dt:integer::Ratio<Num,Den>)->Planar64Vec3
|
||||||
|
where
|
||||||
|
Num:Copy,
|
||||||
|
Den:Copy,
|
||||||
|
Num:core::ops::Mul<Planar64,Output=N1>,
|
||||||
|
Planar64:core::ops::Mul<Den,Output=N1>,
|
||||||
|
N1:integer::Divide<Den,Output=T1>,
|
||||||
|
T1:integer::Fix<Planar64>,
|
||||||
|
{
|
||||||
|
// a*dt + v
|
||||||
|
self.acceleration.map(|elem|(dt*elem).divide().fix())+self.velocity
|
||||||
|
}
|
||||||
|
pub fn advance_time_ratio_dt(&mut self,dt:crate::model_physics::GigaTime){
|
||||||
|
self.position=self.extrapolated_position_ratio_dt(dt);
|
||||||
|
self.velocity=self.extrapolated_velocity_ratio_dt(dt);
|
||||||
|
self.time+=dt.into();
|
||||||
|
}
|
||||||
|
pub fn infinity_dir(&self)->Option<Planar64Vec3>{
|
||||||
|
if self.velocity==vec3::ZERO{
|
||||||
|
if self.acceleration==vec3::ZERO{
|
||||||
|
None
|
||||||
|
}else{
|
||||||
|
Some(self.acceleration)
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
Some(self.velocity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn grow_aabb(&self,aabb:&mut aabb::Aabb,t0:Time<T>,t1:Time<T>){
|
||||||
|
aabb.grow(self.extrapolated_position(t0));
|
||||||
|
aabb.grow(self.extrapolated_position(t1));
|
||||||
|
//v+a*t==0
|
||||||
|
//goober code
|
||||||
|
if !self.acceleration.x.is_zero(){
|
||||||
|
let t=-self.velocity.x/self.acceleration.x;
|
||||||
|
if t0.to_ratio().lt_ratio(t)&&t.lt_ratio(t1.to_ratio()){
|
||||||
|
aabb.grow(self.extrapolated_position_ratio_dt(t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !self.acceleration.y.is_zero(){
|
||||||
|
let t=-self.velocity.y/self.acceleration.y;
|
||||||
|
if t0.to_ratio().lt_ratio(t)&&t.lt_ratio(t1.to_ratio()){
|
||||||
|
aabb.grow(self.extrapolated_position_ratio_dt(t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !self.acceleration.z.is_zero(){
|
||||||
|
let t=-self.velocity.z/self.acceleration.z;
|
||||||
|
if t0.to_ratio().lt_ratio(t)&&t.lt_ratio(t1.to_ratio()){
|
||||||
|
aabb.grow(self.extrapolated_position_ratio_dt(t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
impl<T> std::fmt::Display for Body<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 const fn relative<'a>(body0:&'a Body<T>,body1:&'a Body<T>)->VirtualBody<'a,T>{
|
||||||
|
//(p0,v0,a0,t0)
|
||||||
|
//(p1,v1,a1,t1)
|
||||||
|
VirtualBody{
|
||||||
|
body0,
|
||||||
|
body1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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::physics::Body;
|
|
||||||
use crate::model_physics::{GigaTime,FEV,MeshQuery,DirectedEdge,MinkowskiMesh,MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert};
|
use crate::model_physics::{GigaTime,FEV,MeshQuery,DirectedEdge,MinkowskiMesh,MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert};
|
||||||
use strafesnet_common::integer::{Time,Fixed,Ratio};
|
use strafesnet_common::integer::{Fixed,Ratio};
|
||||||
|
use crate::physics::{Time,Body};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum Transition<F,E:DirectedEdge,V>{
|
enum Transition<F,E:DirectedEdge,V>{
|
||||||
|
@ -98,12 +98,6 @@ impl std::default::Default for GraphicsCamera{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FrameState{
|
|
||||||
pub body:crate::physics::Body,
|
|
||||||
pub camera:crate::physics::PhysicsCamera,
|
|
||||||
pub time:integer::Time,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct GraphicsState{
|
pub struct GraphicsState{
|
||||||
pipelines:GraphicsPipelines,
|
pipelines:GraphicsPipelines,
|
||||||
bind_groups:GraphicsBindGroups,
|
bind_groups:GraphicsBindGroups,
|
||||||
@ -882,7 +876,7 @@ impl GraphicsState{
|
|||||||
view:&wgpu::TextureView,
|
view:&wgpu::TextureView,
|
||||||
device:&wgpu::Device,
|
device:&wgpu::Device,
|
||||||
queue:&wgpu::Queue,
|
queue:&wgpu::Queue,
|
||||||
frame_state:FrameState,
|
frame_state:crate::physics_worker::FrameState,
|
||||||
){
|
){
|
||||||
//TODO:use scheduled frame times to create beautiful smoothing simulation physics extrapolation assuming no input
|
//TODO:use scheduled frame times to create beautiful smoothing simulation physics extrapolation assuming no input
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
pub enum Instruction{
|
pub enum Instruction{
|
||||||
Render(crate::graphics::FrameState),
|
Render(crate::physics_worker::FrameState),
|
||||||
//UpdateModel(crate::graphics::GraphicsModelUpdate),
|
//UpdateModel(crate::graphics::GraphicsModelUpdate),
|
||||||
Resize(winit::dpi::PhysicalSize<u32>,crate::settings::UserSettings),
|
Resize(winit::dpi::PhysicalSize<u32>,crate::settings::UserSettings),
|
||||||
ChangeMap(strafesnet_common::map::CompleteMap),
|
ChangeMap(strafesnet_common::map::CompleteMap),
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
mod body;
|
||||||
mod file;
|
mod file;
|
||||||
mod setup;
|
mod setup;
|
||||||
mod window;
|
mod window;
|
||||||
|
@ -3,6 +3,9 @@ use std::collections::{HashSet,HashMap};
|
|||||||
use strafesnet_common::integer::vec3::Vector3;
|
use strafesnet_common::integer::vec3::Vector3;
|
||||||
use strafesnet_common::model::{self,MeshId,PolygonIter};
|
use strafesnet_common::model::{self,MeshId,PolygonIter};
|
||||||
use strafesnet_common::integer::{self,vec3,Fixed,Planar64,Planar64Vec3,Ratio};
|
use strafesnet_common::integer::{self,vec3,Fixed,Planar64,Planar64Vec3,Ratio};
|
||||||
|
use strafesnet_common::physics::Time;
|
||||||
|
|
||||||
|
type Body=crate::body::Body<strafesnet_common::physics::TimeInner>;
|
||||||
|
|
||||||
pub trait UndirectedEdge{
|
pub trait UndirectedEdge{
|
||||||
type DirectedEdge:Copy+DirectedEdge;
|
type DirectedEdge:Copy+DirectedEdge;
|
||||||
@ -700,7 +703,7 @@ impl MinkowskiMesh<'_>{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn closest_fev_not_inside(&self,mut infinity_body:crate::physics::Body)->Option<FEV::<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert>>{
|
fn closest_fev_not_inside(&self,mut infinity_body:Body)->Option<FEV::<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert>>{
|
||||||
infinity_body.infinity_dir().map_or(None,|dir|{
|
infinity_body.infinity_dir().map_or(None,|dir|{
|
||||||
let infinity_fev=self.infinity_fev(-dir,infinity_body.position);
|
let infinity_fev=self.infinity_fev(-dir,infinity_body.position);
|
||||||
//a line is simpler to solve than a parabola
|
//a line is simpler to solve than a parabola
|
||||||
@ -708,13 +711,13 @@ impl MinkowskiMesh<'_>{
|
|||||||
infinity_body.acceleration=vec3::ZERO;
|
infinity_body.acceleration=vec3::ZERO;
|
||||||
//crawl in from negative infinity along a tangent line to get the closest fev
|
//crawl in from negative infinity along a tangent line to get the closest fev
|
||||||
// TODO: change crawl_fev args to delta time? Optional values?
|
// TODO: change crawl_fev args to delta time? Optional values?
|
||||||
match crate::face_crawler::crawl_fev(infinity_fev,self,&infinity_body,integer::Time::MIN/4,infinity_body.time){
|
match crate::face_crawler::crawl_fev(infinity_fev,self,&infinity_body,Time::MIN/4,infinity_body.time){
|
||||||
crate::face_crawler::CrawlResult::Miss(fev)=>Some(fev),
|
crate::face_crawler::CrawlResult::Miss(fev)=>Some(fev),
|
||||||
crate::face_crawler::CrawlResult::Hit(_,_)=>None,
|
crate::face_crawler::CrawlResult::Hit(_,_)=>None,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn predict_collision_in(&self,relative_body:&crate::physics::Body,time_limit:integer::Time)->Option<(MinkowskiFace,GigaTime)>{
|
pub fn predict_collision_in(&self,relative_body:&Body,time_limit:Time)->Option<(MinkowskiFace,GigaTime)>{
|
||||||
self.closest_fev_not_inside(relative_body.clone()).map_or(None,|fev|{
|
self.closest_fev_not_inside(relative_body.clone()).map_or(None,|fev|{
|
||||||
//continue forwards along the body parabola
|
//continue forwards along the body parabola
|
||||||
match crate::face_crawler::crawl_fev(fev,self,relative_body,relative_body.time,time_limit){
|
match crate::face_crawler::crawl_fev(fev,self,relative_body,relative_body.time,time_limit){
|
||||||
@ -723,9 +726,9 @@ impl MinkowskiMesh<'_>{
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn predict_collision_out(&self,relative_body:&crate::physics::Body,time_limit:integer::Time)->Option<(MinkowskiFace,GigaTime)>{
|
pub fn predict_collision_out(&self,relative_body:&Body,time_limit:Time)->Option<(MinkowskiFace,GigaTime)>{
|
||||||
//create an extrapolated body at time_limit
|
//create an extrapolated body at time_limit
|
||||||
let infinity_body=crate::physics::Body::new(
|
let infinity_body=Body::new(
|
||||||
relative_body.extrapolated_position(time_limit),
|
relative_body.extrapolated_position(time_limit),
|
||||||
-relative_body.extrapolated_velocity(time_limit),
|
-relative_body.extrapolated_velocity(time_limit),
|
||||||
relative_body.acceleration,
|
relative_body.acceleration,
|
||||||
@ -739,7 +742,7 @@ impl MinkowskiMesh<'_>{
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn predict_collision_face_out(&self,relative_body:&crate::physics::Body,time_limit:integer::Time,contact_face_id:MinkowskiFace)->Option<(MinkowskiEdge,GigaTime)>{
|
pub fn predict_collision_face_out(&self,relative_body:&Body,time_limit:Time,contact_face_id:MinkowskiFace)->Option<(MinkowskiEdge,GigaTime)>{
|
||||||
//no algorithm needed, there is only one state and two cases (Edge,None)
|
//no algorithm needed, there is only one state and two cases (Edge,None)
|
||||||
//determine when it passes an edge ("sliding off" case)
|
//determine when it passes an edge ("sliding off" case)
|
||||||
let mut best_time={
|
let mut best_time={
|
||||||
@ -766,15 +769,15 @@ impl MinkowskiMesh<'_>{
|
|||||||
}
|
}
|
||||||
best_edge.map(|e|(e.as_undirected(),best_time))
|
best_edge.map(|e|(e.as_undirected(),best_time))
|
||||||
}
|
}
|
||||||
fn infinity_in(&self,infinity_body:crate::physics::Body)->Option<(MinkowskiFace,GigaTime)>{
|
fn infinity_in(&self,infinity_body:Body)->Option<(MinkowskiFace,GigaTime)>{
|
||||||
let infinity_fev=self.infinity_fev(-infinity_body.velocity,infinity_body.position);
|
let infinity_fev=self.infinity_fev(-infinity_body.velocity,infinity_body.position);
|
||||||
match crate::face_crawler::crawl_fev(infinity_fev,self,&infinity_body,integer::Time::MIN/4,infinity_body.time){
|
match crate::face_crawler::crawl_fev(infinity_fev,self,&infinity_body,Time::MIN/4,infinity_body.time){
|
||||||
crate::face_crawler::CrawlResult::Miss(_)=>None,
|
crate::face_crawler::CrawlResult::Miss(_)=>None,
|
||||||
crate::face_crawler::CrawlResult::Hit(face,time)=>Some((face,time)),
|
crate::face_crawler::CrawlResult::Hit(face,time)=>Some((face,time)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn is_point_in_mesh(&self,point:Planar64Vec3)->bool{
|
pub fn is_point_in_mesh(&self,point:Planar64Vec3)->bool{
|
||||||
let infinity_body=crate::physics::Body::new(point,vec3::Y,vec3::ZERO,integer::Time::ZERO);
|
let infinity_body=Body::new(point,vec3::Y,vec3::ZERO,Time::ZERO);
|
||||||
//movement must escape the mesh forwards and backwards in time,
|
//movement must escape the mesh forwards and backwards in time,
|
||||||
//otherwise the point is not inside the mesh
|
//otherwise the point is not inside the mesh
|
||||||
self.infinity_in(infinity_body)
|
self.infinity_in(infinity_body)
|
||||||
|
@ -5,15 +5,18 @@ use strafesnet_common::map;
|
|||||||
use strafesnet_common::run;
|
use strafesnet_common::run;
|
||||||
use strafesnet_common::aabb;
|
use strafesnet_common::aabb;
|
||||||
use strafesnet_common::model::{MeshId,ModelId};
|
use strafesnet_common::model::{MeshId,ModelId};
|
||||||
use strafesnet_common::mouse::MouseState;
|
|
||||||
use strafesnet_common::gameplay_attributes::{self,CollisionAttributesId};
|
use strafesnet_common::gameplay_attributes::{self,CollisionAttributesId};
|
||||||
use strafesnet_common::gameplay_modes::{self,StageId};
|
use strafesnet_common::gameplay_modes::{self,StageId};
|
||||||
use strafesnet_common::gameplay_style::{self,StyleModifiers};
|
use strafesnet_common::gameplay_style::{self,StyleModifiers};
|
||||||
use strafesnet_common::controls_bitflag::Controls;
|
use strafesnet_common::controls_bitflag::Controls;
|
||||||
use strafesnet_common::instruction::{self,InstructionEmitter,InstructionConsumer,TimedInstruction};
|
use strafesnet_common::instruction::{self,InstructionEmitter,InstructionConsumer,TimedInstruction};
|
||||||
use strafesnet_common::integer::{self,vec3,mat3,Time,Planar64,Planar64Vec3,Planar64Mat3,Angle32,Ratio64Vec2};
|
use strafesnet_common::integer::{self,vec3,mat3,Planar64,Planar64Vec3,Planar64Mat3,Angle32,Ratio64Vec2};
|
||||||
|
pub use strafesnet_common::physics::{Time,TimeInner};
|
||||||
use gameplay::ModeState;
|
use gameplay::ModeState;
|
||||||
|
|
||||||
|
pub type Body=crate::body::Body<TimeInner>;
|
||||||
|
type MouseState=strafesnet_common::mouse::MouseState<TimeInner>;
|
||||||
|
|
||||||
//external influence
|
//external influence
|
||||||
//this is how you influence the physics from outside
|
//this is how you influence the physics from outside
|
||||||
use strafesnet_common::physics::Instruction as PhysicsInputInstruction;
|
use strafesnet_common::physics::Instruction as PhysicsInputInstruction;
|
||||||
@ -21,7 +24,7 @@ use strafesnet_common::physics::Instruction as PhysicsInputInstruction;
|
|||||||
//internal influence
|
//internal influence
|
||||||
//when the physics asks itself what happens next, this is how it's represented
|
//when the physics asks itself what happens next, this is how it's represented
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum PhysicsInternalInstruction{
|
pub enum PhysicsInternalInstruction{
|
||||||
CollisionStart(Collision,model_physics::GigaTime),
|
CollisionStart(Collision,model_physics::GigaTime),
|
||||||
CollisionEnd(Collision,model_physics::GigaTime),
|
CollisionEnd(Collision,model_physics::GigaTime),
|
||||||
StrafeTick,
|
StrafeTick,
|
||||||
@ -29,32 +32,13 @@ enum PhysicsInternalInstruction{
|
|||||||
// Water,
|
// Water,
|
||||||
}
|
}
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum PhysicsInstruction{
|
pub enum PhysicsInstruction{
|
||||||
Internal(PhysicsInternalInstruction),
|
Internal(PhysicsInternalInstruction),
|
||||||
//InputInstructions conditionally activate RefreshWalkTarget
|
//InputInstructions conditionally activate RefreshWalkTarget
|
||||||
//(by doing what SetWalkTargetVelocity used to do and then flagging it)
|
//(by doing what SetWalkTargetVelocity used to do and then flagging it)
|
||||||
Input(PhysicsInputInstruction),
|
Input(PhysicsInputInstruction),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone,Copy,Debug,Hash)]
|
|
||||||
pub struct Body{
|
|
||||||
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,//nanoseconds x xxxxD!
|
|
||||||
}
|
|
||||||
impl std::ops::Neg for Body{
|
|
||||||
type Output=Self;
|
|
||||||
fn neg(self)->Self::Output{
|
|
||||||
Self{
|
|
||||||
position:self.position,
|
|
||||||
velocity:-self.velocity,
|
|
||||||
acceleration:self.acceleration,
|
|
||||||
time:-self.time,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone,Debug,Default)]
|
#[derive(Clone,Debug,Default)]
|
||||||
pub struct InputState{
|
pub struct InputState{
|
||||||
mouse:MouseState,
|
mouse:MouseState,
|
||||||
@ -574,7 +558,7 @@ impl MoveState{
|
|||||||
=>None,
|
=>None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn next_move_instruction(&self,strafe:&Option<gameplay_style::StrafeSettings>,time:Time)->Option<TimedInstruction<PhysicsInternalInstruction>>{
|
fn next_move_instruction(&self,strafe:&Option<gameplay_style::StrafeSettings>,time:Time)->Option<TimedInstruction<PhysicsInternalInstruction,TimeInner>>{
|
||||||
//check if you have a valid walk state and create an instruction
|
//check if you have a valid walk state and create an instruction
|
||||||
match self{
|
match self{
|
||||||
MoveState::Walk(walk_state)|MoveState::Ladder(walk_state)=>match &walk_state.target{
|
MoveState::Walk(walk_state)|MoveState::Ladder(walk_state)=>match &walk_state.target{
|
||||||
@ -800,8 +784,8 @@ impl TouchingState{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn predict_collision_end(&self,collector:&mut instruction::InstructionCollector<PhysicsInternalInstruction>,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,body:&Body,time:Time){
|
fn predict_collision_end(&self,collector:&mut instruction::InstructionCollector<PhysicsInternalInstruction,TimeInner>,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,body:&Body,time:Time){
|
||||||
let relative_body=VirtualBody::relative(&Body::ZERO,body).body(time);
|
let relative_body=crate::body::VirtualBody::relative(&Body::ZERO,body).body(time);
|
||||||
for contact in &self.contacts{
|
for contact in &self.contacts{
|
||||||
//detect face slide off
|
//detect face slide off
|
||||||
let model_mesh=models.contact_mesh(contact);
|
let model_mesh=models.contact_mesh(contact);
|
||||||
@ -833,142 +817,6 @@ impl TouchingState{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Body{
|
|
||||||
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)->Self{
|
|
||||||
Self{
|
|
||||||
position,
|
|
||||||
velocity,
|
|
||||||
acceleration,
|
|
||||||
time,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn extrapolated_position(&self,time:Time)->Planar64Vec3{
|
|
||||||
let dt=time-self.time;
|
|
||||||
self.position
|
|
||||||
+(self.velocity*dt).map(|elem|elem.divide().fix_1())
|
|
||||||
+self.acceleration.map(|elem|(dt*dt*elem/2).divide().fix_1())
|
|
||||||
}
|
|
||||||
pub fn extrapolated_velocity(&self,time:Time)->Planar64Vec3{
|
|
||||||
let dt=time-self.time;
|
|
||||||
self.velocity+(self.acceleration*dt).map(|elem|elem.divide().fix_1())
|
|
||||||
}
|
|
||||||
pub fn advance_time(&mut self,time:Time){
|
|
||||||
self.position=self.extrapolated_position(time);
|
|
||||||
self.velocity=self.extrapolated_velocity(time);
|
|
||||||
self.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
|
|
||||||
// Why?
|
|
||||||
// All of this can be removed with const generics because the type can be specified as
|
|
||||||
// Ratio<Fixed<N,NF>,Fixed<D,DF>>
|
|
||||||
// which is known to implement all the necessary traits
|
|
||||||
Num:Copy,
|
|
||||||
Den:Copy+core::ops::Mul<i64,Output=D1>,
|
|
||||||
D1:Copy,
|
|
||||||
Num:core::ops::Mul<Planar64,Output=N1>,
|
|
||||||
Planar64:core::ops::Mul<D1,Output=N2>,
|
|
||||||
N1:core::ops::Add<N2,Output=N3>,
|
|
||||||
Num:core::ops::Mul<N3,Output=N4>,
|
|
||||||
Den:core::ops::Mul<D1,Output=D2>,
|
|
||||||
D2:Copy,
|
|
||||||
Planar64:core::ops::Mul<D2,Output=N4>,
|
|
||||||
N4:integer::Divide<D2,Output=T1>,
|
|
||||||
T1:integer::Fix<Planar64>,
|
|
||||||
{
|
|
||||||
// a*dt^2/2 + v*dt + p
|
|
||||||
// (a*dt/2+v)*dt+p
|
|
||||||
(self.acceleration.map(|elem|dt*elem/2)+self.velocity).map(|elem|dt.mul_ratio(elem))
|
|
||||||
.map(|elem|elem.divide().fix())+self.position
|
|
||||||
}
|
|
||||||
pub fn extrapolated_velocity_ratio_dt<Num,Den,N1,T1>(&self,dt:integer::Ratio<Num,Den>)->Planar64Vec3
|
|
||||||
where
|
|
||||||
Num:Copy,
|
|
||||||
Den:Copy,
|
|
||||||
Num:core::ops::Mul<Planar64,Output=N1>,
|
|
||||||
Planar64:core::ops::Mul<Den,Output=N1>,
|
|
||||||
N1:integer::Divide<Den,Output=T1>,
|
|
||||||
T1:integer::Fix<Planar64>,
|
|
||||||
{
|
|
||||||
// a*dt + v
|
|
||||||
self.acceleration.map(|elem|(dt*elem).divide().fix())+self.velocity
|
|
||||||
}
|
|
||||||
pub fn advance_time_ratio_dt(&mut self,dt:model_physics::GigaTime){
|
|
||||||
self.position=self.extrapolated_position_ratio_dt(dt);
|
|
||||||
self.velocity=self.extrapolated_velocity_ratio_dt(dt);
|
|
||||||
self.time+=dt.into();
|
|
||||||
}
|
|
||||||
pub fn infinity_dir(&self)->Option<Planar64Vec3>{
|
|
||||||
if self.velocity==vec3::ZERO{
|
|
||||||
if self.acceleration==vec3::ZERO{
|
|
||||||
None
|
|
||||||
}else{
|
|
||||||
Some(self.acceleration)
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
Some(self.velocity)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn grow_aabb(&self,aabb:&mut aabb::Aabb,t0:Time,t1:Time){
|
|
||||||
aabb.grow(self.extrapolated_position(t0));
|
|
||||||
aabb.grow(self.extrapolated_position(t1));
|
|
||||||
//v+a*t==0
|
|
||||||
//goober code
|
|
||||||
if !self.acceleration.x.is_zero(){
|
|
||||||
let t=-self.velocity.x/self.acceleration.x;
|
|
||||||
if t0.to_ratio().lt_ratio(t)&&t.lt_ratio(t1.to_ratio()){
|
|
||||||
aabb.grow(self.extrapolated_position_ratio_dt(t));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !self.acceleration.y.is_zero(){
|
|
||||||
let t=-self.velocity.y/self.acceleration.y;
|
|
||||||
if t0.to_ratio().lt_ratio(t)&&t.lt_ratio(t1.to_ratio()){
|
|
||||||
aabb.grow(self.extrapolated_position_ratio_dt(t));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !self.acceleration.z.is_zero(){
|
|
||||||
let t=-self.velocity.z/self.acceleration.z;
|
|
||||||
if t0.to_ratio().lt_ratio(t)&&t.lt_ratio(t1.to_ratio()){
|
|
||||||
aabb.grow(self.extrapolated_position_ratio_dt(t));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
impl std::fmt::Display for Body{
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct VirtualBody<'a>{
|
|
||||||
body0:&'a Body,
|
|
||||||
body1:&'a Body,
|
|
||||||
}
|
|
||||||
impl VirtualBody<'_>{
|
|
||||||
const fn relative<'a>(body0:&'a Body,body1:&'a Body)->VirtualBody<'a>{
|
|
||||||
//(p0,v0,a0,t0)
|
|
||||||
//(p1,v1,a1,t1)
|
|
||||||
VirtualBody{
|
|
||||||
body0,
|
|
||||||
body1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn extrapolated_position(&self,time:Time)->Planar64Vec3{
|
|
||||||
self.body1.extrapolated_position(time)-self.body0.extrapolated_position(time)
|
|
||||||
}
|
|
||||||
fn extrapolated_velocity(&self,time:Time)->Planar64Vec3{
|
|
||||||
self.body1.extrapolated_velocity(time)-self.body0.extrapolated_velocity(time)
|
|
||||||
}
|
|
||||||
fn acceleration(&self)->Planar64Vec3{
|
|
||||||
self.body1.acceleration-self.body0.acceleration
|
|
||||||
}
|
|
||||||
fn body(&self,time:Time)->Body{
|
|
||||||
Body::new(self.extrapolated_position(time),self.extrapolated_velocity(time),self.acceleration(),time)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone,Debug)]
|
#[derive(Clone,Debug)]
|
||||||
pub struct PhysicsState{
|
pub struct PhysicsState{
|
||||||
time:Time,
|
time:Time,
|
||||||
@ -1036,7 +884,7 @@ impl PhysicsState{
|
|||||||
new_state.camera.sensitivity=self.camera.sensitivity;
|
new_state.camera.sensitivity=self.camera.sensitivity;
|
||||||
*self=new_state;
|
*self=new_state;
|
||||||
}
|
}
|
||||||
fn next_move_instruction(&self)->Option<TimedInstruction<PhysicsInternalInstruction>>{
|
fn next_move_instruction(&self)->Option<TimedInstruction<PhysicsInternalInstruction,TimeInner>>{
|
||||||
self.move_state.next_move_instruction(&self.style.strafe,self.time)
|
self.move_state.next_move_instruction(&self.style.strafe,self.time)
|
||||||
}
|
}
|
||||||
fn cull_velocity(&mut self,data:&PhysicsData,velocity:Planar64Vec3){
|
fn cull_velocity(&mut self,data:&PhysicsData,velocity:Planar64Vec3){
|
||||||
@ -1086,14 +934,18 @@ pub struct PhysicsContext{
|
|||||||
data:PhysicsData,//data currently loaded into memory which is needded for physics to run, but is not part of the state.
|
data:PhysicsData,//data currently loaded into memory which is needded for physics to run, but is not part of the state.
|
||||||
}
|
}
|
||||||
//the physics consumes the generic PhysicsInstruction, but can only emit the more narrow PhysicsInternalInstruction
|
//the physics consumes the generic PhysicsInstruction, but can only emit the more narrow PhysicsInternalInstruction
|
||||||
impl instruction::InstructionConsumer<PhysicsInstruction> for PhysicsContext{
|
impl instruction::InstructionConsumer for PhysicsContext{
|
||||||
fn process_instruction(&mut self,ins:TimedInstruction<PhysicsInstruction>){
|
type Instruction=PhysicsInstruction;
|
||||||
|
type TimeInner=TimeInner;
|
||||||
|
fn process_instruction(&mut self,ins:TimedInstruction<PhysicsInstruction,TimeInner>){
|
||||||
atomic_state_update(&mut self.state,&self.data,ins)
|
atomic_state_update(&mut self.state,&self.data,ins)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl instruction::InstructionEmitter<PhysicsInternalInstruction> for PhysicsContext{
|
impl instruction::InstructionEmitter for PhysicsContext{
|
||||||
|
type Instruction=PhysicsInternalInstruction;
|
||||||
|
type TimeInner=TimeInner;
|
||||||
//this little next instruction function could cache its return value and invalidate the cached value by watching the State.
|
//this little next instruction function could cache its return value and invalidate the cached value by watching the State.
|
||||||
fn next_instruction(&self,time_limit:Time)->Option<TimedInstruction<PhysicsInternalInstruction>>{
|
fn next_instruction(&self,time_limit:Time)->Option<TimedInstruction<PhysicsInternalInstruction,TimeInner>>{
|
||||||
next_instruction_internal(&self.state,&self.data,time_limit)
|
next_instruction_internal(&self.state,&self.data,time_limit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1262,7 +1114,7 @@ impl PhysicsContext{
|
|||||||
//write hash lol
|
//write hash lol
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn run_input_instruction(&mut self,instruction:TimedInstruction<PhysicsInputInstruction>){
|
pub fn run_input_instruction(&mut self,instruction:TimedInstruction<PhysicsInputInstruction,TimeInner>){
|
||||||
self.run_internal_exhaustive(instruction.time);
|
self.run_internal_exhaustive(instruction.time);
|
||||||
self.process_instruction(TimedInstruction{
|
self.process_instruction(TimedInstruction{
|
||||||
time:instruction.time,
|
time:instruction.time,
|
||||||
@ -1272,7 +1124,7 @@ impl PhysicsContext{
|
|||||||
}
|
}
|
||||||
|
|
||||||
//this is the one who asks
|
//this is the one who asks
|
||||||
fn next_instruction_internal(state:&PhysicsState,data:&PhysicsData,time_limit:Time)->Option<TimedInstruction<PhysicsInternalInstruction>>{
|
fn next_instruction_internal(state:&PhysicsState,data:&PhysicsData,time_limit:Time)->Option<TimedInstruction<PhysicsInternalInstruction,TimeInner>>{
|
||||||
//JUST POLLING!!! NO MUTATION
|
//JUST POLLING!!! NO MUTATION
|
||||||
let mut collector = instruction::InstructionCollector::new(time_limit);
|
let mut collector = instruction::InstructionCollector::new(time_limit);
|
||||||
|
|
||||||
@ -1805,7 +1657,7 @@ fn collision_end_intersect(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedInstruction<PhysicsInternalInstruction>){
|
fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedInstruction<PhysicsInternalInstruction,TimeInner>){
|
||||||
state.time=ins.time;
|
state.time=ins.time;
|
||||||
let (should_advance_body,goober_time)=match ins.instruction{
|
let (should_advance_body,goober_time)=match ins.instruction{
|
||||||
PhysicsInternalInstruction::CollisionStart(_,dt)
|
PhysicsInternalInstruction::CollisionStart(_,dt)
|
||||||
@ -1901,7 +1753,7 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedInstruction<PhysicsInputInstruction>){
|
fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedInstruction<PhysicsInputInstruction,TimeInner>){
|
||||||
state.time=ins.time;
|
state.time=ins.time;
|
||||||
let should_advance_body=match ins.instruction{
|
let should_advance_body=match ins.instruction{
|
||||||
//the body may as well be a quantum wave function
|
//the body may as well be a quantum wave function
|
||||||
@ -2027,7 +1879,7 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn atomic_state_update(state:&mut PhysicsState,data:&PhysicsData,ins:TimedInstruction<PhysicsInstruction>){
|
fn atomic_state_update(state:&mut PhysicsState,data:&PhysicsData,ins:TimedInstruction<PhysicsInstruction,TimeInner>){
|
||||||
match &ins.instruction{
|
match &ins.instruction{
|
||||||
PhysicsInstruction::Input(PhysicsInputInstruction::Idle)
|
PhysicsInstruction::Input(PhysicsInputInstruction::Idle)
|
||||||
|PhysicsInstruction::Input(PhysicsInputInstruction::SetNextMouse(_))
|
|PhysicsInstruction::Input(PhysicsInputInstruction::SetNextMouse(_))
|
||||||
@ -2050,6 +1902,7 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test{
|
mod test{
|
||||||
use strafesnet_common::integer::{vec3::{self,int as int3},mat3};
|
use strafesnet_common::integer::{vec3::{self,int as int3},mat3};
|
||||||
|
use crate::body::VirtualBody;
|
||||||
use super::*;
|
use super::*;
|
||||||
fn test_collision_axis_aligned(relative_body:Body,expected_collision_time:Option<Time>){
|
fn test_collision_axis_aligned(relative_body:Body,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 h0=HitboxMesh::new(PhysicsMesh::unit_cube(),integer::Planar64Affine3::new(mat3::from_diagonal(int3(5,1,5)>>1),vec3::ZERO));
|
||||||
|
@ -1,10 +1,17 @@
|
|||||||
use strafesnet_common::mouse::MouseState;
|
use strafesnet_common::mouse::MouseState;
|
||||||
use strafesnet_common::physics::Instruction as PhysicsInputInstruction;
|
use strafesnet_common::session::{Time as SessionTime,TimeInner as SessionTimeInner};
|
||||||
use strafesnet_common::integer::Time;
|
use strafesnet_common::physics::{Time as PhysicsTime,TimeInner as PhysicsTimeInner,Instruction as PhysicsInputInstruction};
|
||||||
use strafesnet_common::instruction::TimedInstruction;
|
use strafesnet_common::instruction::TimedInstruction;
|
||||||
use strafesnet_common::timer::{Scaled,Timer,TimerState};
|
use strafesnet_common::timer::{Scaled,Timer,TimerState};
|
||||||
use mouse_interpolator::MouseInterpolator;
|
use mouse_interpolator::MouseInterpolator;
|
||||||
|
|
||||||
|
|
||||||
|
pub struct FrameState{
|
||||||
|
pub body:crate::physics::Body,
|
||||||
|
pub camera:crate::physics::PhysicsCamera,
|
||||||
|
pub time:PhysicsTime,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum InputInstruction{
|
pub enum InputInstruction{
|
||||||
MoveMouse(glam::IVec2),
|
MoveMouse(glam::IVec2),
|
||||||
@ -36,11 +43,11 @@ pub struct MouseInterpolator{
|
|||||||
//"PlayerController"
|
//"PlayerController"
|
||||||
user_settings:crate::settings::UserSettings,
|
user_settings:crate::settings::UserSettings,
|
||||||
//"MouseInterpolator"
|
//"MouseInterpolator"
|
||||||
timeline:std::collections::VecDeque<TimedInstruction<PhysicsInputInstruction>>,
|
timeline:std::collections::VecDeque<TimedInstruction<PhysicsInputInstruction,PhysicsTimeInner>>,
|
||||||
last_mouse_time:Time,//this value is pre-transformed to simulation time
|
last_mouse_time:PhysicsTime,
|
||||||
mouse_blocking:bool,
|
mouse_blocking:bool,
|
||||||
//"Simulation"
|
//"Simulation"
|
||||||
timer:Timer<Scaled>,
|
timer:Timer<Scaled<SessionTimeInner,PhysicsTimeInner>>,
|
||||||
physics:crate::physics::PhysicsContext,
|
physics:crate::physics::PhysicsContext,
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -58,7 +65,7 @@ impl MouseInterpolator{
|
|||||||
user_settings,
|
user_settings,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn push_mouse_instruction(&mut self,ins:&TimedInstruction<Instruction>,m:glam::IVec2){
|
fn push_mouse_instruction(&mut self,ins:&TimedInstruction<Instruction,SessionTimeInner>,m:glam::IVec2){
|
||||||
if self.mouse_blocking{
|
if self.mouse_blocking{
|
||||||
//tell the game state which is living in the past about its future
|
//tell the game state which is living in the past about its future
|
||||||
self.timeline.push_front(TimedInstruction{
|
self.timeline.push_front(TimedInstruction{
|
||||||
@ -80,7 +87,7 @@ impl MouseInterpolator{
|
|||||||
}
|
}
|
||||||
self.last_mouse_time=self.timer.time(ins.time);
|
self.last_mouse_time=self.timer.time(ins.time);
|
||||||
}
|
}
|
||||||
fn push(&mut self,time:Time,phys_input:PhysicsInputInstruction){
|
fn push(&mut self,time:SessionTime,phys_input:PhysicsInputInstruction){
|
||||||
//This is always a non-mouse event
|
//This is always a non-mouse event
|
||||||
self.timeline.push_back(TimedInstruction{
|
self.timeline.push_back(TimedInstruction{
|
||||||
time:self.timer.time(time),
|
time:self.timer.time(time),
|
||||||
@ -89,7 +96,7 @@ impl MouseInterpolator{
|
|||||||
}
|
}
|
||||||
/// returns should_empty_queue
|
/// returns should_empty_queue
|
||||||
/// may or may not mutate internal state XD!
|
/// may or may not mutate internal state XD!
|
||||||
fn map_instruction(&mut self,ins:&TimedInstruction<Instruction>)->bool{
|
fn map_instruction(&mut self,ins:&TimedInstruction<Instruction,SessionTimeInner>)->bool{
|
||||||
let mut update_mouse_blocking=true;
|
let mut update_mouse_blocking=true;
|
||||||
match &ins.instruction{
|
match &ins.instruction{
|
||||||
Instruction::Input(input_instruction)=>match input_instruction{
|
Instruction::Input(input_instruction)=>match input_instruction{
|
||||||
@ -140,7 +147,7 @@ impl MouseInterpolator{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// must check if self.mouse_blocking==true before calling!
|
/// must check if self.mouse_blocking==true before calling!
|
||||||
fn unblock_mouse(&mut self,time:Time){
|
fn unblock_mouse(&mut self,time:SessionTime){
|
||||||
//push an event to extrapolate no movement from
|
//push an event to extrapolate no movement from
|
||||||
self.timeline.push_front(TimedInstruction{
|
self.timeline.push_front(TimedInstruction{
|
||||||
time:self.last_mouse_time,
|
time:self.last_mouse_time,
|
||||||
@ -150,13 +157,13 @@ impl MouseInterpolator{
|
|||||||
//stop blocking. the mouse is not moving so the physics does not need to live in the past and wait for interpolation targets.
|
//stop blocking. the mouse is not moving so the physics does not need to live in the past and wait for interpolation targets.
|
||||||
self.mouse_blocking=false;
|
self.mouse_blocking=false;
|
||||||
}
|
}
|
||||||
fn update_mouse_blocking(&mut self,time:Time)->bool{
|
fn update_mouse_blocking(&mut self,time:SessionTime)->bool{
|
||||||
if self.mouse_blocking{
|
if self.mouse_blocking{
|
||||||
//assume the mouse has stopped moving after 10ms.
|
//assume the mouse has stopped moving after 10ms.
|
||||||
//shitty mice are 125Hz which is 8ms so this should cover that.
|
//shitty mice are 125Hz which is 8ms so this should cover that.
|
||||||
//setting this to 100us still doesn't print even though it's 10x lower than the polling rate,
|
//setting this to 100us still doesn't print even though it's 10x lower than the polling rate,
|
||||||
//so mouse events are probably not handled separately from drawing and fire right before it :(
|
//so mouse events are probably not handled separately from drawing and fire right before it :(
|
||||||
if Time::from_millis(10)<self.timer.time(time)-self.physics.get_next_mouse().time{
|
if PhysicsTime::from_millis(10)<self.timer.time(time)-self.physics.get_next_mouse().time{
|
||||||
self.unblock_mouse(time);
|
self.unblock_mouse(time);
|
||||||
true
|
true
|
||||||
}else{
|
}else{
|
||||||
@ -174,20 +181,20 @@ impl MouseInterpolator{
|
|||||||
self.physics.run_input_instruction(instruction);
|
self.physics.run_input_instruction(instruction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn handle_instruction(&mut self,ins:&TimedInstruction<Instruction>){
|
pub fn handle_instruction(&mut self,ins:&TimedInstruction<Instruction,SessionTimeInner>){
|
||||||
let should_empty_queue=self.map_instruction(ins);
|
let should_empty_queue=self.map_instruction(ins);
|
||||||
if should_empty_queue{
|
if should_empty_queue{
|
||||||
self.empty_queue();
|
self.empty_queue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_frame_state(&self,time:Time)->crate::graphics::FrameState{
|
pub fn get_frame_state(&self,time:SessionTime)->FrameState{
|
||||||
crate::graphics::FrameState{
|
FrameState{
|
||||||
body:self.physics.camera_body(),
|
body:self.physics.camera_body(),
|
||||||
camera:self.physics.camera(),
|
camera:self.physics.camera(),
|
||||||
time:self.timer.time(time),
|
time:self.timer.time(time),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn change_map(&mut self,time:Time,map:&strafesnet_common::map::CompleteMap){
|
pub fn change_map(&mut self,time:SessionTime,map:&strafesnet_common::map::CompleteMap){
|
||||||
//dump any pending interpolation state
|
//dump any pending interpolation state
|
||||||
if self.mouse_blocking{
|
if self.mouse_blocking{
|
||||||
self.unblock_mouse(time);
|
self.unblock_mouse(time);
|
||||||
@ -215,13 +222,13 @@ impl MouseInterpolator{
|
|||||||
pub fn new<'a>(
|
pub fn new<'a>(
|
||||||
mut graphics_worker:crate::compat_worker::INWorker<'a,crate::graphics_worker::Instruction>,
|
mut graphics_worker:crate::compat_worker::INWorker<'a,crate::graphics_worker::Instruction>,
|
||||||
user_settings:crate::settings::UserSettings,
|
user_settings:crate::settings::UserSettings,
|
||||||
)->crate::compat_worker::QNWorker<'a,TimedInstruction<Instruction>>{
|
)->crate::compat_worker::QNWorker<'a,TimedInstruction<Instruction,SessionTimeInner>>{
|
||||||
let physics=crate::physics::PhysicsContext::default();
|
let physics=crate::physics::PhysicsContext::default();
|
||||||
let mut interpolator=MouseInterpolator::new(
|
let mut interpolator=MouseInterpolator::new(
|
||||||
physics,
|
physics,
|
||||||
user_settings
|
user_settings
|
||||||
);
|
);
|
||||||
crate::compat_worker::QNWorker::new(move |ins:TimedInstruction<Instruction>|{
|
crate::compat_worker::QNWorker::new(move |ins:TimedInstruction<Instruction,SessionTimeInner>|{
|
||||||
interpolator.handle_instruction(&ins);
|
interpolator.handle_instruction(&ins);
|
||||||
match ins.instruction{
|
match ins.instruction{
|
||||||
Instruction::Render=>{
|
Instruction::Render=>{
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crate::window::WindowInstruction;
|
use crate::window::WindowInstruction;
|
||||||
use strafesnet_common::instruction::TimedInstruction;
|
use strafesnet_common::instruction::TimedInstruction;
|
||||||
use strafesnet_common::integer;
|
use strafesnet_common::integer;
|
||||||
|
use strafesnet_common::session::TimeInner as SessionTimeInner;
|
||||||
|
|
||||||
fn optional_features()->wgpu::Features{
|
fn optional_features()->wgpu::Features{
|
||||||
wgpu::Features::TEXTURE_COMPRESSION_ASTC
|
wgpu::Features::TEXTURE_COMPRESSION_ASTC
|
||||||
@ -234,7 +235,7 @@ pub fn setup_and_start(title:&str){
|
|||||||
|
|
||||||
fn run_event_loop(
|
fn run_event_loop(
|
||||||
event_loop:winit::event_loop::EventLoop<()>,
|
event_loop:winit::event_loop::EventLoop<()>,
|
||||||
mut window_thread:crate::compat_worker::QNWorker<TimedInstruction<WindowInstruction>>,
|
mut window_thread:crate::compat_worker::QNWorker<TimedInstruction<WindowInstruction,SessionTimeInner>>,
|
||||||
root_time:std::time::Instant
|
root_time:std::time::Instant
|
||||||
)->Result<(),winit::error::EventLoopError>{
|
)->Result<(),winit::error::EventLoopError>{
|
||||||
event_loop.run(move |event,elwt|{
|
event_loop.run(move |event,elwt|{
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crate::physics_worker::InputInstruction;
|
use crate::physics_worker::InputInstruction;
|
||||||
use strafesnet_common::integer;
|
use strafesnet_common::integer;
|
||||||
use strafesnet_common::instruction::TimedInstruction;
|
use strafesnet_common::instruction::TimedInstruction;
|
||||||
|
use strafesnet_common::session::{Time as SessionTime,TimeInner as SessionTimeInner};
|
||||||
|
|
||||||
pub enum WindowInstruction{
|
pub enum WindowInstruction{
|
||||||
Resize(winit::dpi::PhysicalSize<u32>),
|
Resize(winit::dpi::PhysicalSize<u32>),
|
||||||
@ -13,17 +14,17 @@ pub enum WindowInstruction{
|
|||||||
//holds thread handles to dispatch to
|
//holds thread handles to dispatch to
|
||||||
struct WindowContext<'a>{
|
struct WindowContext<'a>{
|
||||||
manual_mouse_lock:bool,
|
manual_mouse_lock:bool,
|
||||||
mouse:strafesnet_common::mouse::MouseState,//std::sync::Arc<std::sync::Mutex<>>
|
mouse:strafesnet_common::mouse::MouseState<SessionTimeInner>,//std::sync::Arc<std::sync::Mutex<>>
|
||||||
screen_size:glam::UVec2,
|
screen_size:glam::UVec2,
|
||||||
window:&'a winit::window::Window,
|
window:&'a winit::window::Window,
|
||||||
physics_thread:crate::compat_worker::QNWorker<'a,TimedInstruction<crate::physics_worker::Instruction>>,
|
physics_thread:crate::compat_worker::QNWorker<'a,TimedInstruction<crate::physics_worker::Instruction,SessionTimeInner>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowContext<'_>{
|
impl WindowContext<'_>{
|
||||||
fn get_middle_of_screen(&self)->winit::dpi::PhysicalPosition<u32>{
|
fn get_middle_of_screen(&self)->winit::dpi::PhysicalPosition<u32>{
|
||||||
winit::dpi::PhysicalPosition::new(self.screen_size.x/2,self.screen_size.y/2)
|
winit::dpi::PhysicalPosition::new(self.screen_size.x/2,self.screen_size.y/2)
|
||||||
}
|
}
|
||||||
fn window_event(&mut self,time:integer::Time,event:winit::event::WindowEvent){
|
fn window_event(&mut self,time:SessionTime,event:winit::event::WindowEvent){
|
||||||
match event{
|
match event{
|
||||||
winit::event::WindowEvent::DroppedFile(path)=>{
|
winit::event::WindowEvent::DroppedFile(path)=>{
|
||||||
match crate::file::load(path.as_path()){
|
match crate::file::load(path.as_path()){
|
||||||
@ -122,7 +123,7 @@ impl WindowContext<'_>{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn device_event(&mut self,time:integer::Time,event: winit::event::DeviceEvent){
|
fn device_event(&mut self,time:SessionTime,event: winit::event::DeviceEvent){
|
||||||
match event{
|
match event{
|
||||||
winit::event::DeviceEvent::MouseMotion{
|
winit::event::DeviceEvent::MouseMotion{
|
||||||
delta,//these (f64,f64) are integers on my machine
|
delta,//these (f64,f64) are integers on my machine
|
||||||
@ -161,7 +162,7 @@ impl WindowContext<'_>{
|
|||||||
pub fn worker<'a>(
|
pub fn worker<'a>(
|
||||||
window:&'a winit::window::Window,
|
window:&'a winit::window::Window,
|
||||||
setup_context:crate::setup::SetupContext<'a>,
|
setup_context:crate::setup::SetupContext<'a>,
|
||||||
)->crate::compat_worker::QNWorker<'a,TimedInstruction<WindowInstruction>>{
|
)->crate::compat_worker::QNWorker<'a,TimedInstruction<WindowInstruction,SessionTimeInner>>{
|
||||||
// WindowContextSetup::new
|
// WindowContextSetup::new
|
||||||
let user_settings=crate::settings::read_user_settings();
|
let user_settings=crate::settings::read_user_settings();
|
||||||
|
|
||||||
@ -184,7 +185,7 @@ pub fn worker<'a>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
//WindowContextSetup::into_worker
|
//WindowContextSetup::into_worker
|
||||||
crate::compat_worker::QNWorker::new(move |ins:TimedInstruction<WindowInstruction>|{
|
crate::compat_worker::QNWorker::new(move |ins:TimedInstruction<WindowInstruction,SessionTimeInner>|{
|
||||||
match ins.instruction{
|
match ins.instruction{
|
||||||
WindowInstruction::RequestRedraw=>{
|
WindowInstruction::RequestRedraw=>{
|
||||||
window_context.window.request_redraw();
|
window_context.window.request_redraw();
|
||||||
|
@ -176,20 +176,20 @@ impl<'a,Task:Send+'a> INWorker<'a,Task>{
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test{
|
mod test{
|
||||||
use super::{thread,QRWorker};
|
use super::{thread,QRWorker};
|
||||||
use crate::physics;
|
type Body=crate::physics::Body;
|
||||||
use strafesnet_common::{integer,instruction};
|
use strafesnet_common::{integer,instruction};
|
||||||
#[test]//How to run this test with printing: cargo test --release -- --nocapture
|
#[test]//How to run this test with printing: cargo test --release -- --nocapture
|
||||||
fn test_worker() {
|
fn test_worker() {
|
||||||
// Create the worker thread
|
// Create the worker thread
|
||||||
let test_body=physics::Body::new(integer::vec3::ONE,integer::vec3::ONE,integer::vec3::ONE,integer::Time::ZERO);
|
let test_body=Body::new(integer::vec3::ONE,integer::vec3::ONE,integer::vec3::ONE,integer::Time::ZERO);
|
||||||
let worker=QRWorker::new(physics::Body::ZERO,
|
let worker=QRWorker::new(Body::ZERO,
|
||||||
|_|physics::Body::new(integer::vec3::ONE,integer::vec3::ONE,integer::vec3::ONE,integer::Time::ZERO)
|
|_|Body::new(integer::vec3::ONE,integer::vec3::ONE,integer::vec3::ONE,integer::Time::ZERO)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Send tasks to the worker
|
// Send tasks to the worker
|
||||||
for _ in 0..5 {
|
for _ in 0..5 {
|
||||||
let task = instruction::TimedInstruction{
|
let task = instruction::TimedInstruction{
|
||||||
time:integer::Time::ZERO,
|
time:strafesnet_common::physics::Time::ZERO,
|
||||||
instruction:strafesnet_common::physics::Instruction::Idle,
|
instruction:strafesnet_common::physics::Instruction::Idle,
|
||||||
};
|
};
|
||||||
worker.send(task).unwrap();
|
worker.send(task).unwrap();
|
||||||
|
Loading…
Reference in New Issue
Block a user