Compare commits

...

47 Commits

Author SHA1 Message Date
a67aa71fb0 update rbx_loader 2024-10-04 19:42:25 -07:00
7cc0fd59c8 simplify double map into single map 2024-10-04 12:47:52 -07:00
bded9fccdf update rbx_loader 2024-10-03 20:33:10 -07:00
4cd0114567 v0.10.5 update roblox + source loaders 2024-10-01 17:11:23 -07:00
991e01d530 update deps 2024-10-01 17:11:23 -07:00
e2bd9ba692 maybe multiply smaller den faster (this operation sucks) 2024-09-30 21:09:45 -07:00
383df8637f update deps 2024-09-30 17:05:27 -07:00
1a6831cf8b fixed wide vectors 2024-09-30 10:36:37 -07:00
ccae1a45e1 up the date 2024-09-21 15:41:02 -07:00
1e299b7b9c update rbx_loader 2024-09-21 12:42:29 -07:00
f3d4d8dbda v0.10.4 roblox scripts 2024-09-20 11:50:44 -07:00
b2a84e3be1 update common 2024-08-21 14:20:09 -07:00
0468484788 make physics-graphics communication a bit less insane 2024-08-21 14:20:09 -07:00
b9dc97053f graphics: spaces 2024-08-21 14:20:09 -07:00
40ed173b60 physics: unused field 2024-08-21 14:20:09 -07:00
fd5a813357 graphics: bundle FrameState into struct 2024-08-21 14:20:09 -07:00
50726199b9 todo 2024-08-21 14:20:09 -07:00
0676007430 graphics: drop model_buf after upload 2024-08-21 14:20:09 -07:00
97c49c9351 graphics: unused struct 2024-08-21 14:20:09 -07:00
10689784be graphics_worker: untab 2024-08-21 14:20:09 -07:00
2eff5dda9e graphics_worker: tweaks (rust master) 2024-08-21 14:20:09 -07:00
93b04f4f1f physics: recalculate touching parts in set_position 2024-08-21 14:20:09 -07:00
c616e82c47 use const 2024-08-19 17:04:53 -07:00
d3f84c2dbd physics: refactor models and attributes with type safety
make invalid states unrepresentable!!!
2024-08-09 14:47:04 -07:00
5e45753756 update deps 2024-08-09 14:46:54 -07:00
cfee6f119f pull out collision handlers into functions 2024-08-08 15:54:23 -07:00
b9e34f53c3 do not set time on idle 2024-08-08 13:18:28 -07:00
394f1f1dc2 v0.10.3 physics updates + pause game + fix boosters 2024-08-07 19:48:09 -07:00
05ec7ea5d8 physics: rework jumping and boosters 2024-08-07 18:48:57 -07:00
7996df532e remove a source of non-determinism 2024-08-06 14:15:57 -07:00
b4b85b7da4 refactor physics_worker 2024-08-06 11:26:27 -07:00
3a98eaff7c move physics instruction to common 2024-08-06 11:10:43 -07:00
4b5b7dc2fb update deps 2024-08-06 11:02:49 -07:00
8a13640c55 time travel warning 2024-08-02 10:42:10 -07:00
85ba12ff92 pause on focus 2024-08-02 10:42:10 -07:00
4f492d73b0 update deps 2024-08-02 10:42:10 -07:00
a8d54fdd7c minor tweak to loading map from arg 2024-08-02 09:20:34 -07:00
34ac541260 ignore ReachWalkTargetVelocity 2024-08-02 08:03:16 -07:00
099788b746 this is wrong, even when velocity is zero 2024-08-02 08:03:16 -07:00
305017c6c8 iso shortcut 2024-08-02 08:03:16 -07:00
e47d152af2 make a distinction between restart and spawning 2024-08-02 08:03:16 -07:00
755adeaefd refactor physics instruction processing
This is an important engine upgrade: idle events do not donate their timestamp to engine objects and pollute the timeline with unnecessary game ticks that can be represented as analytic continuations of previous game ticks.  This means that all "render" tick updates can be dropped from bot timelines.  In other words, progressing the physics simulation is invariant to differing subdivisions of an overall time advancement with no external input.
2024-08-02 08:03:16 -07:00
e04e754abb v0.10.2 run timer 2024-08-01 09:39:16 -07:00
5c1f69628d update deps 2024-08-01 09:36:11 -07:00
44031c8b83 simple run timer 2024-08-01 09:29:13 -07:00
d6470ee81b denormalize zone data on load 2024-08-01 09:29:09 -07:00
a7c7088c1f model is supposed to be guaranteed to exist 2024-07-31 12:08:57 -07:00
13 changed files with 1951 additions and 1126 deletions

759
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
[package] [package]
name = "strafe-client" name = "strafe-client"
version = "0.10.1" version = "0.10.5"
edition = "2021" edition = "2021"
repository = "https://git.itzana.me/StrafesNET/strafe-client" repository = "https://git.itzana.me/StrafesNET/strafe-client"
license = "Custom" license = "Custom"
@ -18,17 +18,17 @@ roblox = ["dep:strafesnet_deferred_loader", "dep:strafesnet_rbx_loader"]
bytemuck = { version = "1.13.1", features = ["derive"] } bytemuck = { version = "1.13.1", features = ["derive"] }
configparser = "3.0.2" configparser = "3.0.2"
ddsfile = "0.5.1" ddsfile = "0.5.1"
glam = "0.28.0" glam = "0.29.0"
id = { version = "0.1.0", registry = "strafesnet" } id = { version = "0.1.0", registry = "strafesnet" }
parking_lot = "0.12.1" parking_lot = "0.12.1"
pollster = "0.3.0" pollster = "0.3.0"
strafesnet_bsp_loader = { version = "0.1.3", registry = "strafesnet", optional = true } strafesnet_bsp_loader = { version = "0.2.1", registry = "strafesnet", optional = true }
strafesnet_common = { version = "0.2.0", registry = "strafesnet" } strafesnet_common = { version = "0.5.2", registry = "strafesnet" }
strafesnet_deferred_loader = { version = "0.3.1", features = ["legacy"], registry = "strafesnet", optional = true } strafesnet_deferred_loader = { version = "0.4.0", features = ["legacy"], registry = "strafesnet", optional = true }
strafesnet_rbx_loader = { version = "0.3.2", registry = "strafesnet", optional = true } strafesnet_rbx_loader = { version = "0.5.1", registry = "strafesnet", optional = true }
strafesnet_snf = { version = "0.1.0", registry = "strafesnet", optional = true } strafesnet_snf = { version = "0.2.0", registry = "strafesnet", optional = true }
wgpu = "22.0.0" wgpu = "22.1.0"
winit = "0.30.4" winit = "0.30.5"
[profile.release] [profile.release]
#lto = true #lto = true

View File

