This commit is contained in:
Quaternions 2023-10-24 20:22:30 -07:00
parent 657e5dff7f
commit b5a0cf7045
8 changed files with 149 additions and 93 deletions

View File

@ -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,

View File

@ -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<u32>),
}
//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<GraphicsInstruction>{
crate::worker::INWorker::new(a,move |ins:GraphicsInstruction|{
)->crate::compat_worker::INWorker<Instruction>{
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();
}

View File

@ -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]

View File

@ -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
}

View File

@ -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<u32,u32>,
@ -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,

View File

@ -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<u32>),
//Graphics(crate::graphics_worker::Instruction),
}
pub fn new(physics:crate::physics::PhysicsState,graphics_worker:crate::compat_worker::INWorker<crate::graphics_worker::GraphicsInstruction>)->crate::compat_worker::QNWorker<TimedInstruction<InputInstruction>>{
pub fn new(mut physics:crate::physics::PhysicsState,mut graphics_worker:crate::compat_worker::INWorker<crate::graphics_worker::Instruction>)->crate::compat_worker::QNWorker<TimedInstruction<Instruction>>{
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<InputInstruction>|{
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<Instruction>|{
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();
},
_=>(),
}
})
}

View File

@ -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<std::sync::Mutex<>>
device:wgpu::Device,
queue:wgpu::Queue,
screen_size:glam::UVec2,
user_settings:crate::settings::UserSettings,
window:winit::window::Window,
//physics_thread:crate::worker::QNWorker<TimedInstruction<InputInstruction>>,
physics_thread:crate::compat_worker::QNWorker<TimedInstruction<crate::physics_worker::Instruction>>,
}
impl RunContext{
fn get_middle_of_screen(&self)->winit::dpi::PhysicalPosition<f32>{
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<TimedInstruction<RunInstruction>>{
let run_context=self.into_context();
crate::worker::QNWorker::new(move |ins:TimedInstruction<RunInstruction>|{
pub fn into_worker(self,setup_context:crate::setup::SetupContext)->crate::compat_worker::QNWorker<TimedInstruction<RunInstruction>>{
let mut run_context=self.into_context(setup_context);
crate::compat_worker::QNWorker::new(move |ins:TimedInstruction<RunInstruction>|{
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();
}
}
})

View File

@ -47,8 +47,8 @@ impl SetupContextPartial1{
fn create_surface(self,window:&winit::window::Window)->Result<SetupContextPartial2,wgpu::CreateSurfaceError>{
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();