Compare commits

..

101 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
dd6acbfc2f use mem::replace where it is needed 2024-07-31 12:08:57 -07:00
3fea6ec5a2 put imports together 2024-07-30 13:34:34 -07:00
38df41e043 mouse interpolator abstraction 2024-07-30 13:01:42 -07:00
171cd8f2e4 cross compile with all features 2024-07-30 12:02:28 -07:00
914b5da897 switch over tooling to snfm 2024-07-30 12:02:28 -07:00
52e92bfa5a no more textures 2024-07-30 11:37:26 -07:00
938500e39b update deps (don't panic on corrupted files) 2024-07-29 18:24:57 -07:00
d82dfc2bc2 build smaller 2024-07-29 17:41:46 -07:00
c59f40892a snf optional as well because why not 2024-07-29 17:41:46 -07:00
4863605af7 source and roblox map loading feature flags 2024-07-29 17:11:40 -07:00
685e74575a v0.10.1 snf 2024-07-29 16:53:04 -07:00
d1aca4519b change textures path on my pc 2024-07-29 16:51:36 -07:00
4a4ede36ed read snf map 2024-07-29 16:51:36 -07:00
33cc2e1a45 update common 2024-07-25 13:53:45 -07:00
9982a52af5 update deps 2024-07-22 13:52:27 -07:00
34939db8ef v0.10.0 style redesign + mesh loader 2024-07-22 13:23:19 -07:00
b61088195a update deps (aggressive) 2024-07-22 13:21:47 -07:00
19ab098be0 wgpu 22.0.0 2024-07-22 13:21:47 -07:00
bf1fad9fc4 use strafesnet registry 2024-07-22 13:21:47 -07:00
62b5ba2b33 pretty polygon fanning from vbsp code 2024-07-22 13:21:47 -07:00
f2a7b44884 print texture load error 2024-07-22 13:21:47 -07:00
c054b6aab3 update deps 2024-07-22 13:21:47 -07:00
7caec210ce include meshes symbolic link 2024-07-22 13:21:47 -07:00
5fdcf9047c implement roblox mesh loading 2024-07-22 13:21:47 -07:00
3756af9638 update strafesnet deps 2024-07-22 13:21:47 -07:00
8424fea634 redesign style data structures + fly 2024-07-22 13:21:47 -07:00
46c73b80c6 fix missing bsp_loader commit 2024-03-15 19:24:52 -07:00
0ac1c1aa6b adjust clipping planes 2024-02-18 00:23:27 -08:00
0a75e78c90 update deferred_loader 2024-02-17 22:08:06 -08:00
ecc8d2395b update bsp_loader 2024-02-17 20:32:03 -08:00
e2bd8b4038 v0.9.5 fix graphical bug 2024-02-16 20:03:15 -08:00
4a53040011 use .entry().or_insert_with() pattern everywhere 2024-02-16 06:04:24 -08:00
b1d860edf1 fix invisible walls 2024-02-16 04:11:42 -08:00
db6e1e43c1 file is probably gonna be here a long time 2024-02-16 00:18:16 -08:00
03970edeb8 update bsp_loader 2024-02-15 01:47:18 -08:00
e2da41ec99 update rbx_loader 2024-02-15 00:59:37 -08:00
ae4d539ab1 too much spam 2024-02-15 00:22:11 -08:00
9de60a8e19 v0.9.4 valve mesh loading 2024-02-15 00:22:11 -08:00
df8189b874 implement valve mesh loading 2024-02-15 00:22:11 -08:00
b25bcc627d asref path 2024-02-14 23:33:10 -08:00
977069c4eb v0.9.3 bsp legacy texture loader 2024-02-14 23:33:10 -08:00
63655ef931 enable source_legacy style texture loading 2024-02-14 23:33:10 -08:00
39924db94d change PhysicsMesh::from to PhysicsMesh::try_from 2024-02-14 23:33:10 -08:00
3b3ccefebb fix panic when no modes 2024-02-13 23:16:11 -08:00
ae6e4ee6aa v0.9.2 bsp_loader (no props or textures) 2024-02-13 22:36:12 -08:00
746d3eb7ee bsp_loader 2024-02-13 22:34:13 -08:00
7be93d2114 refactor loaders + file loading 2024-02-13 22:14:26 -08:00
e7f01eff80 print instead of panic 2024-02-13 06:08:03 -08:00
f58a17adba v0.9.1 data structure rewrite 2024-02-13 06:08:03 -08:00
3be9730b52 allow texture loading failure 2024-02-13 06:08:03 -08:00
93eeb3354f rewrite data structures, implement texture_loader 2024-02-13 06:08:03 -08:00
69bab269db stop clone 2024-02-13 00:07:30 -08:00
3bad427f61 shrink code 2024-02-07 21:11:50 -08:00
90cca51e6e patch arcane 2024-02-07 19:50:03 -08:00
24 changed files with 3390 additions and 1826 deletions

2
.cargo/config.toml Normal file
View File

@ -0,0 +1,2 @@
[registries.strafesnet]
index = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"

1
.gitignore vendored
View File

@ -1,2 +1 @@
/target /target
/textures

1733
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +1,36 @@
[package] [package]
name = "strafe-client" name = "strafe-client"
version = "0.9.0" version = "0.10.5"
edition = "2021" edition = "2021"
repository = "https://git.itzana.me/StrafesNET/strafe-client"
license = "Custom"
description = "StrafesNET game client for bhop and surf."
authors = ["Rhys Lloyd <krakow20@gmail.com>"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
default = ["snf"]
snf = ["dep:strafesnet_snf"]
source = ["dep:strafesnet_deferred_loader", "dep:strafesnet_bsp_loader"]
roblox = ["dep:strafesnet_deferred_loader", "dep:strafesnet_rbx_loader"]
[dependencies] [dependencies]
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.25.0" glam = "0.29.0"
id = { git = "https://git.itzana.me/Quaternions/id", rev = "1f710976cc786c8853dab73d6e1cee53158deeb0" } 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_rbx_loader = { git = "https://git.itzana.me/StrafesNET/rbx_loader", rev = "05e609521a31d14ca2c9def7a778d792022571dc" } strafesnet_bsp_loader = { version = "0.2.1", registry = "strafesnet", optional = true }
strafesnet_common = { git = "https://git.itzana.me/StrafesNET/common", rev = "fccb13bc6080ea6db94ef9175affd4aef1d9249d" } strafesnet_common = { version = "0.5.2", registry = "strafesnet" }
strafesnet_texture_loader = { git = "https://git.itzana.me/StrafesNET/texture_loader", rev = "94e951ce06fb30873964ec29f30fcf9c8247a9a5", features = ["legacy"] } strafesnet_deferred_loader = { version = "0.4.0", features = ["legacy"], registry = "strafesnet", optional = true }
wgpu = "0.19.0" strafesnet_rbx_loader = { version = "0.5.1", registry = "strafesnet", optional = true }
winit = "0.29.2" strafesnet_snf = { version = "0.2.0", registry = "strafesnet", optional = true }
wgpu = "22.1.0"
winit = "0.30.5"
#[profile.release] [profile.release]
#lto = true #lto = true
#strip = true strip = true
#codegen-units = 1 codegen-units = 1

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,22 +98,30 @@ 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),
} }
} }
//TODO: fix all bugs //TODO: fix all bugs
println!("Too many iterations! Using default behaviour instead of crashing..."); //println!("Too many iterations! Using default behaviour instead of crashing...");
CrawlResult::Miss(fev) CrawlResult::Miss(fev)
} }

