refactor physics to use shared context for multiple simulations

This commit is contained in:
Quaternions 2025-01-16 18:51:22 -08:00
parent 28499800cb
commit 2faa61225f
3 changed files with 65 additions and 56 deletions

View File

@ -829,17 +829,7 @@ pub struct PhysicsState{
//a start zone. If you change mode, a new run is created. //a start zone. If you change mode, a new run is created.
run:run::Run, run:run::Run,
} }
//random collection of contextual data that doesn't belong in PhysicsState
pub struct PhysicsData{
//permanent map data
bvh:bvh::BvhNode<ConvexMeshId>,
//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{ impl Default for PhysicsState{
fn default()->Self{ fn default()->Self{
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{ 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(); self.touching.clear();
} }
fn reset_to_default(&mut self){ fn reset_to_default(&mut self){
@ -917,46 +906,67 @@ impl PhysicsState{
// }); // });
// } // }
} }
// shared geometry for simulations
#[derive(Default)] pub struct PhysicsData{
pub struct PhysicsContext{ //permanent map data
state:PhysicsState,//this captures the entire state of the physics. bvh:bvh::BvhNode<ConvexMeshId>,
data:PhysicsData,//data currently loaded into memory which is needded for physics to run, but is not part of the state. //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, // the physics consumes both Instruction and PhysicsInternalInstruction,
// but can only emit PhysicsInternalInstruction // but can only emit PhysicsInternalInstruction
impl InstructionConsumer<InternalInstruction> for PhysicsContext{ impl InstructionConsumer<InternalInstruction> for PhysicsContext<'_>{
type TimeInner=TimeInner; type TimeInner=TimeInner;
fn process_instruction(&mut self,ins:TimedInstruction<InternalInstruction,TimeInner>){ fn process_instruction(&mut self,ins:TimedInstruction<InternalInstruction,TimeInner>){
atomic_internal_instruction(&mut self.state,&self.data,ins) atomic_internal_instruction(&mut self.state,&self.data,ins)
} }
} }
impl InstructionConsumer<Instruction> for PhysicsContext{ impl InstructionConsumer<Instruction> for PhysicsContext<'_>{
type TimeInner=TimeInner; type TimeInner=TimeInner;
fn process_instruction(&mut self,ins:TimedInstruction<Instruction,TimeInner>){ fn process_instruction(&mut self,ins:TimedInstruction<Instruction,TimeInner>){
atomic_input_instruction(&mut self.state,&self.data,ins) atomic_input_instruction(&mut self.state,&self.data,ins)
} }
} }
impl InstructionEmitter<InternalInstruction> for PhysicsContext{ impl InstructionEmitter<InternalInstruction> for PhysicsContext<'_>{
type TimeInner=TimeInner; type TimeInner=TimeInner;
//this little next instruction function could cache its return value and invalidate the cached value by watching the State. //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<TimedInstruction<InternalInstruction,TimeInner>>{ fn next_instruction(&self,time_limit:Time)->Option<TimedInstruction<InternalInstruction,TimeInner>>{
next_instruction_internal(&self.state,&self.data,time_limit) next_instruction_internal(&self.state,&self.data,time_limit)
} }
} }
impl PhysicsContext{ impl PhysicsContext<'_>{
pub fn camera_body(&self)->Body{ pub fn run_input_instruction(
Body{ state:&mut PhysicsState,
position:self.state.body.position+self.state.style.camera_offset, data:&PhysicsData,
..self.state.body instruction:TimedInstruction<Instruction,TimeInner>
){
let mut context=PhysicsContext{state,data};
context.process_exhaustive(instruction.time);
context.process_instruction(instruction);
} }
} }
pub const fn camera(&self)->PhysicsCamera{ impl PhysicsData{
self.state.camera
}
/// use with caution, this is the only non-instruction way to mess with physics /// use with caution, this is the only non-instruction way to mess with physics
pub fn generate_models(&mut self,map:&map::CompleteMap){ pub fn generate_models(&mut self,map:&map::CompleteMap){
self.state.clear();
let mut modes=map.modes.clone(); let mut modes=map.modes.clone();
for mode in &mut modes.modes{ for mode in &mut modes.modes{
mode.denormalize_data(); mode.denormalize_data();
@ -1087,17 +1097,12 @@ impl PhysicsContext{
(IntersectAttributesId::new(attr_id as u32),attr) (IntersectAttributesId::new(attr_id as u32),attr)
).collect(), ).collect(),
}; };
self.data.bvh=bvh; self.bvh=bvh;
self.data.models=models; self.models=models;
self.data.modes=modes; self.modes=modes;
//hitbox_mesh is unchanged //hitbox_mesh is unchanged
println!("Physics Objects: {}",model_count); println!("Physics Objects: {}",model_count);
} }
pub fn run_input_instruction(&mut self,instruction:TimedInstruction<Instruction,TimeInner>){
self.process_exhaustive(instruction.time);
self.process_instruction(instruction);
}
} }
//this is the one who asks //this is the one who asks

View File

