From 2faa61225f2c7c68ceacc0f12d04497cf7379e83 Mon Sep 17 00:00:00 2001 From: Quaternions Date: Thu, 16 Jan 2025 18:51:22 -0800 Subject: [PATCH] refactor physics to use shared context for multiple simulations --- strafe-client/src/physics.rs | 101 +++++++++++++++------------- strafe-client/src/physics_worker.rs | 2 +- strafe-client/src/session.rs | 18 +++-- 3 files changed, 65 insertions(+), 56 deletions(-) diff --git a/strafe-client/src/physics.rs b/strafe-client/src/physics.rs index 6ac96ef..56218f2 100644 --- a/strafe-client/src/physics.rs +++ b/strafe-client/src/physics.rs @@ -829,17 +829,7 @@ pub struct PhysicsState{ //a start zone. If you change mode, a new run is created. run:run::Run, } -//random collection of contextual data that doesn't belong in PhysicsState -pub struct PhysicsData{ - //permanent map data - bvh:bvh::BvhNode, - //transient map/environment data (open world loads/unloads parts of this data) - models:PhysicsModels, - //semi-transient data - modes:gameplay_modes::Modes, - //cached calculations - hitbox_mesh:HitboxMesh, -} + impl Default for PhysicsState{ fn default()->Self{ Self{ @@ -856,19 +846,18 @@ impl Default for PhysicsState{ } } } -impl Default for PhysicsData{ - fn default()->Self{ - Self{ - bvh:bvh::BvhNode::default(), - models:Default::default(), - modes:Default::default(), - hitbox_mesh:StyleModifiers::default().calculate_mesh(), - } - } -} impl PhysicsState{ - fn clear(&mut self){ + pub fn camera_body(&self)->Body{ + Body{ + position:self.body.position+self.style.camera_offset, + ..self.body + } + } + pub const fn camera(&self)->PhysicsCamera{ + self.camera + } + pub fn clear(&mut self){ self.touching.clear(); } fn reset_to_default(&mut self){ @@ -917,46 +906,67 @@ impl PhysicsState{ // }); // } } - -#[derive(Default)] -pub struct PhysicsContext{ - state:PhysicsState,//this captures the entire state of the physics. - data:PhysicsData,//data currently loaded into memory which is needded for physics to run, but is not part of the state. +// shared geometry for simulations +pub struct PhysicsData{ + //permanent map data + bvh:bvh::BvhNode, + //transient map/environment data (open world loads/unloads parts of this data) + models:PhysicsModels, + //semi-transient data + modes:gameplay_modes::Modes, + //cached calculations + hitbox_mesh:HitboxMesh, +} +impl Default for PhysicsData{ + fn default()->Self{ + Self{ + bvh:bvh::BvhNode::default(), + models:Default::default(), + modes:Default::default(), + hitbox_mesh:StyleModifiers::default().calculate_mesh(), + } + } +} +// the collection of information required to run physics +pub struct PhysicsContext<'a>{ + state:&'a mut PhysicsState,//this captures the entire state of the physics. + data:&'a PhysicsData,//data currently loaded into memory which is needded for physics to run, but is not part of the state. } // the physics consumes both Instruction and PhysicsInternalInstruction, // but can only emit PhysicsInternalInstruction -impl InstructionConsumer for PhysicsContext{ +impl InstructionConsumer for PhysicsContext<'_>{ type TimeInner=TimeInner; fn process_instruction(&mut self,ins:TimedInstruction){ atomic_internal_instruction(&mut self.state,&self.data,ins) } } -impl InstructionConsumer for PhysicsContext{ +impl InstructionConsumer for PhysicsContext<'_>{ type TimeInner=TimeInner; fn process_instruction(&mut self,ins:TimedInstruction){ atomic_input_instruction(&mut self.state,&self.data,ins) } } -impl InstructionEmitter for PhysicsContext{ +impl InstructionEmitter for PhysicsContext<'_>{ 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>{ next_instruction_internal(&self.state,&self.data,time_limit) } } -impl PhysicsContext{ - pub fn camera_body(&self)->Body{ - Body{ - position:self.state.body.position+self.state.style.camera_offset, - ..self.state.body - } - } - pub const fn camera(&self)->PhysicsCamera{ - self.state.camera +impl PhysicsContext<'_>{ + pub fn run_input_instruction( + state:&mut PhysicsState, + data:&PhysicsData, + instruction:TimedInstruction + ){ + let mut context=PhysicsContext{state,data}; + context.process_exhaustive(instruction.time); + context.process_instruction(instruction); } +} +impl PhysicsData{ /// use with caution, this is the only non-instruction way to mess with physics pub fn generate_models(&mut self,map:&map::CompleteMap){ - self.state.clear(); let mut modes=map.modes.clone(); for mode in &mut modes.modes{ mode.denormalize_data(); @@ -1087,17 +1097,12 @@ impl PhysicsContext{ (IntersectAttributesId::new(attr_id as u32),attr) ).collect(), }; - self.data.bvh=bvh; - self.data.models=models; - self.data.modes=modes; + self.bvh=bvh; + self.models=models; + self.modes=modes; //hitbox_mesh is unchanged println!("Physics Objects: {}",model_count); } - - pub fn run_input_instruction(&mut self,instruction:TimedInstruction){ - self.process_exhaustive(instruction.time); - self.process_instruction(instruction); - } } //this is the one who asks diff --git a/strafe-client/src/physics_worker.rs b/strafe-client/src/physics_worker.rs index 9bc3921..6814d11 100644 --- a/strafe-client/src/physics_worker.rs +++ b/strafe-client/src/physics_worker.rs @@ -21,7 +21,7 @@ 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>{ - let physics=crate::physics::PhysicsContext::default(); + let physics=crate::physics::PhysicsState::default(); let timer=Timer::unpaused(SessionTime::ZERO,PhysicsTime::ZERO); let simulation=Simulation::new(timer,physics); let mut session=Session::new( diff --git a/strafe-client/src/session.rs b/strafe-client/src/session.rs index 5d28f06..4f62a37 100644 --- a/strafe-client/src/session.rs +++ b/strafe-client/src/session.rs @@ -14,6 +14,7 @@ use strafesnet_common::timer::{Scaled,Timer}; use strafesnet_common::session::{TimeInner as SessionTimeInner,Time as SessionTime}; use crate::mouse_interpolator::{MouseInterpolator,StepInstruction,Instruction as MouseInterpolatorInstruction}; +use crate::physics::{PhysicsContext,PhysicsData}; use crate::settings::UserSettings; pub enum Instruction<'a>{ @@ -60,12 +61,12 @@ pub struct FrameState{ pub struct Simulation{ timer:Timer>, - physics:crate::physics::PhysicsContext, + physics:crate::physics::PhysicsState, } impl Simulation{ pub const fn new( timer:Timer>, - physics:crate::physics::PhysicsContext, + physics:crate::physics::PhysicsState, )->Self{ Self{ timer, @@ -106,12 +107,12 @@ impl Replay{ simulation, } } - pub fn advance(&mut self,time_limit:SessionTime){ + pub fn advance(&mut self,physics_data:&PhysicsData,time_limit:SessionTime){ let mut time=self.simulation.timer.time(time_limit); loop{ if let Some(ins)=self.recording.instructions.get(self.last_instruction_id){ if ins.timeOption{ match &self.view_state{ @@ -339,7 +343,7 @@ impl InstructionConsumer> for Session{ // this just refreshes the replays for replay in self.replays.values_mut(){ // TODO: filter idles from recording, inject new idles in real time - replay.advance(ins.time); + replay.advance(&self.geometry_shared,ins.time); } } }; @@ -355,7 +359,7 @@ impl InstructionConsumer for Session{ if let Some(instruction)=self.mouse_interpolator.pop_buffered_instruction(ins.set_time(time)){ //record self.recording.instructions.push(instruction.clone()); - self.simulation.physics.run_input_instruction(instruction); + PhysicsContext::run_input_instruction(&mut self.simulation.physics,&self.geometry_shared,instruction); } } }