144
src/file.rs Normal file
View File

@ -0,0 +1,144 @@
use std::io::Read;
#[derive(Debug)]
pub enum ReadError{
#[cfg(feature="roblox")]
Roblox(strafesnet_rbx_loader::ReadError),
#[cfg(feature="source")]
Source(strafesnet_bsp_loader::ReadError),
#[cfg(feature="snf")]
StrafesNET(strafesnet_snf::Error),
#[cfg(feature="snf")]
StrafesNETMap(strafesnet_snf::map::Error),
Io(std::io::Error),
UnknownFileFormat,
}
impl std::fmt::Display for ReadError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for ReadError{}
pub enum DataStructure{
#[cfg(feature="roblox")]
Roblox(strafesnet_rbx_loader::Model),
#[cfg(feature="source")]
Source(strafesnet_bsp_loader::Bsp),
#[cfg(feature="snf")]
StrafesNET(strafesnet_common::map::CompleteMap),
}
pub fn read<R:Read+std::io::Seek>(input:R)->Result<DataStructure,ReadError>{
let mut buf=std::io::BufReader::new(input);
let peek=std::io::BufRead::fill_buf(&mut buf).map_err(ReadError::Io)?;
match &peek[0..4]{
#[cfg(feature="roblox")]
b"<rob"=>Ok(DataStructure::Roblox(strafesnet_rbx_loader::read(buf).map_err(ReadError::Roblox)?)),
#[cfg(feature="source")]
b"VBSP"=>Ok(DataStructure::Source(strafesnet_bsp_loader::read(buf).map_err(ReadError::Source)?)),
#[cfg(feature="snf")]
b"SNFM"=>Ok(DataStructure::StrafesNET(
strafesnet_snf::read_map(buf).map_err(ReadError::StrafesNET)?
.into_complete_map().map_err(ReadError::StrafesNETMap)?
)),
_=>Err(ReadError::UnknownFileFormat),
}
}
#[derive(Debug)]
pub enum LoadError{
ReadError(ReadError),
File(std::io::Error),
Io(std::io::Error),
}
impl std::fmt::Display for LoadError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for LoadError{}
pub fn load<P:AsRef<std::path::Path>>(path:P)->Result<strafesnet_common::map::CompleteMap,LoadError>{
//blocking because it's simpler...
let file=std::fs::File::open(path).map_err(LoadError::File)?;
match read(file).map_err(LoadError::ReadError)?{
#[cfg(feature="snf")]
DataStructure::StrafesNET(map)=>Ok(map),
#[cfg(feature="roblox")]
DataStructure::Roblox(model)=>{
let mut place=model.into_place();
place.run_scripts();
let mut loader=strafesnet_deferred_loader::roblox_legacy();
let (texture_loader,mesh_loader)=loader.get_inner_mut();
let map_step1=strafesnet_rbx_loader::convert(
&place,
|name|texture_loader.acquire_render_config_id(name),
|name|mesh_loader.acquire_mesh_id(name),
);
let meshpart_meshes=mesh_loader.load_meshes().map_err(LoadError::Io)?;
let map_step2=map_step1.add_meshpart_meshes_and_calculate_attributes(
meshpart_meshes.into_iter().map(|(mesh_id,loader_model)|
(mesh_id,strafesnet_rbx_loader::data::RobloxMeshBytes::new(loader_model.get()))
)
);
let (textures,render_configs)=loader.into_render_configs().map_err(LoadError::Io)?.consume();
let map=map_step2.add_render_configs_and_textures(
render_configs.into_iter(),
textures.into_iter().map(|(texture_id,texture)|
(texture_id,match texture{
strafesnet_deferred_loader::texture::Texture::ImageDDS(data)=>data,
})
)
);
Ok(map)
},
#[cfg(feature="source")]
DataStructure::Source(bsp)=>{
let mut loader=strafesnet_deferred_loader::source_legacy();
let (texture_loader,mesh_loader)=loader.get_inner_mut();
let map_step1=strafesnet_bsp_loader::convert(
&bsp,
|name|texture_loader.acquire_render_config_id(name),
|name|mesh_loader.acquire_mesh_id(name),
);
let prop_meshes=mesh_loader.load_meshes(bsp.as_ref());
let map_step2=map_step1.add_prop_meshes(
//the type conflagulator 9000
prop_meshes.into_iter().map(|(mesh_id,loader_model)|
(mesh_id,strafesnet_bsp_loader::data::ModelData{
mdl:strafesnet_bsp_loader::data::MdlData::new(loader_model.mdl.get()),
vtx:strafesnet_bsp_loader::data::VtxData::new(loader_model.vtx.get()),
vvd:strafesnet_bsp_loader::data::VvdData::new(loader_model.vvd.get()),
})
),
|name|texture_loader.acquire_render_config_id(name),
);
let (textures,render_configs)=loader.into_render_configs().map_err(LoadError::Io)?.consume();
let map=map_step2.add_render_configs_and_textures(
render_configs.into_iter(),
textures.into_iter().map(|(texture_id,texture)|
(texture_id,match texture{
strafesnet_deferred_loader::texture::Texture::ImageDDS(data)=>data,
})
),
);
Ok(map)
},
}
}

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,24 +58,24 @@ 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{
pub fn proj(&self)->glam::Mat4{ pub fn proj(&self)->glam::Mat4{
perspective_rh(self.fov.x,self.fov.y,0.5,2000.0) perspective_rh(self.fov.x,self.fov.y,0.4,4000.0)
} }
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,
@ -147,10 +146,17 @@ impl GraphicsState{
pub fn load_user_settings(&mut self,user_settings:&crate::settings::UserSettings){ pub fn load_user_settings(&mut self,user_settings:&crate::settings::UserSettings){
self.camera.fov=user_settings.calculate_fov(1.0,&self.camera.screen_size).as_vec2(); self.camera.fov=user_settings.calculate_fov(1.0,&self.camera.screen_size).as_vec2();
} }
pub fn generate_models(&mut self,device:&wgpu::Device,queue:&wgpu::Queue,map:&map::CompleteMap,textures:strafesnet_texture_loader::texture_loader::Textures){ pub fn generate_models(&mut self,device:&wgpu::Device,queue:&wgpu::Queue,map:&map::CompleteMap){
//generate texture view per texture //generate texture view per texture
let texture_views:Vec<wgpu::TextureView>=textures.into_iter().map(|(texture_id,texture_data)|{ let texture_views:HashMap<strafesnet_common::model::TextureId,wgpu::TextureView>=map.textures.iter().enumerate().filter_map(|(texture_id,texture_data)|{
let image=ddsfile::Dds::read(std::io::Cursor::new(texture_data)).unwrap(); let texture_id=model::TextureId::new(texture_id as u32);
let image=match ddsfile::Dds::read(std::io::Cursor::new(texture_data)){
Ok(image)=>image,
Err(e)=>{
println!("Error loading texture: {e}");
return None;
},
};
let (mut width,mut height)=(image.get_width(),image.get_height()); let (mut width,mut height)=(image.get_width(),image.get_height());
@ -162,7 +168,10 @@ impl GraphicsState{
height=height/4*4; height=height/4*4;
wgpu::TextureFormat::Bc7RgbaUnormSrgb wgpu::TextureFormat::Bc7RgbaUnormSrgb
}, },
other=>panic!("unsupported format{:?}",other), other=>{
println!("unsupported texture format{:?}",other);
return None;
},
}; };
let size=wgpu::Extent3d{ let size=wgpu::Extent3d{
@ -192,11 +201,11 @@ impl GraphicsState{
wgpu::util::TextureDataOrder::LayerMajor, wgpu::util::TextureDataOrder::LayerMajor,
&image.data, &image.data,
); );
texture.create_view(&wgpu::TextureViewDescriptor{ Some((texture_id,texture.create_view(&wgpu::TextureViewDescriptor{
label:Some(format!("Texture{} View",texture_id.get()).as_str()), label:Some(format!("Texture{} View",texture_id.get()).as_str()),
dimension:Some(wgpu::TextureViewDimension::D2), dimension:Some(wgpu::TextureViewDimension::D2),
..wgpu::TextureViewDescriptor::default() ..wgpu::TextureViewDescriptor::default()
}) })))
}).collect(); }).collect();
let num_textures=texture_views.len(); let num_textures=texture_views.len();
@ -210,18 +219,12 @@ 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
if let Some(owned_mesh_map)=owned_mesh_id_from_mesh_id_render_config_id.get(&model.mesh){ let owned_mesh_map=owned_mesh_id_from_mesh_id_render_config_id
//the mesh has already been split into a set of unique renderconfig meshes .entry(model.mesh).or_insert_with(||{
//simply add one instance to each of them
for owned_mesh_id in owned_mesh_map.values(){
let owned_mesh=unique_render_config_models.get_mut(owned_mesh_id.get() as usize).unwrap();
owned_mesh.instances.push(instance.clone());
}
}else{
let mut owned_mesh_map=HashMap::new(); let mut owned_mesh_map=HashMap::new();
//add mesh if renderid never before seen for this model //add mesh if renderid never before seen for this model
//add instance //add instance
@ -229,29 +232,23 @@ impl GraphicsState{
//check each group, if it's using a new render config then make a new clone of the model //check each group, if it's using a new render config then make a new clone of the model
if let Some(mesh)=map.meshes.get(model.mesh.get() as usize){ if let Some(mesh)=map.meshes.get(model.mesh.get() as usize){
for graphics_group in mesh.graphics_groups.iter(){ for graphics_group in mesh.graphics_groups.iter(){
let render_config=&map.render_configs[graphics_group.render.get() as usize];
if model.color.w==0.0&&render_config.texture.is_none(){
continue;
}
//get or create owned mesh //get or create owned mesh
let owned_mesh_id=if let Some(&owned_mesh_id)=owned_mesh_map.get(&graphics_group.render){ let owned_mesh_id=owned_mesh_map
owned_mesh_id .entry(graphics_group.render).or_insert_with(||{
}else{
//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);
owned_mesh_map.insert(graphics_group.render,owned_mesh_id);
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,
polys:model::PolygonGroup::PolygonList(model::PolygonList::new(Vec::new())), polys:model::PolygonGroup::PolygonList(model::PolygonList::new(Vec::new())),
instances:vec![instance.clone()], instances:Vec::new(),
}); });
owned_mesh_id owned_mesh_id
}; });
let owned_mesh=unique_render_config_models.get_mut(owned_mesh_id.get() as usize).unwrap(); let owned_mesh=unique_render_config_models.get_mut(owned_mesh_id.get() as usize).unwrap();
match &mut owned_mesh.polys{ match &mut owned_mesh.polys{
model::PolygonGroup::PolygonList(polygon_list)=>polygon_list.extend( model::PolygonGroup::PolygonList(polygon_list)=>polygon_list.extend(
@ -265,8 +262,16 @@ impl GraphicsState{
} }
} }
} }
owned_mesh_id_from_mesh_id_render_config_id.insert(model.mesh,owned_mesh_map); owned_mesh_map
}; });
for owned_mesh_id in owned_mesh_map.values(){
let owned_mesh=unique_render_config_models.get_mut(owned_mesh_id.get() as usize).unwrap();
let render_config=&map.render_configs[owned_mesh.render_config.get() as usize];
if model.color.w==0.0&&render_config.texture.is_none(){
continue;
}
owned_mesh.instances.push(instance.clone());
}
} }
//check every model to see if it's using the same (texture,color) but has few instances,if it is combine it into one model //check every model to see if it's using the same (texture,color) but has few instances,if it is combine it into one model
//1. collect unique instances of texture and color,note model id //1. collect unique instances of texture and color,note model id
@ -285,24 +290,14 @@ impl GraphicsState{
continue; continue;
} }
//populate hashmap //populate hashmap
let unique_color=if let Some(unique_color)=unique_texture_color.get_mut(&model.render_config){ let unique_color=unique_texture_color
unique_color .entry(model.render_config)
}else{ .or_insert_with(||HashMap::new());
//make new hashmap
let unique_color=HashMap::new();
unique_texture_color.insert(model.render_config,unique_color);
unique_texture_color.get_mut(&model.render_config).unwrap()
};
//separate instances by color //separate instances by color
for (instance_id,instance) in model.instances.iter().enumerate(){ for (instance_id,instance) in model.instances.iter().enumerate(){
let model_instance_list=if let Some(model_instance_list)=unique_color.get_mut(&instance.color){ let model_instance_list=unique_color
model_instance_list .entry(instance.color)
}else{ .or_insert_with(||Vec::new());
//make new hashmap
let model_instance_list=Vec::new();
unique_color.insert(instance.color.clone(),model_instance_list);
unique_color.get_mut(&instance.color).unwrap()
};
//add model instance to list //add model instance to list
model_instance_list.push((model_id,instance_id)); model_instance_list.push((model_id,instance_id));
} }
@ -338,46 +333,34 @@ impl GraphicsState{
let map_pos_id:Vec<PositionId>=model.unique_pos.iter().map(|untransformed_pos|{ let map_pos_id:Vec<PositionId>=model.unique_pos.iter().map(|untransformed_pos|{
let pos=instance.transform.transform_point3(glam::Vec3::from_array(untransformed_pos.clone())).to_array(); let pos=instance.transform.transform_point3(glam::Vec3::from_array(untransformed_pos.clone())).to_array();
let h=bytemuck::cast::<[f32;3],[u32;3]>(pos); let h=bytemuck::cast::<[f32;3],[u32;3]>(pos);
PositionId::new((if let Some(&pos_id)=pos_id_from.get(&h){ PositionId::new(*pos_id_from.entry(h).or_insert_with(||{
pos_id
}else{
let pos_id=unique_pos.len(); let pos_id=unique_pos.len();
unique_pos.push(pos); unique_pos.push(pos);
pos_id_from.insert(h,pos_id);
pos_id pos_id
}) as u32) }) as u32)
}).collect(); }).collect();
let map_tex_id:Vec<TextureCoordinateId>=model.unique_tex.iter().map(|&tex|{ let map_tex_id:Vec<TextureCoordinateId>=model.unique_tex.iter().map(|&tex|{
let h=bytemuck::cast::<[f32;2],[u32;2]>(tex); let h=bytemuck::cast::<[f32;2],[u32;2]>(tex);
TextureCoordinateId::new((if let Some(&tex_id)=tex_id_from.get(&h){ TextureCoordinateId::new(*tex_id_from.entry(h).or_insert_with(||{
tex_id
}else{
let tex_id=unique_tex.len(); let tex_id=unique_tex.len();
unique_tex.push(tex); unique_tex.push(tex);
tex_id_from.insert(h,tex_id);
tex_id tex_id
}) as u32) }) as u32)
}).collect(); }).collect();
let map_normal_id:Vec<NormalId>=model.unique_normal.iter().map(|untransformed_normal|{ let map_normal_id:Vec<NormalId>=model.unique_normal.iter().map(|untransformed_normal|{
let normal=(instance.normal_transform*glam::Vec3::from_array(untransformed_normal.clone())).to_array(); let normal=(instance.normal_transform*glam::Vec3::from_array(untransformed_normal.clone())).to_array();
let h=bytemuck::cast::<[f32;3],[u32;3]>(normal); let h=bytemuck::cast::<[f32;3],[u32;3]>(normal);
NormalId::new((if let Some(&normal_id)=normal_id_from.get(&h){ NormalId::new(*normal_id_from.entry(h).or_insert_with(||{
normal_id
}else{
let normal_id=unique_normal.len(); let normal_id=unique_normal.len();
unique_normal.push(normal); unique_normal.push(normal);
normal_id_from.insert(h,normal_id);
normal_id normal_id
}) as u32) }) as u32)
}).collect(); }).collect();
let map_color_id:Vec<ColorId>=model.unique_color.iter().map(|&color|{ let map_color_id:Vec<ColorId>=model.unique_color.iter().map(|&color|{
let h=bytemuck::cast::<[f32;4],[u32;4]>(color); let h=bytemuck::cast::<[f32;4],[u32;4]>(color);
ColorId::new((if let Some(&color_id)=color_id_from.get(&h){ ColorId::new(*color_id_from.entry(h).or_insert_with(||{
color_id
}else{
let color_id=unique_color.len(); let color_id=unique_color.len();
unique_color.push(color); unique_color.push(color);
color_id_from.insert(h,color_id);
color_id color_id
}) as u32) }) as u32)
}).collect(); }).collect();
@ -390,12 +373,9 @@ impl GraphicsState{
normal:map_normal_id[unmapped_vertex.normal.get() as usize], normal:map_normal_id[unmapped_vertex.normal.get() as usize],
color:map_color_id[unmapped_vertex.color.get() as usize], color:map_color_id[unmapped_vertex.color.get() as usize],
}; };
VertexId::new((if let Some(&vertex_id)=vertex_id_from.get(&vertex){ VertexId::new(*vertex_id_from.entry(vertex.clone()).or_insert_with(||{
vertex_id
}else{
let vertex_id=unique_vertices.len(); let vertex_id=unique_vertices.len();
unique_vertices.push(vertex.clone()); unique_vertices.push(vertex);
vertex_id_from.insert(vertex,vertex_id);
vertex_id vertex_id
}) as u32) }) as u32)
}).collect(); }).collect();
@ -438,25 +418,26 @@ impl GraphicsState{
//this mut be combined in a more complex way if the models use different render patterns per group //this mut be combined in a more complex way if the models use different render patterns per group
let mut indices=Vec::new(); let mut indices=Vec::new();
for poly in model.polys.polys(){ for poly in model.polys.polys(){
for end_index in 2..poly.len(){ let mut poly_vertices=poly.iter()
for index in [0,end_index-1,end_index]{ .map(|&vertex_index|*index_from_vertex.entry(vertex_index).or_insert_with(||{
let vertex_index=poly[index]; let i=vertices.len();
if let Some(&i)=index_from_vertex.get(&vertex_index){ let vertex=&model.unique_vertices[vertex_index.get() as usize];
indices.push(i); vertices.push(GraphicsVertex{
}else{ pos:model.unique_pos[vertex.pos.get() as usize],
let i=vertices.len(); tex:model.unique_tex[vertex.tex.get() as usize],
let vertex=&model.unique_vertices[vertex_index.get() as usize]; normal:model.unique_normal[vertex.normal.get() as usize],
vertices.push(GraphicsVertex{ color:model.unique_color[vertex.color.get() as usize],
pos:model.unique_pos[vertex.pos.get() as usize], });
tex:model.unique_tex[vertex.tex.get() as usize], i
normal:model.unique_normal[vertex.normal.get() as usize], }));
color:model.unique_color[vertex.color.get() as usize],
}); let a=poly_vertices.next().unwrap();
index_from_vertex.insert(vertex_index,i); let mut b=poly_vertices.next().unwrap();
indices.push(i);
} poly_vertices.for_each(|c|{
} indices.extend([a,b,c]);
} b=c;
});
} }
GraphicsMeshOwnedRenderConfig{ GraphicsMeshOwnedRenderConfig{
instances:model.instances, instances:model.instances,
@ -481,17 +462,18 @@ impl GraphicsState{
instance_count+=model.instances.len(); instance_count+=model.instances.len();
for instances_chunk in model.instances.rchunks(chunk_size){ for instances_chunk in model.instances.rchunks(chunk_size){
model_count+=1; model_count+=1;
let model_uniforms=get_instances_buffer_data(instances_chunk); let mut model_uniforms=get_instances_buffer_data(instances_chunk);
//TEMP: fill with zeroes to pass validation
model_uniforms.resize(MODEL_BUFFER_SIZE*512,0.0f32);
let model_buf=device.create_buffer_init(&wgpu::util::BufferInitDescriptor{ let model_buf=device.create_buffer_init(&wgpu::util::BufferInitDescriptor{
label:Some(format!("Model{} Buf",model_count).as_str()), label:Some(format!("Model{} Buf",model_count).as_str()),
contents:bytemuck::cast_slice(&model_uniforms), contents:bytemuck::cast_slice(&model_uniforms),
usage:wgpu::BufferUsages::UNIFORM|wgpu::BufferUsages::COPY_DST, usage:wgpu::BufferUsages::UNIFORM|wgpu::BufferUsages::COPY_DST,
}); });
let render_config=&map.render_configs[model.render_config.get() as usize]; let render_config=&map.render_configs[model.render_config.get() as usize];
let texture_view=match render_config.texture{ let texture_view=render_config.texture.and_then(|texture_id|
Some(texture_id)=>&texture_views[texture_id.get() as usize], texture_views.get(&texture_id)
None=>&self.temp_squid_texture_view, ).unwrap_or(&self.temp_squid_texture_view);
};
let bind_group=device.create_bind_group(&wgpu::BindGroupDescriptor{ let bind_group=device.create_bind_group(&wgpu::BindGroupDescriptor{
layout:&self.bind_group_layouts.model, layout:&self.bind_group_layouts.model,
entries:&[ entries:&[
@ -524,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,
}); });
} }
} }
@ -773,11 +754,13 @@ impl GraphicsState{
module:&shader, module:&shader,
entry_point:"vs_sky", entry_point:"vs_sky",
buffers:&[], buffers:&[],
compilation_options:wgpu::PipelineCompilationOptions::default(),
}, },
fragment:Some(wgpu::FragmentState{ fragment:Some(wgpu::FragmentState{
module:&shader, module:&shader,
entry_point:"fs_sky", entry_point:"fs_sky",
targets:&[Some(config.view_formats[0].into())], targets:&[Some(config.view_formats[0].into())],
compilation_options:wgpu::PipelineCompilationOptions::default(),
}), }),
primitive:wgpu::PrimitiveState{ primitive:wgpu::PrimitiveState{
front_face:wgpu::FrontFace::Cw, front_face:wgpu::FrontFace::Cw,
@ -792,6 +775,7 @@ impl GraphicsState{
}), }),
multisample:wgpu::MultisampleState::default(), multisample:wgpu::MultisampleState::default(),
multiview:None, multiview:None,
cache:None,
}); });
let model_pipeline=device.create_render_pipeline(&wgpu::RenderPipelineDescriptor{ let model_pipeline=device.create_render_pipeline(&wgpu::RenderPipelineDescriptor{
label:Some("Model Pipeline"), label:Some("Model Pipeline"),
@ -804,11 +788,13 @@ impl GraphicsState{
step_mode:wgpu::VertexStepMode::Vertex, step_mode:wgpu::VertexStepMode::Vertex,
attributes:&wgpu::vertex_attr_array![0=>Float32x3,1=>Float32x2,2=>Float32x3,3=>Float32x4], attributes:&wgpu::vertex_attr_array![0=>Float32x3,1=>Float32x2,2=>Float32x3,3=>Float32x4],
}], }],
compilation_options:wgpu::PipelineCompilationOptions::default(),
}, },
fragment:Some(wgpu::FragmentState{ fragment:Some(wgpu::FragmentState{
module:&shader, module:&shader,
entry_point:"fs_entity_texture", entry_point:"fs_entity_texture",
targets:&[Some(config.view_formats[0].into())], targets:&[Some(config.view_formats[0].into())],
compilation_options:wgpu::PipelineCompilationOptions::default(),
}), }),
primitive:wgpu::PrimitiveState{ primitive:wgpu::PrimitiveState{
front_face:wgpu::FrontFace::Cw, front_face:wgpu::FrontFace::Cw,
@ -824,10 +810,11 @@ impl GraphicsState{
}), }),
multisample:wgpu::MultisampleState::default(), multisample:wgpu::MultisampleState::default(),
multiview:None, multiview:None,
cache:None,
}); });
let camera=GraphicsCamera::default(); let camera=GraphicsCamera::default();
let camera_uniforms=camera.to_uniform_data(crate::physics::PhysicsOutputState::default().extrapolate(glam::IVec2::ZERO,integer::Time::ZERO)); 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),
@ -895,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(mouse_pos,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,strafesnet_texture_loader::texture_loader::Textures), 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,textures)=>{ Instruction::ChangeMap(map)=>{
graphics.generate_models(&device,&queue,&map,textures);
},
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,7 +56,7 @@ 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,3 +1,4 @@
mod file;
mod setup; mod setup;
mod window; mod window;
mod worker; mod worker;

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( 4294967296, 0, 0),dot:Planar64::raw(4294967296)}, Face{normal:vec3::raw_xyz( 4294967296, 0, 0),dot:Planar64::raw(4294967296)},
Face{normal:Planar64Vec3::raw( 0, 4294967296, 0),dot:Planar64::raw(4294967296)}, Face{normal:vec3::raw_xyz( 0, 4294967296, 0),dot:Planar64::raw(4294967296)},
Face{normal:Planar64Vec3::raw( 0, 0, 4294967296),dot:Planar64::raw(4294967296)}, Face{normal:vec3::raw_xyz( 0, 0, 4294967296),dot:Planar64::raw(4294967296)},
Face{normal:Planar64Vec3::raw(-4294967296, 0, 0),dot:Planar64::raw(4294967296)}, Face{normal:vec3::raw_xyz(-4294967296, 0, 0),dot:Planar64::raw(4294967296)},
Face{normal:Planar64Vec3::raw( 0,-4294967296, 0),dot:Planar64::raw(4294967296)}, Face{normal:vec3::raw_xyz( 0,-4294967296, 0),dot:Planar64::raw(4294967296)},
Face{normal:Planar64Vec3::raw( 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( 4294967296,-4294967296,-4294967296)), Vert(vec3::raw_xyz( 4294967296,-4294967296,-4294967296)),
Vert(Planar64Vec3::raw( 4294967296, 4294967296,-4294967296)), Vert(vec3::raw_xyz( 4294967296, 4294967296,-4294967296)),
Vert(Planar64Vec3::raw( 4294967296, 4294967296, 4294967296)), Vert(vec3::raw_xyz( 4294967296, 4294967296, 4294967296)),
Vert(Planar64Vec3::raw( 4294967296,-4294967296, 4294967296)), Vert(vec3::raw_xyz( 4294967296,-4294967296, 4294967296)),
Vert(Planar64Vec3::raw(-4294967296, 4294967296,-4294967296)), Vert(vec3::raw_xyz(-4294967296, 4294967296,-4294967296)),
Vert(Planar64Vec3::raw(-4294967296, 4294967296, 4294967296)), Vert(vec3::raw_xyz(-4294967296, 4294967296, 4294967296)),
Vert(Planar64Vec3::raw(-4294967296,-4294967296, 4294967296)), Vert(vec3::raw_xyz(-4294967296,-4294967296, 4294967296)),
Vert(Planar64Vec3::raw(-4294967296,-4294967296,-4294967296)) Vert(vec3::raw_xyz(-4294967296,-4294967296,-4294967296))
] ]
}; };
let mesh_topology=PhysicsMeshTopology{ let mesh_topology=PhysicsMeshTopology{
@ -280,9 +284,25 @@ impl EdgePool{
(&mut unsafe{self.edge_guys.get_unchecked_mut(edge_id.get() as usize)}.1,edge_id) (&mut unsafe{self.edge_guys.get_unchecked_mut(edge_id.get() as usize)}.1,edge_id)
} }
} }
impl From<&model::Mesh> for PhysicsMesh{
fn from(mesh:&model::Mesh)->Self{ #[derive(Debug)]
assert!(mesh.unique_pos.len()!=0,"Mesh cannot have 0 vertices"); pub enum PhysicsMeshError{
ZeroVertices,
NoPhysicsGroups,
}
impl std::fmt::Display for PhysicsMeshError{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f,"{self:?}")
}
}
impl std::error::Error for PhysicsMeshError{}
impl TryFrom<&model::Mesh> for PhysicsMesh{
type Error=PhysicsMeshError;
fn try_from(mesh:&model::Mesh)->Result<Self,PhysicsMeshError>{
if mesh.unique_pos.len()==0{
return Err(PhysicsMeshError::ZeroVertices);
}
let verts=mesh.unique_pos.iter().copied().map(Vert).collect(); let verts=mesh.unique_pos.iter().copied().map(Vert).collect();
//TODO: fix submeshes //TODO: fix submeshes
//flat map mesh.physics_groups[$1].groups.polys()[$2] as face_id //flat map mesh.physics_groups[$1].groups.polys()[$2] as face_id
@ -314,7 +334,7 @@ impl From<&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);
@ -325,11 +345,11 @@ impl From<&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);
@ -346,14 +366,16 @@ impl From<&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=>{
@ -384,14 +406,14 @@ impl From<&model::Mesh> for PhysicsMesh{
).collect(), ).collect(),
} }
}).collect(); }).collect();
Self{ Ok(Self{
data:PhysicsMeshData{ data:PhysicsMeshData{
faces, faces,
verts, verts,
}, },
complete_mesh:mesh_topologies.pop().unwrap(), complete_mesh:mesh_topologies.pop().ok_or(PhysicsMeshError::NoPhysicsGroups)?,
submeshes:mesh_topologies, submeshes:mesh_topologies,
} })
} }
} }
@ -400,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)
@ -428,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,
} }
} }
@ -446,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>{
@ -455,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>>{
@ -509,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),
@ -528,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),
@ -549,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),
@ -565,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
@ -574,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{
@ -584,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);
@ -594,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);
@ -604,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());
@ -664,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);
} }
@ -678,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){
@ -695,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),
@ -711,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(){
@ -724,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;
} }
@ -735,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);
@ -751,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);
@ -800,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);
@ -829,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);
@ -873,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));
@ -897,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));
@ -912,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{
@ -920,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{
@ -937,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,122 +16,227 @@ 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,
} }
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,strafesnet_texture_loader::texture_loader::Textures), 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{
//"PlayerController"
user_settings:crate::settings::UserSettings,
//"MouseInterpolator"
timeline:std::collections::VecDeque<TimedInstruction<PhysicsInputInstruction>>,
last_mouse_time:Time,//this value is pre-transformed to simulation time
mouse_blocking:bool,
//"Simulation"
timer:Timer<Scaled>,
physics:crate::physics::PhysicsContext,
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>>{ }
let mut mouse_blocking=true; impl MouseInterpolator{
let mut last_mouse_time=physics.state.next_mouse.time; pub fn new(
let mut timeline=std::collections::VecDeque::new(); physics:crate::physics::PhysicsContext,
crate::compat_worker::QNWorker::new(move |ins:TimedInstruction<Instruction>|{ user_settings:crate::settings::UserSettings,
if if let Some(phys_input)=match &ins.instruction{ )->MouseInterpolator{
Instruction::Input(input_instruction)=>match input_instruction{ MouseInterpolator{
&InputInstruction::MoveMouse(m)=>{ mouse_blocking:true,
if mouse_blocking{ last_mouse_time:physics.get_next_mouse().time,
//tell the game state which is living in the past about its future timeline:std::collections::VecDeque::new(),
timeline.push_front(TimedInstruction{ timer:Timer::from_state(Scaled::identity(),false),
time:last_mouse_time, physics,
instruction:PhysicsInputInstruction::SetNextMouse(MouseState{time:ins.time,pos:m}), user_settings,
}); }
}else{
//mouse has just started moving again after being still for longer than 10ms.
//replace the entire mouse interpolation state to avoid an intermediate state with identical m0.t m1.t timestamps which will divide by zero
timeline.push_front(TimedInstruction{
time:last_mouse_time,
instruction:PhysicsInputInstruction::ReplaceMouse(
MouseState{time:last_mouse_time,pos:physics.state.next_mouse.pos},
MouseState{time:ins.time,pos:m}
),
});
//delay physics execution until we have an interpolation target
mouse_blocking=true;
}
last_mouse_time=ins.time;
None
},
&InputInstruction::MoveForward(s)=>Some(PhysicsInputInstruction::SetMoveForward(s)),
&InputInstruction::MoveLeft(s)=>Some(PhysicsInputInstruction::SetMoveLeft(s)),
&InputInstruction::MoveBack(s)=>Some(PhysicsInputInstruction::SetMoveBack(s)),
&InputInstruction::MoveRight(s)=>Some(PhysicsInputInstruction::SetMoveRight(s)),
&InputInstruction::MoveUp(s)=>Some(PhysicsInputInstruction::SetMoveUp(s)),
&InputInstruction::MoveDown(s)=>Some(PhysicsInputInstruction::SetMoveDown(s)),
&InputInstruction::Jump(s)=>Some(PhysicsInputInstruction::SetJump(s)),
&InputInstruction::Zoom(s)=>Some(PhysicsInputInstruction::SetZoom(s)),
InputInstruction::Reset=>Some(PhysicsInputInstruction::Reset),
},
Instruction::GenerateModels(_,_)=>Some(PhysicsInputInstruction::Idle),
Instruction::ClearModels=>Some(PhysicsInputInstruction::Idle),
Instruction::Resize(_,_)=>Some(PhysicsInputInstruction::Idle),
Instruction::Render=>Some(PhysicsInputInstruction::Idle),
}{
//non-mouse event
timeline.push_back(TimedInstruction{
time:ins.time,
instruction:phys_input,
});
if mouse_blocking{
//assume the mouse has stopped moving after 10ms.
//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,
//so mouse events are probably not handled separately from drawing and fire right before it :(
if Time::from_millis(10)<ins.time-physics.state.next_mouse.time{
//push an event to extrapolate no movement from
timeline.push_front(TimedInstruction{
time:last_mouse_time,
instruction:PhysicsInputInstruction::SetNextMouse(MouseState{time:ins.time,pos:physics.state.next_mouse.pos}),
});
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.
mouse_blocking=false;
true
}else{
false
}
}else{
//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
last_mouse_time=ins.time;
true
}
}else{
//mouse event
true
}{
//empty queue
while let Some(instruction)=timeline.pop_front(){
physics.run(instruction.time);
physics.process_instruction(TimedInstruction{
time:instruction.time,
instruction:crate::physics::PhysicsInstruction::Input(instruction.instruction),
});
}
}
match ins.instruction{
Instruction::Render=>{
graphics_worker.send(crate::graphics_worker::Instruction::Render(physics.state.output(),ins.time,physics.state.next_mouse.pos)).unwrap();
},
Instruction::Resize(size,user_settings)=>{
graphics_worker.send(crate::graphics_worker::Instruction::Resize(size,user_settings)).unwrap();
},
Instruction::GenerateModels(map,textures)=>{
physics.generate_models(&map);
physics.spawn();
graphics_worker.send(crate::graphics_worker::Instruction::GenerateModels(map,textures)).unwrap();
},
Instruction::ClearModels=>{
physics.state.clear();
graphics_worker.send(crate::graphics_worker::Instruction::ClearModels).unwrap();
},
_=>(),
}
})
} }
fn push_mouse_instruction(&mut self,ins:&TimedInstruction<Instruction>,m:glam::IVec2){
if self.mouse_blocking{
//tell the game state which is living in the past about its future
self.timeline.push_front(TimedInstruction{
time:self.last_mouse_time,
instruction:PhysicsInputInstruction::SetNextMouse(MouseState{time:self.timer.time(ins.time),pos:m}),
});
}else{
//mouse has just started moving again after being still for longer than 10ms.
//replace the entire mouse interpolation state to avoid an intermediate state with identical m0.t m1.t timestamps which will divide by zero
self.timeline.push_front(TimedInstruction{
time:self.last_mouse_time,
instruction:PhysicsInputInstruction::ReplaceMouse(
MouseState{time:self.last_mouse_time,pos:self.physics.get_next_mouse().pos},
MouseState{time:self.timer.time(ins.time),pos:m}
),
});
//delay physics execution until we have an interpolation target
self.mouse_blocking=true;
}
self.last_mouse_time=self.timer.time(ins.time);
}
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!
fn map_instruction(&mut self,ins:&TimedInstruction<Instruction>)->bool{
let mut update_mouse_blocking=true;
match &ins.instruction{
Instruction::Input(input_instruction)=>match input_instruction{
&InputInstruction::MoveMouse(m)=>{
if !self.timer.is_paused(){
self.push_mouse_instruction(ins,m);
}
update_mouse_blocking=false;
},
&InputInstruction::MoveForward(s)=>self.push(ins.time,PhysicsInputInstruction::SetMoveForward(s)),
&InputInstruction::MoveLeft(s)=>self.push(ins.time,PhysicsInputInstruction::SetMoveLeft(s)),
&InputInstruction::MoveBack(s)=>self.push(ins.time,PhysicsInputInstruction::SetMoveBack(s)),
&InputInstruction::MoveRight(s)=>self.push(ins.time,PhysicsInputInstruction::SetMoveRight(s)),
&InputInstruction::MoveUp(s)=>self.push(ins.time,PhysicsInputInstruction::SetMoveUp(s)),
&InputInstruction::MoveDown(s)=>self.push(ins.time,PhysicsInputInstruction::SetMoveDown(s)),
&InputInstruction::Jump(s)=>self.push(ins.time,PhysicsInputInstruction::SetJump(s)),
&InputInstruction::Zoom(s)=>self.push(ins.time,PhysicsInputInstruction::SetZoom(s)),
&InputInstruction::ResetAndSpawn(mode_id,stage_id)=>{
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),
},
//do these really need to idle the physics?
//sending None dumps the instruction queue
Instruction::ChangeMap(_)=>self.push(ins.time,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
}
}
/// 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{
//assume the mouse has stopped moving after 10ms.
//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,
//so mouse events are probably not handled separately from drawing and fire right before it :(
if Time::from_millis(10)<self.timer.time(time)-self.physics.get_next_mouse().time{
self.unblock_mouse(time);
true
}else{
false
}
}else{
//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
self.last_mouse_time=self.timer.time(time);
true
}
}
fn empty_queue(&mut self){
while let Some(instruction)=self.timeline.pop_front(){
self.physics.run_input_instruction(instruction);
}
}
pub fn handle_instruction(&mut self,ins:&TimedInstruction<Instruction>){
let should_empty_queue=self.map_instruction(ins);
if should_empty_queue{
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<'a>(
mut graphics_worker:crate::compat_worker::INWorker<'a,crate::graphics_worker::Instruction>,
user_settings:crate::settings::UserSettings,
)->crate::compat_worker::QNWorker<'a,TimedInstruction<Instruction>>{
let physics=crate::physics::PhysicsContext::default();
let mut interpolator=MouseInterpolator::new(
physics,
user_settings
);
crate::compat_worker::QNWorker::new(move |ins:TimedInstruction<Instruction>|{
interpolator.handle_instruction(&ins);
match ins.instruction{
Instruction::Render=>{
let frame_state=interpolator.get_frame_state(ins.time);
graphics_worker.send(crate::graphics_worker::Instruction::Render(frame_state)).unwrap();
},
Instruction::Resize(size)=>{
graphics_worker.send(crate::graphics_worker::Instruction::Resize(size,interpolator.user_settings().clone())).unwrap();
},
Instruction::ChangeMap(map)=>{
interpolator.change_map(ins.time,&map);
graphics_worker.send(crate::graphics_worker::Instruction::ChangeMap(map)).unwrap();
},
Instruction::Input(_)=>(),
Instruction::SetPaused(_)=>(),
}
})
}

