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 strafesnet_common::integer::{Time,Fixed,Ratio};
|
||||
use strafesnet_common::integer::{Fixed,Ratio};
|
||||
use crate::physics::{Time,Body};
|
||||
|
||||
#[derive(Debug)]
|
||||
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{
|
||||
pipelines:GraphicsPipelines,
|
||||
bind_groups:GraphicsBindGroups,
|
||||
@ -882,7 +876,7 @@ impl GraphicsState{
|
||||
view:&wgpu::TextureView,
|
||||
device:&wgpu::Device,
|
||||
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
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
pub enum Instruction{
|
||||
Render(crate::graphics::FrameState),
|
||||
Render(crate::physics_worker::FrameState),
|
||||
//UpdateModel(crate::graphics::GraphicsModelUpdate),
|
||||
Resize(winit::dpi::PhysicalSize<u32>,crate::settings::UserSettings),
|
||||
ChangeMap(strafesnet_common::map::CompleteMap),
|
||||
|
@ -1,3 +1,4 @@
|
||||
mod body;
|
||||
mod file;
|
||||
mod setup;
|
||||
mod window;
|
||||
|
@ -3,6 +3,9 @@ use std::collections::{HashSet,HashMap};
|
||||
use strafesnet_common::integer::vec3::Vector3;
|
||||
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>;
|
||||
|
||||
pub trait UndirectedEdge{
|
||||
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|{
|
||||
let infinity_fev=self.infinity_fev(-dir,infinity_body.position);
|
||||
//a line is simpler to solve than a parabola
|
||||
@ -708,13 +711,13 @@ impl MinkowskiMesh<'_>{
|
||||
infinity_body.acceleration=vec3::ZERO;
|
||||
//crawl in from negative infinity along a tangent line to get the closest fev
|
||||
// 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::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|{
|
||||
//continue forwards along the body parabola
|
||||
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
|
||||
let infinity_body=crate::physics::Body::new(
|
||||
let infinity_body=Body::new(
|
||||
relative_body.extrapolated_position(time_limit),
|
||||
-relative_body.extrapolated_velocity(time_limit),
|
||||
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)
|
||||
//determine when it passes an edge ("sliding off" case)
|
||||
let mut best_time={
|
||||
@ -766,15 +769,15 @@ impl MinkowskiMesh<'_>{
|
||||
}
|
||||
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);
|
||||
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::Hit(face,time)=>Some((face,time)),
|
||||
}
|
||||
}
|
||||
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,
|
||||
//otherwise the point is not inside the mesh
|
||||
self.infinity_in(infinity_body)
|
||||
|
@ -5,15 +5,18 @@ use strafesnet_common::map;
|
||||
use strafesnet_common::run;
|
||||
use strafesnet_common::aabb;
|
||||
use strafesnet_common::model::{MeshId,ModelId};
|
||||
use strafesnet_common::mouse::MouseState;
|
||||
use strafesnet_common::gameplay_attributes::{self,CollisionAttributesId};
|
||||
use strafesnet_common::gameplay_modes::{self,StageId};
|
||||
use strafesnet_common::gameplay_style::{self,StyleModifiers};
|
||||
use strafesnet_common::controls_bitflag::Controls;
|
||||
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;
|
||||
|
||||
pub type Body=crate::body::Body<TimeInner>;
|
||||
type MouseState=strafesnet_common::mouse::MouseState<TimeInner>;
|
||||
|
||||
//external influence
|
||||
//this is how you influence the physics from outside
|
||||
use strafesnet_common::physics::Instruction as PhysicsInputInstruction;
|
||||
@ -21,7 +24,7 @@ use strafesnet_common::physics::Instruction as PhysicsInputInstruction;
|
||||
//internal influence
|
||||
//when the physics asks itself what happens next, this is how it's represented
|
||||
#[derive(Debug)]
|
||||
enum PhysicsInternalInstruction{
|
||||
pub enum PhysicsInternalInstruction{
|
||||
CollisionStart(Collision,model_physics::GigaTime),
|
||||
CollisionEnd(Collision,model_physics::GigaTime),
|
||||
StrafeTick,
|
||||
@ -29,32 +32,13 @@ enum PhysicsInternalInstruction{
|
||||
// Water,
|
||||
}
|
||||
#[derive(Debug)]
|
||||
enum PhysicsInstruction{
|
||||
pub enum PhysicsInstruction{
|
||||
Internal(PhysicsInternalInstruction),
|
||||
//InputInstructions conditionally activate RefreshWalkTarget
|
||||
//(by doing what SetWalkTargetVelocity used to do and then flagging it)
|
||||
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)]
|
||||
pub struct InputState{
|
||||
mouse:MouseState,
|
||||
@ -574,7 +558,7 @@ impl MoveState{
|
||||
=>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
|
||||
match self{
|
||||
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){
|
||||
let relative_body=VirtualBody::relative(&Body::ZERO,body).body(time);
|
||||
fn predict_collision_end(&self,collector:&mut instruction::InstructionCollector<PhysicsInternalInstruction,TimeInner>,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,body:&Body,time:Time){
|
||||
let relative_body=crate::body::VirtualBody::relative(&Body::ZERO,body).body(time);
|
||||
for contact in &self.contacts{
|
||||
//detect face slide off
|
||||
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)]
|
||||
pub struct PhysicsState{
|
||||
time:Time,
|
||||
@ -1036,7 +884,7 @@ impl PhysicsState{
|
||||
new_state.camera.sensitivity=self.camera.sensitivity;
|
||||
*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)
|
||||
}
|
||||
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.
|
||||
}
|
||||
//the physics consumes the generic PhysicsInstruction, but can only emit the more narrow PhysicsInternalInstruction
|
||||
impl instruction::InstructionConsumer<PhysicsInstruction> for PhysicsContext{
|
||||
fn process_instruction(&mut self,ins:TimedInstruction<PhysicsInstruction>){
|
||||
impl instruction::InstructionConsumer for PhysicsContext{
|
||||
type Instruction=PhysicsInstruction;
|
||||
type TimeInner=TimeInner;
|
||||
fn process_instruction(&mut self,ins:TimedInstruction<PhysicsInstruction,TimeInner>){
|
||||
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.
|
||||
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)
|
||||
}
|
||||
}
|
||||
@ -1262,7 +1114,7 @@ impl PhysicsContext{
|
||||
//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.process_instruction(TimedInstruction{
|
||||
time:instruction.time,
|
||||
@ -1272,7 +1124,7 @@ impl PhysicsContext{
|
||||
}
|
||||
|
||||
//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
|
||||
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;
|
||||
let (should_advance_body,goober_time)=match ins.instruction{
|
||||
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;
|
||||
let should_advance_body=match ins.instruction{
|
||||
//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{
|
||||
PhysicsInstruction::Input(PhysicsInputInstruction::Idle)
|
||||
|PhysicsInstruction::Input(PhysicsInputInstruction::SetNextMouse(_))
|
||||
@ -2050,6 +1902,7 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
|
||||
#[cfg(test)]
|
||||
mod test{
|
||||
use strafesnet_common::integer::{vec3::{self,int as int3},mat3};
|
||||
use crate::body::VirtualBody;
|
||||
use super::*;
|
||||
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));
|
||||
|
@ -1,10 +1,17 @@
|
||||
use strafesnet_common::mouse::MouseState;
|
||||
use strafesnet_common::physics::Instruction as PhysicsInputInstruction;
|
||||
use strafesnet_common::integer::Time;
|
||||
use strafesnet_common::session::{Time as SessionTime,TimeInner as SessionTimeInner};
|
||||
use strafesnet_common::physics::{Time as PhysicsTime,TimeInner as PhysicsTimeInner,Instruction as PhysicsInputInstruction};
|
||||
use strafesnet_common::instruction::TimedInstruction;
|
||||
use strafesnet_common::timer::{Scaled,Timer,TimerState};
|
||||
use mouse_interpolator::MouseInterpolator;
|
||||
|
||||
|
||||
pub struct FrameState{
|
||||
pub body:crate::physics::Body,
|
||||
pub camera:crate::physics::PhysicsCamera,
|
||||
pub time:PhysicsTime,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum InputInstruction{
|
||||
MoveMouse(glam::IVec2),
|
||||
@ -36,11 +43,11 @@ pub struct MouseInterpolator{
|
||||
//"PlayerController"
|
||||
user_settings:crate::settings::UserSettings,
|
||||
//"MouseInterpolator"
|
||||
timeline:std::collections::VecDeque<TimedInstruction<PhysicsInputInstruction>>,
|
||||
last_mouse_time:Time,//this value is pre-transformed to simulation time
|
||||
timeline:std::collections::VecDeque<TimedInstruction<PhysicsInputInstruction,PhysicsTimeInner>>,
|
||||
last_mouse_time:PhysicsTime,
|
||||
mouse_blocking:bool,
|
||||
//"Simulation"
|
||||
timer:Timer<Scaled>,
|
||||
timer:Timer<Scaled<SessionTimeInner,PhysicsTimeInner>>,
|
||||
physics:crate::physics::PhysicsContext,
|
||||
|
||||
}
|
||||
@ -58,7 +65,7 @@ impl MouseInterpolator{
|
||||
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{
|
||||
//tell the game state which is living in the past about its future
|
||||
self.timeline.push_front(TimedInstruction{
|
||||
@ -80,7 +87,7 @@ impl MouseInterpolator{
|
||||
}
|
||||
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
|
||||
self.timeline.push_back(TimedInstruction{
|
||||
time:self.timer.time(time),
|
||||
@ -89,7 +96,7 @@ impl MouseInterpolator{
|
||||
}
|
||||
/// returns should_empty_queue
|
||||
/// 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;
|
||||
match &ins.instruction{
|
||||
Instruction::Input(input_instruction)=>match input_instruction{
|
||||
@ -140,7 +147,7 @@ impl MouseInterpolator{
|
||||
}
|
||||
}
|
||||
/// 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
|
||||
self.timeline.push_front(TimedInstruction{
|
||||
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.
|
||||
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{
|
||||
//assume the mouse has stopped moving after 10ms.
|
||||
//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,
|
||||
//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);
|
||||
true
|
||||
}else{
|
||||
@ -174,20 +181,20 @@ impl MouseInterpolator{
|
||||
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);
|
||||
if should_empty_queue{
|
||||
self.empty_queue();
|
||||
}
|
||||
}
|
||||
pub fn get_frame_state(&self,time:Time)->crate::graphics::FrameState{
|
||||
crate::graphics::FrameState{
|
||||
pub fn get_frame_state(&self,time:SessionTime)->FrameState{
|
||||
FrameState{
|
||||
body:self.physics.camera_body(),
|
||||
camera:self.physics.camera(),
|
||||
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
|
||||
if self.mouse_blocking{
|
||||
self.unblock_mouse(time);
|
||||
@ -215,13 +222,13 @@ impl MouseInterpolator{
|
||||
pub fn new<'a>(
|
||||
mut graphics_worker:crate::compat_worker::INWorker<'a,crate::graphics_worker::Instruction>,
|
||||
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 mut interpolator=MouseInterpolator::new(
|
||||
physics,
|
||||
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);
|
||||
match ins.instruction{
|
||||
Instruction::Render=>{
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::window::WindowInstruction;
|
||||
use strafesnet_common::instruction::TimedInstruction;
|
||||
use strafesnet_common::integer;
|
||||
use strafesnet_common::session::TimeInner as SessionTimeInner;
|
||||
|
||||
fn optional_features()->wgpu::Features{
|
||||
wgpu::Features::TEXTURE_COMPRESSION_ASTC
|
||||
@ -234,7 +235,7 @@ pub fn setup_and_start(title:&str){
|
||||
|
||||
fn run_event_loop(
|
||||
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
|
||||
)->Result<(),winit::error::EventLoopError>{
|
||||
event_loop.run(move |event,elwt|{
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::physics_worker::InputInstruction;
|
||||
use strafesnet_common::integer;
|
||||
use strafesnet_common::instruction::TimedInstruction;
|
||||
use strafesnet_common::session::{Time as SessionTime,TimeInner as SessionTimeInner};
|
||||
|
||||
pub enum WindowInstruction{
|
||||
Resize(winit::dpi::PhysicalSize<u32>),
|
||||
@ -13,17 +14,17 @@ pub enum WindowInstruction{
|
||||
//holds thread handles to dispatch to
|
||||
struct WindowContext<'a>{
|
||||
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,
|
||||
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<'_>{
|
||||
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 window_event(&mut self,time:integer::Time,event:winit::event::WindowEvent){
|
||||
fn window_event(&mut self,time:SessionTime,event:winit::event::WindowEvent){
|
||||
match event{
|
||||
winit::event::WindowEvent::DroppedFile(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{
|
||||
winit::event::DeviceEvent::MouseMotion{
|
||||
delta,//these (f64,f64) are integers on my machine
|
||||
@ -161,7 +162,7 @@ impl WindowContext<'_>{
|
||||
pub fn worker<'a>(
|
||||
window:&'a winit::window::Window,
|
||||
setup_context:crate::setup::SetupContext<'a>,
|
||||
)->crate::compat_worker::QNWorker<'a,TimedInstruction<WindowInstruction>>{
|
||||
)->crate::compat_worker::QNWorker<'a,TimedInstruction<WindowInstruction,SessionTimeInner>>{
|
||||
// WindowContextSetup::new
|
||||
let user_settings=crate::settings::read_user_settings();
|
||||
|
||||
@ -184,7 +185,7 @@ pub fn worker<'a>(
|
||||
};
|
||||
|
||||
//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{
|
||||
WindowInstruction::RequestRedraw=>{
|
||||
window_context.window.request_redraw();
|
||||
|
@ -176,20 +176,20 @@ impl<'a,Task:Send+'a> INWorker<'a,Task>{
|
||||
#[cfg(test)]
|
||||
mod test{
|
||||
use super::{thread,QRWorker};
|
||||
use crate::physics;
|
||||
type Body=crate::physics::Body;
|
||||
use strafesnet_common::{integer,instruction};
|
||||
#[test]//How to run this test with printing: cargo test --release -- --nocapture
|
||||
fn test_worker() {
|
||||
// Create the worker thread
|
||||
let test_body=physics::Body::new(integer::vec3::ONE,integer::vec3::ONE,integer::vec3::ONE,integer::Time::ZERO);
|
||||
let worker=QRWorker::new(physics::Body::ZERO,
|
||||
|_|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(Body::ZERO,
|
||||
|_|Body::new(integer::vec3::ONE,integer::vec3::ONE,integer::vec3::ONE,integer::Time::ZERO)
|
||||
);
|
||||
|
||||
// Send tasks to the worker
|
||||
for _ in 0..5 {
|
||||
let task = instruction::TimedInstruction{
|
||||
time:integer::Time::ZERO,
|
||||
time:strafesnet_common::physics::Time::ZERO,
|
||||
instruction:strafesnet_common::physics::Instruction::Idle,
|
||||
};
|
||||
worker.send(task).unwrap();
|
||||
|
Loading…
Reference in New Issue
Block a user