From 38df41e043c039084665ebbc239adef49de8ba3d Mon Sep 17 00:00:00 2001 From: Quaternions Date: Tue, 30 Jul 2024 11:52:36 -0700 Subject: [PATCH] mouse interpolator abstraction --- src/physics_worker.rs | 236 ++++++++++++++++++++++++------------------ 1 file changed, 134 insertions(+), 102 deletions(-) diff --git a/src/physics_worker.rs b/src/physics_worker.rs index 1f0e970..78a1250 100644 --- a/src/physics_worker.rs +++ b/src/physics_worker.rs @@ -23,109 +23,141 @@ pub enum Instruction{ ClearModels, //Graphics(crate::graphics_worker::Instruction), } - - pub fn new(mut physics:crate::physics::PhysicsContext,mut graphics_worker:crate::compat_worker::INWorker)->crate::compat_worker::QNWorker>{ - let mut mouse_blocking=true; - let mut last_mouse_time=physics.get_next_mouse().time; - let mut timeline=std::collections::VecDeque::new(); - crate::compat_worker::QNWorker::new(move |ins:TimedInstruction|{ - if if let Some(phys_input)=match &ins.instruction{ - Instruction::Input(input_instruction)=>match input_instruction{ - &InputInstruction::MoveMouse(m)=>{ - if mouse_blocking{ - //tell the game state which is living in the past about its future - timeline.push_front(TimedInstruction{ - time:last_mouse_time, - instruction:PhysicsInputInstruction::SetNextMouse(MouseState{time:ins.time,pos:m}), - }); - }else{ - //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 - timeline.push_front(TimedInstruction{ - time:last_mouse_time, - instruction:PhysicsInputInstruction::ReplaceMouse( - MouseState{time:last_mouse_time,pos:physics.get_next_mouse().pos}, - MouseState{time:ins.time,pos:m} - ), - }); - //delay physics execution until we have an interpolation target - mouse_blocking=true; - } - last_mouse_time=ins.time; - None - }, - &InputInstruction::MoveForward(s)=>Some(PhysicsInputInstruction::SetMoveForward(s)), - &InputInstruction::MoveLeft(s)=>Some(PhysicsInputInstruction::SetMoveLeft(s)), - &InputInstruction::MoveBack(s)=>Some(PhysicsInputInstruction::SetMoveBack(s)), - &InputInstruction::MoveRight(s)=>Some(PhysicsInputInstruction::SetMoveRight(s)), - &InputInstruction::MoveUp(s)=>Some(PhysicsInputInstruction::SetMoveUp(s)), - &InputInstruction::MoveDown(s)=>Some(PhysicsInputInstruction::SetMoveDown(s)), - &InputInstruction::Jump(s)=>Some(PhysicsInputInstruction::SetJump(s)), - &InputInstruction::Zoom(s)=>Some(PhysicsInputInstruction::SetZoom(s)), - InputInstruction::Reset=>Some(PhysicsInputInstruction::Reset), - InputInstruction::PracticeFly=>Some(PhysicsInputInstruction::PracticeFly), +pub struct MouseInterpolator{ + timeline:std::collections::VecDeque>, + last_mouse_time:Time, + mouse_blocking:bool, +} +impl MouseInterpolator{ + fn push_mouse_instruction(&mut self,physics:&crate::physics::PhysicsContext,ins:&TimedInstruction,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{ + time:self.last_mouse_time, + instruction:PhysicsInputInstruction::SetNextMouse(MouseState{time:ins.time,pos:m}), + }); + }else{ + //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 + self.timeline.push_front(TimedInstruction{ + time:self.last_mouse_time, + instruction:PhysicsInputInstruction::ReplaceMouse( + MouseState{time:self.last_mouse_time,pos:physics.get_next_mouse().pos}, + MouseState{time:ins.time,pos:m} + ), + }); + //delay physics execution until we have an interpolation target + self.mouse_blocking=true; + } + self.last_mouse_time=ins.time; + } + /// returns the mapped physics input instruction + /// may or may not mutate internal state XD! + fn map_instruction(&mut self,physics:&crate::physics::PhysicsContext,ins:&TimedInstruction)->Option{ + match &ins.instruction{ + Instruction::Input(input_instruction)=>match input_instruction{ + &InputInstruction::MoveMouse(m)=>{ + self.push_mouse_instruction(physics,ins,m); + None }, - Instruction::GenerateModels(_)=>Some(PhysicsInputInstruction::Idle), - Instruction::ClearModels=>Some(PhysicsInputInstruction::Idle), - Instruction::Resize(_,_)=>Some(PhysicsInputInstruction::Idle), - Instruction::Render=>Some(PhysicsInputInstruction::Idle), - }{ - //non-mouse event - timeline.push_back(TimedInstruction{ - time:ins.time, - instruction:phys_input, + &InputInstruction::MoveForward(s)=>Some(PhysicsInputInstruction::SetMoveForward(s)), + &InputInstruction::MoveLeft(s)=>Some(PhysicsInputInstruction::SetMoveLeft(s)), + &InputInstruction::MoveBack(s)=>Some(PhysicsInputInstruction::SetMoveBack(s)), + &InputInstruction::MoveRight(s)=>Some(PhysicsInputInstruction::SetMoveRight(s)), + &InputInstruction::MoveUp(s)=>Some(PhysicsInputInstruction::SetMoveUp(s)), + &InputInstruction::MoveDown(s)=>Some(PhysicsInputInstruction::SetMoveDown(s)), + &InputInstruction::Jump(s)=>Some(PhysicsInputInstruction::SetJump(s)), + &InputInstruction::Zoom(s)=>Some(PhysicsInputInstruction::SetZoom(s)), + InputInstruction::Reset=>Some(PhysicsInputInstruction::Reset), + InputInstruction::PracticeFly=>Some(PhysicsInputInstruction::PracticeFly), + }, + Instruction::GenerateModels(_)=>Some(PhysicsInputInstruction::Idle), + Instruction::ClearModels=>Some(PhysicsInputInstruction::Idle), + Instruction::Resize(_,_)=>Some(PhysicsInputInstruction::Idle), + Instruction::Render=>Some(PhysicsInputInstruction::Idle), + } + } + fn update_mouse_blocking(&mut self,physics:&crate::physics::PhysicsContext,ins:&TimedInstruction)->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){ - graphics_worker.send(crate::graphics_worker::Instruction::Render(physics.output(),ins.time,physics.get_next_mouse().pos)).unwrap(); - }, - Instruction::Resize(size,user_settings)=>{ - graphics_worker.send(crate::graphics_worker::Instruction::Resize(size,user_settings)).unwrap(); - }, - Instruction::GenerateModels(map)=>{ - physics.generate_models(&map); - physics.spawn(); - graphics_worker.send(crate::graphics_worker::Instruction::GenerateModels(map)).unwrap(); - }, - Instruction::ClearModels=>{ - physics.clear(); - graphics_worker.send(crate::graphics_worker::Instruction::ClearModels).unwrap(); - }, - _=>(), - } - }) - } \ No newline at end of file + }else{ + //keep this up to date so that it can be used as a known-timestamp + //that the mouse was not moving when the mouse starts moving again + self.last_mouse_time=ins.time; + true + } + } + /// returns whether or not to empty the instruction queue + fn handle_physics_input(&mut self,physics:&crate::physics::PhysicsContext,ins:&TimedInstruction,phys_input_option:Option)->bool{ + if let Some(phys_input)=phys_input_option{ + //non-mouse event + self.timeline.push_back(TimedInstruction{ + time:ins.time, + instruction:phys_input, + }); + + //this returns the bool for us + self.update_mouse_blocking(physics,ins) + }else{ + //mouse event + true + } + } + fn empty_queue(&mut self,physics:&mut crate::physics::PhysicsContext){ + while let Some(instruction)=self.timeline.pop_front(){ + physics.run_input_instruction(instruction); + } + } + fn handle_instruction(&mut self,physics:&mut crate::physics::PhysicsContext,ins:&TimedInstruction){ + let physics_input_option=self.map_instruction(physics,ins); + let should_empty_queue=self.handle_physics_input(physics,ins,physics_input_option); + if should_empty_queue{ + self.empty_queue(physics); + } + } +} + +pub fn new(mut physics:crate::physics::PhysicsContext,mut graphics_worker:crate::compat_worker::INWorker)->crate::compat_worker::QNWorker>{ + let mut interpolator=MouseInterpolator{ + mouse_blocking:true, + last_mouse_time:physics.get_next_mouse().time, + timeline:std::collections::VecDeque::new(), + }; + crate::compat_worker::QNWorker::new(move |ins:TimedInstruction|{ + interpolator.handle_instruction(&mut physics,&ins); + match ins.instruction{ + Instruction::Render=>{ + graphics_worker.send(crate::graphics_worker::Instruction::Render(physics.output(),ins.time,physics.get_next_mouse().pos)).unwrap(); + }, + Instruction::Resize(size,user_settings)=>{ + graphics_worker.send(crate::graphics_worker::Instruction::Resize(size,user_settings)).unwrap(); + }, + Instruction::GenerateModels(map)=>{ + physics.generate_models(&map); + physics.spawn(); + graphics_worker.send(crate::graphics_worker::Instruction::GenerateModels(map)).unwrap(); + }, + Instruction::ClearModels=>{ + physics.clear(); + graphics_worker.send(crate::graphics_worker::Instruction::ClearModels).unwrap(); + }, + _=>(), + } + }) +}