@ -21,7 +21,7 @@ pub fn new<'a>(
mut graphics_worker:crate::compat_worker::INWorker<'a,crate::graphics_worker::Instruction>, mut graphics_worker:crate::compat_worker::INWorker<'a,crate::graphics_worker::Instruction>,
user_settings:crate::settings::UserSettings, user_settings:crate::settings::UserSettings,
)->crate::compat_worker::QNWorker<'a,TimedInstruction<Instruction,SessionTimeInner>>{ )->crate::compat_worker::QNWorker<'a,TimedInstruction<Instruction,SessionTimeInner>>{
let physics=crate::physics::PhysicsContext::default(); let physics=crate::physics::PhysicsState::default();
let timer=Timer::unpaused(SessionTime::ZERO,PhysicsTime::ZERO); let timer=Timer::unpaused(SessionTime::ZERO,PhysicsTime::ZERO);
let simulation=Simulation::new(timer,physics); let simulation=Simulation::new(timer,physics);
let mut session=Session::new( let mut session=Session::new(

View File

@ -14,6 +14,7 @@ use strafesnet_common::timer::{Scaled,Timer};
use strafesnet_common::session::{TimeInner as SessionTimeInner,Time as SessionTime}; use strafesnet_common::session::{TimeInner as SessionTimeInner,Time as SessionTime};
use crate::mouse_interpolator::{MouseInterpolator,StepInstruction,Instruction as MouseInterpolatorInstruction}; use crate::mouse_interpolator::{MouseInterpolator,StepInstruction,Instruction as MouseInterpolatorInstruction};
use crate::physics::{PhysicsContext,PhysicsData};
use crate::settings::UserSettings; use crate::settings::UserSettings;
pub enum Instruction<'a>{ pub enum Instruction<'a>{
@ -60,12 +61,12 @@ pub struct FrameState{
pub struct Simulation{ pub struct Simulation{
timer:Timer<Scaled<SessionTimeInner,PhysicsTimeInner>>, timer:Timer<Scaled<SessionTimeInner,PhysicsTimeInner>>,
physics:crate::physics::PhysicsContext, physics:crate::physics::PhysicsState,
} }
impl Simulation{ impl Simulation{
pub const fn new( pub const fn new(
timer:Timer<Scaled<SessionTimeInner,PhysicsTimeInner>>, timer:Timer<Scaled<SessionTimeInner,PhysicsTimeInner>>,
physics:crate::physics::PhysicsContext, physics:crate::physics::PhysicsState,
)->Self{ )->Self{
Self{ Self{
timer, timer,
@ -106,12 +107,12 @@ impl Replay{
simulation, 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); let mut time=self.simulation.timer.time(time_limit);
loop{ loop{
if let Some(ins)=self.recording.instructions.get(self.last_instruction_id){ if let Some(ins)=self.recording.instructions.get(self.last_instruction_id){
if ins.time<time{ if ins.time<time{
self.simulation.physics.run_input_instruction(ins.clone()); PhysicsContext::run_input_instruction(&mut self.simulation.physics,physics_data,ins.clone());
self.last_instruction_id+=1; self.last_instruction_id+=1;
}else{ }else{
break; break;
@ -144,6 +145,7 @@ pub struct Session{
mouse_interpolator:crate::mouse_interpolator::MouseInterpolator, mouse_interpolator:crate::mouse_interpolator::MouseInterpolator,
view_state:ViewState, view_state:ViewState,
//gui:GuiState //gui:GuiState
geometry_shared:crate::physics::PhysicsData,
simulation:Simulation, simulation:Simulation,
// below fields not included in lite session // below fields not included in lite session
recording:Recording, recording:Recording,
@ -158,6 +160,7 @@ impl Session{
Self{ Self{
user_settings, user_settings,
mouse_interpolator:MouseInterpolator::new(), mouse_interpolator:MouseInterpolator::new(),
geometry_shared:Default::default(),
simulation, simulation,
view_state:ViewState::Play, view_state:ViewState::Play,
recording:Default::default(), recording:Default::default(),
@ -168,7 +171,8 @@ impl Session{
self.recording.clear(); self.recording.clear();
} }
fn change_map(&mut self,map:&strafesnet_common::map::CompleteMap){ fn change_map(&mut self,map:&strafesnet_common::map::CompleteMap){
self.simulation.physics.generate_models(map); self.simulation.physics.clear();
self.geometry_shared.generate_models(map);
} }
pub fn get_frame_state(&self,time:SessionTime)->Option<FrameState>{ pub fn get_frame_state(&self,time:SessionTime)->Option<FrameState>{
match &self.view_state{ match &self.view_state{
@ -339,7 +343,7 @@ impl InstructionConsumer<Instruction<'_>> for Session{
// this just refreshes the replays // this just refreshes the replays
for replay in self.replays.values_mut(){ for replay in self.replays.values_mut(){
// TODO: filter idles from recording, inject new idles in real time // 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<StepInstruction> for Session{
if let Some(instruction)=self.mouse_interpolator.pop_buffered_instruction(ins.set_time(time)){ if let Some(instruction)=self.mouse_interpolator.pop_buffered_instruction(ins.set_time(time)){
//record //record
self.recording.instructions.push(instruction.clone()); 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);
} }
} }
} }