View File

@ -25,14 +25,14 @@ struct SetupContextPartial1{
instance:wgpu::Instance, instance:wgpu::Instance,
} }
fn create_window(title:&str,event_loop:&winit::event_loop::EventLoop<()>)->Result<winit::window::Window,winit::error::OsError>{ fn create_window(title:&str,event_loop:&winit::event_loop::EventLoop<()>)->Result<winit::window::Window,winit::error::OsError>{
let mut builder = winit::window::WindowBuilder::new(); let mut attr=winit::window::WindowAttributes::default();
builder = builder.with_title(title); attr=attr.with_title(title);
#[cfg(windows_OFF)] // TODO #[cfg(windows_OFF)] // TODO
{ {
use winit::platform::windows::WindowBuilderExtWindows; use winit::platform::windows::WindowBuilderExtWindows;
builder = builder.with_no_redirection_bitmap(true); builder=builder.with_no_redirection_bitmap(true);
} }
builder.build(event_loop) event_loop.create_window(attr)
} }
fn create_instance()->SetupContextPartial1{ fn create_instance()->SetupContextPartial1{
let backends=wgpu::util::backend_bits_from_env().unwrap_or_else(wgpu::Backends::all); let backends=wgpu::util::backend_bits_from_env().unwrap_or_else(wgpu::Backends::all);
@ -143,6 +143,7 @@ impl<'a> SetupContextPartial3<'a>{
label: None, label: None,
required_features: (optional_features & self.adapter.features()) | required_features, required_features: (optional_features & self.adapter.features()) | required_features,
required_limits: needed_limits, required_limits: needed_limits,
memory_hints:wgpu::MemoryHints::Performance,
}, },
trace_dir.ok().as_ref().map(std::path::Path::new), trace_dir.ok().as_ref().map(std::path::Path::new),
)) ))
@ -212,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

