Compare commits

..

No commits in common. "master" and "master" have entirely different histories.

7 changed files with 111 additions and 70 deletions

View File

@ -1,6 +1,2 @@
[registries.strafesnet] [registries.strafesnet]
index = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/" index = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
[target.x86_64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=/usr/bin/mold"]

View File

@ -6,32 +6,16 @@ pub struct TimedInstruction<I,T>{
pub instruction:I, pub instruction:I,
} }
/// Ensure all emitted instructions are processed before consuming external instructions pub trait InstructionEmitter{
pub trait InstructionEmitter<I>{ type Instruction;
type TimeInner; type TimeInner;
fn next_instruction(&self,time_limit:Time<Self::TimeInner>)->Option<TimedInstruction<I,Self::TimeInner>>; fn next_instruction(&self,time_limit:Time<Self::TimeInner>)->Option<TimedInstruction<Self::Instruction,Self::TimeInner>>;
} }
/// Apply an atomic state update pub trait InstructionConsumer{
pub trait InstructionConsumer<I>{ type Instruction;
type TimeInner; type TimeInner;
fn process_instruction(&mut self,instruction:TimedInstruction<I,Self::TimeInner>); fn process_instruction(&mut self, instruction:TimedInstruction<Self::Instruction,Self::TimeInner>);
} }
/// If the object produces its own instructions, allow exhaustively feeding them back in
pub trait InstructionFeedback<I,T>:InstructionEmitter<I,TimeInner=T>+InstructionConsumer<I,TimeInner=T>
where
Time<T>:Copy,
{
fn process_exhaustive(&mut self,time_limit:Time<T>){
while let Some(instruction)=self.next_instruction(time_limit){
self.process_instruction(instruction);
}
}
}
impl<I,T,X> InstructionFeedback<I,T> for X
where
Time<T>:Copy,
X:InstructionEmitter<I,TimeInner=T>+InstructionConsumer<I,TimeInner=T>,
{}
//PROPER PRIVATE FIELDS!!! //PROPER PRIVATE FIELDS!!!
pub struct InstructionCollector<I,T>{ pub struct InstructionCollector<I,T>{

View File

@ -1,6 +1,6 @@
use bnum::{BInt,cast::As}; use bnum::{BInt,cast::As};
#[derive(Clone,Copy,Debug,Default,Hash,PartialEq,PartialOrd,Ord)] #[derive(Clone,Copy,Debug,Default,Hash)]
/// A Fixed point number for which multiply operations widen the bits in the output. (when the wide-mul feature is enabled) /// 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 /// N is the number of u64s to use
/// F is the number of fractional bits (always N*32 lol) /// F is the number of fractional bits (always N*32 lol)
@ -87,6 +87,12 @@ impl_from!(
i8,i16,i32,i64,i128,isize i8,i16,i32,i64,i128,isize
); );
impl<const N:usize,const F:usize> PartialEq for Fixed<N,F>{
#[inline]
fn eq(&self,other:&Self)->bool{
self.bits.eq(&other.bits)
}
}
impl<const N:usize,const F:usize,T> PartialEq<T> for Fixed<N,F> impl<const N:usize,const F:usize,T> PartialEq<T> for Fixed<N,F>
where where
T:Copy, T:Copy,
@ -99,6 +105,12 @@ where
} }
impl<const N:usize,const F:usize> Eq for Fixed<N,F>{} impl<const N:usize,const F:usize> Eq for Fixed<N,F>{}
impl<const N:usize,const F:usize> PartialOrd for Fixed<N,F>{
#[inline]
fn partial_cmp(&self,other:&Self)->Option<std::cmp::Ordering>{
self.bits.partial_cmp(&other.bits)
}
}
impl<const N:usize,const F:usize,T> PartialOrd<T> for Fixed<N,F> impl<const N:usize,const F:usize,T> PartialOrd<T> for Fixed<N,F>
where where
T:Copy, T:Copy,
@ -109,6 +121,12 @@ impl<const N:usize,const F:usize,T> PartialOrd<T> for Fixed<N,F>
self.bits.partial_cmp(&other.into()) self.bits.partial_cmp(&other.into())
} }
} }
impl<const N:usize,const F:usize> Ord for Fixed<N,F>{
#[inline]
fn cmp(&self,other:&Self)->std::cmp::Ordering{
self.bits.cmp(&other.bits)
}
}
impl<const N:usize,const F:usize> std::ops::Neg for Fixed<N,F>{ impl<const N:usize,const F:usize> std::ops::Neg for Fixed<N,F>{
type Output=Self; type Output=Self;

View File

@ -33,7 +33,7 @@ pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
// LMAO look at this function! // LMAO look at this function!
pub fn dom_mut<T>(lua:&mlua::Lua,mut f:impl FnMut(&mut WeakDom)->mlua::Result<T>)->mlua::Result<T>{ pub fn dom_mut<T>(lua:&mlua::Lua,mut f:impl FnMut(&mut WeakDom)->mlua::Result<T>)->mlua::Result<T>{
let mut dom=lua.app_data_mut::<&'static mut WeakDom>().ok_or_else(||mlua::Error::runtime("DataModel missing"))?; let mut dom=lua.app_data_mut::<&'static mut WeakDom>().ok_or_else(||mlua::Error::runtime("DataModel missing"))?;
f(*dom) f(&mut *dom)
} }
fn coerce_float32(value:&mlua::Value)->Option<f32>{ fn coerce_float32(value:&mlua::Value)->Option<f32>{

View File

@ -21,6 +21,7 @@ pub fn new<'a>(
device:wgpu::Device, device:wgpu::Device,
queue:wgpu::Queue, queue:wgpu::Queue,
)->crate::compat_worker::INWorker<'a,Instruction>{ )->crate::compat_worker::INWorker<'a,Instruction>{
let mut resize=None;
crate::compat_worker::INWorker::new(move |ins:Instruction|{ crate::compat_worker::INWorker::new(move |ins:Instruction|{
match ins{ match ins{
Instruction::ChangeMap(map)=>{ Instruction::ChangeMap(map)=>{
@ -28,15 +29,18 @@ pub fn new<'a>(
graphics.generate_models(&device,&queue,&map); graphics.generate_models(&device,&queue,&map);
}, },
Instruction::Resize(size,user_settings)=>{ Instruction::Resize(size,user_settings)=>{
println!("Resizing to {:?}",size); resize=Some((size,user_settings));
let t0=std::time::Instant::now();
config.width=size.width.max(1);
config.height=size.height.max(1);
surface.configure(&device,&config);
graphics.resize(&device,&config,&user_settings);
println!("Resize took {:?}",t0.elapsed());
} }
Instruction::Render(frame_state)=>{ Instruction::Render(frame_state)=>{
if let Some((size,user_settings))=resize.take(){
println!("Resizing to {:?}",size);
let t0=std::time::Instant::now();
config.width=size.width.max(1);
config.height=size.height.max(1);
surface.configure(&device,&config);
graphics.resize(&device,&config,&user_settings);
println!("Resize took {:?}",t0.elapsed());
}
//this has to go deeper somehow //this has to go deeper somehow
let frame=match surface.get_current_texture(){ let frame=match surface.get_current_texture(){
Ok(frame)=>frame, Ok(frame)=>frame,

View File

@ -9,7 +9,7 @@ use strafesnet_common::gameplay_attributes::{self,CollisionAttributesId};
use strafesnet_common::gameplay_modes::{self,StageId}; use strafesnet_common::gameplay_modes::{self,StageId};
use strafesnet_common::gameplay_style::{self,StyleModifiers}; use strafesnet_common::gameplay_style::{self,StyleModifiers};
use strafesnet_common::controls_bitflag::Controls; use strafesnet_common::controls_bitflag::Controls;
use strafesnet_common::instruction::{self,InstructionEmitter,InstructionConsumer,InstructionFeedback,TimedInstruction}; use strafesnet_common::instruction::{self,InstructionEmitter,InstructionConsumer,TimedInstruction};
use strafesnet_common::integer::{self,vec3,mat3,Planar64,Planar64Vec3,Planar64Mat3,Angle32,Ratio64Vec2}; use strafesnet_common::integer::{self,vec3,mat3,Planar64,Planar64Vec3,Planar64Mat3,Angle32,Ratio64Vec2};
pub use strafesnet_common::physics::{Time,TimeInner}; pub use strafesnet_common::physics::{Time,TimeInner};
use gameplay::ModeState; use gameplay::ModeState;
@ -773,7 +773,10 @@ impl TouchingState{
normal:n, normal:n,
} }
}).collect(); }).collect();
*velocity=crate::push_solve::push_solve(&contacts,*velocity); match crate::push_solve::push_solve(&contacts,*velocity){
Some(new_velocity)=>*velocity=new_velocity,
None=>println!("Algorithm silently failing :)"),
}
} }
fn constrain_acceleration(&self,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,acceleration:&mut Planar64Vec3){ fn constrain_acceleration(&self,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,acceleration:&mut Planar64Vec3){
let contacts=self.contacts.iter().map(|contact|{ let contacts=self.contacts.iter().map(|contact|{
@ -784,7 +787,10 @@ impl TouchingState{
normal:n, normal:n,
} }
}).collect(); }).collect();
*acceleration=crate::push_solve::push_solve(&contacts,*acceleration); match crate::push_solve::push_solve(&contacts,*acceleration){
Some(new_acceleration)=>*acceleration=new_acceleration,
None=>println!("Algorithm silently failing :)"),
}
} }
fn predict_collision_end(&self,collector:&mut instruction::InstructionCollector<PhysicsInternalInstruction,TimeInner>,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,body:&Body,time:Time){ fn predict_collision_end(&self,collector:&mut instruction::InstructionCollector<PhysicsInternalInstruction,TimeInner>,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,body:&Body,time:Time){
let relative_body=crate::body::VirtualBody::relative(&Body::ZERO,body).body(time); let relative_body=crate::body::VirtualBody::relative(&Body::ZERO,body).body(time);
@ -935,21 +941,16 @@ pub struct PhysicsContext{
state:PhysicsState,//this captures the entire state of the physics. 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. data:PhysicsData,//data currently loaded into memory which is needded for physics to run, but is not part of the state.
} }
// the physics consumes both PhysicsInputInstruction and PhysicsInternalInstruction, //the physics consumes the generic PhysicsInstruction, but can only emit the more narrow PhysicsInternalInstruction
// but can only emit PhysicsInternalInstruction impl instruction::InstructionConsumer for PhysicsContext{
impl InstructionConsumer<PhysicsInternalInstruction> for PhysicsContext{ type Instruction=PhysicsInstruction;
type TimeInner=TimeInner; type TimeInner=TimeInner;
fn process_instruction(&mut self,ins:TimedInstruction<PhysicsInternalInstruction,TimeInner>){ fn process_instruction(&mut self,ins:TimedInstruction<PhysicsInstruction,TimeInner>){
atomic_internal_instruction(&mut self.state,&self.data,ins) atomic_state_update(&mut self.state,&self.data,ins)
} }
} }
impl InstructionConsumer<PhysicsInputInstruction> for PhysicsContext{ impl instruction::InstructionEmitter for PhysicsContext{
type TimeInner=TimeInner; type Instruction=PhysicsInternalInstruction;
fn process_instruction(&mut self,ins:TimedInstruction<PhysicsInputInstruction,TimeInner>){
atomic_input_instruction(&mut self.state,&self.data,ins)
}
}
impl InstructionEmitter<PhysicsInternalInstruction> 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<PhysicsInternalInstruction,TimeInner>>{ fn next_instruction(&self,time_limit:Time)->Option<TimedInstruction<PhysicsInternalInstruction,TimeInner>>{
@ -1109,9 +1110,24 @@ impl PhysicsContext{
println!("Physics Objects: {}",model_count); println!("Physics Objects: {}",model_count);
} }
//tickless gaming
fn run_internal_exhaustive(&mut self,time_limit:Time){
//prepare is ommitted - everything is done via instructions.
while let Some(instruction)=self.next_instruction(time_limit){//collect
//process
self.process_instruction(TimedInstruction{
time:instruction.time,
instruction:PhysicsInstruction::Internal(instruction.instruction),
});
//write hash lol
}
}
pub fn run_input_instruction(&mut self,instruction:TimedInstruction<PhysicsInputInstruction,TimeInner>){ pub fn run_input_instruction(&mut self,instruction:TimedInstruction<PhysicsInputInstruction,TimeInner>){
self.process_exhaustive(instruction.time); self.run_internal_exhaustive(instruction.time);
self.process_instruction(instruction); self.process_instruction(TimedInstruction{
time:instruction.time,
instruction:PhysicsInstruction::Input(instruction.instruction),
});
} }
} }
@ -1138,7 +1154,6 @@ impl PhysicsContext{
collector.collect(minkowski.predict_collision_in(relative_body,collector.time()) collector.collect(minkowski.predict_collision_in(relative_body,collector.time())
//temp (?) code to avoid collision loops //temp (?) code to avoid collision loops
.map_or(None,|(face,dt)|{ .map_or(None,|(face,dt)|{
// this must be rounded to avoid the infinite loop when hitting the start zone
let time=relative_body.time+dt.into(); let time=relative_body.time+dt.into();
if time<=state.time{None}else{Some((time,face,dt))}}) if time<=state.time{None}else{Some((time,face,dt))}})
.map(|(time,face,dt)| .map(|(time,face,dt)|
@ -1872,6 +1887,26 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
} }
} }
fn atomic_state_update(state:&mut PhysicsState,data:&PhysicsData,ins:TimedInstruction<PhysicsInstruction,TimeInner>){
match &ins.instruction{
PhysicsInstruction::Input(PhysicsInputInstruction::Idle)
|PhysicsInstruction::Input(PhysicsInputInstruction::SetNextMouse(_))
|PhysicsInstruction::Input(PhysicsInputInstruction::ReplaceMouse(_,_))
|PhysicsInstruction::Internal(PhysicsInternalInstruction::StrafeTick)
|PhysicsInstruction::Internal(PhysicsInternalInstruction::ReachWalkTargetVelocity)=>(),
_=>println!("{}|{:?}",ins.time,ins.instruction),
}
if ins.time<state.time{
println!("@@@@ Time travel warning! state.time={} ins.time={}\nInstruction={:?}",state.time,ins.time,ins.instruction);
}
//idle is special, it is specifically a no-op to get Internal events to catch up to real time
match ins.instruction{
PhysicsInstruction::Input(PhysicsInputInstruction::Idle)=>(),
PhysicsInstruction::Internal(instruction)=>atomic_internal_instruction(state,data,TimedInstruction{time:ins.time,instruction}),
PhysicsInstruction::Input(instruction)=>atomic_input_instruction(state,data,TimedInstruction{time:ins.time,instruction}),
}
}
#[cfg(test)] #[cfg(test)]
mod test{ mod test{
use strafesnet_common::integer::{vec3::{self,int as int3},mat3}; use strafesnet_common::integer::{vec3::{self,int as int3},mat3};

View File

@ -164,8 +164,8 @@ fn is_space_enclosed_4(
||is_space_enclosed_3(b,c,d) ||is_space_enclosed_3(b,c,d)
} }
const fn get_push_ray_0(point:Planar64Vec3)->Ray{ const fn get_push_ray_0(point:Planar64Vec3)->Option<Ray>{
Ray{origin:point,direction:vec3::ZERO} Some(Ray{origin:point,direction:vec3::ZERO})
} }
fn get_push_ray_1(point:Planar64Vec3,c0:&Contact)->Option<Ray>{ fn get_push_ray_1(point:Planar64Vec3,c0:&Contact)->Option<Ray>{
let direction=solve1(c0)?.divide().fix_1(); let direction=solve1(c0)?.divide().fix_1();
@ -204,8 +204,11 @@ fn get_push_ray_3(point:Planar64Vec3,c0:&Contact,c1:&Contact,c2:&Contact)->Optio
Some(Ray{origin,direction}) Some(Ray{origin,direction})
} }
const fn get_best_push_ray_and_conts_0<'a>(point:Planar64Vec3)->(Ray,Conts<'a>){ const fn get_best_push_ray_and_conts_0<'a>(point:Planar64Vec3)->Option<(Ray,Conts<'a>)>{
(get_push_ray_0(point),Conts::new_const()) match get_push_ray_0(point){
Some(ray)=>Some((ray,Conts::new_const())),
None=>None,
}
} }
fn get_best_push_ray_and_conts_1(point:Planar64Vec3,c0:&Contact)->Option<(Ray,Conts)>{ fn get_best_push_ray_and_conts_1(point:Planar64Vec3,c0:&Contact)->Option<(Ray,Conts)>{
get_push_ray_1(point,c0) get_push_ray_1(point,c0)
@ -277,14 +280,14 @@ fn get_best_push_ray_and_conts_4<'a>(point:Planar64Vec3,c0:&'a Contact,c1:&'a Co
fn get_best_push_ray_and_conts<'a>( fn get_best_push_ray_and_conts<'a>(
point:Planar64Vec3, point:Planar64Vec3,
conts:&[&'a Contact], conts:Conts<'a>,
)->Option<(Ray,Conts<'a>)>{ )->Option<(Ray,Conts<'a>)>{
match conts{ match conts.as_slice(){
&[c0,c1,c2,c3]=>get_best_push_ray_and_conts_4(point,c0,c1,c2,c3), &[c0,c1,c2,c3]=>get_best_push_ray_and_conts_4(point,c0,c1,c2,c3),
&[c0,c1,c2]=>get_best_push_ray_and_conts_3(point,c0,c1,c2), &[c0,c1,c2]=>get_best_push_ray_and_conts_3(point,c0,c1,c2),
&[c0,c1]=>get_best_push_ray_and_conts_2(point,c0,c1), &[c0,c1]=>get_best_push_ray_and_conts_2(point,c0,c1),
&[c0]=>get_best_push_ray_and_conts_1(point,c0), &[c0]=>get_best_push_ray_and_conts_1(point,c0),
&[]=>Some(get_best_push_ray_and_conts_0(point)), &[]=>get_best_push_ray_and_conts_0(point),
_=>unreachable!(), _=>unreachable!(),
} }
} }
@ -299,16 +302,17 @@ fn get_first_touch<'a>(contacts:&'a Vec<Contact>,ray:&Ray,conts:&Conts)->Option<
.min_by_key(|&(t,_)|t) .min_by_key(|&(t,_)|t)
} }
pub fn push_solve(contacts:&Vec<Contact>,point:Planar64Vec3)->Planar64Vec3{ pub fn push_solve(contacts:&Vec<Contact>,point:Planar64Vec3)->Option<Planar64Vec3>{
let (mut ray,mut conts)=get_best_push_ray_and_conts_0(point); const ZERO:Ratio<Fixed<1,32>,Fixed<1,32>>=Ratio::new(Fixed::ZERO,Fixed::EPSILON);
let (mut ray,mut conts)=get_best_push_ray_and_conts_0(point)?;
loop{ loop{
let (next_t,next_cont)=match get_first_touch(contacts,&ray,&conts){ let (next_t,next_cont)=match get_first_touch(contacts,&ray,&conts){
Some((t,cont))=>(t,cont), Some((t,conts))=>(t,conts),
None=>return ray.origin, None=>return Some(ray.origin),
}; };
if RATIO_ZERO.le_ratio(next_t){ if ZERO.le_ratio(next_t){
return ray.origin; return Some(ray.origin);
} }
//push_front //push_front
@ -322,9 +326,9 @@ pub fn push_solve(contacts:&Vec<Contact>,point:Planar64Vec3)->Planar64Vec3{
} }
let meet_point=ray.extrapolate(next_t); let meet_point=ray.extrapolate(next_t);
match get_best_push_ray_and_conts(meet_point,conts.as_slice()){ match get_best_push_ray_and_conts(meet_point,conts){
Some((new_ray,new_conts))=>(ray,conts)=(new_ray,new_conts), Some((new_ray,new_conts))=>(ray,conts)=(new_ray,new_conts),
None=>return meet_point, None=>return Some(meet_point),
} }
} }
} }
@ -342,7 +346,7 @@ mod tests{
} }
]; ];
assert_eq!( assert_eq!(
vec3::ZERO, Some(vec3::ZERO),
push_solve(&contacts,vec3::NEG_Y) push_solve(&contacts,vec3::NEG_Y)
); );
} }