Compare commits

..

27 Commits

Author SHA1 Message Date
74cc6bc4a5 not best 2025-01-16 20:06:06 -08:00
78cfca8d09 cow mesh can work 2025-01-16 20:05:24 -08:00
83a067320b fix usage of Cow in model_physics 2025-01-16 19:31:49 -08:00
2faa61225f refactor physics to use shared context for multiple simulations 2025-01-16 18:51:22 -08:00
28499800cb comment about bad algorithm 2025-01-16 07:40:08 -08:00
57cc49dc1a explicitly reset and spawn instead of implicitly 2025-01-16 06:35:36 -08:00
d517b78a8c replay controls 2025-01-16 05:10:39 -08:00
e6d1d69241 delete replay on StopSpectate 2025-01-16 05:10:39 -08:00
b28fa25279 comment about determinism 2025-01-16 05:10:39 -08:00
713b235816 maintain replay state according to real time 2025-01-16 05:10:39 -08:00
d2002383cb idle is special 2025-01-16 00:17:39 -08:00
52f7de809d rename variable 2025-01-16 00:17:39 -08:00
4efe1209b8 implement copy instruction into replay 2025-01-15 23:44:42 -08:00
15a9136fc4 session instruction changes for control and playback 2025-01-15 22:59:59 -08:00
035736e7af record 2025-01-15 21:41:44 -08:00
7f9a16a56d refactor physics enums so Mouse-NonMouse distinction is private to mouse_interpolator 2025-01-15 21:09:08 -08:00
814e573d91 rename physics instructions 2025-01-15 20:19:20 -08:00
6fa0f1c83e model_physics: use entry or_insert_with pattern 2025-01-15 08:46:26 -08:00
870cb56dac do not use ResourceType 2025-01-15 08:46:15 -08:00
1aac2a9303 model_physics: remove pointless unsafe usage from the before times 2025-01-15 08:46:06 -08:00
38661b3a68 fixed_wide: another derivable trait 2025-01-15 04:15:41 -08:00
7ad76740d4 common: tidy instruction module 2025-01-15 03:29:48 -08:00
c2d6af8bda TimedInstruction::set_time 2025-01-15 03:29:48 -08:00
fbacef83b9 fix non-determinism bug 2025-01-15 03:16:00 -08:00
2a9e848541 explain non-determinism bug 2025-01-15 03:03:34 -08:00
3413ec8740 mouse_interpolator tweaks 2025-01-15 02:59:34 -08:00
168d6708d1 fix some physics lints 2025-01-15 01:34:10 -08:00
11 changed files with 589 additions and 346 deletions