@ -1,32 +1,33 @@
use crate::physics::Body; use crate::physics::Body;
use crate::model_physics::{FEV,MeshQuery,DirectedEdge}; use crate::model_physics::{GigaTime,FEV,MeshQuery,DirectedEdge,MinkowskiMesh,MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert};
use strafesnet_common::integer::{Time,Planar64}; use strafesnet_common::integer::{Time,Fixed,Ratio};
use strafesnet_common::zeroes::zeroes2;
#[derive(Debug)]
enum Transition<F,E:DirectedEdge,V>{ enum Transition<F,E:DirectedEdge,V>{
Miss, Miss,
Next(FEV<F,E,V>,Time), Next(FEV<F,E,V>,GigaTime),
Hit(F,Time), Hit(F,GigaTime),
} }
fn next_transition<F:Copy,E:Copy+DirectedEdge,V:Copy>(fev:&FEV<F,E,V>,time:Time,mesh:&impl MeshQuery<F,E,V>,body:&Body,time_limit:Time)->Transition<F,E,V>{ type MinkowskiFEV=FEV<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert>;
type MinkowskiTransition=Transition<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert>;
fn next_transition(fev:&MinkowskiFEV,body_time:GigaTime,mesh:&MinkowskiMesh,body:&Body,mut best_time:GigaTime)->MinkowskiTransition{
//conflicting derivative means it crosses in the wrong direction. //conflicting derivative means it crosses in the wrong direction.
//if the transition time is equal to an already tested transition, do not replace the current best. //if the transition time is equal to an already tested transition, do not replace the current best.
let mut best_time=time_limit; let mut best_transition=MinkowskiTransition::Miss;
let mut best_transtition=Transition::Miss;
match fev{ match fev{
&FEV::<F,E,V>::Face(face_id)=>{ &MinkowskiFEV::Face(face_id)=>{
//test own face collision time, ignoring roots with zero or conflicting derivative //test own face collision time, ignoring roots with zero or conflicting derivative
//n=face.normal d=face.dot //n=face.normal d=face.dot
//n.a t^2+n.v t+n.p-d==0 //n.a t^2+n.v t+n.p-d==0
let (n,d)=mesh.face_nd(face_id); let (n,d)=mesh.face_nd(face_id);
//TODO: use higher precision d value? //TODO: use higher precision d value?
//use the mesh transform translation instead of baking it into the d value. //use the mesh transform translation instead of baking it into the d value.
for t in zeroes2((n.dot(body.position)-d)*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){ for dt in Fixed::<4,128>::zeroes2((n.dot(body.position)-d)*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
let t=body.time+Time::from(t); if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
if time<=t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{ best_time=dt;
best_time=t; best_transition=MinkowskiTransition::Hit(face_id,dt);
best_transtition=Transition::Hit(face_id,t);
break; break;
} }
} }
@ -36,18 +37,18 @@ enum Transition<F,E:DirectedEdge,V>{
let n=n.cross(edge_n); let n=n.cross(edge_n);
let verts=mesh.edge_verts(directed_edge_id.as_undirected()); let verts=mesh.edge_verts(directed_edge_id.as_undirected());
//WARNING: d is moved out of the *2 block because of adding two vertices! //WARNING: d is moved out of the *2 block because of adding two vertices!
for t in zeroes2(n.dot(body.position*2-(mesh.vert(verts[0])+mesh.vert(verts[1]))),n.dot(body.velocity)*2,n.dot(body.acceleration)){ //WARNING: precision is swept under the rug!
let t=body.time+Time::from(t); for dt in Fixed::<4,128>::zeroes2(n.dot(body.position*2-(mesh.vert(verts[0])+mesh.vert(verts[1]))).fix_4(),n.dot(body.velocity).fix_4()*2,n.dot(body.acceleration).fix_4()){
if time<=t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{ if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
best_time=t; best_time=dt;
best_transtition=Transition::Next(FEV::<F,E,V>::Edge(directed_edge_id.as_undirected()),t); best_transition=MinkowskiTransition::Next(MinkowskiFEV::Edge(directed_edge_id.as_undirected()),dt);
break; break;
} }
} }
} }
//if none: //if none:
}, },
&FEV::<F,E,V>::Edge(edge_id)=>{ &MinkowskiFEV::Edge(edge_id)=>{
//test each face collision time, ignoring roots with zero or conflicting derivative //test each face collision time, ignoring roots with zero or conflicting derivative
let edge_n=mesh.edge_n(edge_id); let edge_n=mesh.edge_n(edge_id);
let edge_verts=mesh.edge_verts(edge_id); let edge_verts=mesh.edge_verts(edge_id);
@ -57,11 +58,10 @@ enum Transition<F,E:DirectedEdge,V>{
//edge_n gets parity from the order of edge_faces //edge_n gets parity from the order of edge_faces
let n=face_n.cross(edge_n)*((i as i64)*2-1); let n=face_n.cross(edge_n)*((i as i64)*2-1);
//WARNING yada yada d *2 //WARNING yada yada d *2
for t in zeroes2(n.dot(delta_pos),n.dot(body.velocity)*2,n.dot(body.acceleration)){ for dt in Fixed::<4,128>::zeroes2(n.dot(delta_pos).fix_4(),n.dot(body.velocity).fix_4()*2,n.dot(body.acceleration).fix_4()){
let t=body.time+Time::from(t); if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
if time<=t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{ best_time=dt;
best_time=t; best_transition=MinkowskiTransition::Next(MinkowskiFEV::Face(edge_face_id),dt);
best_transtition=Transition::Next(FEV::<F,E,V>::Face(edge_face_id),t);
break; break;
} }
} }
@ -70,27 +70,27 @@ enum Transition<F,E:DirectedEdge,V>{
for (i,&vert_id) in edge_verts.iter().enumerate(){ for (i,&vert_id) in edge_verts.iter().enumerate(){
//vertex normal gets parity from vert index //vertex normal gets parity from vert index
let n=edge_n*(1-2*(i as i64)); let n=edge_n*(1-2*(i as i64));
for t in zeroes2((n.dot(body.position-mesh.vert(vert_id)))*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){ for dt in Fixed::<2,64>::zeroes2((n.dot(body.position-mesh.vert(vert_id)))*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
let t=body.time+Time::from(t); if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
if time<=t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{ let dt=Ratio::new(dt.num.fix_4(),dt.den.fix_4());
best_time=t; best_time=dt;
best_transtition=Transition::Next(FEV::<F,E,V>::Vert(vert_id),t); best_transition=MinkowskiTransition::Next(MinkowskiFEV::Vert(vert_id),dt);
break; break;
} }
} }
} }
//if none: //if none:
}, },
&FEV::<F,E,V>::Vert(vert_id)=>{ &MinkowskiFEV::Vert(vert_id)=>{
//test each edge collision time, ignoring roots with zero or conflicting derivative //test each edge collision time, ignoring roots with zero or conflicting derivative
for &directed_edge_id in mesh.vert_edges(vert_id).iter(){ for &directed_edge_id in mesh.vert_edges(vert_id).iter(){
//edge is directed away from vertex, but we want the dot product to turn out negative //edge is directed away from vertex, but we want the dot product to turn out negative
let n=-mesh.directed_edge_n(directed_edge_id); let n=-mesh.directed_edge_n(directed_edge_id);
for t in zeroes2((n.dot(body.position-mesh.vert(vert_id)))*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){ for dt in Fixed::<2,64>::zeroes2((n.dot(body.position-mesh.vert(vert_id)))*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
let t=body.time+Time::from(t); if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
if time<=t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{ let dt=Ratio::new(dt.num.fix_4(),dt.den.fix_4());
best_time=t; best_time=dt;
best_transtition=Transition::Next(FEV::<F,E,V>::Edge(directed_edge_id.as_undirected()),t); best_transition=MinkowskiTransition::Next(MinkowskiFEV::Edge(directed_edge_id.as_undirected()),dt);
break; break;
} }
} }
@ -98,18 +98,26 @@ enum Transition<F,E:DirectedEdge,V>{
//if none: //if none:
}, },
} }
best_transtition best_transition
} }
pub enum CrawlResult<F,E:DirectedEdge,V>{ pub enum CrawlResult<F,E:DirectedEdge,V>{
Miss(FEV<F,E,V>), Miss(FEV<F,E,V>),
Hit(F,Time), Hit(F,GigaTime),
} }
pub fn crawl_fev<F:Copy,E:Copy+DirectedEdge,V:Copy>(mut fev:FEV<F,E,V>,mesh:&impl MeshQuery<F,E,V>,relative_body:&Body,start_time:Time,time_limit:Time)->CrawlResult<F,E,V>{ type MinkowskiCrawlResult=CrawlResult<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert>;
let mut time=start_time; pub fn crawl_fev(mut fev:MinkowskiFEV,mesh:&MinkowskiMesh,relative_body:&Body,start_time:Time,time_limit:Time)->MinkowskiCrawlResult{
let mut body_time={
let r=(start_time-relative_body.time).to_ratio();
Ratio::new(r.num.fix_4(),r.den.fix_4())
};
let time_limit={
let r=(time_limit-relative_body.time).to_ratio();
Ratio::new(r.num.fix_4(),r.den.fix_4())
};
for _ in 0..20{ for _ in 0..20{
match next_transition(&fev,time,mesh,relative_body,time_limit){ match next_transition(&fev,body_time,mesh,relative_body,time_limit){
Transition::Miss=>return CrawlResult::Miss(fev), Transition::Miss=>return CrawlResult::Miss(fev),
Transition::Next(next_fev,next_time)=>(fev,time)=(next_fev,next_time), Transition::Next(next_fev,next_time)=>(fev,body_time)=(next_fev,next_time),
Transition::Hit(face,time)=>return CrawlResult::Hit(face,time), Transition::Hit(face,time)=>return CrawlResult::Hit(face,time),
} }
} }

View File

@ -22,7 +22,7 @@ impl std::error::Error for ReadError{}
pub enum DataStructure{ pub enum DataStructure{
#[cfg(feature="roblox")] #[cfg(feature="roblox")]
Roblox(strafesnet_rbx_loader::Dom), Roblox(strafesnet_rbx_loader::Model),
#[cfg(feature="source")] #[cfg(feature="source")]
Source(strafesnet_bsp_loader::Bsp), Source(strafesnet_bsp_loader::Bsp),
#[cfg(feature="snf")] #[cfg(feature="snf")]
@ -66,13 +66,16 @@ pub fn load<P:AsRef<std::path::Path>>(path:P)->Result<strafesnet_common::map::Co
#[cfg(feature="snf")] #[cfg(feature="snf")]
DataStructure::StrafesNET(map)=>Ok(map), DataStructure::StrafesNET(map)=>Ok(map),
#[cfg(feature="roblox")] #[cfg(feature="roblox")]
DataStructure::Roblox(dom)=>{ DataStructure::Roblox(model)=>{
let mut place=model.into_place();
place.run_scripts();
let mut loader=strafesnet_deferred_loader::roblox_legacy(); let mut loader=strafesnet_deferred_loader::roblox_legacy();
let (texture_loader,mesh_loader)=loader.get_inner_mut(); let (texture_loader,mesh_loader)=loader.get_inner_mut();
let map_step1=strafesnet_rbx_loader::convert( let map_step1=strafesnet_rbx_loader::convert(
&dom, &place,
|name|texture_loader.acquire_render_config_id(name), |name|texture_loader.acquire_render_config_id(name),
|name|mesh_loader.acquire_mesh_id(name), |name|mesh_loader.acquire_mesh_id(name),
); );
@ -110,7 +113,7 @@ pub fn load<P:AsRef<std::path::Path>>(path:P)->Result<strafesnet_common::map::Co
|name|mesh_loader.acquire_mesh_id(name), |name|mesh_loader.acquire_mesh_id(name),
); );
let prop_meshes=mesh_loader.load_meshes(&bsp.as_ref()); let prop_meshes=mesh_loader.load_meshes(bsp.as_ref());
let map_step2=map_step1.add_prop_meshes( let map_step2=map_step1.add_prop_meshes(
//the type conflagulator 9000 //the type conflagulator 9000

View File

@ -6,12 +6,6 @@ use strafesnet_common::model::{self, ColorId, NormalId, PolygonIter, PositionId,
use wgpu::{util::DeviceExt,AstcBlock,AstcChannel}; use wgpu::{util::DeviceExt,AstcBlock,AstcChannel};
use crate::model_graphics::{self,IndexedGraphicsMeshOwnedRenderConfig,IndexedGraphicsMeshOwnedRenderConfigId,GraphicsMeshOwnedRenderConfig,GraphicsModelColor4,GraphicsModelOwned,GraphicsVertex}; use crate::model_graphics::{self,IndexedGraphicsMeshOwnedRenderConfig,IndexedGraphicsMeshOwnedRenderConfigId,GraphicsMeshOwnedRenderConfig,GraphicsModelColor4,GraphicsModelOwned,GraphicsVertex};
#[derive(Clone)]
pub struct GraphicsModelUpdate{
transform:Option<glam::Mat4>,
color:Option<glam::Vec4>,
}
struct Indices{ struct Indices{
count:u32, count:u32,
buf:wgpu::Buffer, buf:wgpu::Buffer,
@ -32,7 +26,6 @@ impl Indices{
} }
struct GraphicsModel{ struct GraphicsModel{
indices:Indices, indices:Indices,
model_buf:wgpu::Buffer,
vertex_buf:wgpu::Buffer, vertex_buf:wgpu::Buffer,
bind_group:wgpu::BindGroup, bind_group:wgpu::BindGroup,
instance_count:u32, instance_count:u32,
@ -65,12 +58,12 @@ struct GraphicsCamera{
#[inline] #[inline]
fn perspective_rh(fov_x_slope:f32,fov_y_slope:f32,z_near:f32,z_far:f32)->glam::Mat4{ fn perspective_rh(fov_x_slope:f32,fov_y_slope:f32,z_near:f32,z_far:f32)->glam::Mat4{
//glam_assert!(z_near > 0.0 && z_far > 0.0); //glam_assert!(z_near > 0.0 && z_far > 0.0);
let r=z_far / (z_near-z_far); let r=z_far/(z_near-z_far);
glam::Mat4::from_cols( glam::Mat4::from_cols(
glam::Vec4::new(1.0/fov_x_slope,0.0,0.0,0.0), glam::Vec4::new(1.0/fov_x_slope,0.0,0.0,0.0),
glam::Vec4::new(0.0,1.0/fov_y_slope,0.0,0.0), glam::Vec4::new(0.0,1.0/fov_y_slope,0.0,0.0),
glam::Vec4::new(0.0,0.0,r,-1.0), glam::Vec4::new(0.0,0.0,r,-1.0),
glam::Vec4::new(0.0,0.0,r * z_near,0.0), glam::Vec4::new(0.0,0.0,r*z_near,0.0),
) )
} }
impl GraphicsCamera{ impl GraphicsCamera{
@ -79,10 +72,10 @@ impl GraphicsCamera{
} }
pub fn world(&self,pos:glam::Vec3,angles:glam::Vec2)->glam::Mat4{ pub fn world(&self,pos:glam::Vec3,angles:glam::Vec2)->glam::Mat4{
//f32 good enough for view matrix //f32 good enough for view matrix
glam::Mat4::from_translation(pos) * glam::Mat4::from_euler(glam::EulerRot::YXZ,angles.x,angles.y,0f32) glam::Mat4::from_translation(pos)*glam::Mat4::from_euler(glam::EulerRot::YXZ,angles.x,angles.y,0f32)
} }
pub fn to_uniform_data(&self,(pos,angles):(glam::Vec3,glam::Vec2))->[f32; 16 * 4]{ pub fn to_uniform_data(&self,pos:glam::Vec3,angles:glam::Vec2)->[f32;16*4]{
let proj=self.proj(); let proj=self.proj();
let proj_inv=proj.inverse(); let proj_inv=proj.inverse();
let view_inv=self.world(pos,angles); let view_inv=self.world(pos,angles);
@ -105,6 +98,12 @@ impl std::default::Default for GraphicsCamera{
} }
} }
pub struct FrameState{
pub body:crate::physics::Body,
pub camera:crate::physics::PhysicsCamera,
pub time:integer::Time,
}
pub struct GraphicsState{ pub struct GraphicsState{
pipelines:GraphicsPipelines, pipelines:GraphicsPipelines,
bind_groups:GraphicsBindGroups, bind_groups:GraphicsBindGroups,
@ -220,7 +219,7 @@ impl GraphicsState{
//wow //wow
let instance=GraphicsModelOwned{ let instance=GraphicsModelOwned{
transform:model.transform.into(), transform:model.transform.into(),
normal_transform:Into::<glam::Mat3>::into(model.transform.matrix3).inverse().transpose(), normal_transform:glam::Mat3::from_cols_array_2d(&model.transform.matrix3.to_array().map(|row|row.map(Into::into))).inverse().transpose(),
color:GraphicsModelColor4::new(model.color), color:GraphicsModelColor4::new(model.color),
}; };
//get or create owned mesh map //get or create owned mesh map
@ -239,9 +238,9 @@ impl GraphicsState{
//create //create
let owned_mesh_id=IndexedGraphicsMeshOwnedRenderConfigId::new(unique_render_config_models.len() as u32); let owned_mesh_id=IndexedGraphicsMeshOwnedRenderConfigId::new(unique_render_config_models.len() as u32);
unique_render_config_models.push(IndexedGraphicsMeshOwnedRenderConfig{ unique_render_config_models.push(IndexedGraphicsMeshOwnedRenderConfig{
unique_pos:mesh.unique_pos.iter().map(|&v|*Into::<glam::Vec3>::into(v).as_ref()).collect(), unique_pos:mesh.unique_pos.iter().map(|v|v.to_array().map(Into::into)).collect(),
unique_tex:mesh.unique_tex.iter().map(|v|*v.as_ref()).collect(), unique_tex:mesh.unique_tex.iter().map(|v|*v.as_ref()).collect(),
unique_normal:mesh.unique_normal.iter().map(|&v|*Into::<glam::Vec3>::into(v).as_ref()).collect(), unique_normal:mesh.unique_normal.iter().map(|v|v.to_array().map(Into::into)).collect(),
unique_color:mesh.unique_color.iter().map(|v|*v.as_ref()).collect(), unique_color:mesh.unique_color.iter().map(|v|*v.as_ref()).collect(),
unique_vertices:mesh.unique_vertices.clone(), unique_vertices:mesh.unique_vertices.clone(),
render_config:graphics_group.render, render_config:graphics_group.render,
@ -507,7 +506,6 @@ impl GraphicsState{
model_graphics::Indices::U16(indices)=>Indices::new(device,indices,wgpu::IndexFormat::Uint16), model_graphics::Indices::U16(indices)=>Indices::new(device,indices,wgpu::IndexFormat::Uint16),
}, },
bind_group, bind_group,
model_buf,
}); });
} }
} }
@ -816,7 +814,7 @@ impl GraphicsState{
}); });
let camera=GraphicsCamera::default(); let camera=GraphicsCamera::default();
let camera_uniforms=camera.to_uniform_data(crate::physics::PhysicsOutputState::default().extrapolate(crate::physics::MouseState::default())); let camera_uniforms=camera.to_uniform_data(glam::Vec3::ZERO,glam::Vec2::ZERO);
let camera_buf=device.create_buffer_init(&wgpu::util::BufferInitDescriptor{ let camera_buf=device.create_buffer_init(&wgpu::util::BufferInitDescriptor{
label:Some("Camera"), label:Some("Camera"),
contents:bytemuck::cast_slice(&camera_uniforms), contents:bytemuck::cast_slice(&camera_uniforms),
@ -884,16 +882,17 @@ impl GraphicsState{
view:&wgpu::TextureView, view:&wgpu::TextureView,
device:&wgpu::Device, device:&wgpu::Device,
queue:&wgpu::Queue, queue:&wgpu::Queue,
physics_output:crate::physics::PhysicsOutputState, frame_state:FrameState,
predicted_time:integer::Time,
mouse_pos:glam::IVec2,
){ ){
//TODO:use scheduled frame times to create beautiful smoothing simulation physics extrapolation assuming no input //TODO:use scheduled frame times to create beautiful smoothing simulation physics extrapolation assuming no input
let mut encoder=device.create_command_encoder(&wgpu::CommandEncoderDescriptor{label:None}); let mut encoder=device.create_command_encoder(&wgpu::CommandEncoderDescriptor{label:None});
// update rotation // update rotation
let camera_uniforms=self.camera.to_uniform_data(physics_output.extrapolate(crate::physics::MouseState{pos:mouse_pos,time:predicted_time})); let camera_uniforms=self.camera.to_uniform_data(
frame_state.body.extrapolated_position(frame_state.time).map(Into::<f32>::into).to_array().into(),
frame_state.camera.simulate_move_angles(glam::IVec2::ZERO)
);
self.staging_belt self.staging_belt
.write_buffer( .write_buffer(
&mut encoder, &mut encoder,

View File

@ -1,11 +1,8 @@
use strafesnet_common::integer;
pub enum Instruction{ pub enum Instruction{
Render(crate::physics::PhysicsOutputState,integer::Time,glam::IVec2), Render(crate::graphics::FrameState),
//UpdateModel(crate::graphics::GraphicsModelUpdate), //UpdateModel(crate::graphics::GraphicsModelUpdate),
Resize(winit::dpi::PhysicalSize<u32>,crate::settings::UserSettings), Resize(winit::dpi::PhysicalSize<u32>,crate::settings::UserSettings),
GenerateModels(strafesnet_common::map::CompleteMap), ChangeMap(strafesnet_common::map::CompleteMap),
ClearModels,
} }
//Ideally the graphics thread worker description is: //Ideally the graphics thread worker description is:
@ -18,36 +15,32 @@ WorkerDescription{
//up to three frames in flight, dropping new frame requests when all three are busy, and dropping output frames when one renders out of order //up to three frames in flight, dropping new frame requests when all three are busy, and dropping output frames when one renders out of order
pub fn new<'a>( pub fn new<'a>(
mut graphics:crate::graphics::GraphicsState, mut graphics:crate::graphics::GraphicsState,
mut config:wgpu::SurfaceConfiguration, mut config:wgpu::SurfaceConfiguration,
surface:wgpu::Surface<'a>, surface:wgpu::Surface<'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; 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::GenerateModels(map)=>{ Instruction::ChangeMap(map)=>{
graphics.generate_models(&device,&queue,&map);
},
Instruction::ClearModels=>{
graphics.clear(); graphics.clear();
graphics.generate_models(&device,&queue,&map);
}, },
Instruction::Resize(size,user_settings)=>{ Instruction::Resize(size,user_settings)=>{
resize=Some((size,user_settings)); resize=Some((size,user_settings));
} }
Instruction::Render(physics_output,predicted_time,mouse_pos)=>{ Instruction::Render(frame_state)=>{
if let Some((size,user_settings))=&resize{ if let Some((size,user_settings))=resize.take(){
println!("Resizing to {:?}",size); println!("Resizing to {:?}",size);
let t0=std::time::Instant::now(); let t0=std::time::Instant::now();
config.width=size.width.max(1); config.width=size.width.max(1);
config.height=size.height.max(1); config.height=size.height.max(1);
surface.configure(&device,&config); surface.configure(&device,&config);
graphics.resize(&device,&config,user_settings); graphics.resize(&device,&config,&user_settings);
println!("Resize took {:?}",t0.elapsed()); println!("Resize took {:?}",t0.elapsed());
} }
//clear every time w/e
resize=None;
//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,
@ -63,10 +56,10 @@ pub fn new<'a>(
..wgpu::TextureViewDescriptor::default() ..wgpu::TextureViewDescriptor::default()
}); });
graphics.render(&view,&device,&queue,physics_output,predicted_time,mouse_pos); graphics.render(&view,&device,&queue,frame_state);
frame.present(); frame.present();
} }
} }
}) })
} }

View File

@ -1,15 +1,15 @@
use std::borrow::{Borrow,Cow}; use std::borrow::{Borrow,Cow};
use std::collections::{HashSet,HashMap}; use std::collections::{HashSet,HashMap};
use strafesnet_common::integer::vec3::Vector3;
use strafesnet_common::model::{self,MeshId,PolygonIter}; use strafesnet_common::model::{self,MeshId,PolygonIter};
use strafesnet_common::zeroes; use strafesnet_common::integer::{self,vec3,Fixed,Planar64,Planar64Vec3,Ratio};
use strafesnet_common::integer::{self,Planar64,Planar64Vec3};
pub trait UndirectedEdge{ pub trait UndirectedEdge{
type DirectedEdge:Copy+DirectedEdge; type DirectedEdge:Copy+DirectedEdge;
fn as_directed(&self,parity:bool)->Self::DirectedEdge; fn as_directed(&self,parity:bool)->Self::DirectedEdge;
} }
pub trait DirectedEdge{ pub trait DirectedEdge{
type UndirectedEdge:Copy+UndirectedEdge; type UndirectedEdge:Copy+std::fmt::Debug+UndirectedEdge;
fn as_undirected(&self)->Self::UndirectedEdge; fn as_undirected(&self)->Self::UndirectedEdge;
fn parity(&self)->bool; fn parity(&self)->bool;
//this is stupid but may work fine //this is stupid but may work fine
@ -50,6 +50,7 @@ impl DirectedEdge for SubmeshDirectedEdgeId{
} }
//Vertex <-> Edge <-> Face -> Collide //Vertex <-> Edge <-> Face -> Collide
#[derive(Debug)]
pub enum FEV<F,E:DirectedEdge,V>{ pub enum FEV<F,E:DirectedEdge,V>{
Face(F), Face(F),
Edge(E::UndirectedEdge), Edge(E::UndirectedEdge),
@ -64,6 +65,9 @@ struct Face{
} }
struct Vert(Planar64Vec3); struct Vert(Planar64Vec3);
pub trait MeshQuery<FACE:Clone,EDGE:Clone+DirectedEdge,VERT:Clone>{ pub trait MeshQuery<FACE:Clone,EDGE:Clone+DirectedEdge,VERT:Clone>{
// Vertex must be Planar64Vec3 because it represents an actual position
type Normal;
type Offset;
fn edge_n(&self,edge_id:EDGE::UndirectedEdge)->Planar64Vec3{ fn edge_n(&self,edge_id:EDGE::UndirectedEdge)->Planar64Vec3{
let verts=self.edge_verts(edge_id); let verts=self.edge_verts(edge_id);
self.vert(verts[1].clone())-self.vert(verts[0].clone()) self.vert(verts[1].clone())-self.vert(verts[0].clone())
@ -73,7 +77,7 @@ pub trait MeshQuery<FACE:Clone,EDGE:Clone+DirectedEdge,VERT:Clone>{
(self.vert(verts[1].clone())-self.vert(verts[0].clone()))*((directed_edge_id.parity() as i64)*2-1) (self.vert(verts[1].clone())-self.vert(verts[0].clone()))*((directed_edge_id.parity() as i64)*2-1)
} }
fn vert(&self,vert_id:VERT)->Planar64Vec3; fn vert(&self,vert_id:VERT)->Planar64Vec3;
fn face_nd(&self,face_id:FACE)->(Planar64Vec3,Planar64); fn face_nd(&self,face_id:FACE)->(Self::Normal,Self::Offset);
fn face_edges(&self,face_id:FACE)->Cow<Vec<EDGE>>; fn face_edges(&self,face_id:FACE)->Cow<Vec<EDGE>>;
fn edge_faces(&self,edge_id:EDGE::UndirectedEdge)->Cow<[FACE;2]>; fn edge_faces(&self,edge_id:EDGE::UndirectedEdge)->Cow<[FACE;2]>;
fn edge_verts(&self,edge_id:EDGE::UndirectedEdge)->Cow<[VERT;2]>; fn edge_verts(&self,edge_id:EDGE::UndirectedEdge)->Cow<[VERT;2]>;
@ -137,22 +141,22 @@ impl PhysicsMesh{
//go go gadget debug print mesh //go go gadget debug print mesh
let data=PhysicsMeshData{ let data=PhysicsMeshData{
faces:vec![ faces:vec![
Face{normal:Planar64Vec3::raw_xyz( 4294967296, 0, 0),dot:Planar64::raw(4294967296)}, Face{normal:vec3::raw_xyz( 4294967296, 0, 0),dot:Planar64::raw(4294967296)},
Face{normal:Planar64Vec3::raw_xyz( 0, 4294967296, 0),dot:Planar64::raw(4294967296)}, Face{normal:vec3::raw_xyz( 0, 4294967296, 0),dot:Planar64::raw(4294967296)},
Face{normal:Planar64Vec3::raw_xyz( 0, 0, 4294967296),dot:Planar64::raw(4294967296)}, Face{normal:vec3::raw_xyz( 0, 0, 4294967296),dot:Planar64::raw(4294967296)},
Face{normal:Planar64Vec3::raw_xyz(-4294967296, 0, 0),dot:Planar64::raw(4294967296)}, Face{normal:vec3::raw_xyz(-4294967296, 0, 0),dot:Planar64::raw(4294967296)},
Face{normal:Planar64Vec3::raw_xyz( 0,-4294967296, 0),dot:Planar64::raw(4294967296)}, Face{normal:vec3::raw_xyz( 0,-4294967296, 0),dot:Planar64::raw(4294967296)},
Face{normal:Planar64Vec3::raw_xyz( 0, 0,-4294967296),dot:Planar64::raw(4294967296)} Face{normal:vec3::raw_xyz( 0, 0,-4294967296),dot:Planar64::raw(4294967296)}
], ],
verts:vec![ verts:vec![
Vert(Planar64Vec3::raw_xyz( 4294967296,-4294967296,-4294967296)), Vert(vec3::raw_xyz( 4294967296,-4294967296,-4294967296)),
Vert(Planar64Vec3::raw_xyz( 4294967296, 4294967296,-4294967296)), Vert(vec3::raw_xyz( 4294967296, 4294967296,-4294967296)),
Vert(Planar64Vec3::raw_xyz( 4294967296, 4294967296, 4294967296)), Vert(vec3::raw_xyz( 4294967296, 4294967296, 4294967296)),
Vert(Planar64Vec3::raw_xyz( 4294967296,-4294967296, 4294967296)), Vert(vec3::raw_xyz( 4294967296,-4294967296, 4294967296)),
Vert(Planar64Vec3::raw_xyz(-4294967296, 4294967296,-4294967296)), Vert(vec3::raw_xyz(-4294967296, 4294967296,-4294967296)),
Vert(Planar64Vec3::raw_xyz(-4294967296, 4294967296, 4294967296)), Vert(vec3::raw_xyz(-4294967296, 4294967296, 4294967296)),
Vert(Planar64Vec3::raw_xyz(-4294967296,-4294967296, 4294967296)), Vert(vec3::raw_xyz(-4294967296,-4294967296, 4294967296)),
Vert(Planar64Vec3::raw_xyz(-4294967296,-4294967296,-4294967296)) Vert(vec3::raw_xyz(-4294967296,-4294967296,-4294967296))
] ]
}; };
let mesh_topology=PhysicsMeshTopology{ let mesh_topology=PhysicsMeshTopology{
@ -330,7 +334,7 @@ impl TryFrom<&model::Mesh> for PhysicsMesh{
for poly_vertices in polygon_group.polys(){ for poly_vertices in polygon_group.polys(){
let submesh_face_id=SubmeshFaceId::new(submesh_faces.len() as u32); let submesh_face_id=SubmeshFaceId::new(submesh_faces.len() as u32);
//one face per poly //one face per poly
let mut normal=Planar64Vec3::ZERO; let mut normal=Vector3::new([Fixed::ZERO,Fixed::ZERO,Fixed::ZERO]);
let len=poly_vertices.len(); let len=poly_vertices.len();
let face_edges=poly_vertices.into_iter().enumerate().map(|(i,vert_id)|{ let face_edges=poly_vertices.into_iter().enumerate().map(|(i,vert_id)|{
let vert0_id=MeshVertId::new(mesh.unique_vertices[vert_id.get() as usize].pos.get() as u32); let vert0_id=MeshVertId::new(mesh.unique_vertices[vert_id.get() as usize].pos.get() as u32);
@ -341,11 +345,11 @@ impl TryFrom<&model::Mesh> for PhysicsMesh{
//https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal (Newell's Method) //https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal (Newell's Method)
let v0=mesh.unique_pos[vert0_id.get() as usize]; let v0=mesh.unique_pos[vert0_id.get() as usize];
let v1=mesh.unique_pos[vert1_id.get() as usize]; let v1=mesh.unique_pos[vert1_id.get() as usize];
normal+=Planar64Vec3::new( normal+=Vector3::new([
(v0.y()-v1.y())*(v0.z()+v1.z()), (v0.y-v1.y)*(v0.z+v1.z),
(v0.z()-v1.z())*(v0.x()+v1.x()), (v0.z-v1.z)*(v0.x+v1.x),
(v0.x()-v1.x())*(v0.y()+v1.y()), (v0.x-v1.x)*(v0.y+v1.y),
); ]);
//get/create edge and push face into it //get/create edge and push face into it
let (edge_ref_verts,is_sorted)=EdgeRefVerts::new(submesh_vert0_id,submesh_vert1_id); let (edge_ref_verts,is_sorted)=EdgeRefVerts::new(submesh_vert0_id,submesh_vert1_id);
let (edge_ref_faces,edge_id)=edge_pool.push(edge_ref_verts); let (edge_ref_faces,edge_id)=edge_pool.push(edge_ref_verts);
@ -362,14 +366,16 @@ impl TryFrom<&model::Mesh> for PhysicsMesh{
//return directed_edge_id //return directed_edge_id
edge_id.as_directed(is_sorted) edge_id.as_directed(is_sorted)
}).collect(); }).collect();
//choose precision loss randomly idk let mut dot=Fixed::ZERO;
normal=normal/len as i64; // find the average dot
let mut dot=Planar64::ZERO;
for &v in poly_vertices{ for &v in poly_vertices{
dot+=normal.dot(mesh.unique_pos[mesh.unique_vertices[v.get() as usize].pos.get() as usize]); dot+=normal.dot(mesh.unique_pos[mesh.unique_vertices[v.get() as usize].pos.get() as usize]);
} }
//assume face hash is stable, and there are no flush faces... //assume face hash is stable, and there are no flush faces...
let face=Face{normal,dot:dot/len as i64}; let face=Face{
normal:(normal/len as i64).divide().fix_1(),
dot:(dot/(len*len) as i64).fix_1(),
};
let face_id=match face_id_from_face.get(&face){ let face_id=match face_id_from_face.get(&face){
Some(&face_id)=>face_id, Some(&face_id)=>face_id,
None=>{ None=>{
@ -416,6 +422,8 @@ pub struct PhysicsMeshView<'a>{
topology:&'a PhysicsMeshTopology, topology:&'a PhysicsMeshTopology,
} }
impl MeshQuery<SubmeshFaceId,SubmeshDirectedEdgeId,SubmeshVertId> for PhysicsMeshView<'_>{ impl MeshQuery<SubmeshFaceId,SubmeshDirectedEdgeId,SubmeshVertId> for PhysicsMeshView<'_>{
type Normal=Planar64Vec3;
type Offset=Planar64;
fn face_nd(&self,face_id:SubmeshFaceId)->(Planar64Vec3,Planar64){ fn face_nd(&self,face_id:SubmeshFaceId)->(Planar64Vec3,Planar64){
let face_idx=self.topology.faces[face_id.get() as usize].get() as usize; let face_idx=self.topology.faces[face_id.get() as usize].get() as usize;
(self.data.faces[face_idx].normal,self.data.faces[face_idx].dot) (self.data.faces[face_idx].normal,self.data.faces[face_idx].dot)
@ -444,14 +452,14 @@ impl MeshQuery<SubmeshFaceId,SubmeshDirectedEdgeId,SubmeshVertId> for PhysicsMes
pub struct PhysicsMeshTransform{ pub struct PhysicsMeshTransform{
pub vertex:integer::Planar64Affine3, pub vertex:integer::Planar64Affine3,
pub normal:integer::Planar64Mat3, pub normal:integer::mat3::Matrix3<Fixed<2,64>>,
pub det:Planar64, pub det:Fixed<3,96>,
} }
impl PhysicsMeshTransform{ impl PhysicsMeshTransform{
pub const fn new(transform:integer::Planar64Affine3)->Self{ pub fn new(transform:integer::Planar64Affine3)->Self{
Self{ Self{
normal:transform.matrix3.inverse_times_det().transpose(), normal:transform.matrix3.adjugate().transpose(),
det:transform.matrix3.determinant(), det:transform.matrix3.det(),
vertex:transform, vertex:transform,
} }
} }
@ -462,7 +470,7 @@ pub struct TransformedMesh<'a>{
transform:&'a PhysicsMeshTransform, transform:&'a PhysicsMeshTransform,
} }
impl TransformedMesh<'_>{ impl TransformedMesh<'_>{
pub fn new<'a>( pub const fn new<'a>(
view:PhysicsMeshView<'a>, view:PhysicsMeshView<'a>,
transform:&'a PhysicsMeshTransform, transform:&'a PhysicsMeshTransform,
)->TransformedMesh<'a>{ )->TransformedMesh<'a>{
@ -471,33 +479,33 @@ impl TransformedMesh<'_>{
transform, transform,
} }
} }
pub fn verts<'a>(&'a self)->impl Iterator<Item=Planar64Vec3>+'a{ pub fn verts<'a>(&'a self)->impl Iterator<Item=vec3::Vector3<Fixed<2,64>>>+'a{
self.view.data.verts.iter().map(|&Vert(pos)|self.transform.vertex.transform_point3(pos)) self.view.data.verts.iter().map(|&Vert(pos)|self.transform.vertex.transform_point3(pos))
} }
fn farthest_vert(&self,dir:Planar64Vec3)->SubmeshVertId{ fn farthest_vert(&self,dir:Planar64Vec3)->SubmeshVertId{
let mut best_dot=Planar64::MIN;
let mut best_vert=SubmeshVertId(0);
//this happens to be well-defined. there are no virtual virtices //this happens to be well-defined. there are no virtual virtices
for (i,vert_id) in self.view.topology.verts.iter().enumerate(){ SubmeshVertId::new(
let p=self.transform.vertex.transform_point3(self.view.data.verts[vert_id.get() as usize].0); self.view.topology.verts.iter()
let d=dir.dot(p); .enumerate()
if best_dot<d{ .max_by_key(|(_,&vert_id)|
best_dot=d; dir.dot(self.transform.vertex.transform_point3(self.view.data.verts[vert_id.get() as usize].0))
best_vert=SubmeshVertId::new(i as u32); )
} //assume there is more than zero vertices.
} .unwrap().0 as u32
best_vert )
} }
} }
impl MeshQuery<SubmeshFaceId,SubmeshDirectedEdgeId,SubmeshVertId> for TransformedMesh<'_>{ impl MeshQuery<SubmeshFaceId,SubmeshDirectedEdgeId,SubmeshVertId> for TransformedMesh<'_>{
fn face_nd(&self,face_id:SubmeshFaceId)->(Planar64Vec3,Planar64){ type Normal=Vector3<Fixed<3,96>>;
type Offset=Fixed<4,128>;
fn face_nd(&self,face_id:SubmeshFaceId)->(Self::Normal,Self::Offset){
let (n,d)=self.view.face_nd(face_id); let (n,d)=self.view.face_nd(face_id);
let transformed_n=self.transform.normal*n; let transformed_n=self.transform.normal*n;
let transformed_d=d+transformed_n.dot(self.transform.vertex.translation)/self.transform.det; let transformed_d=d*self.transform.det+transformed_n.dot(self.transform.vertex.translation);
(transformed_n/self.transform.det,transformed_d) (transformed_n,transformed_d)
} }
fn vert(&self,vert_id:SubmeshVertId)->Planar64Vec3{ fn vert(&self,vert_id:SubmeshVertId)->Planar64Vec3{
self.transform.vertex.transform_point3(self.view.vert(vert_id)) self.transform.vertex.transform_point3(self.view.vert(vert_id)).fix_1()
} }
#[inline] #[inline]
fn face_edges(&self,face_id:SubmeshFaceId)->Cow<Vec<SubmeshDirectedEdgeId>>{ fn face_edges(&self,face_id:SubmeshFaceId)->Cow<Vec<SubmeshDirectedEdgeId>>{
@ -525,11 +533,11 @@ impl MeshQuery<SubmeshFaceId,SubmeshDirectedEdgeId,SubmeshVertId> for Transforme
//(face,vertex) //(face,vertex)
//(edge,edge) //(edge,edge)
//(vertex,face) //(vertex,face)
#[derive(Clone,Copy)] #[derive(Clone,Copy,Debug)]
pub enum MinkowskiVert{ pub enum MinkowskiVert{
VertVert(SubmeshVertId,SubmeshVertId), VertVert(SubmeshVertId,SubmeshVertId),
} }
#[derive(Clone,Copy)] #[derive(Clone,Copy,Debug)]
pub enum MinkowskiEdge{ pub enum MinkowskiEdge{
VertEdge(SubmeshVertId,SubmeshEdgeId), VertEdge(SubmeshVertId,SubmeshEdgeId),
EdgeVert(SubmeshEdgeId,SubmeshVertId), EdgeVert(SubmeshEdgeId,SubmeshVertId),
@ -544,7 +552,7 @@ impl UndirectedEdge for MinkowskiEdge{
} }
} }
} }
#[derive(Clone,Copy)] #[derive(Clone,Copy,Debug)]
pub enum MinkowskiDirectedEdge{ pub enum MinkowskiDirectedEdge{
VertEdge(SubmeshVertId,SubmeshDirectedEdgeId), VertEdge(SubmeshVertId,SubmeshDirectedEdgeId),
EdgeVert(SubmeshDirectedEdgeId,SubmeshVertId), EdgeVert(SubmeshDirectedEdgeId,SubmeshVertId),
@ -565,7 +573,7 @@ impl DirectedEdge for MinkowskiDirectedEdge{
} }
} }
} }
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)] #[derive(Clone,Copy,Debug,Hash,Eq,PartialEq)]
pub enum MinkowskiFace{ pub enum MinkowskiFace{
VertFace(SubmeshVertId,SubmeshFaceId), VertFace(SubmeshVertId,SubmeshFaceId),
EdgeEdge(SubmeshEdgeId,SubmeshEdgeId,bool), EdgeEdge(SubmeshEdgeId,SubmeshEdgeId,bool),
@ -581,6 +589,7 @@ pub struct MinkowskiMesh<'a>{
} }
//infinity fev algorithm state transition //infinity fev algorithm state transition
#[derive(Debug)]
enum Transition{ enum Transition{
Done,//found closest vert, no edges are better Done,//found closest vert, no edges are better
Vert(MinkowskiVert),//transition to vert Vert(MinkowskiVert),//transition to vert
@ -590,6 +599,8 @@ enum EV{
Edge(MinkowskiEdge), Edge(MinkowskiEdge),
} }
pub type GigaTime=Ratio<Fixed<4,128>,Fixed<4,128>>;
impl MinkowskiMesh<'_>{ impl MinkowskiMesh<'_>{
pub fn minkowski_sum<'a>(mesh0:TransformedMesh<'a>,mesh1:TransformedMesh<'a>)->MinkowskiMesh<'a>{ pub fn minkowski_sum<'a>(mesh0:TransformedMesh<'a>,mesh1:TransformedMesh<'a>)->MinkowskiMesh<'a>{
MinkowskiMesh{ MinkowskiMesh{
@ -600,7 +611,7 @@ impl MinkowskiMesh<'_>{
fn farthest_vert(&self,dir:Planar64Vec3)->MinkowskiVert{ fn farthest_vert(&self,dir:Planar64Vec3)->MinkowskiVert{
MinkowskiVert::VertVert(self.mesh0.farthest_vert(dir),self.mesh1.farthest_vert(-dir)) MinkowskiVert::VertVert(self.mesh0.farthest_vert(dir),self.mesh1.farthest_vert(-dir))
} }
fn next_transition_vert(&self,vert_id:MinkowskiVert,best_distance_squared:&mut Planar64,infinity_dir:Planar64Vec3,point:Planar64Vec3)->Transition{ fn next_transition_vert(&self,vert_id:MinkowskiVert,best_distance_squared:&mut Fixed<2,64>,infinity_dir:Planar64Vec3,point:Planar64Vec3)->Transition{
let mut best_transition=Transition::Done; let mut best_transition=Transition::Done;
for &directed_edge_id in self.vert_edges(vert_id).iter(){ for &directed_edge_id in self.vert_edges(vert_id).iter(){
let edge_n=self.directed_edge_n(directed_edge_id); let edge_n=self.directed_edge_n(directed_edge_id);
@ -610,7 +621,7 @@ impl MinkowskiMesh<'_>{
let test_vert_id=edge_verts[directed_edge_id.parity() as usize]; let test_vert_id=edge_verts[directed_edge_id.parity() as usize];
//test if it's closer //test if it's closer
let diff=point-self.vert(test_vert_id); let diff=point-self.vert(test_vert_id);
if zeroes::zeroes1(edge_n.dot(diff),edge_n.dot(infinity_dir)).len()==0{ if edge_n.dot(infinity_dir).is_zero(){
let distance_squared=diff.dot(diff); let distance_squared=diff.dot(diff);
if distance_squared<*best_distance_squared{ if distance_squared<*best_distance_squared{
best_transition=Transition::Vert(test_vert_id); best_transition=Transition::Vert(test_vert_id);
@ -620,21 +631,21 @@ impl MinkowskiMesh<'_>{
} }
best_transition best_transition
} }
fn final_ev(&self,vert_id:MinkowskiVert,best_distance_squared:&mut Planar64,infinity_dir:Planar64Vec3,point:Planar64Vec3)->EV{ fn final_ev(&self,vert_id:MinkowskiVert,best_distance_squared:&mut Fixed<2,64>,infinity_dir:Planar64Vec3,point:Planar64Vec3)->EV{
let mut best_transition=EV::Vert(vert_id); let mut best_transition=EV::Vert(vert_id);
let diff=point-self.vert(vert_id); let diff=point-self.vert(vert_id);
for &directed_edge_id in self.vert_edges(vert_id).iter(){ for &directed_edge_id in self.vert_edges(vert_id).iter(){
let edge_n=self.directed_edge_n(directed_edge_id); let edge_n=self.directed_edge_n(directed_edge_id);
//is boundary uncrossable by a crawl from infinity //is boundary uncrossable by a crawl from infinity
//check if time of collision is outside Time::MIN..Time::MAX //check if time of collision is outside Time::MIN..Time::MAX
let d=edge_n.dot(diff); if edge_n.dot(infinity_dir).is_zero(){
if zeroes::zeroes1(d,edge_n.dot(infinity_dir)).len()==0{ let d=edge_n.dot(diff);
//test the edge //test the edge
let edge_nn=edge_n.dot(edge_n); let edge_nn=edge_n.dot(edge_n);
if Planar64::ZERO<=d&&d<=edge_nn{ if !d.is_negative()&&d<=edge_nn{
let distance_squared={ let distance_squared={
let c=diff.cross(edge_n); let c=diff.cross(edge_n);
c.dot(c)/edge_nn (c.dot(c)/edge_nn).divide().fix_2()
}; };
if distance_squared<=*best_distance_squared{ if distance_squared<=*best_distance_squared{
best_transition=EV::Edge(directed_edge_id.as_undirected()); best_transition=EV::Edge(directed_edge_id.as_undirected());
@ -680,7 +691,7 @@ impl MinkowskiMesh<'_>{
let boundary_d=boundary_n.dot(delta_pos); let boundary_d=boundary_n.dot(delta_pos);
//check if time of collision is outside Time::MIN..Time::MAX //check if time of collision is outside Time::MIN..Time::MAX
//infinity_dir can always be treated as a velocity //infinity_dir can always be treated as a velocity
if (boundary_d)<=Planar64::ZERO&&zeroes::zeroes1(boundary_d,boundary_n.dot(infinity_dir)*2).len()==0{ if !boundary_d.is_positive()&&boundary_n.dot(infinity_dir).is_zero(){
//both faces cannot pass this condition, return early if one does. //both faces cannot pass this condition, return early if one does.
return FEV::<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert>::Face(face_id); return FEV::<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert>::Face(face_id);
} }
@ -694,15 +705,16 @@ impl MinkowskiMesh<'_>{
let infinity_fev=self.infinity_fev(-dir,infinity_body.position); let infinity_fev=self.infinity_fev(-dir,infinity_body.position);
//a line is simpler to solve than a parabola //a line is simpler to solve than a parabola
infinity_body.velocity=dir; infinity_body.velocity=dir;
infinity_body.acceleration=Planar64Vec3::ZERO; infinity_body.acceleration=vec3::ZERO;
//crawl in from negative infinity along a tangent line to get the closest fev //crawl in from negative infinity along a tangent line to get the closest fev
match crate::face_crawler::crawl_fev(infinity_fev,self,&infinity_body,integer::Time::MIN,infinity_body.time){ // TODO: change crawl_fev args to delta time? Optional values?
match crate::face_crawler::crawl_fev(infinity_fev,self,&infinity_body,integer::Time::MIN/4,infinity_body.time){
crate::face_crawler::CrawlResult::Miss(fev)=>Some(fev), crate::face_crawler::CrawlResult::Miss(fev)=>Some(fev),
crate::face_crawler::CrawlResult::Hit(_,_)=>None, crate::face_crawler::CrawlResult::Hit(_,_)=>None,
} }
}) })
} }
pub fn predict_collision_in(&self,relative_body:&crate::physics::Body,time_limit:integer::Time)->Option<(MinkowskiFace,integer::Time)>{ pub fn predict_collision_in(&self,relative_body:&crate::physics::Body,time_limit:integer::Time)->Option<(MinkowskiFace,GigaTime)>{
self.closest_fev_not_inside(relative_body.clone()).map_or(None,|fev|{ self.closest_fev_not_inside(relative_body.clone()).map_or(None,|fev|{
//continue forwards along the body parabola //continue forwards along the body parabola
match crate::face_crawler::crawl_fev(fev,self,relative_body,relative_body.time,time_limit){ match crate::face_crawler::crawl_fev(fev,self,relative_body,relative_body.time,time_limit){
@ -711,7 +723,7 @@ impl MinkowskiMesh<'_>{
} }
}) })
} }
pub fn predict_collision_out(&self,relative_body:&crate::physics::Body,time_limit:integer::Time)->Option<(MinkowskiFace,integer::Time)>{ pub fn predict_collision_out(&self,relative_body:&crate::physics::Body,time_limit:integer::Time)->Option<(MinkowskiFace,GigaTime)>{
//create an extrapolated body at time_limit //create an extrapolated body at time_limit
let infinity_body=crate::physics::Body::new( let infinity_body=crate::physics::Body::new(
relative_body.extrapolated_position(time_limit), relative_body.extrapolated_position(time_limit),
@ -727,10 +739,13 @@ impl MinkowskiMesh<'_>{
} }
}) })
} }
pub fn predict_collision_face_out(&self,relative_body:&crate::physics::Body,time_limit:integer::Time,contact_face_id:MinkowskiFace)->Option<(MinkowskiEdge,integer::Time)>{ pub fn predict_collision_face_out(&self,relative_body:&crate::physics::Body,time_limit:integer::Time,contact_face_id:MinkowskiFace)->Option<(MinkowskiEdge,GigaTime)>{
//no algorithm needed, there is only one state and two cases (Edge,None) //no algorithm needed, there is only one state and two cases (Edge,None)
//determine when it passes an edge ("sliding off" case) //determine when it passes an edge ("sliding off" case)
let mut best_time=time_limit; let mut best_time={
let r=(time_limit-relative_body.time).to_ratio();
Ratio::new(r.num.fix_4(),r.den.fix_4())
};
let mut best_edge=None; let mut best_edge=None;
let face_n=self.face_nd(contact_face_id).0; let face_n=self.face_nd(contact_face_id).0;
for &directed_edge_id in self.face_edges(contact_face_id).iter(){ for &directed_edge_id in self.face_edges(contact_face_id).iter(){
@ -740,10 +755,10 @@ impl MinkowskiMesh<'_>{
let verts=self.edge_verts(directed_edge_id.as_undirected()); let verts=self.edge_verts(directed_edge_id.as_undirected());
let d=n.dot(self.vert(verts[0])+self.vert(verts[1])); let d=n.dot(self.vert(verts[0])+self.vert(verts[1]));
//WARNING! d outside of *2 //WARNING! d outside of *2
for t in zeroes::zeroes2((n.dot(relative_body.position))*2-d,n.dot(relative_body.velocity)*2,n.dot(relative_body.acceleration)){ //WARNING: truncated precision
let t=relative_body.time+integer::Time::from(t); for dt in Fixed::<4,128>::zeroes2(((n.dot(relative_body.position))*2-d).fix_4(),n.dot(relative_body.velocity).fix_4()*2,n.dot(relative_body.acceleration).fix_4()){
if relative_body.time<t&&t<best_time&&n.dot(relative_body.extrapolated_velocity(t))<Planar64::ZERO{ if Ratio::new(Planar64::ZERO,Planar64::EPSILON).le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(relative_body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
best_time=t; best_time=dt;
best_edge=Some(directed_edge_id); best_edge=Some(directed_edge_id);
break; break;
} }
@ -751,9 +766,28 @@ impl MinkowskiMesh<'_>{
} }
best_edge.map(|e|(e.as_undirected(),best_time)) best_edge.map(|e|(e.as_undirected(),best_time))
} }
fn infinity_in(&self,infinity_body:crate::physics::Body)->Option<(MinkowskiFace,GigaTime)>{
let infinity_fev=self.infinity_fev(-infinity_body.velocity,infinity_body.position);
match crate::face_crawler::crawl_fev(infinity_fev,self,&infinity_body,integer::Time::MIN/4,infinity_body.time){
crate::face_crawler::CrawlResult::Miss(_)=>None,
crate::face_crawler::CrawlResult::Hit(face,time)=>Some((face,time)),
}
}
pub fn is_point_in_mesh(&self,point:Planar64Vec3)->bool{
let infinity_body=crate::physics::Body::new(point,vec3::Y,vec3::ZERO,integer::Time::ZERO);
//movement must escape the mesh forwards and backwards in time,
//otherwise the point is not inside the mesh
self.infinity_in(infinity_body)
.is_some_and(|_|
self.infinity_in(-infinity_body)
.is_some()
)
}
} }
impl MeshQuery<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert> for MinkowskiMesh<'_>{ impl MeshQuery<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert> for MinkowskiMesh<'_>{
fn face_nd(&self,face_id:MinkowskiFace)->(Planar64Vec3,Planar64){ type Normal=Vector3<Fixed<3,96>>;
type Offset=Fixed<4,128>;
fn face_nd(&self,face_id:MinkowskiFace)->(Self::Normal,Self::Offset){
match face_id{ match face_id{
MinkowskiFace::VertFace(v0,f1)=>{ MinkowskiFace::VertFace(v0,f1)=>{
let (n,d)=self.mesh1.face_nd(f1); let (n,d)=self.mesh1.face_nd(f1);
@ -767,7 +801,7 @@ impl MeshQuery<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert> for MinkowskiM
let n=edge0_n.cross(edge1_n); let n=edge0_n.cross(edge1_n);
let e0d=n.dot(self.mesh0.vert(e0v0)+self.mesh0.vert(e0v1)); let e0d=n.dot(self.mesh0.vert(e0v0)+self.mesh0.vert(e0v1));
let e1d=n.dot(self.mesh1.vert(e1v0)+self.mesh1.vert(e1v1)); let e1d=n.dot(self.mesh1.vert(e1v0)+self.mesh1.vert(e1v1));
(n*(parity as i64*4-2),(e0d-e1d)*(parity as i64*2-1)) ((n*(parity as i64*4-2)).fix_3(),((e0d-e1d)*(parity as i64*2-1)).fix_4())
}, },
MinkowskiFace::FaceVert(f0,v1)=>{ MinkowskiFace::FaceVert(f0,v1)=>{
let (n,d)=self.mesh0.face_nd(f0); let (n,d)=self.mesh0.face_nd(f0);
@ -816,17 +850,18 @@ impl MeshQuery<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert> for MinkowskiM
let &[e1f0,e1f1]=self.mesh1.edge_faces(e1).borrow(); let &[e1f0,e1f1]=self.mesh1.edge_faces(e1).borrow();
Cow::Owned([(e1f1,false),(e1f0,true)].map(|(edge_face_id1,face_parity)|{ Cow::Owned([(e1f1,false),(e1f0,true)].map(|(edge_face_id1,face_parity)|{
let mut best_edge=None; let mut best_edge=None;
let mut best_d=Planar64::ZERO; let mut best_d:Ratio<Fixed<8,256>,Fixed<8,256>>=Ratio::new(Fixed::ZERO,Fixed::ONE);
let edge_face1_n=self.mesh1.face_nd(edge_face_id1).0; let edge_face1_n=self.mesh1.face_nd(edge_face_id1).0;
let edge_face1_nn=edge_face1_n.dot(edge_face1_n); let edge_face1_nn=edge_face1_n.dot(edge_face1_n);
for &directed_edge_id0 in v0e.iter(){ for &directed_edge_id0 in v0e.iter(){
let edge0_n=self.mesh0.directed_edge_n(directed_edge_id0); let edge0_n=self.mesh0.directed_edge_n(directed_edge_id0);
//must be behind other face. //must be behind other face.
let d=edge_face1_n.dot(edge0_n); let d=edge_face1_n.dot(edge0_n);
if d<Planar64::ZERO{ if d.is_negative(){
let edge0_nn=edge0_n.dot(edge0_n); let edge0_nn=edge0_n.dot(edge0_n);
//divide by zero??? // Assume not every number is huge
let dd=d*d/(edge_face1_nn*edge0_nn); // TODO: revisit this
let dd=(d*d)/(edge_face1_nn*edge0_nn);
if best_d<dd{ if best_d<dd{
best_d=dd; best_d=dd;
best_edge=Some(directed_edge_id0); best_edge=Some(directed_edge_id0);
@ -845,15 +880,15 @@ impl MeshQuery<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert> for MinkowskiM
let &[e0f0,e0f1]=self.mesh0.edge_faces(e0).borrow(); let &[e0f0,e0f1]=self.mesh0.edge_faces(e0).borrow();
Cow::Owned([(e0f0,true),(e0f1,false)].map(|(edge_face_id0,face_parity)|{ Cow::Owned([(e0f0,true),(e0f1,false)].map(|(edge_face_id0,face_parity)|{
let mut best_edge=None; let mut best_edge=None;
let mut best_d=Planar64::ZERO; let mut best_d:Ratio<Fixed<8,256>,Fixed<8,256>>=Ratio::new(Fixed::ZERO,Fixed::ONE);
let edge_face0_n=self.mesh0.face_nd(edge_face_id0).0; let edge_face0_n=self.mesh0.face_nd(edge_face_id0).0;
let edge_face0_nn=edge_face0_n.dot(edge_face0_n); let edge_face0_nn=edge_face0_n.dot(edge_face0_n);
for &directed_edge_id1 in v1e.iter(){ for &directed_edge_id1 in v1e.iter(){
let edge1_n=self.mesh1.directed_edge_n(directed_edge_id1); let edge1_n=self.mesh1.directed_edge_n(directed_edge_id1);
let d=edge_face0_n.dot(edge1_n); let d=edge_face0_n.dot(edge1_n);
if d<Planar64::ZERO{ if d.is_negative(){
let edge1_nn=edge1_n.dot(edge1_n); let edge1_nn=edge1_n.dot(edge1_n);
let dd=d*d/(edge_face0_nn*edge1_nn); let dd=(d*d)/(edge_face0_nn*edge1_nn);
if best_d<dd{ if best_d<dd{
best_d=dd; best_d=dd;
best_edge=Some(directed_edge_id1); best_edge=Some(directed_edge_id1);
@ -889,19 +924,20 @@ impl MeshQuery<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert> for MinkowskiM
//detect shared volume when the other mesh is mirrored along a test edge dir //detect shared volume when the other mesh is mirrored along a test edge dir
let v0f=self.mesh0.vert_faces(v0); let v0f=self.mesh0.vert_faces(v0);
let v1f=self.mesh1.vert_faces(v1); let v1f=self.mesh1.vert_faces(v1);
let v0f_n:Vec<Planar64Vec3>=v0f.iter().map(|&face_id|self.mesh0.face_nd(face_id).0).collect(); let v0f_n:Vec<_>=v0f.iter().map(|&face_id|self.mesh0.face_nd(face_id).0).collect();
let v1f_n:Vec<Planar64Vec3>=v1f.iter().map(|&face_id|self.mesh1.face_nd(face_id).0).collect(); let v1f_n:Vec<_>=v1f.iter().map(|&face_id|self.mesh1.face_nd(face_id).0).collect();
let the_len=v0f.len()+v1f.len(); let the_len=v0f.len()+v1f.len();
for &directed_edge_id in self.mesh0.vert_edges(v0).iter(){ for &directed_edge_id in self.mesh0.vert_edges(v0).iter(){
let n=self.mesh0.directed_edge_n(directed_edge_id); let n=self.mesh0.directed_edge_n(directed_edge_id);
let nn=n.dot(n); let nn=n.dot(n);
// TODO: there's gotta be a better way to do this
//make a set of faces //make a set of faces
let mut face_normals=Vec::with_capacity(the_len); let mut face_normals=Vec::with_capacity(the_len);
//add mesh0 faces as-is //add mesh0 faces as-is
face_normals.clone_from(&v0f_n); face_normals.clone_from(&v0f_n);
for face_n in &v1f_n{ for face_n in &v1f_n{
//add reflected mesh1 faces //add reflected mesh1 faces
face_normals.push(*face_n-n*(face_n.dot(n)*2/nn)); face_normals.push(*face_n-(n*face_n.dot(n)*2/nn).divide().fix_3());
} }
if is_empty_volume(face_normals){ if is_empty_volume(face_normals){
edges.push(MinkowskiDirectedEdge::EdgeVert(directed_edge_id,v1)); edges.push(MinkowskiDirectedEdge::EdgeVert(directed_edge_id,v1));
@ -913,7 +949,7 @@ impl MeshQuery<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert> for MinkowskiM
let mut face_normals=Vec::with_capacity(the_len); let mut face_normals=Vec::with_capacity(the_len);
face_normals.clone_from(&v1f_n); face_normals.clone_from(&v1f_n);
for face_n in &v0f_n{ for face_n in &v0f_n{
face_normals.push(*face_n-n*(face_n.dot(n)*2/nn)); face_normals.push(*face_n-(n*face_n.dot(n)*2/nn).divide().fix_3());
} }
if is_empty_volume(face_normals){ if is_empty_volume(face_normals){
edges.push(MinkowskiDirectedEdge::VertEdge(v0,directed_edge_id)); edges.push(MinkowskiDirectedEdge::VertEdge(v0,directed_edge_id));
@ -928,7 +964,7 @@ impl MeshQuery<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert> for MinkowskiM
} }
} }
fn is_empty_volume(normals:Vec<Planar64Vec3>)->bool{ fn is_empty_volume(normals:Vec<Vector3<Fixed<3,96>>>)->bool{
let len=normals.len(); let len=normals.len();
for i in 0..len-1{ for i in 0..len-1{
for j in i+1..len{ for j in i+1..len{
@ -936,9 +972,10 @@ fn is_empty_volume(normals:Vec<Planar64Vec3>)->bool{
let mut d_comp=None; let mut d_comp=None;
for k in 0..len{ for k in 0..len{
if k!=i&&k!=j{ if k!=i&&k!=j{
let d=n.dot(normals[k]); let d=n.dot(normals[k]).is_negative();
if let Some(comp)=&d_comp{ if let Some(comp)=&d_comp{
if *comp*d<Planar64::ZERO{ // This is testing if d_comp*d < 0
if comp^d{
return true; return true;
} }
}else{ }else{
@ -953,8 +990,8 @@ fn is_empty_volume(normals:Vec<Planar64Vec3>)->bool{
#[test] #[test]
fn test_is_empty_volume(){ fn test_is_empty_volume(){
assert!(!is_empty_volume([Planar64Vec3::X,Planar64Vec3::Y,Planar64Vec3::Z].to_vec())); assert!(!is_empty_volume([vec3::X.fix_3(),vec3::Y.fix_3(),vec3::Z.fix_3()].to_vec()));
assert!(is_empty_volume([Planar64Vec3::X,Planar64Vec3::Y,Planar64Vec3::Z,Planar64Vec3::NEG_X].to_vec())); assert!(is_empty_volume([vec3::X.fix_3(),vec3::Y.fix_3(),vec3::Z.fix_3(),vec3::NEG_X.fix_3()].to_vec()));
} }
#[test] #[test]

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,10 @@
use crate::physics::{MouseState,PhysicsInputInstruction}; use strafesnet_common::mouse::MouseState;
use strafesnet_common::physics::Instruction as PhysicsInputInstruction;
use strafesnet_common::integer::Time; use strafesnet_common::integer::Time;
use strafesnet_common::instruction::{TimedInstruction,InstructionConsumer}; use strafesnet_common::instruction::TimedInstruction;
use strafesnet_common::timer::{Scaled,Timer,TimerState};
use mouse_interpolator::MouseInterpolator;
#[derive(Debug)] #[derive(Debug)]
pub enum InputInstruction{ pub enum InputInstruction{
MoveMouse(glam::IVec2), MoveMouse(glam::IVec2),
@ -12,29 +16,54 @@ pub enum InputInstruction{
MoveForward(bool), MoveForward(bool),
Jump(bool), Jump(bool),
Zoom(bool), Zoom(bool),
Reset, ResetAndRestart,
ResetAndSpawn(strafesnet_common::gameplay_modes::ModeId,strafesnet_common::gameplay_modes::StageId),
PracticeFly, PracticeFly,
} }
pub enum Instruction{ pub enum Instruction{
Input(InputInstruction), Input(InputInstruction),
Render, Render,
Resize(winit::dpi::PhysicalSize<u32>,crate::settings::UserSettings), Resize(winit::dpi::PhysicalSize<u32>),
GenerateModels(strafesnet_common::map::CompleteMap), ChangeMap(strafesnet_common::map::CompleteMap),
ClearModels, //SetPaused is not an InputInstruction: the physics doesn't know that it's paused.
SetPaused(bool),
//Graphics(crate::graphics_worker::Instruction), //Graphics(crate::graphics_worker::Instruction),
} }
mod mouse_interpolator{
use super::*;
//TODO: move this or tab
pub struct MouseInterpolator{ pub struct MouseInterpolator{
//"PlayerController"
user_settings:crate::settings::UserSettings,
//"MouseInterpolator"
timeline:std::collections::VecDeque<TimedInstruction<PhysicsInputInstruction>>, timeline:std::collections::VecDeque<TimedInstruction<PhysicsInputInstruction>>,
last_mouse_time:Time, last_mouse_time:Time,//this value is pre-transformed to simulation time
mouse_blocking:bool, mouse_blocking:bool,
//"Simulation"
timer:Timer<Scaled>,
physics:crate::physics::PhysicsContext,
} }
impl MouseInterpolator{ impl MouseInterpolator{
fn push_mouse_instruction(&mut self,physics:&crate::physics::PhysicsContext,ins:&TimedInstruction<Instruction>,m:glam::IVec2){ pub fn new(
physics:crate::physics::PhysicsContext,
user_settings:crate::settings::UserSettings,
)->MouseInterpolator{
MouseInterpolator{
mouse_blocking:true,
last_mouse_time:physics.get_next_mouse().time,
timeline:std::collections::VecDeque::new(),
timer:Timer::from_state(Scaled::identity(),false),
physics,
user_settings,
}
}
fn push_mouse_instruction(&mut self,ins:&TimedInstruction<Instruction>,m:glam::IVec2){
if self.mouse_blocking{ if self.mouse_blocking{
//tell the game state which is living in the past about its future //tell the game state which is living in the past about its future
self.timeline.push_front(TimedInstruction{ self.timeline.push_front(TimedInstruction{
time:self.last_mouse_time, time:self.last_mouse_time,
instruction:PhysicsInputInstruction::SetNextMouse(MouseState{time:ins.time,pos:m}), instruction:PhysicsInputInstruction::SetNextMouse(MouseState{time:self.timer.time(ins.time),pos:m}),
}); });
}else{ }else{
//mouse has just started moving again after being still for longer than 10ms. //mouse has just started moving again after being still for longer than 10ms.
@ -42,56 +71,93 @@ impl MouseInterpolator{
self.timeline.push_front(TimedInstruction{ self.timeline.push_front(TimedInstruction{
time:self.last_mouse_time, time:self.last_mouse_time,
instruction:PhysicsInputInstruction::ReplaceMouse( instruction:PhysicsInputInstruction::ReplaceMouse(
MouseState{time:self.last_mouse_time,pos:physics.get_next_mouse().pos}, MouseState{time:self.last_mouse_time,pos:self.physics.get_next_mouse().pos},
MouseState{time:ins.time,pos:m} MouseState{time:self.timer.time(ins.time),pos:m}
), ),
}); });
//delay physics execution until we have an interpolation target //delay physics execution until we have an interpolation target
self.mouse_blocking=true; self.mouse_blocking=true;
} }
self.last_mouse_time=ins.time; self.last_mouse_time=self.timer.time(ins.time);
} }
/// returns the mapped physics input instruction fn push(&mut self,time:Time,phys_input:PhysicsInputInstruction){
//This is always a non-mouse event
self.timeline.push_back(TimedInstruction{
time:self.timer.time(time),
instruction:phys_input,
});
}
/// returns should_empty_queue
/// may or may not mutate internal state XD! /// may or may not mutate internal state XD!
fn map_instruction(&mut self,physics:&crate::physics::PhysicsContext,ins:&TimedInstruction<Instruction>)->Option<PhysicsInputInstruction>{ fn map_instruction(&mut self,ins:&TimedInstruction<Instruction>)->bool{
let mut update_mouse_blocking=true;
match &ins.instruction{ match &ins.instruction{
Instruction::Input(input_instruction)=>match input_instruction{ Instruction::Input(input_instruction)=>match input_instruction{
&InputInstruction::MoveMouse(m)=>{ &InputInstruction::MoveMouse(m)=>{
self.push_mouse_instruction(physics,ins,m); if !self.timer.is_paused(){
None self.push_mouse_instruction(ins,m);
}
update_mouse_blocking=false;
}, },
&InputInstruction::MoveForward(s)=>Some(PhysicsInputInstruction::SetMoveForward(s)), &InputInstruction::MoveForward(s)=>self.push(ins.time,PhysicsInputInstruction::SetMoveForward(s)),
&InputInstruction::MoveLeft(s)=>Some(PhysicsInputInstruction::SetMoveLeft(s)), &InputInstruction::MoveLeft(s)=>self.push(ins.time,PhysicsInputInstruction::SetMoveLeft(s)),
&InputInstruction::MoveBack(s)=>Some(PhysicsInputInstruction::SetMoveBack(s)), &InputInstruction::MoveBack(s)=>self.push(ins.time,PhysicsInputInstruction::SetMoveBack(s)),
&InputInstruction::MoveRight(s)=>Some(PhysicsInputInstruction::SetMoveRight(s)), &InputInstruction::MoveRight(s)=>self.push(ins.time,PhysicsInputInstruction::SetMoveRight(s)),
&InputInstruction::MoveUp(s)=>Some(PhysicsInputInstruction::SetMoveUp(s)), &InputInstruction::MoveUp(s)=>self.push(ins.time,PhysicsInputInstruction::SetMoveUp(s)),
&InputInstruction::MoveDown(s)=>Some(PhysicsInputInstruction::SetMoveDown(s)), &InputInstruction::MoveDown(s)=>self.push(ins.time,PhysicsInputInstruction::SetMoveDown(s)),
&InputInstruction::Jump(s)=>Some(PhysicsInputInstruction::SetJump(s)), &InputInstruction::Jump(s)=>self.push(ins.time,PhysicsInputInstruction::SetJump(s)),
&InputInstruction::Zoom(s)=>Some(PhysicsInputInstruction::SetZoom(s)), &InputInstruction::Zoom(s)=>self.push(ins.time,PhysicsInputInstruction::SetZoom(s)),
InputInstruction::Reset=>Some(PhysicsInputInstruction::Reset), &InputInstruction::ResetAndSpawn(mode_id,stage_id)=>{
InputInstruction::PracticeFly=>Some(PhysicsInputInstruction::PracticeFly), self.push(ins.time,PhysicsInputInstruction::Reset);
self.push(ins.time,PhysicsInputInstruction::SetSensitivity(self.user_settings.calculate_sensitivity()));
self.push(ins.time,PhysicsInputInstruction::Spawn(mode_id,stage_id));
},
InputInstruction::ResetAndRestart=>{
self.push(ins.time,PhysicsInputInstruction::Reset);
self.push(ins.time,PhysicsInputInstruction::SetSensitivity(self.user_settings.calculate_sensitivity()));
self.push(ins.time,PhysicsInputInstruction::Restart);
},
InputInstruction::PracticeFly=>self.push(ins.time,PhysicsInputInstruction::PracticeFly),
}, },
Instruction::GenerateModels(_)=>Some(PhysicsInputInstruction::Idle), //do these really need to idle the physics?
Instruction::ClearModels=>Some(PhysicsInputInstruction::Idle), //sending None dumps the instruction queue
Instruction::Resize(_,_)=>Some(PhysicsInputInstruction::Idle), Instruction::ChangeMap(_)=>self.push(ins.time,PhysicsInputInstruction::Idle),
Instruction::Render=>Some(PhysicsInputInstruction::Idle), Instruction::Resize(_)=>self.push(ins.time,PhysicsInputInstruction::Idle),
Instruction::Render=>self.push(ins.time,PhysicsInputInstruction::Idle),
&Instruction::SetPaused(paused)=>{
if let Err(e)=self.timer.set_paused(ins.time,paused){
println!("Cannot pause: {e}");
}
self.push(ins.time,PhysicsInputInstruction::Idle);
},
}
if update_mouse_blocking{
//this returns the bool for us
self.update_mouse_blocking(ins.time)
}else{
//do flush that queue
true
} }
} }
fn update_mouse_blocking(&mut self,physics:&crate::physics::PhysicsContext,ins:&TimedInstruction<Instruction>)->bool{ /// must check if self.mouse_blocking==true before calling!
fn unblock_mouse(&mut self,time:Time){
//push an event to extrapolate no movement from
self.timeline.push_front(TimedInstruction{
time:self.last_mouse_time,
instruction:PhysicsInputInstruction::SetNextMouse(MouseState{time:self.timer.time(time),pos:self.physics.get_next_mouse().pos}),
});
self.last_mouse_time=self.timer.time(time);
//stop blocking. the mouse is not moving so the physics does not need to live in the past and wait for interpolation targets.
self.mouse_blocking=false;
}
fn update_mouse_blocking(&mut self,time:Time)->bool{
if self.mouse_blocking{ if self.mouse_blocking{
//assume the mouse has stopped moving after 10ms. //assume the mouse has stopped moving after 10ms.
//shitty mice are 125Hz which is 8ms so this should cover that. //shitty mice are 125Hz which is 8ms so this should cover that.
//setting this to 100us still doesn't print even though it's 10x lower than the polling rate, //setting this to 100us still doesn't print even though it's 10x lower than the polling rate,
//so mouse events are probably not handled separately from drawing and fire right before it :( //so mouse events are probably not handled separately from drawing and fire right before it :(
if Time::from_millis(10)<ins.time-physics.get_next_mouse().time{ if Time::from_millis(10)<self.timer.time(time)-self.physics.get_next_mouse().time{
//push an event to extrapolate no movement from self.unblock_mouse(time);
self.timeline.push_front(TimedInstruction{
time:self.last_mouse_time,
instruction:PhysicsInputInstruction::SetNextMouse(MouseState{time:ins.time,pos:physics.get_next_mouse().pos}),
});
self.last_mouse_time=ins.time;
//stop blocking. the mouse is not moving so the physics does not need to live in the past and wait for interpolation targets.
self.mouse_blocking=false;
true true
}else{ }else{
false false
@ -99,65 +165,78 @@ impl MouseInterpolator{
}else{ }else{
//keep this up to date so that it can be used as a known-timestamp //keep this up to date so that it can be used as a known-timestamp
//that the mouse was not moving when the mouse starts moving again //that the mouse was not moving when the mouse starts moving again
self.last_mouse_time=ins.time; self.last_mouse_time=self.timer.time(time);
true true
} }
} }
/// returns whether or not to empty the instruction queue fn empty_queue(&mut self){
fn handle_physics_input(&mut self,physics:&crate::physics::PhysicsContext,ins:&TimedInstruction<Instruction>,phys_input_option:Option<PhysicsInputInstruction>)->bool{
if let Some(phys_input)=phys_input_option{
//non-mouse event
self.timeline.push_back(TimedInstruction{
time:ins.time,
instruction:phys_input,
});
//this returns the bool for us
self.update_mouse_blocking(physics,ins)
}else{
//mouse event
true
}
}
fn empty_queue(&mut self,physics:&mut crate::physics::PhysicsContext){
while let Some(instruction)=self.timeline.pop_front(){ while let Some(instruction)=self.timeline.pop_front(){
physics.run_input_instruction(instruction); self.physics.run_input_instruction(instruction);
} }
} }
fn handle_instruction(&mut self,physics:&mut crate::physics::PhysicsContext,ins:&TimedInstruction<Instruction>){ pub fn handle_instruction(&mut self,ins:&TimedInstruction<Instruction>){
let physics_input_option=self.map_instruction(physics,ins); let should_empty_queue=self.map_instruction(ins);
let should_empty_queue=self.handle_physics_input(physics,ins,physics_input_option);
if should_empty_queue{ if should_empty_queue{
self.empty_queue(physics); self.empty_queue();
} }
} }
pub fn get_frame_state(&self,time:Time)->crate::graphics::FrameState{
crate::graphics::FrameState{
body:self.physics.camera_body(),
camera:self.physics.camera(),
time:self.timer.time(time),
}
}
pub fn change_map(&mut self,time:Time,map:&strafesnet_common::map::CompleteMap){
//dump any pending interpolation state
if self.mouse_blocking{
self.unblock_mouse(time);
}
self.empty_queue();
//doing it like this to avoid doing PhysicsInstruction::ChangeMap(Rc<CompleteMap>)
self.physics.generate_models(&map);
//use the standard input interface so the instructions are written out to bots
self.handle_instruction(&TimedInstruction{
time:self.timer.time(time),
instruction:Instruction::Input(InputInstruction::ResetAndSpawn(
strafesnet_common::gameplay_modes::ModeId::MAIN,
strafesnet_common::gameplay_modes::StageId::FIRST,
)),
});
}
pub const fn user_settings(&self)->&crate::settings::UserSettings{
&self.user_settings
}
}
} }
pub fn new(mut physics:crate::physics::PhysicsContext,mut graphics_worker:crate::compat_worker::INWorker<crate::graphics_worker::Instruction>)->crate::compat_worker::QNWorker<TimedInstruction<Instruction>>{ pub fn new<'a>(
let mut interpolator=MouseInterpolator{ mut graphics_worker:crate::compat_worker::INWorker<'a,crate::graphics_worker::Instruction>,
mouse_blocking:true, user_settings:crate::settings::UserSettings,
last_mouse_time:physics.get_next_mouse().time, )->crate::compat_worker::QNWorker<'a,TimedInstruction<Instruction>>{
timeline:std::collections::VecDeque::new(), let physics=crate::physics::PhysicsContext::default();
}; let mut interpolator=MouseInterpolator::new(
physics,
user_settings
);
crate::compat_worker::QNWorker::new(move |ins:TimedInstruction<Instruction>|{ crate::compat_worker::QNWorker::new(move |ins:TimedInstruction<Instruction>|{
interpolator.handle_instruction(&mut physics,&ins); interpolator.handle_instruction(&ins);
match ins.instruction{ match ins.instruction{
Instruction::Render=>{ Instruction::Render=>{
graphics_worker.send(crate::graphics_worker::Instruction::Render(physics.output(),ins.time,physics.get_next_mouse().pos)).unwrap(); let frame_state=interpolator.get_frame_state(ins.time);
graphics_worker.send(crate::graphics_worker::Instruction::Render(frame_state)).unwrap();
}, },
Instruction::Resize(size,user_settings)=>{ Instruction::Resize(size)=>{
graphics_worker.send(crate::graphics_worker::Instruction::Resize(size,user_settings)).unwrap(); graphics_worker.send(crate::graphics_worker::Instruction::Resize(size,interpolator.user_settings().clone())).unwrap();
}, },
Instruction::GenerateModels(map)=>{ Instruction::ChangeMap(map)=>{
physics.generate_models(&map); interpolator.change_map(ins.time,&map);
physics.spawn(); graphics_worker.send(crate::graphics_worker::Instruction::ChangeMap(map)).unwrap();
graphics_worker.send(crate::graphics_worker::Instruction::GenerateModels(map)).unwrap();
}, },
Instruction::ClearModels=>{ Instruction::Input(_)=>(),
physics.clear(); Instruction::SetPaused(_)=>(),
graphics_worker.send(crate::graphics_worker::Instruction::ClearModels).unwrap();
},
_=>(),
} }
}) })
} }

View File

@ -213,13 +213,14 @@ pub fn setup_and_start(title:String){
//dedicated thread to ping request redraw back and resize the window doesn't seem logical //dedicated thread to ping request redraw back and resize the window doesn't seem logical
let window=crate::window::WindowContextSetup::new(&setup_context,&window);
//the thread that spawns the physics thread //the thread that spawns the physics thread
let mut window_thread=window.into_worker(setup_context); let mut window_thread=crate::window::worker(
&window,
setup_context,
);
let args:Vec<String>=std::env::args().collect(); if let Some(arg)=std::env::args().nth(1){
if args.len()==2{ let path=std::path::PathBuf::from(arg);
let path=std::path::PathBuf::from(&args[1]);
window_thread.send(TimedInstruction{ window_thread.send(TimedInstruction{
time:integer::Time::ZERO, time:integer::Time::ZERO,
instruction:WindowInstruction::WindowEvent(winit::event::WindowEvent::DroppedFile(path)), instruction:WindowInstruction::WindowEvent(winit::event::WindowEvent::DroppedFile(path)),

View File

@ -13,9 +13,8 @@ pub enum WindowInstruction{
//holds thread handles to dispatch to //holds thread handles to dispatch to
struct WindowContext<'a>{ struct WindowContext<'a>{
manual_mouse_lock:bool, manual_mouse_lock:bool,
mouse:crate::physics::MouseState,//std::sync::Arc<std::sync::Mutex<>> mouse:strafesnet_common::mouse::MouseState,//std::sync::Arc<std::sync::Mutex<>>
screen_size:glam::UVec2, screen_size:glam::UVec2,
user_settings:crate::settings::UserSettings,
window:&'a winit::window::Window, window:&'a winit::window::Window,
physics_thread:crate::compat_worker::QNWorker<'a, TimedInstruction<crate::physics_worker::Instruction>>, physics_thread:crate::compat_worker::QNWorker<'a, TimedInstruction<crate::physics_worker::Instruction>>,
} }
@ -28,15 +27,16 @@ impl WindowContext<'_>{
match event { match event {
winit::event::WindowEvent::DroppedFile(path)=>{ winit::event::WindowEvent::DroppedFile(path)=>{
match crate::file::load(path.as_path()){ match crate::file::load(path.as_path()){
Ok(map)=>{ Ok(map)=>self.physics_thread.send(TimedInstruction{time,instruction:crate::physics_worker::Instruction::ChangeMap(map)}).unwrap(),
self.physics_thread.send(TimedInstruction{time,instruction:crate::physics_worker::Instruction::ClearModels}).unwrap();
self.physics_thread.send(TimedInstruction{time,instruction:crate::physics_worker::Instruction::GenerateModels(map)}).unwrap();
},
Err(e)=>println!("Failed to load map: {e}"), Err(e)=>println!("Failed to load map: {e}"),
} }
}, },
winit::event::WindowEvent::Focused(_state)=>{ winit::event::WindowEvent::Focused(state)=>{
//pause unpause //pause unpause
self.physics_thread.send(TimedInstruction{
time,
instruction:crate::physics_worker::Instruction::SetPaused(!state),
}).unwrap();
//recalculate pressed keys on focus //recalculate pressed keys on focus
}, },
winit::event::WindowEvent::KeyboardInput{ winit::event::WindowEvent::KeyboardInput{
@ -107,7 +107,11 @@ impl WindowContext<'_>{
"e"=>Some(InputInstruction::MoveUp(s)), "e"=>Some(InputInstruction::MoveUp(s)),
"q"=>Some(InputInstruction::MoveDown(s)), "q"=>Some(InputInstruction::MoveDown(s)),
"z"=>Some(InputInstruction::Zoom(s)), "z"=>Some(InputInstruction::Zoom(s)),
"r"=>if s{Some(InputInstruction::Reset)}else{None}, "r"=>if s{
//mouse needs to be reset since the position is absolute
self.mouse=strafesnet_common::mouse::MouseState::default();
Some(InputInstruction::ResetAndRestart)
}else{None},
"f"=>if s{Some(InputInstruction::PracticeFly)}else{None}, "f"=>if s{Some(InputInstruction::PracticeFly)}else{None},
_=>None, _=>None,
}, },
@ -161,48 +165,32 @@ impl WindowContext<'_>{
} }
} }
} }
pub fn worker<'a>(
pub struct WindowContextSetup<'a>{
user_settings:crate::settings::UserSettings,
window:&'a winit::window::Window, window:&'a winit::window::Window,
physics:crate::physics::PhysicsContext, setup_context:crate::setup::SetupContext<'a>,
graphics:crate::graphics::GraphicsState, )->crate::compat_worker::QNWorker<'a,TimedInstruction<WindowInstruction>>{
} // WindowContextSetup::new
impl<'a> WindowContextSetup<'a>{
pub fn new(context:&crate::setup::SetupContext,window:&'a winit::window::Window)->Self{
let user_settings=crate::settings::read_user_settings(); let user_settings=crate::settings::read_user_settings();
let mut physics=crate::physics::PhysicsContext::default(); let mut graphics=crate::graphics::GraphicsState::new(&setup_context.device,&setup_context.queue,&setup_context.config);
physics.load_user_settings(&user_settings);
let mut graphics=crate::graphics::GraphicsState::new(&context.device,&context.queue,&context.config);
graphics.load_user_settings(&user_settings); graphics.load_user_settings(&user_settings);
Self{ //WindowContextSetup::into_context
user_settings,
window,
graphics,
physics,
}
}
fn into_context(self,setup_context:crate::setup::SetupContext<'a>)->WindowContext<'a>{
let screen_size=glam::uvec2(setup_context.config.width,setup_context.config.height); let screen_size=glam::uvec2(setup_context.config.width,setup_context.config.height);
let graphics_thread=crate::graphics_worker::new(self.graphics,setup_context.config,setup_context.surface,setup_context.device,setup_context.queue); let graphics_thread=crate::graphics_worker::new(graphics,setup_context.config,setup_context.surface,setup_context.device,setup_context.queue);
WindowContext{ let mut window_context=WindowContext{
manual_mouse_lock:false, manual_mouse_lock:false,
mouse:crate::physics::MouseState::default(), mouse:strafesnet_common::mouse::MouseState::default(),
//make sure to update this!!!!! //make sure to update this!!!!!
screen_size, screen_size,
user_settings:self.user_settings, window,
window:self.window, physics_thread:crate::physics_worker::new(
physics_thread:crate::physics_worker::new(self.physics,graphics_thread), graphics_thread,
} user_settings,
} ),
};
pub fn into_worker(self,setup_context:crate::setup::SetupContext<'a>)->crate::compat_worker::QNWorker<'a,TimedInstruction<WindowInstruction>>{ //WindowContextSetup::into_worker
let mut window_context=self.into_context(setup_context);
crate::compat_worker::QNWorker::new(move |ins:TimedInstruction<WindowInstruction>|{ crate::compat_worker::QNWorker::new(move |ins:TimedInstruction<WindowInstruction>|{
match ins.instruction{ match ins.instruction{
WindowInstruction::RequestRedraw=>{ WindowInstruction::RequestRedraw=>{
@ -218,7 +206,7 @@ impl<'a> WindowContextSetup<'a>{
window_context.physics_thread.send( window_context.physics_thread.send(
TimedInstruction{ TimedInstruction{
time:ins.time, time:ins.time,
instruction:crate::physics_worker::Instruction::Resize(size,window_context.user_settings.clone()) instruction:crate::physics_worker::Instruction::Resize(size)
} }
).unwrap(); ).unwrap();
} }
@ -232,5 +220,4 @@ impl<'a> WindowContextSetup<'a>{
} }
} }
}) })
} }
}

View File

@ -181,16 +181,16 @@ mod test{
#[test]//How to run this test with printing: cargo test --release -- --nocapture #[test]//How to run this test with printing: cargo test --release -- --nocapture
fn test_worker() { fn test_worker() {
// Create the worker thread // Create the worker thread
let test_body=physics::Body::new(integer::Planar64Vec3::ONE,integer::Planar64Vec3::ONE,integer::Planar64Vec3::ONE,integer::Time::ZERO); let test_body=physics::Body::new(integer::vec3::ONE,integer::vec3::ONE,integer::vec3::ONE,integer::Time::ZERO);
let worker=QRWorker::new(physics::Body::default(), let worker=QRWorker::new(physics::Body::ZERO,
|_|physics::Body::new(integer::Planar64Vec3::ONE,integer::Planar64Vec3::ONE,integer::Planar64Vec3::ONE,integer::Time::ZERO) |_|physics::Body::new(integer::vec3::ONE,integer::vec3::ONE,integer::vec3::ONE,integer::Time::ZERO)
); );
// Send tasks to the worker // Send tasks to the worker
for _ in 0..5 { for _ in 0..5 {
let task = instruction::TimedInstruction{ let task = instruction::TimedInstruction{
time:integer::Time::ZERO, time:integer::Time::ZERO,
instruction:physics::PhysicsInstruction::StrafeTick, instruction:strafesnet_common::physics::Instruction::Idle,
}; };
worker.send(task).unwrap(); worker.send(task).unwrap();
} }
@ -204,7 +204,7 @@ mod test{
// Send a new task // Send a new task
let task = instruction::TimedInstruction{ let task = instruction::TimedInstruction{
time:integer::Time::ZERO, time:integer::Time::ZERO,
instruction:physics::PhysicsInstruction::StrafeTick, instruction:strafesnet_common::physics::Instruction::Idle,
}; };
worker.send(task).unwrap(); worker.send(task).unwrap();

1
tools/iso Executable file
View File

@ -0,0 +1 @@
mangohud ../target/release/strafe-client bhop_maps/5692124338.snfm