@ -48,7 +48,7 @@ struct ModelInstance{
//my fancy idea is to create a megatexture for each model that includes all the textures each intance will need //my fancy idea is to create a megatexture for each model that includes all the textures each intance will need
//the texture transform then maps the texture coordinates to the location of the specific texture //the texture transform then maps the texture coordinates to the location of the specific texture
//group 1 is the model //group 1 is the model
const MAX_MODEL_INSTANCES=4096; const MAX_MODEL_INSTANCES=512;
@group(2) @group(2)
@binding(0) @binding(0)
var<uniform> model_instances: array<ModelInstance, MAX_MODEL_INSTANCES>; var<uniform> model_instances: array<ModelInstance, MAX_MODEL_INSTANCES>;

View File

@ -1,7 +1,6 @@
use crate::physics_worker::InputInstruction; use crate::physics_worker::InputInstruction;
use strafesnet_common::integer; use strafesnet_common::integer;
use strafesnet_common::instruction::TimedInstruction; use strafesnet_common::instruction::TimedInstruction;
use strafesnet_texture_loader::texture_loader::TextureLoaderTrait;
pub enum WindowInstruction{ pub enum WindowInstruction{
Resize(winit::dpi::PhysicalSize<u32>), Resize(winit::dpi::PhysicalSize<u32>),
@ -14,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,43 +26,17 @@ impl WindowContext<'_>{
fn window_event(&mut self,time:integer::Time,event: winit::event::WindowEvent) { fn window_event(&mut self,time:integer::Time,event: winit::event::WindowEvent) {
match event { match event {
winit::event::WindowEvent::DroppedFile(path)=>{ winit::event::WindowEvent::DroppedFile(path)=>{
let path=path.as_path(); match crate::file::load(path.as_path()){
//blocking because it's simpler... Ok(map)=>self.physics_thread.send(TimedInstruction{time,instruction:crate::physics_worker::Instruction::ChangeMap(map)}).unwrap(),
if let Ok(file)=std::fs::File::open(path){ Err(e)=>println!("Failed to load map: {e}"),
// match strafesnet_snf::read_snf(std::io::BufReader::new(file)){
// Ok(strafesnet_snf::SNF::Map(streamable_map))=>{
// if let Ok(indexed_model_instances)=streamable_map.load_all(){
// 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(indexed_model_instances)}).unwrap();
// }
// },
// Ok(strafesnet_snf::SNF::Bot(streamable_map))=>println!("File type not yet supported"),
// Ok(strafesnet_snf::SNF::Demo(streamable_map))=>println!("File type not yet supported"),
// Err(e)=>println!("Error reading file: {e:?}"),
// }
let mut texture_loader=strafesnet_texture_loader::legacy();
match (strafesnet_rbx_loader::read(file,|name|{
match texture_loader.acquire_id(name){
Ok(texture_id)=>Some(texture_id),
Err(e)=>{
println!("there was error: {e}");
None
},
}
}),texture_loader.load()){
(Ok(map),Ok(textures))=>{
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,textures)}).unwrap();
},
(Err(e),_)=>println!("Error reading file: {e:?}"),
(_,Err(e))=>println!("Error loading textures: {e:?}"),
}
}else{
println!("Failed to open file {path:?}");
} }
}, },
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{
@ -135,7 +107,12 @@ 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},
_=>None, _=>None,
}, },
_=>None, _=>None,
@ -188,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.state.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=>{
@ -245,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();
} }
@ -259,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();

View File

@ -1 +1 @@
mangohud ../target/release/strafe-client bhop_maps/5692113331.rbxm mangohud ../target/release/strafe-client bhop_maps/5692113331.snfm

View File

@ -1 +1 @@
/run/media/quat/Files/Documents/map-files/verify-scripts/maps/bhop_all/ /run/media/quat/Files/Documents/map-files/verify-scripts/maps/bhop_snfm

View File

@ -1 +1 @@
cargo build --release --target x86_64-pc-windows-gnu cargo build --release --target x86_64-pc-windows-gnu --all-features

1
tools/iso Executable file
View File

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

View File

@ -1 +1 @@
/run/media/quat/Files/Documents/map-files/verify-scripts/maps/surf_all/ /run/media/quat/Files/Documents/map-files/verify-scripts/maps/surf_snfm

View File

@ -1 +0,0 @@
/run/media/quat/Files/Documents/map-files/verify-scripts/textures/dds/

View File

@ -1 +1 @@
mangohud ../target/release/strafe-client bhop_maps/5692152916.rbxm mangohud ../target/release/strafe-client bhop_maps/5692152916.snfm

View File

@ -1 +1 @@
mangohud ../target/release/strafe-client surf_maps/5692145408.rbxm mangohud ../target/release/strafe-client surf_maps/5692145408.snfm