@ -1,10 +1,19 @@
use crate::integer::Time;
#[derive(Debug)]
#[derive(Clone,Debug)]
pub struct TimedInstruction<I,T>{
pub time:Time<T>,
pub instruction:I,
}
impl<I,T> TimedInstruction<I,T>{
#[inline]
pub fn set_time<TimeInner>(self,new_time:Time<TimeInner>)->TimedInstruction<I,TimeInner>{
TimedInstruction{
time:new_time,
instruction:self.instruction,
}
}
}
/// Ensure all emitted instructions are processed before consuming external instructions
pub trait InstructionEmitter<I>{
@ -21,6 +30,7 @@ pub trait InstructionFeedback<I,T>:InstructionEmitter<I,TimeInner=T>+Instruction
where
Time<T>:Copy,
{
#[inline]
fn process_exhaustive(&mut self,time_limit:Time<T>){
while let Some(instruction)=self.next_instruction(time_limit){
self.process_instruction(instruction);
@ -33,42 +43,6 @@ impl<I,T,X> InstructionFeedback<I,T> for X
X:InstructionEmitter<I,TimeInner=T>+InstructionConsumer<I,TimeInner=T>,
{}
pub struct InstructionCache<S,I,T>{
instruction_machine:S,
cached_instruction:Option<TimedInstruction<I,T>>,
time_limit:Time<T>,
}
impl<S,I,T> InstructionCache<S,I,T>
where
Time<T>:Copy+Ord,
Option<TimedInstruction<I,T>>:Clone,
S:InstructionEmitter<I,TimeInner=T>+InstructionConsumer<I,TimeInner=T>
{
pub fn new(
instruction_machine:S,
)->Self{
Self{
instruction_machine,
cached_instruction:None,
time_limit:Time::MIN,
}
}
pub fn next_instruction_cached(&mut self,time_limit:Time<T>)->Option<TimedInstruction<I,T>>{
if time_limit<self.time_limit{
return self.cached_instruction.clone();
}
let next_instruction=self.instruction_machine.next_instruction(time_limit);
self.cached_instruction=next_instruction.clone();
self.time_limit=time_limit;
next_instruction
}
pub fn process_instruction(&mut self,instruction:TimedInstruction<I,T>){
// invalidate cache
self.time_limit=Time::MIN;
self.instruction_machine.process_instruction(instruction);
}
}
//PROPER PRIVATE FIELDS!!!
pub struct InstructionCollector<I,T>{
time:Time<T>,
@ -77,6 +51,7 @@ pub struct InstructionCollector<I,T>{
impl<I,T> InstructionCollector<I,T>
where Time<T>:Copy+PartialOrd,
{
#[inline]
pub const fn new(time:Time<T>)->Self{
Self{
time,
@ -88,24 +63,18 @@ impl<I,T> InstructionCollector<I,T>
self.time
}
pub fn collect(&mut self,instruction:Option<TimedInstruction<I,T>>){
match instruction{
Some(unwrap_instruction)=>{
if unwrap_instruction.time<self.time {
self.time=unwrap_instruction.time;
self.instruction=Some(unwrap_instruction.instruction);
}
},
None=>(),
if let Some(ins)=instruction{
if ins.time<self.time{
self.time=ins.time;
self.instruction=Some(ins.instruction);
}
}
}
pub fn instruction(self)->Option<TimedInstruction<I,T>>{
//STEAL INSTRUCTION AND DESTROY INSTRUCTIONCOLLECTOR
match self.instruction{
Some(instruction)=>Some(TimedInstruction{
time:self.time,
instruction
}),
None=>None,
}
self.instruction.map(|instruction|TimedInstruction{
time:self.time,
instruction
})
}
}

@ -568,7 +568,7 @@ pub mod vec3{
Planar64Vec3::new(array.map(Planar64::raw))
}
#[inline]
pub fn raw_xyz(x:i64,y:i64,z:i64)->Planar64Vec3{
pub const fn raw_xyz(x:i64,y:i64,z:i64)->Planar64Vec3{
Planar64Vec3::new([Planar64::raw(x),Planar64::raw(y),Planar64::raw(z)])
}
#[inline]

@ -7,16 +7,14 @@ pub type Time=crate::integer::Time<TimeInner>;
#[derive(Clone,Debug)]
pub enum Instruction{
Mouse(MouseInstruction),
Other(OtherInstruction),
}
impl Instruction{
pub const IDLE:Self=Self::Other(OtherInstruction::Other(OtherOtherInstruction::Idle));
}
#[derive(Clone,Debug)]
pub enum OtherInstruction{
SetControl(SetControlInstruction),
Mode(ModeInstruction),
Other(OtherOtherInstruction),
Misc(MiscInstruction),
/// Idle: there were no input events, but the simulation is safe to advance to this timestep
Idle,
}
impl Instruction{
pub const IDLE:Self=Self::Idle;
}
#[derive(Clone,Debug)]
pub enum MouseInstruction{
@ -50,9 +48,7 @@ pub enum ModeInstruction{
Spawn(crate::gameplay_modes::ModeId,crate::gameplay_modes::StageId),
}
#[derive(Clone,Debug)]
pub enum OtherOtherInstruction{
/// Idle: there were no input events, but the simulation is safe to advance to this timestep
Idle,
pub enum MiscInstruction{
PracticeFly,
SetSensitivity(crate::integer::Ratio64Vec2),
}

@ -1,6 +1,6 @@
use bnum::{BInt,cast::As};
#[derive(Clone,Copy,Debug,Default,Hash,PartialEq,PartialOrd,Ord)]
#[derive(Clone,Copy,Debug,Default,Hash,PartialEq,Eq,PartialOrd,Ord)]
/// A Fixed point number for which multiply operations widen the bits in the output. (when the wide-mul feature is enabled)
/// N is the number of u64s to use
/// F is the number of fractional bits (always N*32 lol)
@ -97,7 +97,6 @@ where
self.bits.eq(&other.into())
}
}
impl<const N:usize,const F:usize> Eq for Fixed<N,F>{}
impl<const N:usize,const F:usize,T> PartialOrd<T> for Fixed<N,F>
where

@ -86,6 +86,7 @@ for model_id in 0..num_models{
//if you hash the resource itself and set the first 8 bits to this, that's the resource uuid
#[binrw]
#[brw(little,repr=u8)]
#[repr(u8)]
enum ResourceType{
Mesh,
Texture,

@ -66,6 +66,7 @@ struct Face{
normal:Planar64Vec3,
dot:Planar64,
}
#[derive(Clone)]
struct Vert(Planar64Vec3);
pub trait MeshQuery{
type Face:Clone;
@ -84,41 +85,45 @@ pub trait MeshQuery{
}
fn vert(&self,vert_id:Self::Vert)->Planar64Vec3;
fn face_nd(&self,face_id:Self::Face)->(Self::Normal,Self::Offset);
fn face_edges(&self,face_id:Self::Face)->Cow<Vec<Self::Edge>>;
fn face_edges(&self,face_id:Self::Face)->Cow<[Self::Edge]>;
fn edge_faces(&self,edge_id:<Self::Edge as DirectedEdge>::UndirectedEdge)->Cow<[Self::Face;2]>;
fn edge_verts(&self,edge_id:<Self::Edge as DirectedEdge>::UndirectedEdge)->Cow<[Self::Vert;2]>;
fn vert_edges(&self,vert_id:Self::Vert)->Cow<Vec<Self::Edge>>;
fn vert_faces(&self,vert_id:Self::Vert)->Cow<Vec<Self::Face>>;
fn vert_edges(&self,vert_id:Self::Vert)->Cow<[Self::Edge]>;
fn vert_faces(&self,vert_id:Self::Vert)->Cow<[Self::Face]>;
}
#[derive(Clone)]
struct FaceRefs{
edges:Vec<SubmeshDirectedEdgeId>,
//verts:Vec<VertId>,
edges:Cow<'static,[SubmeshDirectedEdgeId]>,
//verts:Cow<'static,[VertId]>,
}
#[derive(Clone)]
struct EdgeRefs{
faces:[SubmeshFaceId;2],//left, right
verts:[SubmeshVertId;2],//bottom, top
}
#[derive(Clone)]
struct VertRefs{
faces:Vec<SubmeshFaceId>,
edges:Vec<SubmeshDirectedEdgeId>,
faces:Cow<'static,[SubmeshFaceId]>,
edges:Cow<'static,[SubmeshDirectedEdgeId]>,
}
// I don't want to use cow in the mesh, I'd rather have another type and monomorphize using the MeshQuery trait
pub struct PhysicsMeshData{
//this contains all real and virtual faces used in both the complete mesh and convex submeshes
//faces are sorted such that all faces that belong to the complete mesh appear first, and then
//all remaining faces are virtual to operate internal logic of the face crawler
//and cannot be part of a physics collision
//virtual faces are only used in convex submeshes.
faces:Vec<Face>,//MeshFaceId indexes this list
verts:Vec<Vert>,//MeshVertId indexes this list
faces:Cow<'static,[Face]>,//MeshFaceId indexes this list
verts:Cow<'static,[Vert]>,//MeshVertId indexes this list
}
pub struct PhysicsMeshTopology{
//mapping of local ids to PhysicsMeshData ids
faces:Vec<MeshFaceId>,//SubmeshFaceId indexes this list
verts:Vec<MeshVertId>,//SubmeshVertId indexes this list
faces:Cow<'static,[MeshFaceId]>,//SubmeshFaceId indexes this list
verts:Cow<'static,[MeshVertId]>,//SubmeshVertId indexes this list
//all ids here are local to this object
face_topology:Vec<FaceRefs>,
edge_topology:Vec<EdgeRefs>,
vert_topology:Vec<VertRefs>,
face_topology:Cow<'static,[FaceRefs]>,
edge_topology:Cow<'static,[EdgeRefs]>,
vert_topology:Cow<'static,[VertRefs]>,
}
#[derive(Clone,Copy,Hash,id::Id,Eq,PartialEq)]
pub struct PhysicsMeshId(u32);
@ -144,39 +149,41 @@ pub struct PhysicsMesh{
}
impl PhysicsMesh{
pub fn unit_cube()->Self{
const MESH_DATA_FACES:[Face;6]=[
Face{normal:vec3::raw_xyz( 4294967296, 0, 0),dot:Planar64::raw(4294967296)},
Face{normal:vec3::raw_xyz( 0, 4294967296, 0),dot:Planar64::raw(4294967296)},
Face{normal:vec3::raw_xyz( 0, 0, 4294967296),dot:Planar64::raw(4294967296)},
Face{normal:vec3::raw_xyz(-4294967296, 0, 0),dot:Planar64::raw(4294967296)},
Face{normal:vec3::raw_xyz( 0,-4294967296, 0),dot:Planar64::raw(4294967296)},
Face{normal:vec3::raw_xyz( 0, 0,-4294967296),dot:Planar64::raw(4294967296)}
];
const MESH_DATA_VERTS:[Vert;8]=[
Vert(vec3::raw_xyz( 4294967296,-4294967296,-4294967296)),
Vert(vec3::raw_xyz( 4294967296, 4294967296,-4294967296)),
Vert(vec3::raw_xyz( 4294967296, 4294967296, 4294967296)),
Vert(vec3::raw_xyz( 4294967296,-4294967296, 4294967296)),
Vert(vec3::raw_xyz(-4294967296, 4294967296,-4294967296)),
Vert(vec3::raw_xyz(-4294967296, 4294967296, 4294967296)),
Vert(vec3::raw_xyz(-4294967296,-4294967296, 4294967296)),
Vert(vec3::raw_xyz(-4294967296,-4294967296,-4294967296))
];
//go go gadget debug print mesh
let data=PhysicsMeshData{
faces:vec![
Face{normal:vec3::raw_xyz( 4294967296, 0, 0),dot:Planar64::raw(4294967296)},
Face{normal:vec3::raw_xyz( 0, 4294967296, 0),dot:Planar64::raw(4294967296)},
Face{normal:vec3::raw_xyz( 0, 0, 4294967296),dot:Planar64::raw(4294967296)},
Face{normal:vec3::raw_xyz(-4294967296, 0, 0),dot:Planar64::raw(4294967296)},
Face{normal:vec3::raw_xyz( 0,-4294967296, 0),dot:Planar64::raw(4294967296)},
Face{normal:vec3::raw_xyz( 0, 0,-4294967296),dot:Planar64::raw(4294967296)}
],
verts:vec![
Vert(vec3::raw_xyz( 4294967296,-4294967296,-4294967296)),
Vert(vec3::raw_xyz( 4294967296, 4294967296,-4294967296)),
Vert(vec3::raw_xyz( 4294967296, 4294967296, 4294967296)),
Vert(vec3::raw_xyz( 4294967296,-4294967296, 4294967296)),
Vert(vec3::raw_xyz(-4294967296, 4294967296,-4294967296)),
Vert(vec3::raw_xyz(-4294967296, 4294967296, 4294967296)),
Vert(vec3::raw_xyz(-4294967296,-4294967296, 4294967296)),
Vert(vec3::raw_xyz(-4294967296,-4294967296,-4294967296))
]
faces:Cow::Borrowed(&MESH_DATA_FACES),
verts:Cow::Borrowed(&MESH_DATA_VERTS)
};
let mesh_topology=PhysicsMeshTopology{
faces:(0..data.faces.len() as u32).map(MeshFaceId::new).collect(),
verts:(0..data.verts.len() as u32).map(MeshVertId::new).collect(),
face_topology:vec![
FaceRefs{edges:vec![SubmeshDirectedEdgeId((9223372036854775808u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId((9223372036854775809u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId((9223372036854775810u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId(3)]},
FaceRefs{edges:vec![SubmeshDirectedEdgeId((9223372036854775812u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId((9223372036854775813u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId(6),SubmeshDirectedEdgeId(1)]},
FaceRefs{edges:vec![SubmeshDirectedEdgeId(7),SubmeshDirectedEdgeId(2),SubmeshDirectedEdgeId((9223372036854775814u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId((9223372036854775816u64-(1<<63)+(1<<31)) as u32)]},
FaceRefs{edges:vec![SubmeshDirectedEdgeId(8),SubmeshDirectedEdgeId(5),SubmeshDirectedEdgeId((9223372036854775817u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId(10)]},
FaceRefs{edges:vec![SubmeshDirectedEdgeId((9223372036854775815u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId((9223372036854775818u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId(11),SubmeshDirectedEdgeId((9223372036854775811u64-(1<<63)+(1<<31)) as u32)]},
FaceRefs{edges:vec![SubmeshDirectedEdgeId(4),SubmeshDirectedEdgeId(0),SubmeshDirectedEdgeId((9223372036854775819u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId(9)]}
],
edge_topology:vec![
face_topology:Cow::Borrowed(&[
FaceRefs{edges:Cow::Borrowed(&[SubmeshDirectedEdgeId((9223372036854775808u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId((9223372036854775809u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId((9223372036854775810u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId(3)])},
FaceRefs{edges:Cow::Borrowed(&[SubmeshDirectedEdgeId((9223372036854775812u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId((9223372036854775813u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId(6),SubmeshDirectedEdgeId(1)])},
FaceRefs{edges:Cow::Borrowed(&[SubmeshDirectedEdgeId(7),SubmeshDirectedEdgeId(2),SubmeshDirectedEdgeId((9223372036854775814u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId((9223372036854775816u64-(1<<63)+(1<<31)) as u32)])},
FaceRefs{edges:Cow::Borrowed(&[SubmeshDirectedEdgeId(8),SubmeshDirectedEdgeId(5),SubmeshDirectedEdgeId((9223372036854775817u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId(10)])},
FaceRefs{edges:Cow::Borrowed(&[SubmeshDirectedEdgeId((9223372036854775815u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId((9223372036854775818u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId(11),SubmeshDirectedEdgeId((9223372036854775811u64-(1<<63)+(1<<31)) as u32)])},
FaceRefs{edges:Cow::Borrowed(&[SubmeshDirectedEdgeId(4),SubmeshDirectedEdgeId(0),SubmeshDirectedEdgeId((9223372036854775819u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId(9)])},
]),
edge_topology:Cow::Borrowed(&[
EdgeRefs{faces:[SubmeshFaceId(0),SubmeshFaceId(5)],verts:[SubmeshVertId(0),SubmeshVertId(1)]},
EdgeRefs{faces:[SubmeshFaceId(0),SubmeshFaceId(1)],verts:[SubmeshVertId(1),SubmeshVertId(2)]},
EdgeRefs{faces:[SubmeshFaceId(0),SubmeshFaceId(2)],verts:[SubmeshVertId(2),SubmeshVertId(3)]},
@ -189,17 +196,17 @@ impl PhysicsMesh{
EdgeRefs{faces:[SubmeshFaceId(3),SubmeshFaceId(5)],verts:[SubmeshVertId(4),SubmeshVertId(7)]},
EdgeRefs{faces:[SubmeshFaceId(4),SubmeshFaceId(3)],verts:[SubmeshVertId(6),SubmeshVertId(7)]},
EdgeRefs{faces:[SubmeshFaceId(5),SubmeshFaceId(4)],verts:[SubmeshVertId(0),SubmeshVertId(7)]}
],
vert_topology:vec![
VertRefs{faces:vec![SubmeshFaceId(0),SubmeshFaceId(4),SubmeshFaceId(5)],edges:vec![SubmeshDirectedEdgeId((9223372036854775811u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId((9223372036854775819u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId((9223372036854775808u64-(1<<63)+(1<<31)) as u32)]},
VertRefs{faces:vec![SubmeshFaceId(0),SubmeshFaceId(5),SubmeshFaceId(1)],edges:vec![SubmeshDirectedEdgeId((9223372036854775812u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId(0),SubmeshDirectedEdgeId((9223372036854775809u64-(1<<63)+(1<<31)) as u32)]},
VertRefs{faces:vec![SubmeshFaceId(0),SubmeshFaceId(2),SubmeshFaceId(1)],edges:vec![SubmeshDirectedEdgeId(1),SubmeshDirectedEdgeId((9223372036854775810u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId((9223372036854775814u64-(1<<63)+(1<<31)) as u32)]},
VertRefs{faces:vec![SubmeshFaceId(0),SubmeshFaceId(2),SubmeshFaceId(4)],edges:vec![SubmeshDirectedEdgeId(2),SubmeshDirectedEdgeId(3),SubmeshDirectedEdgeId((9223372036854775815u64-(1<<63)+(1<<31)) as u32)]},
VertRefs{faces:vec![SubmeshFaceId(3),SubmeshFaceId(5),SubmeshFaceId(1)],edges:vec![SubmeshDirectedEdgeId(4),SubmeshDirectedEdgeId((9223372036854775817u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId((9223372036854775813u64-(1<<63)+(1<<31)) as u32)]},
VertRefs{faces:vec![SubmeshFaceId(2),SubmeshFaceId(3),SubmeshFaceId(1)],edges:vec![SubmeshDirectedEdgeId(5),SubmeshDirectedEdgeId(6),SubmeshDirectedEdgeId((9223372036854775816u64-(1<<63)+(1<<31)) as u32)]},
VertRefs{faces:vec![SubmeshFaceId(2),SubmeshFaceId(3),SubmeshFaceId(4)],edges:vec![SubmeshDirectedEdgeId(7),SubmeshDirectedEdgeId(8),SubmeshDirectedEdgeId((9223372036854775818u64-(1<<63)+(1<<31)) as u32)]},
VertRefs{faces:vec![SubmeshFaceId(4),SubmeshFaceId(3),SubmeshFaceId(5)],edges:vec![SubmeshDirectedEdgeId(10),SubmeshDirectedEdgeId(11),SubmeshDirectedEdgeId(9)]}
]
]),
vert_topology:Cow::Borrowed(&[
VertRefs{faces:Cow::Borrowed(&[SubmeshFaceId(0),SubmeshFaceId(4),SubmeshFaceId(5)]),edges:Cow::Borrowed(&[SubmeshDirectedEdgeId((9223372036854775811u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId((9223372036854775819u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId((9223372036854775808u64-(1<<63)+(1<<31)) as u32)])},
VertRefs{faces:Cow::Borrowed(&[SubmeshFaceId(0),SubmeshFaceId(5),SubmeshFaceId(1)]),edges:Cow::Borrowed(&[SubmeshDirectedEdgeId((9223372036854775812u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId(0),SubmeshDirectedEdgeId((9223372036854775809u64-(1<<63)+(1<<31)) as u32)])},
VertRefs{faces:Cow::Borrowed(&[SubmeshFaceId(0),SubmeshFaceId(2),SubmeshFaceId(1)]),edges:Cow::Borrowed(&[SubmeshDirectedEdgeId(1),SubmeshDirectedEdgeId((9223372036854775810u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId((9223372036854775814u64-(1<<63)+(1<<31)) as u32)])},
VertRefs{faces:Cow::Borrowed(&[SubmeshFaceId(0),SubmeshFaceId(2),SubmeshFaceId(4)]),edges:Cow::Borrowed(&[SubmeshDirectedEdgeId(2),SubmeshDirectedEdgeId(3),SubmeshDirectedEdgeId((9223372036854775815u64-(1<<63)+(1<<31)) as u32)])},
VertRefs{faces:Cow::Borrowed(&[SubmeshFaceId(3),SubmeshFaceId(5),SubmeshFaceId(1)]),edges:Cow::Borrowed(&[SubmeshDirectedEdgeId(4),SubmeshDirectedEdgeId((9223372036854775817u64-(1<<63)+(1<<31)) as u32),SubmeshDirectedEdgeId((9223372036854775813u64-(1<<63)+(1<<31)) as u32)])},
VertRefs{faces:Cow::Borrowed(&[SubmeshFaceId(2),SubmeshFaceId(3),SubmeshFaceId(1)]),edges:Cow::Borrowed(&[SubmeshDirectedEdgeId(5),SubmeshDirectedEdgeId(6),SubmeshDirectedEdgeId((9223372036854775816u64-(1<<63)+(1<<31)) as u32)])},
VertRefs{faces:Cow::Borrowed(&[SubmeshFaceId(2),SubmeshFaceId(3),SubmeshFaceId(4)]),edges:Cow::Borrowed(&[SubmeshDirectedEdgeId(7),SubmeshDirectedEdgeId(8),SubmeshDirectedEdgeId((9223372036854775818u64-(1<<63)+(1<<31)) as u32)])},
VertRefs{faces:Cow::Borrowed(&[SubmeshFaceId(4),SubmeshFaceId(3),SubmeshFaceId(5)]),edges:Cow::Borrowed(&[SubmeshDirectedEdgeId(10),SubmeshDirectedEdgeId(11),SubmeshDirectedEdgeId(9)])},
])
};
Self{
data,
@ -279,15 +286,12 @@ struct EdgePool{
}
impl EdgePool{
fn push(&mut self,edge_ref_verts:EdgeRefVerts)->(&mut EdgeRefFaces,SubmeshEdgeId){
let edge_id=if let Some(&edge_id)=self.edge_id_from_guy.get(&edge_ref_verts){
edge_id
}else{
let edge_id=*self.edge_id_from_guy.entry(edge_ref_verts.clone()).or_insert_with(||{
let edge_id=SubmeshEdgeId::new(self.edge_guys.len() as u32);
self.edge_guys.push((edge_ref_verts.clone(),EdgeRefFaces::new()));
self.edge_id_from_guy.insert(edge_ref_verts,edge_id);
self.edge_guys.push((edge_ref_verts,EdgeRefFaces::new()));
edge_id
};
(&mut unsafe{self.edge_guys.get_unchecked_mut(edge_id.get() as usize)}.1,edge_id)
});
(&mut self.edge_guys[edge_id.get() as usize].1,edge_id)
}
}
@ -322,16 +326,11 @@ impl TryFrom<&model::Mesh> for PhysicsMesh{
let mut submesh_verts=Vec::new();
let mut submesh_vert_id_from_mesh_vert_id=HashMap::<MeshVertId,SubmeshVertId>::new();
//lazy closure
let mut get_submesh_vert_id=|vert_id:MeshVertId|{
if let Some(&submesh_vert_id)=submesh_vert_id_from_mesh_vert_id.get(&vert_id){
submesh_vert_id
}else{
let submesh_vert_id=SubmeshVertId::new(submesh_verts.len() as u32);
submesh_verts.push(vert_id);
submesh_vert_id_from_mesh_vert_id.insert(vert_id,submesh_vert_id);
submesh_vert_id
}
};
let mut get_submesh_vert_id=|vert_id:MeshVertId|*submesh_vert_id_from_mesh_vert_id.entry(vert_id).or_insert_with(||{
let submesh_vert_id=SubmeshVertId::new(submesh_verts.len() as u32);
submesh_verts.push(vert_id);
submesh_vert_id
});
let mut edge_pool=EdgePool::default();
let mut vert_ref_guys=vec![VertRefGuy::default();mesh.unique_pos.len()];
let mut face_ref_guys=Vec::new();
@ -364,10 +363,10 @@ impl TryFrom<&model::Mesh> for PhysicsMesh{
edge_ref_faces.push(!is_sorted as usize,submesh_face_id);
//index edges & face into vertices
{
let vert_ref_guy=unsafe{vert_ref_guys.get_unchecked_mut(submesh_vert0_id.get() as usize)};
let vert_ref_guy=&mut vert_ref_guys[submesh_vert0_id.get() as usize];
vert_ref_guy.edges.insert(edge_id.as_directed(is_sorted));
vert_ref_guy.faces.insert(submesh_face_id);
unsafe{vert_ref_guys.get_unchecked_mut(submesh_vert1_id.get() as usize)}.edges.insert(edge_id.as_directed(!is_sorted));
vert_ref_guys[submesh_vert1_id.get() as usize].edges.insert(edge_id.as_directed(!is_sorted));
}
//return directed_edge_id
edge_id.as_directed(is_sorted)
@ -396,10 +395,10 @@ impl TryFrom<&model::Mesh> for PhysicsMesh{
}
}
PhysicsMeshTopology{
faces:submesh_faces,
verts:submesh_verts,
faces:submesh_faces.into(),
verts:submesh_verts.into(),
face_topology:face_ref_guys.into_iter().map(|face_ref_guy|{
FaceRefs{edges:face_ref_guy.0}
FaceRefs{edges:face_ref_guy.0.into()}
}).collect(),
edge_topology:edge_pool.edge_guys.into_iter().map(|(edge_ref_verts,edge_ref_faces)|
EdgeRefs{faces:edge_ref_faces.0,verts:edge_ref_verts.0}
@ -414,7 +413,7 @@ impl TryFrom<&model::Mesh> for PhysicsMesh{
}).collect();
Ok(Self{
data:PhysicsMeshData{
faces,
faces:faces.into(),
verts,
},
complete_mesh:mesh_topologies.pop().ok_or(PhysicsMeshError::NoPhysicsGroups)?,
@ -442,7 +441,7 @@ impl MeshQuery for PhysicsMeshView<'_>{
let vert_idx=self.topology.verts[vert_id.get() as usize].get() as usize;
self.data.verts[vert_idx].0
}
fn face_edges(&self,face_id:SubmeshFaceId)->Cow<Vec<SubmeshDirectedEdgeId>>{
fn face_edges(&self,face_id:SubmeshFaceId)->Cow<[SubmeshDirectedEdgeId]>{
Cow::Borrowed(&self.topology.face_topology[face_id.get() as usize].edges)
}
fn edge_faces(&self,edge_id:SubmeshEdgeId)->Cow<[SubmeshFaceId;2]>{
@ -451,10 +450,10 @@ impl MeshQuery for PhysicsMeshView<'_>{
fn edge_verts(&self,edge_id:SubmeshEdgeId)->Cow<[SubmeshVertId;2]>{
Cow::Borrowed(&self.topology.edge_topology[edge_id.get() as usize].verts)
}
fn vert_edges(&self,vert_id:SubmeshVertId)->Cow<Vec<SubmeshDirectedEdgeId>>{
fn vert_edges(&self,vert_id:SubmeshVertId)->Cow<[SubmeshDirectedEdgeId]>{
Cow::Borrowed(&self.topology.vert_topology[vert_id.get() as usize].edges)
}
fn vert_faces(&self,vert_id:SubmeshVertId)->Cow<Vec<SubmeshFaceId>>{
fn vert_faces(&self,vert_id:SubmeshVertId)->Cow<[SubmeshFaceId]>{
Cow::Borrowed(&self.topology.vert_topology[vert_id.get() as usize].faces)
}
}
@ -520,7 +519,7 @@ impl MeshQuery for TransformedMesh<'_>{
self.transform.vertex.transform_point3(self.view.vert(vert_id)).fix_1()
}
#[inline]
fn face_edges(&self,face_id:SubmeshFaceId)->Cow<Vec<SubmeshDirectedEdgeId>>{
fn face_edges(&self,face_id:SubmeshFaceId)->Cow<[SubmeshDirectedEdgeId]>{
self.view.face_edges(face_id)
}
#[inline]
@ -532,11 +531,11 @@ impl MeshQuery for TransformedMesh<'_>{
self.view.edge_verts(edge_id)
}
#[inline]
fn vert_edges(&self,vert_id:SubmeshVertId)->Cow<Vec<SubmeshDirectedEdgeId>>{
fn vert_edges(&self,vert_id:SubmeshVertId)->Cow<[SubmeshDirectedEdgeId]>{
self.view.vert_edges(vert_id)
}
#[inline]
fn vert_faces(&self,vert_id:SubmeshVertId)->Cow<Vec<SubmeshFaceId>>{
fn vert_faces(&self,vert_id:SubmeshVertId)->Cow<[SubmeshFaceId]>{
self.view.vert_faces(vert_id)
}
}
@ -712,6 +711,20 @@ impl MinkowskiMesh<'_>{
},
}
}
// TODO: fundamentally improve this algorithm.
// All it needs to do is find the closest point on the mesh
// and return the FEV which the point resides on.
//
// What it actually does is use the above functions to trace a ray in from infinity,
// crawling the closest point along the mesh surface until the ray reaches
// the starting point to discover the final FEV.
//
// The actual collision prediction probably does a single test
// and then immediately returns with 0 FEV transitions on average,
// because of the strict time_limit constraint.
//
// Most of the calculation time is just calculating the starting point
// for the "actual" crawling algorithm below (predict_collision_{in|out}).
fn closest_fev_not_inside(&self,mut infinity_body:Body)->Option<FEV<MinkowskiMesh>>{
infinity_body.infinity_dir().map_or(None,|dir|{
let infinity_fev=self.infinity_fev(-dir,infinity_body.position);
@ -832,7 +845,7 @@ impl MeshQuery for MinkowskiMesh<'_>{
},
}
}
fn face_edges(&self,face_id:MinkowskiFace)->Cow<Vec<MinkowskiDirectedEdge>>{
fn face_edges(&self,face_id:MinkowskiFace)->Cow<[MinkowskiDirectedEdge]>{
match face_id{
MinkowskiFace::VertFace(v0,f1)=>{
Cow::Owned(self.mesh1.face_edges(f1).iter().map(|&edge_id1|{
@ -933,7 +946,7 @@ impl MeshQuery for MinkowskiMesh<'_>{
},
}
}
fn vert_edges(&self,vert_id:MinkowskiVert)->Cow<Vec<MinkowskiDirectedEdge>>{
fn vert_edges(&self,vert_id:MinkowskiVert)->Cow<[MinkowskiDirectedEdge]>{
match vert_id{
MinkowskiVert::VertVert(v0,v1)=>{
let mut edges=Vec::new();
@ -975,7 +988,7 @@ impl MeshQuery for MinkowskiMesh<'_>{
},
}
}
fn vert_faces(&self,_vert_id:MinkowskiVert)->Cow<Vec<MinkowskiFace>>{
fn vert_faces(&self,_vert_id:MinkowskiVert)->Cow<[MinkowskiFace]>{
unimplemented!()
}
}

@ -1,28 +1,74 @@
use strafesnet_common::mouse::MouseState;
use strafesnet_common::physics::{
Instruction as PhysicsInputInstruction,
MouseInstruction,SetControlInstruction,ModeInstruction,MiscInstruction,
Instruction as PhysicsInstruction,
TimeInner as PhysicsTimeInner,
Time as PhysicsTime,
MouseInstruction,
OtherInstruction,
};
use strafesnet_common::session::{Time as SessionTime,TimeInner as SessionTimeInner};
use strafesnet_common::instruction::{InstructionConsumer,InstructionEmitter,TimedInstruction};
type TimedPhysicsInstruction=TimedInstruction<PhysicsInputInstruction,PhysicsTimeInner>;
type TimedUnbufferedInstruction=TimedInstruction<Instruction,PhysicsTimeInner>;
type DoubleTimedUnbufferedInstruction=TimedInstruction<TimedUnbufferedInstruction,SessionTimeInner>;
type TimedSelfInstruction=TimedInstruction<Instruction,PhysicsTimeInner>;
type DoubleTimedSelfInstruction=TimedInstruction<TimedSelfInstruction,SessionTimeInner>;
type TimedPhysicsInstruction=TimedInstruction<PhysicsInstruction,PhysicsTimeInner>;
const MOUSE_TIMEOUT:SessionTime=SessionTime::from_millis(10);
/// To be fed into MouseInterpolator
#[derive(Clone,Debug)]
pub enum Instruction{
pub(crate) enum Instruction{
MoveMouse(glam::IVec2),
Other(OtherInstruction),
SetControl(SetControlInstruction),
Mode(ModeInstruction),
Misc(MiscInstruction),
Idle,
}
#[derive(Clone,Debug)]
enum UnbufferedInstruction{
MoveMouse(glam::IVec2),
NonMouse(NonMouseInstruction),
}
#[derive(Clone,Debug)]
enum BufferedInstruction{
Mouse(MouseInstruction),
NonMouse(NonMouseInstruction),
}
#[derive(Clone,Debug)]
pub(crate) enum NonMouseInstruction{
SetControl(SetControlInstruction),
Mode(ModeInstruction),
Misc(MiscInstruction),
Idle,
}
impl From<Instruction> for UnbufferedInstruction{
#[inline]
fn from(value:Instruction)->Self{
match value{
Instruction::MoveMouse(mouse_instruction)=>UnbufferedInstruction::MoveMouse(mouse_instruction),
Instruction::SetControl(set_control_instruction)=>UnbufferedInstruction::NonMouse(NonMouseInstruction::SetControl(set_control_instruction)),
Instruction::Mode(mode_instruction)=>UnbufferedInstruction::NonMouse(NonMouseInstruction::Mode(mode_instruction)),
Instruction::Misc(misc_instruction)=>UnbufferedInstruction::NonMouse(NonMouseInstruction::Misc(misc_instruction)),
Instruction::Idle=>UnbufferedInstruction::NonMouse(NonMouseInstruction::Idle),
}
}
}
impl From<BufferedInstruction> for PhysicsInstruction{
#[inline]
fn from(value:BufferedInstruction)->Self{
match value{
BufferedInstruction::Mouse(mouse_instruction)=>PhysicsInstruction::Mouse(mouse_instruction),
BufferedInstruction::NonMouse(non_mouse_instruction)=>match non_mouse_instruction{
NonMouseInstruction::SetControl(set_control_instruction)=>PhysicsInstruction::SetControl(set_control_instruction),
NonMouseInstruction::Mode(mode_instruction)=>PhysicsInstruction::Mode(mode_instruction),
NonMouseInstruction::Misc(misc_instruction)=>PhysicsInstruction::Misc(misc_instruction),
NonMouseInstruction::Idle=>PhysicsInstruction::Idle,
},
}
}
}
pub enum StepInstruction{
pub(crate) enum StepInstruction{
Pop,
Timeout,
}
@ -42,10 +88,10 @@ pub struct MouseInterpolator{
}
// Maybe MouseInterpolator manipulation is better expressed using impls
// and called from Instruction trait impls in session
impl InstructionConsumer<TimedUnbufferedInstruction> for MouseInterpolator{
impl InstructionConsumer<TimedSelfInstruction> for MouseInterpolator{
type TimeInner=SessionTimeInner;
fn process_instruction(&mut self,ins:DoubleTimedUnbufferedInstruction){
self.push_unbuffered_input(ins)
fn process_instruction(&mut self,ins:DoubleTimedSelfInstruction){
self.push_unbuffered_input(ins.time,ins.instruction.time,ins.instruction.instruction.into())
}
}
impl InstructionEmitter<StepInstruction> for MouseInterpolator{
@ -65,7 +111,7 @@ impl MouseInterpolator{
fn push_mouse_and_flush_buffer(&mut self,ins:TimedInstruction<MouseInstruction,PhysicsTimeInner>){
self.buffer.push_front(TimedInstruction{
time:ins.time,
instruction:PhysicsInputInstruction::Mouse(ins.instruction),
instruction:BufferedInstruction::Mouse(ins.instruction).into(),
});
// flush buffer to output
if self.output.len()==0{
@ -86,7 +132,8 @@ impl MouseInterpolator{
}
}
}
fn timeout_mouse(&mut self,time:PhysicsTime){
fn timeout_mouse(&mut self,timeout_time:PhysicsTime){
// the state always changes to unbuffered
let buffer_state=core::mem::replace(&mut self.buffer_state,BufferState::Unbuffered);
match buffer_state{
BufferState::Unbuffered=>(),
@ -95,28 +142,21 @@ impl MouseInterpolator{
self.push_mouse_and_flush_buffer(TimedInstruction{
time:mouse_state.time,
instruction:MouseInstruction::ReplaceMouse{
m1:MouseState{pos:mouse_state.pos,time},
m1:MouseState{pos:mouse_state.pos,time:timeout_time},
m0:mouse_state,
},
});
}
BufferState::Buffered(_time,mouse_state)=>{
// convert to BufferState::Unbuffered
// use the first instruction which should be a mouse instruction
// to push a ReplaceMouse instruction
// duplicate the current mouse
// duplicate the currently buffered mouse state but at a later (future, from the physics perspective) time
self.push_mouse_and_flush_buffer(TimedInstruction{
// This should be simulation_timer.time(timeout)
// but the timer is not accessible from this scope
// and it's just here to say that the mouse isn't moving anyways.
// I think this is a divide by zero bug, two identical mouse_states will occupy the interpolation state
time:mouse_state.time,
instruction:MouseInstruction::SetNextMouse(MouseState{pos:mouse_state.pos,time}),
instruction:MouseInstruction::SetNextMouse(MouseState{pos:mouse_state.pos,time:timeout_time}),
});
},
}
}
pub fn push_unbuffered_input(&mut self,ins:DoubleTimedUnbufferedInstruction){
fn push_unbuffered_input(&mut self,session_time:SessionTime,physics_time:PhysicsTime,ins:UnbufferedInstruction){
// new input
// if there is zero instruction buffered, it means the mouse is not moving
// case 1: unbuffered
@ -128,18 +168,16 @@ impl MouseInterpolator{
// case 3: stop
// a mouse event is buffered, but no mouse events have transpired within 10ms
// push buffered mouse instruction and flush buffer to output
if self.get_mouse_timedout_at(ins.time).is_some(){
self.timeout_mouse(ins.instruction.time);
}
// replace_with allows the enum variant to safely be replaced from behind a mutable reference
// replace_with allows the enum variant to safely be replaced
// from behind a mutable reference, but a panic in the closure means that
// the entire program terminates rather than completing an unwind.
let (ins_mouse,ins_other)=replace_with::replace_with_or_abort_and_return(&mut self.buffer_state,|buffer_state|{
match ins.instruction.instruction{
Instruction::MoveMouse(pos)=>{
let next_mouse_state=MouseState{pos,time:ins.instruction.time};
match ins{
UnbufferedInstruction::MoveMouse(pos)=>{
let next_mouse_state=MouseState{pos,time:physics_time};
match buffer_state{
BufferState::Unbuffered=>{
((None,None),BufferState::Initializing(ins.time,next_mouse_state))
((None,None),BufferState::Initializing(session_time,next_mouse_state))
},
BufferState::Initializing(_time,mouse_state)=>{
let ins_mouse=TimedInstruction{
@ -149,19 +187,19 @@ impl MouseInterpolator{
m1:next_mouse_state.clone(),
},
};
((Some(ins_mouse),None),BufferState::Buffered(ins.time,next_mouse_state))
((Some(ins_mouse),None),BufferState::Buffered(session_time,next_mouse_state))
},
BufferState::Buffered(_time,mouse_state)=>{
let ins_mouse=TimedInstruction{
time:mouse_state.time,
instruction:MouseInstruction::SetNextMouse(next_mouse_state.clone()),
};
((Some(ins_mouse),None),BufferState::Buffered(ins.time,next_mouse_state))
((Some(ins_mouse),None),BufferState::Buffered(session_time,next_mouse_state))
},
}
},
Instruction::Other(other_instruction)=>((None,Some(TimedInstruction{
time:ins.instruction.time,
UnbufferedInstruction::NonMouse(other_instruction)=>((None,Some(TimedInstruction{
time:physics_time,
instruction:other_instruction,
})),buffer_state),
}
@ -172,7 +210,7 @@ impl MouseInterpolator{
if let Some(ins)=ins_other{
let instruction=TimedInstruction{
time:ins.time,
instruction:PhysicsInputInstruction::Other(ins.instruction),
instruction:BufferedInstruction::NonMouse(ins.instruction).into(),
};
if matches!(self.buffer_state,BufferState::Unbuffered){
self.output.push_back(instruction);
@ -181,7 +219,7 @@ impl MouseInterpolator{
}
}
}
pub fn buffered_instruction_with_timeout(&self,time_limit:SessionTime)->Option<TimedInstruction<StepInstruction,SessionTimeInner>>{
fn buffered_instruction_with_timeout(&self,time_limit:SessionTime)->Option<TimedInstruction<StepInstruction,SessionTimeInner>>{
match self.get_mouse_timedout_at(time_limit){
Some(timeout)=>Some(TimedInstruction{
time:timeout,
@ -194,7 +232,7 @@ impl MouseInterpolator{
}),
}
}
pub fn pop_buffered_instruction(&mut self,ins:TimedInstruction<StepInstruction,PhysicsTimeInner>)->Option<TimedInstruction<PhysicsInputInstruction,PhysicsTimeInner>>{
pub fn pop_buffered_instruction(&mut self,ins:TimedInstruction<StepInstruction,PhysicsTimeInner>)->Option<TimedPhysicsInstruction>{
match ins.instruction{
StepInstruction::Pop=>(),
StepInstruction::Timeout=>self.timeout_mouse(ins.time),
@ -215,13 +253,11 @@ mod test{
macro_rules! push{
($time:expr,$ins:expr)=>{
println!("in={:?}",$ins);
interpolator.push_unbuffered_input(TimedInstruction{
time:$time,
instruction:TimedInstruction{
time:timer.time($time),
instruction:$ins,
}
});
interpolator.push_unbuffered_input(
$time,
timer.time($time),
$ins,
);
while let Some(ins)=interpolator.buffered_instruction_with_timeout($time){
let ins_retimed=TimedInstruction{
time:timer.time(ins.time),
@ -235,11 +271,11 @@ mod test{
// test each buffer_state transition
let mut t=SessionTime::ZERO;
push!(t,Instruction::MoveMouse(glam::ivec2(0,0)));
push!(t,UnbufferedInstruction::MoveMouse(glam::ivec2(0,0)));
t+=SessionTime::from_millis(5);
push!(t,Instruction::MoveMouse(glam::ivec2(0,0)));
push!(t,UnbufferedInstruction::MoveMouse(glam::ivec2(0,0)));
t+=SessionTime::from_millis(5);
push!(t,Instruction::MoveMouse(glam::ivec2(0,0)));
push!(t,UnbufferedInstruction::MoveMouse(glam::ivec2(0,0)));
t+=SessionTime::from_millis(1);
}
}

@ -19,7 +19,7 @@ type MouseState=strafesnet_common::mouse::MouseState<TimeInner>;
//external influence
//this is how you influence the physics from outside
use strafesnet_common::physics::{Instruction,OtherInstruction,MouseInstruction,ModeInstruction,OtherOtherInstruction,SetControlInstruction};
use strafesnet_common::physics::{Instruction,MouseInstruction,ModeInstruction,MiscInstruction,SetControlInstruction};
//internal influence
//when the physics asks itself what happens next, this is how it's represented
@ -39,9 +39,6 @@ pub struct InputState{
controls:strafesnet_common::controls_bitflag::Controls,
}
impl InputState{
pub const fn get_next_mouse(&self)->&MouseState{
&self.next_mouse
}
fn set_next_mouse(&mut self,next_mouse:MouseState){
//I like your functions magic language
self.mouse=std::mem::replace(&mut self.next_mouse,next_mouse);
@ -693,18 +690,18 @@ struct IntersectModel{
}
#[derive(Debug,Clone,Copy,Eq,Hash,PartialEq)]
struct ContactCollision{
pub struct ContactCollision{
face_id:model_physics::MinkowskiFace,
model_id:ContactModelId,
submesh_id:PhysicsSubmeshId,
}
#[derive(Debug,Clone,Copy,Eq,Hash,PartialEq)]
struct IntersectCollision{
pub struct IntersectCollision{
model_id:IntersectModelId,
submesh_id:PhysicsSubmeshId,
}
#[derive(Debug,Clone,Eq,Hash,PartialEq)]
enum Collision{
pub enum Collision{
Contact(ContactCollision),
Intersect(IntersectCollision),
}
@ -832,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<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{
fn default()->Self{
Self{
@ -859,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){
@ -920,49 +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<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 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<InternalInstruction> for PhysicsContext{
impl InstructionConsumer<InternalInstruction> for PhysicsContext<'_>{
type TimeInner=TimeInner;
fn process_instruction(&mut self,ins:TimedInstruction<InternalInstruction,TimeInner>){
atomic_internal_instruction(&mut self.state,&self.data,ins)
}
}
impl InstructionConsumer<Instruction> for PhysicsContext{
impl InstructionConsumer<Instruction> for PhysicsContext<'_>{
type TimeInner=TimeInner;
fn process_instruction(&mut self,ins:TimedInstruction<Instruction,TimeInner>){
atomic_input_instruction(&mut self.state,&self.data,ins)
}
}
impl InstructionEmitter<InternalInstruction> for PhysicsContext{
impl InstructionEmitter<InternalInstruction> 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<TimedInstruction<InternalInstruction,TimeInner>>{
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
}
pub const fn get_next_mouse(&self)->&MouseState{
self.state.input_state.get_next_mouse()
impl PhysicsContext<'_>{
pub fn run_input_instruction(
state:&mut PhysicsState,
data:&PhysicsData,
instruction:TimedInstruction<Instruction,TimeInner>
){
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();
@ -1093,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<Instruction,TimeInner>){
self.process_exhaustive(instruction.time);
self.process_instruction(instruction);
}
}
//this is the one who asks
@ -1742,13 +1741,13 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
let should_advance_body=match ins.instruction{
//the body may as well be a quantum wave function
//as far as these instruction are concerned (they don't care where it is)
Instruction::Other(OtherInstruction::Other(OtherOtherInstruction::SetSensitivity(..)))
|Instruction::Other(OtherInstruction::Mode(_))
|Instruction::Other(OtherInstruction::SetControl(SetControlInstruction::SetZoom(..)))
|Instruction::Other(OtherInstruction::Other(OtherOtherInstruction::Idle))=>false,
Instruction::Misc(MiscInstruction::SetSensitivity(..))
|Instruction::Mode(_)
|Instruction::SetControl(SetControlInstruction::SetZoom(..))
|Instruction::Idle=>false,
//these controls only update the body if you are on the ground
Instruction::Mouse(_)
|Instruction::Other(OtherInstruction::SetControl(_))=>{
|Instruction::SetControl(_)=>{
match &state.move_state{
MoveState::Fly
|MoveState::Water
@ -1758,7 +1757,7 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
}
},
//the body must be updated unconditionally
Instruction::Other(OtherInstruction::Other(OtherOtherInstruction::PracticeFly))=>true,
Instruction::Misc(MiscInstruction::PracticeFly)=>true,
};
if should_advance_body{
state.body.advance_time(state.time);
@ -1774,14 +1773,14 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
state.camera.move_mouse(m0.pos-state.input_state.mouse.pos);
state.input_state.replace_mouse(m0,m1);
},
Instruction::Other(OtherInstruction::Other(OtherOtherInstruction::SetSensitivity(sensitivity)))=>state.camera.sensitivity=sensitivity,
Instruction::Other(OtherInstruction::SetControl(SetControlInstruction::SetMoveForward(s)))=>state.input_state.set_control(Controls::MoveForward,s),
Instruction::Other(OtherInstruction::SetControl(SetControlInstruction::SetMoveLeft(s)))=>state.input_state.set_control(Controls::MoveLeft,s),
Instruction::Other(OtherInstruction::SetControl(SetControlInstruction::SetMoveBack(s)))=>state.input_state.set_control(Controls::MoveBackward,s),
Instruction::Other(OtherInstruction::SetControl(SetControlInstruction::SetMoveRight(s)))=>state.input_state.set_control(Controls::MoveRight,s),
Instruction::Other(OtherInstruction::SetControl(SetControlInstruction::SetMoveUp(s)))=>state.input_state.set_control(Controls::MoveUp,s),
Instruction::Other(OtherInstruction::SetControl(SetControlInstruction::SetMoveDown(s)))=>state.input_state.set_control(Controls::MoveDown,s),
Instruction::Other(OtherInstruction::SetControl(SetControlInstruction::SetJump(s)))=>{
Instruction::Misc(MiscInstruction::SetSensitivity(sensitivity))=>state.camera.sensitivity=sensitivity,
Instruction::SetControl(SetControlInstruction::SetMoveForward(s))=>state.input_state.set_control(Controls::MoveForward,s),
Instruction::SetControl(SetControlInstruction::SetMoveLeft(s))=>state.input_state.set_control(Controls::MoveLeft,s),
Instruction::SetControl(SetControlInstruction::SetMoveBack(s))=>state.input_state.set_control(Controls::MoveBackward,s),
Instruction::SetControl(SetControlInstruction::SetMoveRight(s))=>state.input_state.set_control(Controls::MoveRight,s),
Instruction::SetControl(SetControlInstruction::SetMoveUp(s))=>state.input_state.set_control(Controls::MoveUp,s),
Instruction::SetControl(SetControlInstruction::SetMoveDown(s))=>state.input_state.set_control(Controls::MoveDown,s),
Instruction::SetControl(SetControlInstruction::SetJump(s))=>{
state.input_state.set_control(Controls::Jump,s);
if let Some(walk_state)=state.move_state.get_walk_state(){
if let Some(jump_settings)=&state.style.jump{
@ -1793,16 +1792,16 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
}
b_refresh_walk_target=false;
},
Instruction::Other(OtherInstruction::SetControl(SetControlInstruction::SetZoom(s)))=>{
Instruction::SetControl(SetControlInstruction::SetZoom(s))=>{
state.input_state.set_control(Controls::Zoom,s);
b_refresh_walk_target=false;
},
Instruction::Other(OtherInstruction::Mode(ModeInstruction::Reset))=>{
Instruction::Mode(ModeInstruction::Reset)=>{
//totally reset physics state
state.reset_to_default();
b_refresh_walk_target=false;
},
Instruction::Other(OtherInstruction::Mode(ModeInstruction::Restart))=>{
Instruction::Mode(ModeInstruction::Restart)=>{
//teleport to start zone
let mode=data.modes.get_mode(state.mode_state.get_mode_id());
let spawn_point=mode.and_then(|mode|
@ -1818,7 +1817,7 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
b_refresh_walk_target=false;
}
// Spawn does not necessarily imply reset
Instruction::Other(OtherInstruction::Mode(ModeInstruction::Spawn(mode_id,stage_id)))=>{
Instruction::Mode(ModeInstruction::Spawn(mode_id,stage_id))=>{
//spawn at a particular stage
if let Some(mode)=data.modes.get_mode(mode_id){
if let Some(stage)=mode.get_stage(stage_id){
@ -1832,7 +1831,7 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
}
b_refresh_walk_target=false;
},
Instruction::Other(OtherInstruction::Other(OtherOtherInstruction::PracticeFly))=>{
Instruction::Misc(MiscInstruction::PracticeFly)=>{
match &state.move_state{
MoveState::Fly=>{
state.set_move_state(data,MoveState::Air);
@ -1843,7 +1842,7 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
}
b_refresh_walk_target=false;
},
Instruction::Other(OtherInstruction::Other(OtherOtherInstruction::Idle))=>{
Instruction::Idle=>{
//literally idle!
b_refresh_walk_target=false;
},

@ -1,25 +1,27 @@
use crate::graphics_worker::Instruction as GraphicsInstruction;
use crate::session::{SessionInputInstruction,Instruction as SessionInstruction,Session,Simulation};
use crate::session::{
Session,Simulation,SessionInputInstruction,SessionControlInstruction,SessionPlaybackInstruction,
Instruction as SessionInstruction,
};
use strafesnet_common::instruction::{TimedInstruction,InstructionConsumer};
use strafesnet_common::physics::Time as PhysicsTime;
use strafesnet_common::session::{Time as SessionTime,TimeInner as SessionTimeInner};
use strafesnet_common::timer::Timer;
pub enum Instruction{
Input(SessionInputInstruction),
SetPaused(bool),
SessionInput(SessionInputInstruction),
SessionControl(SessionControlInstruction),
SessionPlayback(SessionPlaybackInstruction),
Render,
Resize(winit::dpi::PhysicalSize<u32>),
ChangeMap(strafesnet_common::map::CompleteMap),
}
const SESSION_INSTRUCTION_IDLE:SessionInstruction=SessionInstruction::Input(SessionInputInstruction::Other(strafesnet_common::physics::OtherOtherInstruction::Idle));
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<Instruction,SessionTimeInner>>{
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(
@ -42,24 +44,29 @@ pub fn new<'a>(
};
}
match ins.instruction{
Instruction::Input(unbuffered_instruction)=>{
Instruction::SessionInput(unbuffered_instruction)=>{
run_session_instruction!(ins.time,SessionInstruction::Input(unbuffered_instruction));
},
Instruction::SetPaused(paused)=>{
run_session_instruction!(ins.time,SessionInstruction::SetPaused(paused));
Instruction::SessionControl(unbuffered_instruction)=>{
run_session_instruction!(ins.time,SessionInstruction::Control(unbuffered_instruction));
},
Instruction::SessionPlayback(unbuffered_instruction)=>{
run_session_instruction!(ins.time,SessionInstruction::Playback(unbuffered_instruction));
},
Instruction::Render=>{
run_session_instruction!(ins.time,SESSION_INSTRUCTION_IDLE);
let frame_state=session.get_frame_state(ins.time);
run_graphics_worker_instruction!(GraphicsInstruction::Render(frame_state));
run_session_instruction!(ins.time,SessionInstruction::Idle);
if let Some(frame_state)=session.get_frame_state(ins.time){
run_graphics_worker_instruction!(GraphicsInstruction::Render(frame_state));
}
},
Instruction::Resize(physical_size)=>{
run_session_instruction!(ins.time,SESSION_INSTRUCTION_IDLE);
run_session_instruction!(ins.time,SessionInstruction::Idle);
let user_settings=session.user_settings().clone();
run_graphics_worker_instruction!(GraphicsInstruction::Resize(physical_size,user_settings));
},
Instruction::ChangeMap(complete_map)=>{
run_session_instruction!(ins.time,SessionInstruction::ChangeMap(&complete_map));
run_session_instruction!(ins.time,SessionInstruction::Input(SessionInputInstruction::Mode(crate::session::ImplicitModeInstruction::ResetAndSpawn(strafesnet_common::gameplay_modes::ModeId::MAIN,strafesnet_common::gameplay_modes::StageId::FIRST))));
run_graphics_worker_instruction!(GraphicsInstruction::ChangeMap(complete_map));
},
}

@ -1,9 +1,11 @@
use std::collections::HashMap;
use strafesnet_common::gameplay_modes::{ModeId,StageId};
use strafesnet_common::instruction::{InstructionConsumer,InstructionEmitter,InstructionFeedback,TimedInstruction};
// session represents the non-hardware state of the client.
// Ideally it is a deterministic state which is atomically updated by instructions, same as the simulation state.
use strafesnet_common::physics::{
ModeInstruction,OtherInstruction,OtherOtherInstruction,
ModeInstruction,MiscInstruction,
Instruction as PhysicsInputInstruction,
TimeInner as PhysicsTimeInner,
Time as PhysicsTime
@ -12,20 +14,22 @@ 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>{
Input(SessionInputInstruction),
SetPaused(bool),
Control(SessionControlInstruction),
Playback(SessionPlaybackInstruction),
ChangeMap(&'a strafesnet_common::map::CompleteMap),
//Graphics(crate::graphics_worker::Instruction),
Idle,
}
pub enum SessionInputInstruction{
Mouse(glam::IVec2),
SetControl(strafesnet_common::physics::SetControlInstruction),
Mode(ImplicitModeInstruction),
Other(strafesnet_common::physics::OtherOtherInstruction),
Misc(strafesnet_common::physics::MiscInstruction),
}
/// Implicit mode instruction are fed separately to session.
/// Session generates the explicit mode instructions interlaced with a SetSensitivity instruction
@ -35,6 +39,20 @@ pub enum ImplicitModeInstruction{
ResetAndSpawn(strafesnet_common::gameplay_modes::ModeId,strafesnet_common::gameplay_modes::StageId),
}
pub enum SessionControlInstruction{
SetPaused(bool),
// copy the current session simulation recording into a replay and view it
CopyRecordingIntoReplayAndSpectate,
StopSpectate,
}
pub enum SessionPlaybackInstruction{
SkipForward,
SkipBack,
TogglePaused,
DecreaseTimescale,
IncreaseTimescale,
}
pub struct FrameState{
pub body:crate::physics::Body,
pub camera:crate::physics::PhysicsCamera,
@ -43,12 +61,12 @@ pub struct FrameState{
pub struct Simulation{
timer:Timer<Scaled<SessionTimeInner,PhysicsTimeInner>>,
physics:crate::physics::PhysicsContext,
physics:crate::physics::PhysicsState,
}
impl Simulation{
pub const fn new(
timer:Timer<Scaled<SessionTimeInner,PhysicsTimeInner>>,
physics:crate::physics::PhysicsContext,
physics:crate::physics::PhysicsState,
)->Self{
Self{
timer,
@ -64,30 +82,75 @@ impl Simulation{
}
}
#[derive(Default)]
pub struct Recording{
instructions:Vec<TimedInstruction<PhysicsInputInstruction,PhysicsTimeInner>>,
}
impl Recording{
fn clear(&mut self){
self.instructions.clear();
}
}
pub struct Replay{
last_instruction_id:usize,
instructions:Vec<PhysicsInputInstruction>,
recording:Recording,
simulation:Simulation,
}
impl Replay{
pub const fn new(
instructions:Vec<PhysicsInputInstruction>,
recording:Recording,
simulation:Simulation,
)->Self{
Self{
last_instruction_id:0,
instructions,
recording,
simulation,
}
}
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.time<time{
PhysicsContext::run_input_instruction(&mut self.simulation.physics,physics_data,ins.clone());
self.last_instruction_id+=1;
}else{
break;
}
}else{
// loop playback
self.last_instruction_id=0;
// No need to reset physics because the very first instruction is 'Reset'
let new_time=self.recording.instructions.first().map_or(PhysicsTime::ZERO,|ins|ins.time);
self.simulation.timer.set_time(time_limit,new_time);
time=new_time;
}
}
}
}
#[derive(Clone,Copy,Hash,PartialEq,Eq)]
struct BotId(u32);
//#[derive(Clone,Copy,Hash,PartialEq,Eq)]
//struct PlayerId(u32);
enum ViewState{
Play,
//Spectate(PlayerId),
Replay(BotId),
}
pub struct Session{
user_settings:UserSettings,
mouse_interpolator:crate::mouse_interpolator::MouseInterpolator,
view_state:ViewState,
//gui:GuiState
geometry_shared:crate::physics::PhysicsData,
simulation:Simulation,
replays:Vec<Replay>,
// below fields not included in lite session
recording:Recording,
//players:HashMap<PlayerId,Simulation>,
replays:HashMap<BotId,Replay>,
}
impl Session{
pub fn new(
@ -97,15 +160,27 @@ impl Session{
Self{
user_settings,
mouse_interpolator:MouseInterpolator::new(),
geometry_shared:Default::default(),
simulation,
replays:Vec::new(),
view_state:ViewState::Play,
recording:Default::default(),
replays:HashMap::new(),
}
}
fn change_map(&mut self,map:&strafesnet_common::map::CompleteMap){
self.simulation.physics.generate_models(map);
fn clear_recording(&mut self){
self.recording.clear();
}
pub fn get_frame_state(&self,time:SessionTime)->FrameState{
self.simulation.get_frame_state(time)
fn change_map(&mut self,map:&strafesnet_common::map::CompleteMap){
self.simulation.physics.clear();
self.geometry_shared.generate_models(map);
}
pub fn get_frame_state(&self,time:SessionTime)->Option<FrameState>{
match &self.view_state{
ViewState::Play=>Some(self.simulation.get_frame_state(time)),
ViewState::Replay(bot_id)=>self.replays.get(bot_id).map(|replay|
replay.simulation.get_frame_state(time)
),
}
}
pub fn user_settings(&self)->&UserSettings{
&self.user_settings
@ -122,6 +197,7 @@ impl Session{
impl InstructionConsumer<Instruction<'_>> for Session{
type TimeInner=SessionTimeInner;
fn process_instruction(&mut self,ins:TimedInstruction<Instruction,Self::TimeInner>){
// repetitive procedure macro
macro_rules! run_mouse_interpolator_instruction{
($instruction:expr)=>{
self.mouse_interpolator.process_instruction(TimedInstruction{
@ -133,55 +209,157 @@ impl InstructionConsumer<Instruction<'_>> for Session{
});
};
}
// process any timeouts that occured since the last instruction
self.process_exhaustive(ins.time);
match ins.instruction{
// send it down to MouseInterpolator with two timestamps, SessionTime and PhysicsTime
Instruction::Input(SessionInputInstruction::Mouse(pos))=>{
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::MoveMouse(pos));
},
Instruction::Input(SessionInputInstruction::SetControl(set_control_instruction))=>{
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Other(OtherInstruction::SetControl(set_control_instruction)));
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::SetControl(set_control_instruction));
},
Instruction::Input(SessionInputInstruction::Mode(ImplicitModeInstruction::ResetAndRestart))=>{
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Other(OtherInstruction::Mode(ModeInstruction::Reset)));
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Other(OtherInstruction::Other(OtherOtherInstruction::SetSensitivity(self.user_settings().calculate_sensitivity()))));
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Other(OtherInstruction::Mode(ModeInstruction::Restart)));
self.clear_recording();
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Mode(ModeInstruction::Reset));
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Misc(MiscInstruction::SetSensitivity(self.user_settings().calculate_sensitivity())));
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Mode(ModeInstruction::Restart));
},
Instruction::Input(SessionInputInstruction::Mode(ImplicitModeInstruction::ResetAndSpawn(mode_id,spawn_id)))=>{
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Other(OtherInstruction::Mode(ModeInstruction::Reset)));
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Other(OtherInstruction::Other(OtherOtherInstruction::SetSensitivity(self.user_settings().calculate_sensitivity()))));
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Other(OtherInstruction::Mode(ModeInstruction::Spawn(mode_id,spawn_id))));
self.clear_recording();
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Mode(ModeInstruction::Reset));
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Misc(MiscInstruction::SetSensitivity(self.user_settings().calculate_sensitivity())));
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Mode(ModeInstruction::Spawn(mode_id,spawn_id)));
},
Instruction::Input(SessionInputInstruction::Other(other_other_instruction))=>{
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Other(OtherInstruction::Other(other_other_instruction)));
Instruction::Input(SessionInputInstruction::Misc(misc_instruction))=>{
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Misc(misc_instruction));
},
Instruction::SetPaused(paused)=>{
Instruction::Control(SessionControlInstruction::SetPaused(paused))=>{
// don't flush the buffered instructions in the mouse interpolator
// until the mouse is confirmed to be not moving at a later time
// what if they pause for 5ms lmao
_=self.simulation.timer.set_paused(ins.time,paused);
},
Instruction::Control(SessionControlInstruction::CopyRecordingIntoReplayAndSpectate)=>{
// Bind: B
// pause simulation
_=self.simulation.timer.set_paused(ins.time,true);
// create recording
let mut recording=Recording::default();
recording.instructions.extend(self.recording.instructions.iter().cloned());
// create timer starting at first instruction (or zero if the list is empty)
let new_time=recording.instructions.first().map_or(PhysicsTime::ZERO,|ins|ins.time);
let timer=Timer::unpaused(ins.time,new_time);
// create default physics state
let simulation=Simulation::new(timer,Default::default());
// invent a new bot id and insert the replay
let bot_id=BotId(self.replays.len() as u32);
self.replays.insert(bot_id,Replay::new(
recording,
simulation,
));
// begin spectate
self.view_state=ViewState::Replay(bot_id);
},
Instruction::Control(SessionControlInstruction::StopSpectate)=>{
let view_state=core::mem::replace(&mut self.view_state,ViewState::Play);
// delete the bot, otherwise it's inaccessible and wastes CPU
match view_state{
ViewState::Play=>(),
ViewState::Replay(bot_id)=>{
self.replays.remove(&bot_id);
},
}
_=self.simulation.timer.set_paused(ins.time,false);
},
Instruction::Playback(SessionPlaybackInstruction::IncreaseTimescale)=>{
match &self.view_state{
ViewState::Play=>{
// allow simulation timescale for fun
let scale=self.simulation.timer.get_scale();
self.simulation.timer.set_scale(ins.time,scale*5/4);
},
ViewState::Replay(bot_id)=>if let Some(replay)=self.replays.get_mut(bot_id){
let scale=replay.simulation.timer.get_scale();
replay.simulation.timer.set_scale(ins.time,scale*5/4);
},
}
},
Instruction::Playback(SessionPlaybackInstruction::DecreaseTimescale)=>{
match &self.view_state{
ViewState::Play=>{
// allow simulation timescale for fun
let scale=self.simulation.timer.get_scale();
self.simulation.timer.set_scale(ins.time,scale*4/5);
},
ViewState::Replay(bot_id)=>if let Some(replay)=self.replays.get_mut(bot_id){
let scale=replay.simulation.timer.get_scale();
replay.simulation.timer.set_scale(ins.time,scale*4/5);
},
}
},
Instruction::Playback(SessionPlaybackInstruction::SkipForward)=>{
match &self.view_state{
ViewState::Play=>(),
ViewState::Replay(bot_id)=>if let Some(replay)=self.replays.get_mut(bot_id){
let time=replay.simulation.timer.time(ins.time+SessionTime::from_secs(5));
replay.simulation.timer.set_time(ins.time,time);
},
}
},
Instruction::Playback(SessionPlaybackInstruction::SkipBack)=>{
match &self.view_state{
ViewState::Play=>(),
ViewState::Replay(bot_id)=>if let Some(replay)=self.replays.get_mut(bot_id){
let time=replay.simulation.timer.time(ins.time+SessionTime::from_secs(5));
replay.simulation.timer.set_time(ins.time,time);
// resimulate the entire playback lol
replay.last_instruction_id=0;
},
}
},
Instruction::Playback(SessionPlaybackInstruction::TogglePaused)=>{
match &self.view_state{
ViewState::Play=>(),
ViewState::Replay(bot_id)=>if let Some(replay)=self.replays.get_mut(bot_id){
_=replay.simulation.timer.set_paused(ins.time,!replay.simulation.timer.is_paused());
},
}
}
Instruction::ChangeMap(complete_map)=>{
self.clear_recording();
self.change_map(complete_map);
// ResetAndSpawn
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Other(OtherInstruction::Mode(ModeInstruction::Reset)));
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Other(OtherInstruction::Other(OtherOtherInstruction::SetSensitivity(self.user_settings().calculate_sensitivity()))));
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Other(OtherInstruction::Mode(ModeInstruction::Spawn(ModeId::MAIN,StageId::FIRST))));
},
Instruction::Idle=>{
run_mouse_interpolator_instruction!(MouseInterpolatorInstruction::Idle);
// 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(&self.geometry_shared,ins.time);
}
}
};
// run all buffered instruction produced
// process all emitted output instructions
self.process_exhaustive(ins.time);
}
}
impl InstructionConsumer<StepInstruction> for Session{
type TimeInner=SessionTimeInner;
fn process_instruction(&mut self,ins:TimedInstruction<StepInstruction,Self::TimeInner>){
// ins.time ignored???
let ins_retimed=TimedInstruction{
time:self.simulation.timer.time(ins.time),
instruction:ins.instruction,
};
if let Some(instruction)=self.mouse_interpolator.pop_buffered_instruction(ins_retimed){
self.simulation.physics.run_input_instruction(instruction);
let time=self.simulation.timer.time(ins.time);
if let Some(instruction)=self.mouse_interpolator.pop_buffered_instruction(ins.set_time(time)){
//record
self.recording.instructions.push(instruction.clone());
PhysicsContext::run_input_instruction(&mut self.simulation.physics,&self.geometry_shared,instruction);
}
}
}

@ -1,8 +1,8 @@
use strafesnet_common::instruction::TimedInstruction;
use strafesnet_common::session::{Time as SessionTime,TimeInner as SessionTimeInner};
use strafesnet_common::physics::{OtherInstruction,OtherOtherInstruction,SetControlInstruction};
use strafesnet_common::physics::{MiscInstruction,SetControlInstruction};
use crate::physics_worker::Instruction as PhysicsWorkerInstruction;
use crate::session::SessionInputInstruction;
use crate::session::{SessionInputInstruction,SessionControlInstruction,SessionPlaybackInstruction};
pub enum Instruction{
Resize(winit::dpi::PhysicalSize<u32>),
@ -37,7 +37,7 @@ impl WindowContext<'_>{
//pause unpause
self.physics_thread.send(TimedInstruction{
time,
instruction:PhysicsWorkerInstruction::SetPaused(!state),
instruction:PhysicsWorkerInstruction::SessionControl(SessionControlInstruction::SetPaused(!state)),
}).unwrap();
//recalculate pressed keys on focus
},
@ -92,29 +92,74 @@ impl WindowContext<'_>{
},
(keycode,state)=>{
let s=state.is_pressed();
if let Some(session_input_instruction)=match keycode{
winit::keyboard::Key::Named(winit::keyboard::NamedKey::Space)=>Some(SessionInputInstruction::SetControl(SetControlInstruction::SetJump(s))),
// internal variants for this scope
enum SessionInstructionSubset{
Input(SessionInputInstruction),
Control(SessionControlInstruction),
Playback(SessionPlaybackInstruction),
}
macro_rules! input_ctrl{
($variant:ident,$state:expr)=>{
Some(SessionInstructionSubset::Input(SessionInputInstruction::SetControl(SetControlInstruction::$variant($state))))
};
}
macro_rules! input_misc{
($variant:ident,$state:expr)=>{
s.then_some(SessionInstructionSubset::Input(SessionInputInstruction::Misc(MiscInstruction::$variant)))
};
}
macro_rules! session_ctrl{
($variant:ident,$state:expr)=>{
s.then_some(SessionInstructionSubset::Control(SessionControlInstruction::$variant))
};
}
macro_rules! session_playback{
($variant:ident,$state:expr)=>{
s.then_some(SessionInstructionSubset::Playback(SessionPlaybackInstruction::$variant))
};
}
impl From<SessionInstructionSubset> for PhysicsWorkerInstruction{
fn from(value:SessionInstructionSubset)->Self{
match value{
SessionInstructionSubset::Input(session_input_instruction)=>PhysicsWorkerInstruction::SessionInput(session_input_instruction),
SessionInstructionSubset::Control(session_control_instruction)=>PhysicsWorkerInstruction::SessionControl(session_control_instruction),
SessionInstructionSubset::Playback(session_playback_instruction)=>PhysicsWorkerInstruction::SessionPlayback(session_playback_instruction),
}
}
}
if let Some(session_instruction)=match keycode{
winit::keyboard::Key::Named(winit::keyboard::NamedKey::Space)=>input_ctrl!(SetJump,s),
// TODO: bind system so playback pausing can use spacebar
winit::keyboard::Key::Named(winit::keyboard::NamedKey::Enter)=>session_playback!(TogglePaused,s),
winit::keyboard::Key::Named(winit::keyboard::NamedKey::ArrowUp)=>session_playback!(IncreaseTimescale,s),
winit::keyboard::Key::Named(winit::keyboard::NamedKey::ArrowDown)=>session_playback!(DecreaseTimescale,s),
winit::keyboard::Key::Named(winit::keyboard::NamedKey::ArrowLeft)=>session_playback!(SkipBack,s),
winit::keyboard::Key::Named(winit::keyboard::NamedKey::ArrowRight)=>session_playback!(SkipForward,s),
winit::keyboard::Key::Character(key)=>match key.as_str(){
"W"|"w"=>Some(SessionInputInstruction::SetControl(SetControlInstruction::SetMoveForward(s))),
"A"|"a"=>Some(SessionInputInstruction::SetControl(SetControlInstruction::SetMoveLeft(s))),
"S"|"s"=>Some(SessionInputInstruction::SetControl(SetControlInstruction::SetMoveBack(s))),
"D"|"d"=>Some(SessionInputInstruction::SetControl(SetControlInstruction::SetMoveRight(s))),
"E"|"e"=>Some(SessionInputInstruction::SetControl(SetControlInstruction::SetMoveUp(s))),
"Q"|"q"=>Some(SessionInputInstruction::SetControl(SetControlInstruction::SetMoveDown(s))),
"Z"|"z"=>Some(SessionInputInstruction::SetControl(SetControlInstruction::SetZoom(s))),
"W"|"w"=>input_ctrl!(SetMoveForward,s),
"A"|"a"=>input_ctrl!(SetMoveLeft,s),
"S"|"s"=>input_ctrl!(SetMoveBack,s),
"D"|"d"=>input_ctrl!(SetMoveRight,s),
"E"|"e"=>input_ctrl!(SetMoveUp,s),
"Q"|"q"=>input_ctrl!(SetMoveDown,s),
"Z"|"z"=>input_ctrl!(SetZoom,s),
"R"|"r"=>s.then(||{
//mouse needs to be reset since the position is absolute
self.mouse_pos=glam::DVec2::ZERO;
SessionInputInstruction::Mode(crate::session::ImplicitModeInstruction::ResetAndRestart)
SessionInstructionSubset::Input(SessionInputInstruction::Mode(crate::session::ImplicitModeInstruction::ResetAndRestart))
}),
"F"|"f"=>s.then_some(SessionInputInstruction::Other(OtherOtherInstruction::PracticeFly)),
"F"|"f"=>input_misc!(PracticeFly,s),
"B"|"b"=>session_ctrl!(CopyRecordingIntoReplayAndSpectate,s),
"X"|"x"=>session_ctrl!(StopSpectate,s),
_=>None,
},
_=>None,
}{
self.physics_thread.send(TimedInstruction{
time,
instruction:PhysicsWorkerInstruction::Input(session_input_instruction),
instruction:session_instruction.into(),
}).unwrap();
}
},
@ -138,7 +183,7 @@ impl WindowContext<'_>{
self.mouse_pos+=glam::dvec2(delta.0,delta.1);
self.physics_thread.send(TimedInstruction{
time,
instruction:PhysicsWorkerInstruction::Input(SessionInputInstruction::Mouse(self.mouse_pos.as_ivec2())),
instruction:PhysicsWorkerInstruction::SessionInput(SessionInputInstruction::Mouse(self.mouse_pos.as_ivec2())),
}).unwrap();
},
winit::event::DeviceEvent::MouseWheel {
@ -148,7 +193,7 @@ impl WindowContext<'_>{
if false{//self.physics.style.use_scroll{
self.physics_thread.send(TimedInstruction{
time,
instruction:PhysicsWorkerInstruction::Input(SessionInputInstruction::SetControl(SetControlInstruction::SetJump(true))),//activates the immediate jump path, but the style modifier prevents controls&CONTROL_JUMP bit from being set to auto jump
instruction:PhysicsWorkerInstruction::SessionInput(SessionInputInstruction::SetControl(SetControlInstruction::SetJump(true))),//activates the immediate jump path, but the style modifier prevents controls&CONTROL_JUMP bit from being set to auto jump
}).unwrap();
}
},