diff --git a/src/graphics.rs b/src/graphics.rs index 448914f..5d4455b 100644 --- a/src/graphics.rs +++ b/src/graphics.rs @@ -85,6 +85,14 @@ impl GraphicsCamera{ raw } } +impl std::default::Default for GraphicsCamera{ + fn default()->Self{ + Self{ + screen_size:glam::UVec2::ONE, + fov:glam::Vec2::ONE, + } + } +} pub struct GraphicsState{ pipelines: GraphicsPipelines, @@ -128,7 +136,7 @@ impl GraphicsState{ pub fn load_user_settings(&mut self,user_settings:&crate::settings::UserSettings){ self.camera.fov=user_settings.calculate_fov(1.0,&self.camera.screen_size).as_vec2(); } - fn generate_model_graphics(&mut self,device:&wgpu::Device,queue:&wgpu::Queue,indexed_models:crate::model::IndexedModelInstances){ + pub fn generate_models(&mut self,device:&wgpu::Device,queue:&wgpu::Queue,indexed_models:crate::model::IndexedModelInstances){ //generate texture view per texture //idk how to do this gooder lol @@ -448,7 +456,7 @@ impl GraphicsState{ //.into_iter() the modeldata vec so entities can be /moved/ to models.entities let mut model_count=0; let mut instance_count=0; - let uniform_buffer_binding_size=crate::graphics_context::required_limits().max_uniform_buffer_binding_size as usize; + let uniform_buffer_binding_size=crate::setup::required_limits().max_uniform_buffer_binding_size as usize; let chunk_size=uniform_buffer_binding_size/MODEL_BUFFER_SIZE_BYTES; self.models.reserve(models.len()); for model in models.into_iter() { @@ -521,7 +529,7 @@ impl GraphicsState{ println!("Graphics Instances: {}",instance_count); } - pub fn init( + pub fn new( device:&wgpu::Device, queue:&wgpu::Queue, config:&wgpu::SurfaceConfiguration, @@ -807,14 +815,8 @@ impl GraphicsState{ multiview: None, }); - let mut physics = crate::physics::PhysicsState::default(); - - physics.load_user_settings(&user_settings); - - let screen_size=glam::uvec2(config.width,config.height); - - let camera=GraphicsCamera::new(screen_size,user_settings.calculate_fov(1.0,&screen_size).as_vec2()); - let camera_uniforms = camera.to_uniform_data(physics.output().adjust_mouse(&crate::physics::MouseState::default())); + let camera=GraphicsCamera::default(); + let camera_uniforms = camera.to_uniform_data(crate::physics::PhysicsOutputState::default().extrapolate(glam::IVec2::ZERO,crate::integer::Time::ZERO)); let camera_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Camera"), contents: bytemuck::cast_slice(&camera_uniforms), @@ -883,8 +885,9 @@ impl GraphicsState{ view:&wgpu::TextureView, device:&wgpu::Device, queue:&wgpu::Queue, - predicted_time:crate::integer::Time, physics_output:crate::physics::PhysicsOutputState, + predicted_time:crate::integer::Time, + mouse_pos:glam::IVec2, ) { //TODO: use scheduled frame times to create beautiful smoothing simulation physics extrapolation assuming no input @@ -892,7 +895,7 @@ impl GraphicsState{ device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); // update rotation - let camera_uniforms = self.camera.to_uniform_data(physics_output.extrapolate(self.global_mouse.lock().clone(),predicted_time)); + let camera_uniforms = self.camera.to_uniform_data(physics_output.extrapolate(mouse_pos,predicted_time)); self.staging_belt .write_buffer( &mut encoder, diff --git a/src/graphics_worker.rs b/src/graphics_worker.rs index 6a2dd3c..81e58e1 100644 --- a/src/graphics_worker.rs +++ b/src/graphics_worker.rs @@ -1,7 +1,8 @@ #[derive(Clone)] -pub enum GraphicsInstruction{ - Render(crate::physics::PhysicsOutputState,crate::integer::Time), +pub enum Instruction{ + Render(crate::physics::PhysicsOutputState,crate::integer::Time,glam::IVec2), //UpdateModel(crate::graphics::ModelUpdate), + Resize(winit::dpi::PhysicalSize), } //Ideally the graphics thread worker description is: @@ -14,19 +15,25 @@ WorkerDescription{ //up to three frames in flight, dropping new frame requests when all three are busy, and dropping output frames when one renders out of order pub fn new( - graphics:crate::graphics::GraphicsState, - surface:&wgpu::Surface, + mut graphics:crate::graphics::GraphicsState, + mut config:wgpu::SurfaceConfiguration, + surface:wgpu::Surface, device:&wgpu::Device, queue:&wgpu::Queue, - )->crate::worker::INWorker{ - crate::worker::INWorker::new(a,move |ins:GraphicsInstruction|{ + )->crate::compat_worker::INWorker{ + crate::compat_worker::INWorker::new(move |ins:Instruction|{ match ins{ - GraphicsInstruction::Render(physics_output,predicted_time)=>{ + Instruction::Resize(size)=>{ + config.width=size.width.max(1); + config.height=size.height.max(1); + surface.configure(device,&config); + } + Instruction::Render(physics_output,predicted_time,mouse_pos)=>{ //this has to go deeper somehow let frame=match surface.get_current_texture(){ Ok(frame)=>frame, Err(_)=>{ - surface.configure(device,config); + surface.configure(device,&config); surface .get_current_texture() .expect("Failed to acquire next surface texture!") @@ -37,7 +44,7 @@ pub fn new( ..wgpu::TextureViewDescriptor::default() }); - graphics.render(&view,device,queue,physics_output,predicted_time); + graphics.render(&view,device,queue,physics_output,predicted_time,mouse_pos); frame.present(); } diff --git a/src/integer.rs b/src/integer.rs index 56c25e2..2c56016 100644 --- a/src/integer.rs +++ b/src/integer.rs @@ -41,6 +41,11 @@ impl std::fmt::Display for Time{ write!(f,"{}s+{:09}ns",self.0/Self::ONE_SECOND.0,self.0%Self::ONE_SECOND.0) } } +impl std::default::Default for Time{ + fn default()->Self{ + Self(0) + } +} impl std::ops::Neg for Time{ type Output=Time; #[inline] diff --git a/src/main.rs b/src/main.rs index ad920d4..cbf1f91 100644 --- a/src/main.rs +++ b/src/main.rs @@ -119,7 +119,6 @@ pub fn default_models()->model::IndexedModelInstances{ } fn main(){ - let title=format!("Strafe Client v{}",env!("CARGO_PKG_VERSION")).as_str(); - let context=setup::setup(title); + let context=setup::setup(format!("Strafe Client v{}",env!("CARGO_PKG_VERSION")).as_str()); context.start();//creates and runs a run context } diff --git a/src/physics.rs b/src/physics.rs index ec32cad..9aa219d 100644 --- a/src/physics.rs +++ b/src/physics.rs @@ -31,9 +31,12 @@ pub enum PhysicsInputInstruction { SetZoom(bool), Reset, Idle, + //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 + //to be 1 instruction ahead to generate the next state for interpolation. } -#[derive(Clone,Hash)] +#[derive(Clone,Hash,Default)] pub struct Body { position: Planar64Vec3,//I64 where 2^32 = 1 u velocity: Planar64Vec3,//I64 where 2^32 = 1 u/s @@ -241,6 +244,19 @@ impl PhysicsCamera { } } +impl std::default::Default for PhysicsCamera{ + fn default()->Self{ + Self{ + offset:Planar64Vec3::ZERO,//TODO: delete this from PhysicsCamera, it should be GraphicsCamera only + sensitivity:Ratio64Vec2::ONE*200_000, + mouse:MouseState::default(),//t=0 does not cause divide by zero because it's immediately replaced + clamped_mouse_pos:glam::IVec2::ZERO, + angle_pitch_lower_limit:-Angle32::FRAC_PI_2, + angle_pitch_upper_limit:Angle32::FRAC_PI_2, + } + } +} + pub struct GameMechanicsState{ pub stage_id:u32, //jump_counts:HashMap, @@ -527,7 +543,7 @@ pub struct PhysicsState{ //This is not the same as Reset which teleports you to Spawn0 spawn_point:Planar64Vec3, } -#[derive(Clone)] +#[derive(Clone,Default)] pub struct PhysicsOutputState{ camera:PhysicsCamera, body:Body, diff --git a/src/physics_worker.rs b/src/physics_worker.rs index 92e7d49..6d06148 100644 --- a/src/physics_worker.rs +++ b/src/physics_worker.rs @@ -1,7 +1,6 @@ use crate::integer::Time; use crate::physics::{MouseState,PhysicsInputInstruction}; use crate::instruction::{TimedInstruction,InstructionConsumer}; - #[derive(Debug)] pub enum InputInstruction { MoveMouse(glam::IVec2), @@ -14,52 +13,56 @@ pub enum InputInstruction { Jump(bool), Zoom(bool), Reset, +} +pub enum Instruction{ + Input(InputInstruction), Render, - //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 - //to be 1 instruction ahead to generate the next state for interpolation. + Resize(winit::dpi::PhysicalSize), + //Graphics(crate::graphics_worker::Instruction), } - pub fn new(physics:crate::physics::PhysicsState,graphics_worker:crate::compat_worker::INWorker)->crate::compat_worker::QNWorker>{ + pub fn new(mut physics:crate::physics::PhysicsState,mut graphics_worker:crate::compat_worker::INWorker)->crate::compat_worker::QNWorker>{ let mut mouse_blocking=true; let mut last_mouse_time=physics.next_mouse.time; let mut timeline=std::collections::VecDeque::new(); - crate::compat_worker::QNWorker::new(move |ins:TimedInstruction|{ - let mut render=false; - if if let Some(phys_input)=match ins.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.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 + 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.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::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::Render=>{render=true;Some(PhysicsInputInstruction::Idle)}, + Instruction::Resize(_)=>Some(PhysicsInputInstruction::Idle), + Instruction::Render=>Some(PhysicsInputInstruction::Idle), }{ //non-mouse event timeline.push_back(TimedInstruction{ @@ -104,8 +107,14 @@ pub enum InputInstruction { }); } } - if render{ - graphics_worker.send(crate::graphics_worker::GraphicsInstruction::Render(physics.output(),ins.time)); + match ins.instruction{ + Instruction::Render=>{ + graphics_worker.send(crate::graphics_worker::Instruction::Render(physics.output(),ins.time,physics.next_mouse.pos)).unwrap(); + }, + Instruction::Resize(size)=>{ + graphics_worker.send(crate::graphics_worker::Instruction::Resize(size)).unwrap(); + }, + _=>(), } }) } \ No newline at end of file diff --git a/src/run.rs b/src/run.rs index 11d0166..01678c5 100644 --- a/src/run.rs +++ b/src/run.rs @@ -1,5 +1,5 @@ use crate::physics::PhysicsInstruction; -use crate::physics_context::InputInstruction; +use crate::physics_worker::InputInstruction; use crate::instruction::TimedInstruction; pub enum RunInstruction{ @@ -14,13 +14,18 @@ pub enum RunInstruction{ struct RunContext{ manual_mouse_lock:bool, mouse:crate::physics::MouseState,//std::sync::Arc> + device:wgpu::Device, + queue:wgpu::Queue, screen_size:glam::UVec2, user_settings:crate::settings::UserSettings, window:winit::window::Window, - //physics_thread:crate::worker::QNWorker>, + physics_thread:crate::compat_worker::QNWorker>, } impl RunContext{ + fn get_middle_of_screen(&self)->winit::dpi::PhysicalPosition{ + winit::dpi::PhysicalPosition::new(self.screen_size.x as f32/2.0, self.screen_size.y as f32/2.0) + } fn window_event(&mut self,time:crate::integer::Time,event: winit::event::WindowEvent) { match event { winit::event::WindowEvent::DroppedFile(path)=>{ @@ -49,7 +54,7 @@ impl RunContext{ winit::keyboard::Key::Named(winit::keyboard::NamedKey::Tab)=>{ if s{ self.manual_mouse_lock=false; - match self.window.set_cursor_position(winit::dpi::PhysicalPosition::new(self.screen_size.x as f32/2.0, self.screen_size.y as f32/2.0)){ + match self.window.set_cursor_position(self.get_middle_of_screen()){ Ok(())=>(), Err(e)=>println!("Could not set cursor position: {:?}",e), } @@ -112,7 +117,7 @@ impl RunContext{ }{ self.physics_thread.send(TimedInstruction{ time, - instruction:input_instruction, + instruction:crate::physics_worker::Instruction::Input(input_instruction), }).unwrap(); } }, @@ -123,13 +128,13 @@ impl RunContext{ } } - fn device_event(&self,time:crate::integer::Time,event: winit::event::DeviceEvent) { + fn device_event(&mut self,time:crate::integer::Time,event: winit::event::DeviceEvent) { match event { winit::event::DeviceEvent::MouseMotion { delta,//these (f64,f64) are integers on my machine } => { if self.manual_mouse_lock{ - match self.window.set_cursor_position(winit::dpi::PhysicalPosition::new(self.screen_size.x as f32/2.0, self.screen_size.y as f32/2.0)){ + match self.window.set_cursor_position(self.get_middle_of_screen()){ Ok(())=>(), Err(e)=>println!("Could not set cursor position: {:?}",e), } @@ -141,7 +146,7 @@ impl RunContext{ self.mouse.pos+=delta; self.physics_thread.send(TimedInstruction{ time, - instruction:InputInstruction::MoveMouse(self.mouse.pos), + instruction:crate::physics_worker::Instruction::Input(InputInstruction::MoveMouse(self.mouse.pos)), }).unwrap(); }, winit::event::DeviceEvent::MouseWheel { @@ -151,7 +156,7 @@ impl RunContext{ if false{//self.physics.style.use_scroll{ self.physics_thread.send(TimedInstruction{ time, - instruction:InputInstruction::Jump(true),//activates the immediate jump path, but the style modifier prevents controls&CONTROL_JUMP bit from being set to auto jump + instruction:crate::physics_worker::Instruction::Input(InputInstruction::Jump(true)),//activates the immediate jump path, but the style modifier prevents controls&CONTROL_JUMP bit from being set to auto jump }).unwrap(); } } @@ -179,14 +184,14 @@ impl RunContextSetup{ None }.unwrap_or(crate::default_models()); - let mut graphics=crate::graphics::GraphicsState::new(&context.device,&context.queue); - graphics.load_user_settings(&user_settings); - graphics.generate_models(&context.device,&context.queue,indexed_model_instances); - let mut physics=crate::physics::PhysicsState::default(); physics.load_user_settings(&user_settings); physics.generate_models(&indexed_model_instances); + let mut graphics=crate::graphics::GraphicsState::new(&context.device,&context.queue,&context.config); + graphics.load_user_settings(&user_settings); + graphics.generate_models(&context.device,&context.queue,indexed_model_instances); + Self{ user_settings, window, @@ -195,20 +200,25 @@ impl RunContextSetup{ } } - fn into_context(self)->RunContext{ + fn into_context(self,setup_context:crate::setup::SetupContext)->RunContext{ + let screen_size=glam::uvec2(setup_context.config.width,setup_context.config.height); + let graphics_thread=crate::graphics_worker::new(self.graphics,setup_context.config,setup_context.surface,&setup_context.device,&setup_context.queue); RunContext{ manual_mouse_lock:false, mouse:crate::physics::MouseState::default(), + //make sure to update this!!!!! + screen_size, + device:setup_context.device, + queue:setup_context.queue, user_settings:self.user_settings, window:self.window, - physics:self.physics, - graphics:self.graphics, + physics_thread:crate::physics_worker::new(self.physics,graphics_thread), } } - pub fn into_worker(self,setup_context:crate::setup::SetupContext)->crate::worker::QNWorker>{ - let run_context=self.into_context(); - crate::worker::QNWorker::new(move |ins:TimedInstruction|{ + pub fn into_worker(self,setup_context:crate::setup::SetupContext)->crate::compat_worker::QNWorker>{ + let mut run_context=self.into_context(setup_context); + crate::compat_worker::QNWorker::new(move |ins:TimedInstruction|{ match ins.instruction{ RunInstruction::RequestRedraw=>{ run_context.window.request_redraw(); @@ -220,13 +230,20 @@ impl RunContextSetup{ run_context.device_event(ins.time,device_event); }, RunInstruction::Resize(size)=>{ - setup_context.config.width=size.width.max(1); - setup_context.config.height=size.height.max(1); - setup_context.surface.configure(&setup_context.device,&setup_context.config); - run_context.physics_thread.send(TimedInstruction{time:ins.time,instruction:PhysicsInstruction::Resize(size)}); + run_context.physics_thread.send( + TimedInstruction{ + time:ins.time, + instruction:crate::physics_worker::Instruction::Resize(size) + } + ).unwrap(); } RunInstruction::Render=>{ - // + run_context.physics_thread.send( + TimedInstruction{ + time:ins.time, + instruction:crate::physics_worker::Instruction::Render + } + ).unwrap(); } } }) diff --git a/src/setup.rs b/src/setup.rs index 3de933c..f1b0555 100644 --- a/src/setup.rs +++ b/src/setup.rs @@ -47,8 +47,8 @@ impl SetupContextPartial1{ fn create_surface(self,window:&winit::window::Window)->Result{ Ok(SetupContextPartial2{ backends:self.backends, + surface:unsafe{self.instance.create_surface(window)}?, instance:self.instance, - surface:unsafe{self.instance.create_surface(window)}? }) } } @@ -231,7 +231,7 @@ impl SetupContextSetup{ let run=crate::run::RunContextSetup::new(&setup_context,window); //the thread that spawns the physics thread - let run_thread=run.into_worker(setup_context); + let mut run_thread=run.into_worker(setup_context); println!("Entering render loop..."); let root_time=std::time::Instant::now();