Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
64e1e762a1 | |||
ad862ae8c9 | |||
0ee17ac3d9 |
@ -7,6 +7,7 @@ use instruction::{TimedInstruction, InstructionConsumer};
|
|||||||
mod bvh;
|
mod bvh;
|
||||||
mod aabb;
|
mod aabb;
|
||||||
mod model;
|
mod model;
|
||||||
|
mod timers;
|
||||||
mod zeroes;
|
mod zeroes;
|
||||||
mod worker;
|
mod worker;
|
||||||
mod physics;
|
mod physics;
|
||||||
|
@ -42,12 +42,13 @@ pub enum InputInstruction {
|
|||||||
Jump(bool),
|
Jump(bool),
|
||||||
Zoom(bool),
|
Zoom(bool),
|
||||||
Reset,
|
Reset,
|
||||||
|
SetPaused(bool),
|
||||||
Idle,
|
Idle,
|
||||||
//Idle: there were no input events, but the simulation is safe to advance to this timestep
|
//Idle: there were no input events, but the simulation is safe to advance to this timestep
|
||||||
//for interpolation / networking / playback reasons, most playback heads will always want
|
//for interpolation / networking / playback reasons, most playback heads will always want
|
||||||
//to be 1 instruction ahead to generate the next state for interpolation.
|
//to be 1 instruction ahead to generate the next state for interpolation.
|
||||||
}
|
}
|
||||||
#[derive(Clone)]
|
#[derive(Clone,Debug)]
|
||||||
pub struct Body {
|
pub struct Body {
|
||||||
position: glam::Vec3,//I64 where 2^32 = 1 u
|
position: glam::Vec3,//I64 where 2^32 = 1 u
|
||||||
velocity: glam::Vec3,//I64 where 2^32 = 1 u/s
|
velocity: glam::Vec3,//I64 where 2^32 = 1 u/s
|
||||||
@ -453,6 +454,7 @@ impl PhysicsState {
|
|||||||
pub fn into_worker(mut self)->crate::worker::CompatWorker<TimedInstruction<InputInstruction>,PhysicsOutputState,Box<dyn FnMut(TimedInstruction<InputInstruction>)->PhysicsOutputState>>{
|
pub fn into_worker(mut self)->crate::worker::CompatWorker<TimedInstruction<InputInstruction>,PhysicsOutputState,Box<dyn FnMut(TimedInstruction<InputInstruction>)->PhysicsOutputState>>{
|
||||||
let mut mouse_blocking=true;
|
let mut mouse_blocking=true;
|
||||||
let mut last_mouse_time=self.next_mouse.time;
|
let mut last_mouse_time=self.next_mouse.time;
|
||||||
|
let mut simulation_timer=crate::timers::UnscaledTimer::unpaused();
|
||||||
let mut timeline=std::collections::VecDeque::new();
|
let mut timeline=std::collections::VecDeque::new();
|
||||||
crate::worker::CompatWorker::new(self.output(),Box::new(move |ins:TimedInstruction<InputInstruction>|{
|
crate::worker::CompatWorker::new(self.output(),Box::new(move |ins:TimedInstruction<InputInstruction>|{
|
||||||
if if let Some(phys_input)=match ins.instruction{
|
if if let Some(phys_input)=match ins.instruction{
|
||||||
@ -460,17 +462,17 @@ impl PhysicsState {
|
|||||||
if mouse_blocking{
|
if 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
|
||||||
timeline.push_front(TimedInstruction{
|
timeline.push_front(TimedInstruction{
|
||||||
time:last_mouse_time,
|
time:simulation_timer.time(last_mouse_time),
|
||||||
instruction:PhysicsInputInstruction::SetNextMouse(MouseState{time:ins.time,pos:m}),
|
instruction:PhysicsInputInstruction::SetNextMouse(MouseState{time:ins.time,pos:m}),
|
||||||
});
|
});
|
||||||
}else{
|
}else{
|
||||||
//mouse has just started moving again after being still for longer than 10ms.
|
//mouse has just started moving again after being still for longer than 10ms.
|
||||||
//replace the entire mouse interpolation state to avoid an intermediate state with identical m0.t m1.t timestamps which will divide by zero
|
//replace the entire mouse interpolation state to avoid an intermediate state with identical m0.t m1.t timestamps which will divide by zero
|
||||||
timeline.push_front(TimedInstruction{
|
timeline.push_front(TimedInstruction{
|
||||||
time:last_mouse_time,
|
time:simulation_timer.time(last_mouse_time),
|
||||||
instruction:PhysicsInputInstruction::ReplaceMouse(
|
instruction:PhysicsInputInstruction::ReplaceMouse(
|
||||||
MouseState{time:last_mouse_time,pos:self.next_mouse.pos},
|
MouseState{time:simulation_timer.time(last_mouse_time),pos:self.next_mouse.pos},
|
||||||
MouseState{time:ins.time,pos:m}
|
MouseState{time:simulation_timer.time(ins.time),pos:m}
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
//delay physics execution until we have an interpolation target
|
//delay physics execution until we have an interpolation target
|
||||||
@ -479,6 +481,14 @@ impl PhysicsState {
|
|||||||
last_mouse_time=ins.time;
|
last_mouse_time=ins.time;
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
|
InputInstruction::SetPaused(s)=>{
|
||||||
|
if s{
|
||||||
|
simulation_timer.pause(ins.time);
|
||||||
|
}else{
|
||||||
|
simulation_timer.unpause(ins.time);
|
||||||
|
}
|
||||||
|
Some(PhysicsInputInstruction::Idle)
|
||||||
|
}
|
||||||
InputInstruction::MoveForward(s)=>Some(PhysicsInputInstruction::SetMoveForward(s)),
|
InputInstruction::MoveForward(s)=>Some(PhysicsInputInstruction::SetMoveForward(s)),
|
||||||
InputInstruction::MoveLeft(s)=>Some(PhysicsInputInstruction::SetMoveLeft(s)),
|
InputInstruction::MoveLeft(s)=>Some(PhysicsInputInstruction::SetMoveLeft(s)),
|
||||||
InputInstruction::MoveBack(s)=>Some(PhysicsInputInstruction::SetMoveBack(s)),
|
InputInstruction::MoveBack(s)=>Some(PhysicsInputInstruction::SetMoveBack(s)),
|
||||||
@ -492,7 +502,7 @@ impl PhysicsState {
|
|||||||
}{
|
}{
|
||||||
//non-mouse event
|
//non-mouse event
|
||||||
timeline.push_back(TimedInstruction{
|
timeline.push_back(TimedInstruction{
|
||||||
time:ins.time,
|
time:simulation_timer.time(ins.time),
|
||||||
instruction:phys_input,
|
instruction:phys_input,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -504,7 +514,7 @@ impl PhysicsState {
|
|||||||
if 10_000_000<ins.time-self.next_mouse.time{
|
if 10_000_000<ins.time-self.next_mouse.time{
|
||||||
//push an event to extrapolate no movement from
|
//push an event to extrapolate no movement from
|
||||||
timeline.push_front(TimedInstruction{
|
timeline.push_front(TimedInstruction{
|
||||||
time:last_mouse_time,
|
time:simulation_timer.time(last_mouse_time),
|
||||||
instruction:PhysicsInputInstruction::SetNextMouse(MouseState{time:ins.time,pos:self.next_mouse.pos}),
|
instruction:PhysicsInputInstruction::SetNextMouse(MouseState{time:ins.time,pos:self.next_mouse.pos}),
|
||||||
});
|
});
|
||||||
last_mouse_time=ins.time;
|
last_mouse_time=ins.time;
|
||||||
@ -526,9 +536,10 @@ impl PhysicsState {
|
|||||||
}{
|
}{
|
||||||
//empty queue
|
//empty queue
|
||||||
while let Some(instruction)=timeline.pop_front(){
|
while let Some(instruction)=timeline.pop_front(){
|
||||||
self.run(instruction.time);
|
let simulation_time=simulation_timer.time(instruction.time);
|
||||||
|
self.run(simulation_time);
|
||||||
self.process_instruction(TimedInstruction{
|
self.process_instruction(TimedInstruction{
|
||||||
time:instruction.time,
|
time:simulation_time,
|
||||||
instruction:PhysicsInstruction::Input(instruction.instruction),
|
instruction:PhysicsInstruction::Input(instruction.instruction),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
237
src/timers.rs
Normal file
237
src/timers.rs
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
type TIME=crate::physics::TIME;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Timescale{
|
||||||
|
num:i64,
|
||||||
|
den:std::num::NonZeroU64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Paused{}
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Unpaused{}
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PausedScaled{scale:Timescale}
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct UnpausedScaled{scale:Timescale}
|
||||||
|
|
||||||
|
pub trait TimerState{}
|
||||||
|
impl TimerState for Paused{}
|
||||||
|
impl TimerState for Unpaused{}
|
||||||
|
impl TimerState for PausedScaled{}
|
||||||
|
impl TimerState for UnpausedScaled{}
|
||||||
|
|
||||||
|
pub trait IsPaused{}
|
||||||
|
impl IsPaused for Paused{}
|
||||||
|
impl IsPaused for PausedScaled{}
|
||||||
|
|
||||||
|
pub trait IsUnpaused{}
|
||||||
|
impl IsUnpaused for Unpaused{}
|
||||||
|
impl IsUnpaused for UnpausedScaled{}
|
||||||
|
|
||||||
|
pub trait IsScaled{}
|
||||||
|
impl IsScaled for PausedScaled{}
|
||||||
|
impl IsScaled for UnpausedScaled{}
|
||||||
|
|
||||||
|
pub trait IsUnscaled{}
|
||||||
|
impl IsUnscaled for Paused{}
|
||||||
|
impl IsUnscaled for Unpaused{}
|
||||||
|
|
||||||
|
//scaled timer wrapper
|
||||||
|
enum Scaled{
|
||||||
|
Paused(Timer<PausedScaled>),
|
||||||
|
Unpaused(Timer<UnpausedScaled>),
|
||||||
|
}
|
||||||
|
pub struct ScaledTimer{
|
||||||
|
timer:Scaled,
|
||||||
|
}
|
||||||
|
impl ScaledTimer{
|
||||||
|
pub fn unpaused()->Self{
|
||||||
|
Self{
|
||||||
|
timer:Scaled::Unpaused(unpaused_scaled(Timescale{num:1,den:std::num::NonZeroU64::new(1).unwrap()}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn time(&self,time:TIME)->TIME{
|
||||||
|
match &self.timer{
|
||||||
|
Scaled::Paused(timer)=>timer.time(),
|
||||||
|
Scaled::Unpaused(timer)=>timer.time(time),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn pause(&mut self,time:TIME){
|
||||||
|
match &self.timer{
|
||||||
|
Scaled::Paused(_)=>(),
|
||||||
|
Scaled::Unpaused(timer)=>self.timer=Scaled::Paused(timer.clone().pause(time)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
pub fn unpause(&mut self,time:TIME){
|
||||||
|
match &self.timer{
|
||||||
|
Scaled::Paused(timer)=>self.timer=Scaled::Unpaused(timer.clone().unpause(time)),
|
||||||
|
Scaled::Unpaused(_)=>(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//unscaled timer wrapper
|
||||||
|
enum Unscaled{
|
||||||
|
Paused(Timer<Paused>),
|
||||||
|
Unpaused(Timer<Unpaused>),
|
||||||
|
}
|
||||||
|
pub struct UnscaledTimer{
|
||||||
|
timer:Unscaled,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnscaledTimer{
|
||||||
|
pub fn unpaused()->Self{
|
||||||
|
Self{
|
||||||
|
timer:Unscaled::Unpaused(unpaused())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn time(&self,time:TIME)->TIME{
|
||||||
|
match &self.timer{
|
||||||
|
Unscaled::Paused(timer)=>timer.time(),
|
||||||
|
Unscaled::Unpaused(timer)=>timer.time(time),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn pause(&mut self,time:TIME){
|
||||||
|
match &self.timer{
|
||||||
|
Unscaled::Paused(_)=>(),
|
||||||
|
Unscaled::Unpaused(timer)=>self.timer=Unscaled::Paused(timer.clone().pause(time)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
pub fn unpause(&mut self,time:TIME){
|
||||||
|
match &self.timer{
|
||||||
|
Unscaled::Paused(timer)=>self.timer=Unscaled::Unpaused(timer.clone().unpause(time)),
|
||||||
|
Unscaled::Unpaused(_)=>(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Timer<State:TimerState>{
|
||||||
|
offset:crate::physics::TIME,
|
||||||
|
state:State,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_offset(time:TIME,write_time:TIME)->TIME{
|
||||||
|
write_time-time
|
||||||
|
}
|
||||||
|
fn get_offset_scaled(time:TIME,write_time:TIME,scale:&Timescale)->TIME{
|
||||||
|
write_time-time*scale.num/scale.den.get() as i64
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paused()->Timer<Paused>{
|
||||||
|
Timer{
|
||||||
|
offset:0,
|
||||||
|
state:Paused{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn unpaused()->Timer<Unpaused>{
|
||||||
|
Timer{
|
||||||
|
offset:0,
|
||||||
|
state:Unpaused{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn paused_scaled(scale:Timescale)->Timer<PausedScaled>{
|
||||||
|
Timer{
|
||||||
|
offset:0,
|
||||||
|
state:PausedScaled{scale},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn unpaused_scaled(scale:Timescale)->Timer<UnpausedScaled>{
|
||||||
|
Timer{
|
||||||
|
offset:0,
|
||||||
|
state:UnpausedScaled{scale},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Timer<Paused>{
|
||||||
|
pub fn time(&self)->TIME{
|
||||||
|
self.offset
|
||||||
|
}
|
||||||
|
pub fn unpause(self,time:TIME)->Timer<Unpaused>{
|
||||||
|
Timer{
|
||||||
|
offset:get_offset(time,self.time()),
|
||||||
|
state:Unpaused{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn set_time(&mut self,time:TIME,write_time:TIME){
|
||||||
|
self.offset=get_offset(time,write_time);
|
||||||
|
}
|
||||||
|
pub fn set_scale(self,time:TIME,scale:Timescale)->Timer<PausedScaled>{
|
||||||
|
Timer{
|
||||||
|
offset:get_offset_scaled(time,self.time(),&scale),
|
||||||
|
state:PausedScaled{scale},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Timer<Unpaused>{
|
||||||
|
pub fn time(&self,time:TIME)->TIME{
|
||||||
|
self.offset+time
|
||||||
|
}
|
||||||
|
pub fn pause(self,time:TIME)->Timer<Paused>{
|
||||||
|
Timer{
|
||||||
|
offset:self.time(time),
|
||||||
|
state:Paused{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn set_time(&mut self,time:TIME,write_time:TIME){
|
||||||
|
self.offset=get_offset(time,write_time);
|
||||||
|
}
|
||||||
|
pub fn set_scale(self,time:TIME,scale:Timescale)->Timer<UnpausedScaled>{
|
||||||
|
Timer{
|
||||||
|
offset:get_offset_scaled(time,self.time(time),&scale),
|
||||||
|
state:UnpausedScaled{scale},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Timer<PausedScaled>{
|
||||||
|
pub fn time(&self)->TIME{
|
||||||
|
self.offset
|
||||||
|
}
|
||||||
|
pub fn unpause(self,time:TIME)->Timer<UnpausedScaled>{
|
||||||
|
Timer{
|
||||||
|
offset:get_offset_scaled(time,self.time(),&self.state.scale),
|
||||||
|
state:UnpausedScaled{scale:self.state.scale},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn set_time(&mut self,time:TIME,write_time:TIME){
|
||||||
|
self.offset=get_offset_scaled(time,write_time,&self.state.scale);
|
||||||
|
}
|
||||||
|
pub fn set_scale(self,time:TIME,scale:Timescale)->Timer<PausedScaled>{
|
||||||
|
Timer{
|
||||||
|
offset:get_offset_scaled(time,self.time(),&scale),
|
||||||
|
state:PausedScaled{scale},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Timer<UnpausedScaled>{
|
||||||
|
pub fn time(&self,time:TIME)->TIME{
|
||||||
|
self.offset+time*self.state.scale.num/self.state.scale.den.get() as i64
|
||||||
|
}
|
||||||
|
pub fn pause(self,time:TIME)->Timer<PausedScaled>{
|
||||||
|
Timer{
|
||||||
|
offset:self.time(time),
|
||||||
|
state:PausedScaled{scale:self.state.scale},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn set_time(&mut self,time:TIME,write_time:TIME){
|
||||||
|
self.offset=get_offset_scaled(time,write_time,&self.state.scale);
|
||||||
|
}
|
||||||
|
pub fn set_scale(self,time:TIME,scale:Timescale)->Timer<UnpausedScaled>{
|
||||||
|
Timer{
|
||||||
|
offset:get_offset_scaled(time,self.time(time),&scale),
|
||||||
|
//self.offset+time*self.state.scale.num/self.state.scale.den.get() as i64-time*scale.num/scale.den.get() as i64
|
||||||
|
state:UnpausedScaled{scale},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_timer_unscaled(){
|
||||||
|
const ONE_SECOND:TIME=1_000_000_000;
|
||||||
|
let run_prepare=paused();
|
||||||
|
|
||||||
|
let run_start=run_prepare.unpause(ONE_SECOND);
|
||||||
|
let run_finish=run_start.pause(11*ONE_SECOND);
|
||||||
|
|
||||||
|
assert_eq!(run_finish.time(),10*ONE_SECOND);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user