forked from StrafesNET/strafe-client
fixed wide vectors
This commit is contained in:
parent
ccae1a45e1
commit
1a6831cf8b
68
Cargo.lock
generated
68
Cargo.lock
generated
@ -105,9 +105,9 @@ checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayvec"
|
name = "arrayvec"
|
||||||
version = "0.7.4"
|
version = "0.7.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
|
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "as-raw-xcb-connection"
|
name = "as-raw-xcb-connection"
|
||||||
@ -245,6 +245,12 @@ dependencies = [
|
|||||||
"objc2",
|
"objc2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bnum"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "50202def95bf36cb7d1d7a7962cea1c36a3f8ad42425e5d2b71d7acb8041b5b8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bstr"
|
name = "bstr"
|
||||||
version = "1.10.0"
|
version = "1.10.0"
|
||||||
@ -624,6 +630,18 @@ version = "0.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da"
|
checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fixed_wide"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
|
||||||
|
checksum = "7a8d6e10c51c9df39ead915c62288afbc41d13e00368e526037e530ee5c58e13"
|
||||||
|
dependencies = [
|
||||||
|
"arrayvec",
|
||||||
|
"bnum",
|
||||||
|
"paste",
|
||||||
|
"ratio_ops",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "foreign-types"
|
name = "foreign-types"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
@ -942,6 +960,17 @@ dependencies = [
|
|||||||
"redox_syscall 0.4.1",
|
"redox_syscall 0.4.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linear_ops"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
|
||||||
|
checksum = "b2e6977ac24f47086d8a7a2d4ae1c720e86dfdc8407cf5e34c18bfa01053c456"
|
||||||
|
dependencies = [
|
||||||
|
"fixed_wide",
|
||||||
|
"paste",
|
||||||
|
"ratio_ops",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.4.14"
|
version = "0.4.14"
|
||||||
@ -1609,6 +1638,12 @@ version = "0.1.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab"
|
checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ratio_ops"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
|
||||||
|
checksum = "01239195d6afe0509e7e3511b716c0540251dfe7ece0a9a5a27116afb766c42c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "raw-window-handle"
|
name = "raw-window-handle"
|
||||||
version = "0.6.2"
|
version = "0.6.2"
|
||||||
@ -1955,7 +1990,7 @@ dependencies = [
|
|||||||
"parking_lot",
|
"parking_lot",
|
||||||
"pollster",
|
"pollster",
|
||||||
"strafesnet_bsp_loader",
|
"strafesnet_bsp_loader",
|
||||||
"strafesnet_common",
|
"strafesnet_common 0.5.0",
|
||||||
"strafesnet_deferred_loader",
|
"strafesnet_deferred_loader",
|
||||||
"strafesnet_rbx_loader",
|
"strafesnet_rbx_loader",
|
||||||
"strafesnet_snf",
|
"strafesnet_snf",
|
||||||
@ -1970,7 +2005,7 @@ source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
|
|||||||
checksum = "35ee2c534efa039ad17ca41893ba1d75fafff014076353ac676c73fc808b9e44"
|
checksum = "35ee2c534efa039ad17ca41893ba1d75fafff014076353ac676c73fc808b9e44"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"glam",
|
"glam",
|
||||||
"strafesnet_common",
|
"strafesnet_common 0.4.1",
|
||||||
"vbsp",
|
"vbsp",
|
||||||
"vmdl",
|
"vmdl",
|
||||||
]
|
]
|
||||||
@ -1987,6 +2022,21 @@ dependencies = [
|
|||||||
"id",
|
"id",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strafesnet_common"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
|
||||||
|
checksum = "d8fcc44793ae84a1d80882f367980913292241c94eb87584de4010bdad4a918d"
|
||||||
|
dependencies = [
|
||||||
|
"arrayvec",
|
||||||
|
"bitflags 2.6.0",
|
||||||
|
"fixed_wide",
|
||||||
|
"glam",
|
||||||
|
"id",
|
||||||
|
"linear_ops",
|
||||||
|
"ratio_ops",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strafesnet_deferred_loader"
|
name = "strafesnet_deferred_loader"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
@ -1994,7 +2044,7 @@ source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
|
|||||||
checksum = "596aba6d2747818781336ad95a1ee496e37f70052fd625a299fc7a555a6938d4"
|
checksum = "596aba6d2747818781336ad95a1ee496e37f70052fd625a299fc7a555a6938d4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy-regex",
|
"lazy-regex",
|
||||||
"strafesnet_common",
|
"strafesnet_common 0.4.1",
|
||||||
"vbsp",
|
"vbsp",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -2013,18 +2063,18 @@ dependencies = [
|
|||||||
"rbx_reflection_database",
|
"rbx_reflection_database",
|
||||||
"rbx_xml",
|
"rbx_xml",
|
||||||
"roblox_emulator",
|
"roblox_emulator",
|
||||||
"strafesnet_common",
|
"strafesnet_common 0.4.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strafesnet_snf"
|
name = "strafesnet_snf"
|
||||||
version = "0.1.3"
|
version = "0.2.0"
|
||||||
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
|
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
|
||||||
checksum = "a9ae481152d0389be29967e1d5f0377498df8ff9638175d56cd8e2c2e6982bfa"
|
checksum = "c6e8856d79c29bd5687b08bc1653370f7e242c84d5c06afa8629bd3e00c433bf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"binrw 0.14.0",
|
"binrw 0.14.0",
|
||||||
"id",
|
"id",
|
||||||
"strafesnet_common",
|
"strafesnet_common 0.5.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -23,10 +23,10 @@ id = { version = "0.1.0", registry = "strafesnet" }
|
|||||||
parking_lot = "0.12.1"
|
parking_lot = "0.12.1"
|
||||||
pollster = "0.3.0"
|
pollster = "0.3.0"
|
||||||
strafesnet_bsp_loader = { version = "0.1.3", registry = "strafesnet", optional = true }
|
strafesnet_bsp_loader = { version = "0.1.3", registry = "strafesnet", optional = true }
|
||||||
strafesnet_common = { version = "0.4.0", registry = "strafesnet" }
|
strafesnet_common = { version = "0.5.0", registry = "strafesnet" }
|
||||||
strafesnet_deferred_loader = { version = "0.3.1", features = ["legacy"], registry = "strafesnet", optional = true }
|
strafesnet_deferred_loader = { version = "0.3.1", features = ["legacy"], registry = "strafesnet", optional = true }
|
||||||
strafesnet_rbx_loader = { version = "0.3.7", registry = "strafesnet", optional = true }
|
strafesnet_rbx_loader = { version = "0.3.7", registry = "strafesnet", optional = true }
|
||||||
strafesnet_snf = { version = "0.1.2", registry = "strafesnet", optional = true }
|
strafesnet_snf = { version = "0.2.0", registry = "strafesnet", optional = true }
|
||||||
wgpu = "22.0.0"
|
wgpu = "22.0.0"
|
||||||
winit = "0.30.4"
|
winit = "0.30.4"
|
||||||
|
|
||||||
|
@ -1,32 +1,33 @@
|
|||||||
use crate::physics::Body;
|
use crate::physics::Body;
|
||||||
use crate::model_physics::{FEV,MeshQuery,DirectedEdge};
|
use crate::model_physics::{GigaTime,FEV,MeshQuery,DirectedEdge,MinkowskiMesh,MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert};
|
||||||
use strafesnet_common::integer::{Time,Planar64};
|
use strafesnet_common::integer::{Time,Fixed,Ratio};
|
||||||
use strafesnet_common::zeroes::zeroes2;
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
enum Transition<F,E:DirectedEdge,V>{
|
enum Transition<F,E:DirectedEdge,V>{
|
||||||
Miss,
|
Miss,
|
||||||
Next(FEV<F,E,V>,Time),
|
Next(FEV<F,E,V>,GigaTime),
|
||||||
Hit(F,Time),
|
Hit(F,GigaTime),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_transition<F:Copy,E:Copy+DirectedEdge,V:Copy>(fev:&FEV<F,E,V>,time:Time,mesh:&impl MeshQuery<F,E,V>,body:&Body,time_limit:Time)->Transition<F,E,V>{
|
type MinkowskiFEV=FEV<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert>;
|
||||||
|
type MinkowskiTransition=Transition<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert>;
|
||||||
|
|
||||||
|
fn next_transition(fev:&MinkowskiFEV,body_time:GigaTime,mesh:&MinkowskiMesh,body:&Body,mut best_time:GigaTime)->MinkowskiTransition{
|
||||||
//conflicting derivative means it crosses in the wrong direction.
|
//conflicting derivative means it crosses in the wrong direction.
|
||||||
//if the transition time is equal to an already tested transition, do not replace the current best.
|
//if the transition time is equal to an already tested transition, do not replace the current best.
|
||||||
let mut best_time=time_limit;
|
let mut best_transition=MinkowskiTransition::Miss;
|
||||||
let mut best_transtition=Transition::Miss;
|
|
||||||
match fev{
|
match fev{
|
||||||
&FEV::<F,E,V>::Face(face_id)=>{
|
&MinkowskiFEV::Face(face_id)=>{
|
||||||
//test own face collision time, ignoring roots with zero or conflicting derivative
|
//test own face collision time, ignoring roots with zero or conflicting derivative
|
||||||
//n=face.normal d=face.dot
|
//n=face.normal d=face.dot
|
||||||
//n.a t^2+n.v t+n.p-d==0
|
//n.a t^2+n.v t+n.p-d==0
|
||||||
let (n,d)=mesh.face_nd(face_id);
|
let (n,d)=mesh.face_nd(face_id);
|
||||||
//TODO: use higher precision d value?
|
//TODO: use higher precision d value?
|
||||||
//use the mesh transform translation instead of baking it into the d value.
|
//use the mesh transform translation instead of baking it into the d value.
|
||||||
for t in zeroes2((n.dot(body.position)-d)*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
|
for dt in Fixed::<4,128>::zeroes2((n.dot(body.position)-d)*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
|
||||||
let t=body.time+Time::from(t);
|
if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
|
||||||
if time<=t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{
|
best_time=dt;
|
||||||
best_time=t;
|
best_transition=MinkowskiTransition::Hit(face_id,dt);
|
||||||
best_transtition=Transition::Hit(face_id,t);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -36,18 +37,18 @@ enum Transition<F,E:DirectedEdge,V>{
|
|||||||
let n=n.cross(edge_n);
|
let n=n.cross(edge_n);
|
||||||
let verts=mesh.edge_verts(directed_edge_id.as_undirected());
|
let verts=mesh.edge_verts(directed_edge_id.as_undirected());
|
||||||
//WARNING: d is moved out of the *2 block because of adding two vertices!
|
//WARNING: d is moved out of the *2 block because of adding two vertices!
|
||||||
for t in zeroes2(n.dot(body.position*2-(mesh.vert(verts[0])+mesh.vert(verts[1]))),n.dot(body.velocity)*2,n.dot(body.acceleration)){
|
//WARNING: precision is swept under the rug!
|
||||||
let t=body.time+Time::from(t);
|
for dt in Fixed::<4,128>::zeroes2(n.dot(body.position*2-(mesh.vert(verts[0])+mesh.vert(verts[1]))).fix_4(),n.dot(body.velocity).fix_4()*2,n.dot(body.acceleration).fix_4()){
|
||||||
if time<=t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{
|
if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
|
||||||
best_time=t;
|
best_time=dt;
|
||||||
best_transtition=Transition::Next(FEV::<F,E,V>::Edge(directed_edge_id.as_undirected()),t);
|
best_transition=MinkowskiTransition::Next(MinkowskiFEV::Edge(directed_edge_id.as_undirected()),dt);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//if none:
|
//if none:
|
||||||
},
|
},
|
||||||
&FEV::<F,E,V>::Edge(edge_id)=>{
|
&MinkowskiFEV::Edge(edge_id)=>{
|
||||||
//test each face collision time, ignoring roots with zero or conflicting derivative
|
//test each face collision time, ignoring roots with zero or conflicting derivative
|
||||||
let edge_n=mesh.edge_n(edge_id);
|
let edge_n=mesh.edge_n(edge_id);
|
||||||
let edge_verts=mesh.edge_verts(edge_id);
|
let edge_verts=mesh.edge_verts(edge_id);
|
||||||
@ -57,11 +58,10 @@ enum Transition<F,E:DirectedEdge,V>{
|
|||||||
//edge_n gets parity from the order of edge_faces
|
//edge_n gets parity from the order of edge_faces
|
||||||
let n=face_n.cross(edge_n)*((i as i64)*2-1);
|
let n=face_n.cross(edge_n)*((i as i64)*2-1);
|
||||||
//WARNING yada yada d *2
|
//WARNING yada yada d *2
|
||||||
for t in zeroes2(n.dot(delta_pos),n.dot(body.velocity)*2,n.dot(body.acceleration)){
|
for dt in Fixed::<4,128>::zeroes2(n.dot(delta_pos).fix_4(),n.dot(body.velocity).fix_4()*2,n.dot(body.acceleration).fix_4()){
|
||||||
let t=body.time+Time::from(t);
|
if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
|
||||||
if time<=t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{
|
best_time=dt;
|
||||||
best_time=t;
|
best_transition=MinkowskiTransition::Next(MinkowskiFEV::Face(edge_face_id),dt);
|
||||||
best_transtition=Transition::Next(FEV::<F,E,V>::Face(edge_face_id),t);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -70,27 +70,27 @@ enum Transition<F,E:DirectedEdge,V>{
|
|||||||
for (i,&vert_id) in edge_verts.iter().enumerate(){
|
for (i,&vert_id) in edge_verts.iter().enumerate(){
|
||||||
//vertex normal gets parity from vert index
|
//vertex normal gets parity from vert index
|
||||||
let n=edge_n*(1-2*(i as i64));
|
let n=edge_n*(1-2*(i as i64));
|
||||||
for t in zeroes2((n.dot(body.position-mesh.vert(vert_id)))*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
|
for dt in Fixed::<2,64>::zeroes2((n.dot(body.position-mesh.vert(vert_id)))*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
|
||||||
let t=body.time+Time::from(t);
|
if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
|
||||||
if time<=t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{
|
let dt=Ratio::new(dt.num.fix_4(),dt.den.fix_4());
|
||||||
best_time=t;
|
best_time=dt;
|
||||||
best_transtition=Transition::Next(FEV::<F,E,V>::Vert(vert_id),t);
|
best_transition=MinkowskiTransition::Next(MinkowskiFEV::Vert(vert_id),dt);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//if none:
|
//if none:
|
||||||
},
|
},
|
||||||
&FEV::<F,E,V>::Vert(vert_id)=>{
|
&MinkowskiFEV::Vert(vert_id)=>{
|
||||||
//test each edge collision time, ignoring roots with zero or conflicting derivative
|
//test each edge collision time, ignoring roots with zero or conflicting derivative
|
||||||
for &directed_edge_id in mesh.vert_edges(vert_id).iter(){
|
for &directed_edge_id in mesh.vert_edges(vert_id).iter(){
|
||||||
//edge is directed away from vertex, but we want the dot product to turn out negative
|
//edge is directed away from vertex, but we want the dot product to turn out negative
|
||||||
let n=-mesh.directed_edge_n(directed_edge_id);
|
let n=-mesh.directed_edge_n(directed_edge_id);
|
||||||
for t in zeroes2((n.dot(body.position-mesh.vert(vert_id)))*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
|
for dt in Fixed::<2,64>::zeroes2((n.dot(body.position-mesh.vert(vert_id)))*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
|
||||||
let t=body.time+Time::from(t);
|
if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
|
||||||
if time<=t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{
|
let dt=Ratio::new(dt.num.fix_4(),dt.den.fix_4());
|
||||||
best_time=t;
|
best_time=dt;
|
||||||
best_transtition=Transition::Next(FEV::<F,E,V>::Edge(directed_edge_id.as_undirected()),t);
|
best_transition=MinkowskiTransition::Next(MinkowskiFEV::Edge(directed_edge_id.as_undirected()),dt);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -98,18 +98,26 @@ enum Transition<F,E:DirectedEdge,V>{
|
|||||||
//if none:
|
//if none:
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
best_transtition
|
best_transition
|
||||||
}
|
}
|
||||||
pub enum CrawlResult<F,E:DirectedEdge,V>{
|
pub enum CrawlResult<F,E:DirectedEdge,V>{
|
||||||
Miss(FEV<F,E,V>),
|
Miss(FEV<F,E,V>),
|
||||||
Hit(F,Time),
|
Hit(F,GigaTime),
|
||||||
}
|
}
|
||||||
pub fn crawl_fev<F:Copy,E:Copy+DirectedEdge,V:Copy>(mut fev:FEV<F,E,V>,mesh:&impl MeshQuery<F,E,V>,relative_body:&Body,start_time:Time,time_limit:Time)->CrawlResult<F,E,V>{
|
type MinkowskiCrawlResult=CrawlResult<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert>;
|
||||||
let mut time=start_time;
|
pub fn crawl_fev(mut fev:MinkowskiFEV,mesh:&MinkowskiMesh,relative_body:&Body,start_time:Time,time_limit:Time)->MinkowskiCrawlResult{
|
||||||
|
let mut body_time={
|
||||||
|
let r=(start_time-relative_body.time).to_ratio();
|
||||||
|
Ratio::new(r.num.fix_4(),r.den.fix_4())
|
||||||
|
};
|
||||||
|
let time_limit={
|
||||||
|
let r=(time_limit-relative_body.time).to_ratio();
|
||||||
|
Ratio::new(r.num.fix_4(),r.den.fix_4())
|
||||||
|
};
|
||||||
for _ in 0..20{
|
for _ in 0..20{
|
||||||
match next_transition(&fev,time,mesh,relative_body,time_limit){
|
match next_transition(&fev,body_time,mesh,relative_body,time_limit){
|
||||||
Transition::Miss=>return CrawlResult::Miss(fev),
|
Transition::Miss=>return CrawlResult::Miss(fev),
|
||||||
Transition::Next(next_fev,next_time)=>(fev,time)=(next_fev,next_time),
|
Transition::Next(next_fev,next_time)=>(fev,body_time)=(next_fev,next_time),
|
||||||
Transition::Hit(face,time)=>return CrawlResult::Hit(face,time),
|
Transition::Hit(face,time)=>return CrawlResult::Hit(face,time),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -219,7 +219,7 @@ impl GraphicsState{
|
|||||||
//wow
|
//wow
|
||||||
let instance=GraphicsModelOwned{
|
let instance=GraphicsModelOwned{
|
||||||
transform:model.transform.into(),
|
transform:model.transform.into(),
|
||||||
normal_transform:Into::<glam::Mat3>::into(model.transform.matrix3).inverse().transpose(),
|
normal_transform:glam::Mat3::from_cols_array_2d(&model.transform.matrix3.to_array().map(|row|row.map(Into::into))).inverse().transpose(),
|
||||||
color:GraphicsModelColor4::new(model.color),
|
color:GraphicsModelColor4::new(model.color),
|
||||||
};
|
};
|
||||||
//get or create owned mesh map
|
//get or create owned mesh map
|
||||||
@ -238,9 +238,9 @@ impl GraphicsState{
|
|||||||
//create
|
//create
|
||||||
let owned_mesh_id=IndexedGraphicsMeshOwnedRenderConfigId::new(unique_render_config_models.len() as u32);
|
let owned_mesh_id=IndexedGraphicsMeshOwnedRenderConfigId::new(unique_render_config_models.len() as u32);
|
||||||
unique_render_config_models.push(IndexedGraphicsMeshOwnedRenderConfig{
|
unique_render_config_models.push(IndexedGraphicsMeshOwnedRenderConfig{
|
||||||
unique_pos:mesh.unique_pos.iter().map(|&v|*Into::<glam::Vec3>::into(v).as_ref()).collect(),
|
unique_pos:mesh.unique_pos.iter().map(|v|v.to_array().map(Into::into)).collect(),
|
||||||
unique_tex:mesh.unique_tex.iter().map(|v|*v.as_ref()).collect(),
|
unique_tex:mesh.unique_tex.iter().map(|v|*v.as_ref()).collect(),
|
||||||
unique_normal:mesh.unique_normal.iter().map(|&v|*Into::<glam::Vec3>::into(v).as_ref()).collect(),
|
unique_normal:mesh.unique_normal.iter().map(|v|v.to_array().map(Into::into)).collect(),
|
||||||
unique_color:mesh.unique_color.iter().map(|v|*v.as_ref()).collect(),
|
unique_color:mesh.unique_color.iter().map(|v|*v.as_ref()).collect(),
|
||||||
unique_vertices:mesh.unique_vertices.clone(),
|
unique_vertices:mesh.unique_vertices.clone(),
|
||||||
render_config:graphics_group.render,
|
render_config:graphics_group.render,
|
||||||
@ -890,7 +890,7 @@ impl GraphicsState{
|
|||||||
|
|
||||||
// update rotation
|
// update rotation
|
||||||
let camera_uniforms=self.camera.to_uniform_data(
|
let camera_uniforms=self.camera.to_uniform_data(
|
||||||
frame_state.body.extrapolated_position(frame_state.time).into(),
|
frame_state.body.extrapolated_position(frame_state.time).map(Into::<f32>::into).to_array().into(),
|
||||||
frame_state.camera.simulate_move_angles(glam::IVec2::ZERO)
|
frame_state.camera.simulate_move_angles(glam::IVec2::ZERO)
|
||||||
);
|
);
|
||||||
self.staging_belt
|
self.staging_belt
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
use std::borrow::{Borrow,Cow};
|
use std::borrow::{Borrow,Cow};
|
||||||
use std::collections::{HashSet,HashMap};
|
use std::collections::{HashSet,HashMap};
|
||||||
|
use strafesnet_common::integer::vec3::Vector3;
|
||||||
use strafesnet_common::model::{self,MeshId,PolygonIter};
|
use strafesnet_common::model::{self,MeshId,PolygonIter};
|
||||||
use strafesnet_common::zeroes;
|
use strafesnet_common::integer::{self,vec3,Fixed,Planar64,Planar64Vec3,Ratio};
|
||||||
use strafesnet_common::integer::{self,Planar64,Planar64Vec3};
|
|
||||||
|
|
||||||
pub trait UndirectedEdge{
|
pub trait UndirectedEdge{
|
||||||
type DirectedEdge:Copy+DirectedEdge;
|
type DirectedEdge:Copy+DirectedEdge;
|
||||||
fn as_directed(&self,parity:bool)->Self::DirectedEdge;
|
fn as_directed(&self,parity:bool)->Self::DirectedEdge;
|
||||||
}
|
}
|
||||||
pub trait DirectedEdge{
|
pub trait DirectedEdge{
|
||||||
type UndirectedEdge:Copy+UndirectedEdge;
|
type UndirectedEdge:Copy+std::fmt::Debug+UndirectedEdge;
|
||||||
fn as_undirected(&self)->Self::UndirectedEdge;
|
fn as_undirected(&self)->Self::UndirectedEdge;
|
||||||
fn parity(&self)->bool;
|
fn parity(&self)->bool;
|
||||||
//this is stupid but may work fine
|
//this is stupid but may work fine
|
||||||
@ -50,6 +50,7 @@ impl DirectedEdge for SubmeshDirectedEdgeId{
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Vertex <-> Edge <-> Face -> Collide
|
//Vertex <-> Edge <-> Face -> Collide
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum FEV<F,E:DirectedEdge,V>{
|
pub enum FEV<F,E:DirectedEdge,V>{
|
||||||
Face(F),
|
Face(F),
|
||||||
Edge(E::UndirectedEdge),
|
Edge(E::UndirectedEdge),
|
||||||
@ -64,6 +65,9 @@ struct Face{
|
|||||||
}
|
}
|
||||||
struct Vert(Planar64Vec3);
|
struct Vert(Planar64Vec3);
|
||||||
pub trait MeshQuery<FACE:Clone,EDGE:Clone+DirectedEdge,VERT:Clone>{
|
pub trait MeshQuery<FACE:Clone,EDGE:Clone+DirectedEdge,VERT:Clone>{
|
||||||
|
// Vertex must be Planar64Vec3 because it represents an actual position
|
||||||
|
type Normal;
|
||||||
|
type Offset;
|
||||||
fn edge_n(&self,edge_id:EDGE::UndirectedEdge)->Planar64Vec3{
|
fn edge_n(&self,edge_id:EDGE::UndirectedEdge)->Planar64Vec3{
|
||||||
let verts=self.edge_verts(edge_id);
|
let verts=self.edge_verts(edge_id);
|
||||||
self.vert(verts[1].clone())-self.vert(verts[0].clone())
|
self.vert(verts[1].clone())-self.vert(verts[0].clone())
|
||||||
@ -73,7 +77,7 @@ pub trait MeshQuery<FACE:Clone,EDGE:Clone+DirectedEdge,VERT:Clone>{
|
|||||||
(self.vert(verts[1].clone())-self.vert(verts[0].clone()))*((directed_edge_id.parity() as i64)*2-1)
|
(self.vert(verts[1].clone())-self.vert(verts[0].clone()))*((directed_edge_id.parity() as i64)*2-1)
|
||||||
}
|
}
|
||||||
fn vert(&self,vert_id:VERT)->Planar64Vec3;
|
fn vert(&self,vert_id:VERT)->Planar64Vec3;
|
||||||
fn face_nd(&self,face_id:FACE)->(Planar64Vec3,Planar64);
|
fn face_nd(&self,face_id:FACE)->(Self::Normal,Self::Offset);
|
||||||
fn face_edges(&self,face_id:FACE)->Cow<Vec<EDGE>>;
|
fn face_edges(&self,face_id:FACE)->Cow<Vec<EDGE>>;
|
||||||
fn edge_faces(&self,edge_id:EDGE::UndirectedEdge)->Cow<[FACE;2]>;
|
fn edge_faces(&self,edge_id:EDGE::UndirectedEdge)->Cow<[FACE;2]>;
|
||||||
fn edge_verts(&self,edge_id:EDGE::UndirectedEdge)->Cow<[VERT;2]>;
|
fn edge_verts(&self,edge_id:EDGE::UndirectedEdge)->Cow<[VERT;2]>;
|
||||||
@ -137,22 +141,22 @@ impl PhysicsMesh{
|
|||||||
//go go gadget debug print mesh
|
//go go gadget debug print mesh
|
||||||
let data=PhysicsMeshData{
|
let data=PhysicsMeshData{
|
||||||
faces:vec![
|
faces:vec![
|
||||||
Face{normal:Planar64Vec3::raw_xyz( 4294967296, 0, 0),dot:Planar64::raw(4294967296)},
|
Face{normal:vec3::raw_xyz( 4294967296, 0, 0),dot:Planar64::raw(4294967296)},
|
||||||
Face{normal:Planar64Vec3::raw_xyz( 0, 4294967296, 0),dot:Planar64::raw(4294967296)},
|
Face{normal:vec3::raw_xyz( 0, 4294967296, 0),dot:Planar64::raw(4294967296)},
|
||||||
Face{normal:Planar64Vec3::raw_xyz( 0, 0, 4294967296),dot:Planar64::raw(4294967296)},
|
Face{normal:vec3::raw_xyz( 0, 0, 4294967296),dot:Planar64::raw(4294967296)},
|
||||||
Face{normal:Planar64Vec3::raw_xyz(-4294967296, 0, 0),dot:Planar64::raw(4294967296)},
|
Face{normal:vec3::raw_xyz(-4294967296, 0, 0),dot:Planar64::raw(4294967296)},
|
||||||
Face{normal:Planar64Vec3::raw_xyz( 0,-4294967296, 0),dot:Planar64::raw(4294967296)},
|
Face{normal:vec3::raw_xyz( 0,-4294967296, 0),dot:Planar64::raw(4294967296)},
|
||||||
Face{normal:Planar64Vec3::raw_xyz( 0, 0,-4294967296),dot:Planar64::raw(4294967296)}
|
Face{normal:vec3::raw_xyz( 0, 0,-4294967296),dot:Planar64::raw(4294967296)}
|
||||||
],
|
],
|
||||||
verts:vec![
|
verts:vec![
|
||||||
Vert(Planar64Vec3::raw_xyz( 4294967296,-4294967296,-4294967296)),
|
Vert(vec3::raw_xyz( 4294967296,-4294967296,-4294967296)),
|
||||||
Vert(Planar64Vec3::raw_xyz( 4294967296, 4294967296,-4294967296)),
|
Vert(vec3::raw_xyz( 4294967296, 4294967296,-4294967296)),
|
||||||
Vert(Planar64Vec3::raw_xyz( 4294967296, 4294967296, 4294967296)),
|
Vert(vec3::raw_xyz( 4294967296, 4294967296, 4294967296)),
|
||||||
Vert(Planar64Vec3::raw_xyz( 4294967296,-4294967296, 4294967296)),
|
Vert(vec3::raw_xyz( 4294967296,-4294967296, 4294967296)),
|
||||||
Vert(Planar64Vec3::raw_xyz(-4294967296, 4294967296,-4294967296)),
|
Vert(vec3::raw_xyz(-4294967296, 4294967296,-4294967296)),
|
||||||
Vert(Planar64Vec3::raw_xyz(-4294967296, 4294967296, 4294967296)),
|
Vert(vec3::raw_xyz(-4294967296, 4294967296, 4294967296)),
|
||||||
Vert(Planar64Vec3::raw_xyz(-4294967296,-4294967296, 4294967296)),
|
Vert(vec3::raw_xyz(-4294967296,-4294967296, 4294967296)),
|
||||||
Vert(Planar64Vec3::raw_xyz(-4294967296,-4294967296,-4294967296))
|
Vert(vec3::raw_xyz(-4294967296,-4294967296,-4294967296))
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
let mesh_topology=PhysicsMeshTopology{
|
let mesh_topology=PhysicsMeshTopology{
|
||||||
@ -330,7 +334,7 @@ impl TryFrom<&model::Mesh> for PhysicsMesh{
|
|||||||
for poly_vertices in polygon_group.polys(){
|
for poly_vertices in polygon_group.polys(){
|
||||||
let submesh_face_id=SubmeshFaceId::new(submesh_faces.len() as u32);
|
let submesh_face_id=SubmeshFaceId::new(submesh_faces.len() as u32);
|
||||||
//one face per poly
|
//one face per poly
|
||||||
let mut normal=Planar64Vec3::ZERO;
|
let mut normal=Vector3::new([Fixed::ZERO,Fixed::ZERO,Fixed::ZERO]);
|
||||||
let len=poly_vertices.len();
|
let len=poly_vertices.len();
|
||||||
let face_edges=poly_vertices.into_iter().enumerate().map(|(i,vert_id)|{
|
let face_edges=poly_vertices.into_iter().enumerate().map(|(i,vert_id)|{
|
||||||
let vert0_id=MeshVertId::new(mesh.unique_vertices[vert_id.get() as usize].pos.get() as u32);
|
let vert0_id=MeshVertId::new(mesh.unique_vertices[vert_id.get() as usize].pos.get() as u32);
|
||||||
@ -341,11 +345,11 @@ impl TryFrom<&model::Mesh> for PhysicsMesh{
|
|||||||
//https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal (Newell's Method)
|
//https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal (Newell's Method)
|
||||||
let v0=mesh.unique_pos[vert0_id.get() as usize];
|
let v0=mesh.unique_pos[vert0_id.get() as usize];
|
||||||
let v1=mesh.unique_pos[vert1_id.get() as usize];
|
let v1=mesh.unique_pos[vert1_id.get() as usize];
|
||||||
normal+=Planar64Vec3::new(
|
normal+=Vector3::new([
|
||||||
(v0.y()-v1.y())*(v0.z()+v1.z()),
|
(v0.y-v1.y)*(v0.z+v1.z),
|
||||||
(v0.z()-v1.z())*(v0.x()+v1.x()),
|
(v0.z-v1.z)*(v0.x+v1.x),
|
||||||
(v0.x()-v1.x())*(v0.y()+v1.y()),
|
(v0.x-v1.x)*(v0.y+v1.y),
|
||||||
);
|
]);
|
||||||
//get/create edge and push face into it
|
//get/create edge and push face into it
|
||||||
let (edge_ref_verts,is_sorted)=EdgeRefVerts::new(submesh_vert0_id,submesh_vert1_id);
|
let (edge_ref_verts,is_sorted)=EdgeRefVerts::new(submesh_vert0_id,submesh_vert1_id);
|
||||||
let (edge_ref_faces,edge_id)=edge_pool.push(edge_ref_verts);
|
let (edge_ref_faces,edge_id)=edge_pool.push(edge_ref_verts);
|
||||||
@ -362,14 +366,16 @@ impl TryFrom<&model::Mesh> for PhysicsMesh{
|
|||||||
//return directed_edge_id
|
//return directed_edge_id
|
||||||
edge_id.as_directed(is_sorted)
|
edge_id.as_directed(is_sorted)
|
||||||
}).collect();
|
}).collect();
|
||||||
//choose precision loss randomly idk
|
let mut dot=Fixed::ZERO;
|
||||||
normal=normal/len as i64;
|
// find the average dot
|
||||||
let mut dot=Planar64::ZERO;
|
|
||||||
for &v in poly_vertices{
|
for &v in poly_vertices{
|
||||||
dot+=normal.dot(mesh.unique_pos[mesh.unique_vertices[v.get() as usize].pos.get() as usize]);
|
dot+=normal.dot(mesh.unique_pos[mesh.unique_vertices[v.get() as usize].pos.get() as usize]);
|
||||||
}
|
}
|
||||||
//assume face hash is stable, and there are no flush faces...
|
//assume face hash is stable, and there are no flush faces...
|
||||||
let face=Face{normal,dot:dot/len as i64};
|
let face=Face{
|
||||||
|
normal:(normal/len as i64).divide().fix_1(),
|
||||||
|
dot:(dot/(len*len) as i64).fix_1(),
|
||||||
|
};
|
||||||
let face_id=match face_id_from_face.get(&face){
|
let face_id=match face_id_from_face.get(&face){
|
||||||
Some(&face_id)=>face_id,
|
Some(&face_id)=>face_id,
|
||||||
None=>{
|
None=>{
|
||||||
@ -416,6 +422,8 @@ pub struct PhysicsMeshView<'a>{
|
|||||||
topology:&'a PhysicsMeshTopology,
|
topology:&'a PhysicsMeshTopology,
|
||||||
}
|
}
|
||||||
impl MeshQuery<SubmeshFaceId,SubmeshDirectedEdgeId,SubmeshVertId> for PhysicsMeshView<'_>{
|
impl MeshQuery<SubmeshFaceId,SubmeshDirectedEdgeId,SubmeshVertId> for PhysicsMeshView<'_>{
|
||||||
|
type Normal=Planar64Vec3;
|
||||||
|
type Offset=Planar64;
|
||||||
fn face_nd(&self,face_id:SubmeshFaceId)->(Planar64Vec3,Planar64){
|
fn face_nd(&self,face_id:SubmeshFaceId)->(Planar64Vec3,Planar64){
|
||||||
let face_idx=self.topology.faces[face_id.get() as usize].get() as usize;
|
let face_idx=self.topology.faces[face_id.get() as usize].get() as usize;
|
||||||
(self.data.faces[face_idx].normal,self.data.faces[face_idx].dot)
|
(self.data.faces[face_idx].normal,self.data.faces[face_idx].dot)
|
||||||
@ -444,14 +452,14 @@ impl MeshQuery<SubmeshFaceId,SubmeshDirectedEdgeId,SubmeshVertId> for PhysicsMes
|
|||||||
|
|
||||||
pub struct PhysicsMeshTransform{
|
pub struct PhysicsMeshTransform{
|
||||||
pub vertex:integer::Planar64Affine3,
|
pub vertex:integer::Planar64Affine3,
|
||||||
pub normal:integer::Planar64Mat3,
|
pub normal:integer::mat3::Matrix3<Fixed<2,64>>,
|
||||||
pub det:Planar64,
|
pub det:Fixed<3,96>,
|
||||||
}
|
}
|
||||||
impl PhysicsMeshTransform{
|
impl PhysicsMeshTransform{
|
||||||
pub const fn new(transform:integer::Planar64Affine3)->Self{
|
pub fn new(transform:integer::Planar64Affine3)->Self{
|
||||||
Self{
|
Self{
|
||||||
normal:transform.matrix3.inverse_times_det().transpose(),
|
normal:transform.matrix3.adjugate().transpose(),
|
||||||
det:transform.matrix3.determinant(),
|
det:transform.matrix3.det(),
|
||||||
vertex:transform,
|
vertex:transform,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -471,33 +479,33 @@ impl TransformedMesh<'_>{
|
|||||||
transform,
|
transform,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn verts<'a>(&'a self)->impl Iterator<Item=Planar64Vec3>+'a{
|
pub fn verts<'a>(&'a self)->impl Iterator<Item=vec3::Vector3<Fixed<2,64>>>+'a{
|
||||||
self.view.data.verts.iter().map(|&Vert(pos)|self.transform.vertex.transform_point3(pos))
|
self.view.data.verts.iter().map(|&Vert(pos)|self.transform.vertex.transform_point3(pos))
|
||||||
}
|
}
|
||||||
fn farthest_vert(&self,dir:Planar64Vec3)->SubmeshVertId{
|
fn farthest_vert(&self,dir:Planar64Vec3)->SubmeshVertId{
|
||||||
let mut best_dot=Planar64::MIN;
|
|
||||||
let mut best_vert=SubmeshVertId(0);
|
|
||||||
//this happens to be well-defined. there are no virtual virtices
|
//this happens to be well-defined. there are no virtual virtices
|
||||||
for (i,vert_id) in self.view.topology.verts.iter().enumerate(){
|
SubmeshVertId::new(
|
||||||
let p=self.transform.vertex.transform_point3(self.view.data.verts[vert_id.get() as usize].0);
|
self.view.topology.verts.iter()
|
||||||
let d=dir.dot(p);
|
.enumerate()
|
||||||
if best_dot<d{
|
.max_by_key(|(_,&vert_id)|
|
||||||
best_dot=d;
|
dir.dot(self.transform.vertex.transform_point3(self.view.data.verts[vert_id.get() as usize].0))
|
||||||
best_vert=SubmeshVertId::new(i as u32);
|
)
|
||||||
}
|
//assume there is more than zero vertices.
|
||||||
}
|
.unwrap().0 as u32
|
||||||
best_vert
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl MeshQuery<SubmeshFaceId,SubmeshDirectedEdgeId,SubmeshVertId> for TransformedMesh<'_>{
|
impl MeshQuery<SubmeshFaceId,SubmeshDirectedEdgeId,SubmeshVertId> for TransformedMesh<'_>{
|
||||||
fn face_nd(&self,face_id:SubmeshFaceId)->(Planar64Vec3,Planar64){
|
type Normal=Vector3<Fixed<3,96>>;
|
||||||
|
type Offset=Fixed<4,128>;
|
||||||
|
fn face_nd(&self,face_id:SubmeshFaceId)->(Self::Normal,Self::Offset){
|
||||||
let (n,d)=self.view.face_nd(face_id);
|
let (n,d)=self.view.face_nd(face_id);
|
||||||
let transformed_n=self.transform.normal*n;
|
let transformed_n=self.transform.normal*n;
|
||||||
let transformed_d=d+transformed_n.dot(self.transform.vertex.translation)/self.transform.det;
|
let transformed_d=d*self.transform.det+transformed_n.dot(self.transform.vertex.translation);
|
||||||
(transformed_n/self.transform.det,transformed_d)
|
(transformed_n,transformed_d)
|
||||||
}
|
}
|
||||||
fn vert(&self,vert_id:SubmeshVertId)->Planar64Vec3{
|
fn vert(&self,vert_id:SubmeshVertId)->Planar64Vec3{
|
||||||
self.transform.vertex.transform_point3(self.view.vert(vert_id))
|
self.transform.vertex.transform_point3(self.view.vert(vert_id)).fix_1()
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn face_edges(&self,face_id:SubmeshFaceId)->Cow<Vec<SubmeshDirectedEdgeId>>{
|
fn face_edges(&self,face_id:SubmeshFaceId)->Cow<Vec<SubmeshDirectedEdgeId>>{
|
||||||
@ -525,11 +533,11 @@ impl MeshQuery<SubmeshFaceId,SubmeshDirectedEdgeId,SubmeshVertId> for Transforme
|
|||||||
//(face,vertex)
|
//(face,vertex)
|
||||||
//(edge,edge)
|
//(edge,edge)
|
||||||
//(vertex,face)
|
//(vertex,face)
|
||||||
#[derive(Clone,Copy)]
|
#[derive(Clone,Copy,Debug)]
|
||||||
pub enum MinkowskiVert{
|
pub enum MinkowskiVert{
|
||||||
VertVert(SubmeshVertId,SubmeshVertId),
|
VertVert(SubmeshVertId,SubmeshVertId),
|
||||||
}
|
}
|
||||||
#[derive(Clone,Copy)]
|
#[derive(Clone,Copy,Debug)]
|
||||||
pub enum MinkowskiEdge{
|
pub enum MinkowskiEdge{
|
||||||
VertEdge(SubmeshVertId,SubmeshEdgeId),
|
VertEdge(SubmeshVertId,SubmeshEdgeId),
|
||||||
EdgeVert(SubmeshEdgeId,SubmeshVertId),
|
EdgeVert(SubmeshEdgeId,SubmeshVertId),
|
||||||
@ -544,7 +552,7 @@ impl UndirectedEdge for MinkowskiEdge{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Clone,Copy)]
|
#[derive(Clone,Copy,Debug)]
|
||||||
pub enum MinkowskiDirectedEdge{
|
pub enum MinkowskiDirectedEdge{
|
||||||
VertEdge(SubmeshVertId,SubmeshDirectedEdgeId),
|
VertEdge(SubmeshVertId,SubmeshDirectedEdgeId),
|
||||||
EdgeVert(SubmeshDirectedEdgeId,SubmeshVertId),
|
EdgeVert(SubmeshDirectedEdgeId,SubmeshVertId),
|
||||||
@ -565,7 +573,7 @@ impl DirectedEdge for MinkowskiDirectedEdge{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
|
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq)]
|
||||||
pub enum MinkowskiFace{
|
pub enum MinkowskiFace{
|
||||||
VertFace(SubmeshVertId,SubmeshFaceId),
|
VertFace(SubmeshVertId,SubmeshFaceId),
|
||||||
EdgeEdge(SubmeshEdgeId,SubmeshEdgeId,bool),
|
EdgeEdge(SubmeshEdgeId,SubmeshEdgeId,bool),
|
||||||
@ -581,6 +589,7 @@ pub struct MinkowskiMesh<'a>{
|
|||||||
}
|
}
|
||||||
|
|
||||||
//infinity fev algorithm state transition
|
//infinity fev algorithm state transition
|
||||||
|
#[derive(Debug)]
|
||||||
enum Transition{
|
enum Transition{
|
||||||
Done,//found closest vert, no edges are better
|
Done,//found closest vert, no edges are better
|
||||||
Vert(MinkowskiVert),//transition to vert
|
Vert(MinkowskiVert),//transition to vert
|
||||||
@ -590,6 +599,8 @@ enum EV{
|
|||||||
Edge(MinkowskiEdge),
|
Edge(MinkowskiEdge),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type GigaTime=Ratio<Fixed<4,128>,Fixed<4,128>>;
|
||||||
|
|
||||||
impl MinkowskiMesh<'_>{
|
impl MinkowskiMesh<'_>{
|
||||||
pub fn minkowski_sum<'a>(mesh0:TransformedMesh<'a>,mesh1:TransformedMesh<'a>)->MinkowskiMesh<'a>{
|
pub fn minkowski_sum<'a>(mesh0:TransformedMesh<'a>,mesh1:TransformedMesh<'a>)->MinkowskiMesh<'a>{
|
||||||
MinkowskiMesh{
|
MinkowskiMesh{
|
||||||
@ -600,7 +611,7 @@ impl MinkowskiMesh<'_>{
|
|||||||
fn farthest_vert(&self,dir:Planar64Vec3)->MinkowskiVert{
|
fn farthest_vert(&self,dir:Planar64Vec3)->MinkowskiVert{
|
||||||
MinkowskiVert::VertVert(self.mesh0.farthest_vert(dir),self.mesh1.farthest_vert(-dir))
|
MinkowskiVert::VertVert(self.mesh0.farthest_vert(dir),self.mesh1.farthest_vert(-dir))
|
||||||
}
|
}
|
||||||
fn next_transition_vert(&self,vert_id:MinkowskiVert,best_distance_squared:&mut Planar64,infinity_dir:Planar64Vec3,point:Planar64Vec3)->Transition{
|
fn next_transition_vert(&self,vert_id:MinkowskiVert,best_distance_squared:&mut Fixed<2,64>,infinity_dir:Planar64Vec3,point:Planar64Vec3)->Transition{
|
||||||
let mut best_transition=Transition::Done;
|
let mut best_transition=Transition::Done;
|
||||||
for &directed_edge_id in self.vert_edges(vert_id).iter(){
|
for &directed_edge_id in self.vert_edges(vert_id).iter(){
|
||||||
let edge_n=self.directed_edge_n(directed_edge_id);
|
let edge_n=self.directed_edge_n(directed_edge_id);
|
||||||
@ -610,7 +621,7 @@ impl MinkowskiMesh<'_>{
|
|||||||
let test_vert_id=edge_verts[directed_edge_id.parity() as usize];
|
let test_vert_id=edge_verts[directed_edge_id.parity() as usize];
|
||||||
//test if it's closer
|
//test if it's closer
|
||||||
let diff=point-self.vert(test_vert_id);
|
let diff=point-self.vert(test_vert_id);
|
||||||
if zeroes::zeroes1(edge_n.dot(diff),edge_n.dot(infinity_dir)).len()==0{
|
if edge_n.dot(infinity_dir).is_zero(){
|
||||||
let distance_squared=diff.dot(diff);
|
let distance_squared=diff.dot(diff);
|
||||||
if distance_squared<*best_distance_squared{
|
if distance_squared<*best_distance_squared{
|
||||||
best_transition=Transition::Vert(test_vert_id);
|
best_transition=Transition::Vert(test_vert_id);
|
||||||
@ -620,21 +631,21 @@ impl MinkowskiMesh<'_>{
|
|||||||
}
|
}
|
||||||
best_transition
|
best_transition
|
||||||
}
|
}
|
||||||
fn final_ev(&self,vert_id:MinkowskiVert,best_distance_squared:&mut Planar64,infinity_dir:Planar64Vec3,point:Planar64Vec3)->EV{
|
fn final_ev(&self,vert_id:MinkowskiVert,best_distance_squared:&mut Fixed<2,64>,infinity_dir:Planar64Vec3,point:Planar64Vec3)->EV{
|
||||||
let mut best_transition=EV::Vert(vert_id);
|
let mut best_transition=EV::Vert(vert_id);
|
||||||
let diff=point-self.vert(vert_id);
|
let diff=point-self.vert(vert_id);
|
||||||
for &directed_edge_id in self.vert_edges(vert_id).iter(){
|
for &directed_edge_id in self.vert_edges(vert_id).iter(){
|
||||||
let edge_n=self.directed_edge_n(directed_edge_id);
|
let edge_n=self.directed_edge_n(directed_edge_id);
|
||||||
//is boundary uncrossable by a crawl from infinity
|
//is boundary uncrossable by a crawl from infinity
|
||||||
//check if time of collision is outside Time::MIN..Time::MAX
|
//check if time of collision is outside Time::MIN..Time::MAX
|
||||||
|
if edge_n.dot(infinity_dir).is_zero(){
|
||||||
let d=edge_n.dot(diff);
|
let d=edge_n.dot(diff);
|
||||||
if zeroes::zeroes1(d,edge_n.dot(infinity_dir)).len()==0{
|
|
||||||
//test the edge
|
//test the edge
|
||||||
let edge_nn=edge_n.dot(edge_n);
|
let edge_nn=edge_n.dot(edge_n);
|
||||||
if Planar64::ZERO<=d&&d<=edge_nn{
|
if !d.is_negative()&&d<=edge_nn{
|
||||||
let distance_squared={
|
let distance_squared={
|
||||||
let c=diff.cross(edge_n);
|
let c=diff.cross(edge_n);
|
||||||
c.dot(c)/edge_nn
|
(c.dot(c)/edge_nn).divide().fix_2()
|
||||||
};
|
};
|
||||||
if distance_squared<=*best_distance_squared{
|
if distance_squared<=*best_distance_squared{
|
||||||
best_transition=EV::Edge(directed_edge_id.as_undirected());
|
best_transition=EV::Edge(directed_edge_id.as_undirected());
|
||||||
@ -680,7 +691,7 @@ impl MinkowskiMesh<'_>{
|
|||||||
let boundary_d=boundary_n.dot(delta_pos);
|
let boundary_d=boundary_n.dot(delta_pos);
|
||||||
//check if time of collision is outside Time::MIN..Time::MAX
|
//check if time of collision is outside Time::MIN..Time::MAX
|
||||||
//infinity_dir can always be treated as a velocity
|
//infinity_dir can always be treated as a velocity
|
||||||
if (boundary_d)<=Planar64::ZERO&&zeroes::zeroes1(boundary_d,boundary_n.dot(infinity_dir)*2).len()==0{
|
if !boundary_d.is_positive()&&boundary_n.dot(infinity_dir).is_zero(){
|
||||||
//both faces cannot pass this condition, return early if one does.
|
//both faces cannot pass this condition, return early if one does.
|
||||||
return FEV::<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert>::Face(face_id);
|
return FEV::<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert>::Face(face_id);
|
||||||
}
|
}
|
||||||
@ -694,15 +705,16 @@ impl MinkowskiMesh<'_>{
|
|||||||
let infinity_fev=self.infinity_fev(-dir,infinity_body.position);
|
let infinity_fev=self.infinity_fev(-dir,infinity_body.position);
|
||||||
//a line is simpler to solve than a parabola
|
//a line is simpler to solve than a parabola
|
||||||
infinity_body.velocity=dir;
|
infinity_body.velocity=dir;
|
||||||
infinity_body.acceleration=Planar64Vec3::ZERO;
|
infinity_body.acceleration=vec3::ZERO;
|
||||||
//crawl in from negative infinity along a tangent line to get the closest fev
|
//crawl in from negative infinity along a tangent line to get the closest fev
|
||||||
match crate::face_crawler::crawl_fev(infinity_fev,self,&infinity_body,integer::Time::MIN,infinity_body.time){
|
// TODO: change crawl_fev args to delta time? Optional values?
|
||||||
|
match crate::face_crawler::crawl_fev(infinity_fev,self,&infinity_body,integer::Time::MIN/4,infinity_body.time){
|
||||||
crate::face_crawler::CrawlResult::Miss(fev)=>Some(fev),
|
crate::face_crawler::CrawlResult::Miss(fev)=>Some(fev),
|
||||||
crate::face_crawler::CrawlResult::Hit(_,_)=>None,
|
crate::face_crawler::CrawlResult::Hit(_,_)=>None,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn predict_collision_in(&self,relative_body:&crate::physics::Body,time_limit:integer::Time)->Option<(MinkowskiFace,integer::Time)>{
|
pub fn predict_collision_in(&self,relative_body:&crate::physics::Body,time_limit:integer::Time)->Option<(MinkowskiFace,GigaTime)>{
|
||||||
self.closest_fev_not_inside(relative_body.clone()).map_or(None,|fev|{
|
self.closest_fev_not_inside(relative_body.clone()).map_or(None,|fev|{
|
||||||
//continue forwards along the body parabola
|
//continue forwards along the body parabola
|
||||||
match crate::face_crawler::crawl_fev(fev,self,relative_body,relative_body.time,time_limit){
|
match crate::face_crawler::crawl_fev(fev,self,relative_body,relative_body.time,time_limit){
|
||||||
@ -711,7 +723,7 @@ impl MinkowskiMesh<'_>{
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn predict_collision_out(&self,relative_body:&crate::physics::Body,time_limit:integer::Time)->Option<(MinkowskiFace,integer::Time)>{
|
pub fn predict_collision_out(&self,relative_body:&crate::physics::Body,time_limit:integer::Time)->Option<(MinkowskiFace,GigaTime)>{
|
||||||
//create an extrapolated body at time_limit
|
//create an extrapolated body at time_limit
|
||||||
let infinity_body=crate::physics::Body::new(
|
let infinity_body=crate::physics::Body::new(
|
||||||
relative_body.extrapolated_position(time_limit),
|
relative_body.extrapolated_position(time_limit),
|
||||||
@ -727,10 +739,13 @@ impl MinkowskiMesh<'_>{
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn predict_collision_face_out(&self,relative_body:&crate::physics::Body,time_limit:integer::Time,contact_face_id:MinkowskiFace)->Option<(MinkowskiEdge,integer::Time)>{
|
pub fn predict_collision_face_out(&self,relative_body:&crate::physics::Body,time_limit:integer::Time,contact_face_id:MinkowskiFace)->Option<(MinkowskiEdge,GigaTime)>{
|
||||||
//no algorithm needed, there is only one state and two cases (Edge,None)
|
//no algorithm needed, there is only one state and two cases (Edge,None)
|
||||||
//determine when it passes an edge ("sliding off" case)
|
//determine when it passes an edge ("sliding off" case)
|
||||||
let mut best_time=time_limit;
|
let mut best_time={
|
||||||
|
let r=(time_limit-relative_body.time).to_ratio();
|
||||||
|
Ratio::new(r.num.fix_4(),r.den.fix_4())
|
||||||
|
};
|
||||||
let mut best_edge=None;
|
let mut best_edge=None;
|
||||||
let face_n=self.face_nd(contact_face_id).0;
|
let face_n=self.face_nd(contact_face_id).0;
|
||||||
for &directed_edge_id in self.face_edges(contact_face_id).iter(){
|
for &directed_edge_id in self.face_edges(contact_face_id).iter(){
|
||||||
@ -740,10 +755,10 @@ impl MinkowskiMesh<'_>{
|
|||||||
let verts=self.edge_verts(directed_edge_id.as_undirected());
|
let verts=self.edge_verts(directed_edge_id.as_undirected());
|
||||||
let d=n.dot(self.vert(verts[0])+self.vert(verts[1]));
|
let d=n.dot(self.vert(verts[0])+self.vert(verts[1]));
|
||||||
//WARNING! d outside of *2
|
//WARNING! d outside of *2
|
||||||
for t in zeroes::zeroes2((n.dot(relative_body.position))*2-d,n.dot(relative_body.velocity)*2,n.dot(relative_body.acceleration)){
|
//WARNING: truncated precision
|
||||||
let t=relative_body.time+integer::Time::from(t);
|
for dt in Fixed::<4,128>::zeroes2(((n.dot(relative_body.position))*2-d).fix_4(),n.dot(relative_body.velocity).fix_4()*2,n.dot(relative_body.acceleration).fix_4()){
|
||||||
if relative_body.time<t&&t<best_time&&n.dot(relative_body.extrapolated_velocity(t))<Planar64::ZERO{
|
if Ratio::new(Planar64::ZERO,Planar64::ONE).le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(relative_body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
|
||||||
best_time=t;
|
best_time=dt;
|
||||||
best_edge=Some(directed_edge_id);
|
best_edge=Some(directed_edge_id);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -751,15 +766,15 @@ 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,integer::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);
|
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,infinity_body.time){
|
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::Miss(_)=>None,
|
||||||
crate::face_crawler::CrawlResult::Hit(face,time)=>Some((face,time)),
|
crate::face_crawler::CrawlResult::Hit(face,time)=>Some((face,time)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn is_point_in_mesh(&self,point:Planar64Vec3)->bool{
|
pub fn is_point_in_mesh(&self,point:Planar64Vec3)->bool{
|
||||||
let infinity_body=crate::physics::Body::new(point,Planar64Vec3::Y,Planar64Vec3::ZERO,integer::Time::ZERO);
|
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,
|
//movement must escape the mesh forwards and backwards in time,
|
||||||
//otherwise the point is not inside the mesh
|
//otherwise the point is not inside the mesh
|
||||||
self.infinity_in(infinity_body)
|
self.infinity_in(infinity_body)
|
||||||
@ -770,7 +785,9 @@ impl MinkowskiMesh<'_>{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
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);
|
||||||
@ -784,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);
|
||||||
@ -833,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);
|
||||||
@ -862,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);
|
||||||
@ -906,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));
|
||||||
@ -930,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));
|
||||||
@ -945,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{
|
||||||
@ -953,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{
|
||||||
@ -970,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]
|
||||||
|
378
src/physics.rs
378
src/physics.rs
@ -11,7 +11,7 @@ use strafesnet_common::gameplay_modes::{self,StageId};
|
|||||||
use strafesnet_common::gameplay_style::{self,StyleModifiers};
|
use strafesnet_common::gameplay_style::{self,StyleModifiers};
|
||||||
use strafesnet_common::controls_bitflag::Controls;
|
use strafesnet_common::controls_bitflag::Controls;
|
||||||
use strafesnet_common::instruction::{self,InstructionEmitter,InstructionConsumer,TimedInstruction};
|
use strafesnet_common::instruction::{self,InstructionEmitter,InstructionConsumer,TimedInstruction};
|
||||||
use strafesnet_common::integer::{self,Time,Planar64,Planar64Vec3,Planar64Mat3,Angle32,Ratio64Vec2};
|
use strafesnet_common::integer::{self,vec3,mat3,Time,Planar64,Planar64Vec3,Planar64Mat3,Angle32,Ratio64Vec2};
|
||||||
use gameplay::ModeState;
|
use gameplay::ModeState;
|
||||||
|
|
||||||
//external influence
|
//external influence
|
||||||
@ -22,8 +22,8 @@ use strafesnet_common::physics::Instruction as PhysicsInputInstruction;
|
|||||||
//when the physics asks itself what happens next, this is how it's represented
|
//when the physics asks itself what happens next, this is how it's represented
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum PhysicsInternalInstruction{
|
enum PhysicsInternalInstruction{
|
||||||
CollisionStart(Collision),
|
CollisionStart(Collision,model_physics::GigaTime),
|
||||||
CollisionEnd(Collision),
|
CollisionEnd(Collision,model_physics::GigaTime),
|
||||||
StrafeTick,
|
StrafeTick,
|
||||||
ReachWalkTargetVelocity,
|
ReachWalkTargetVelocity,
|
||||||
// Water,
|
// Water,
|
||||||
@ -36,7 +36,7 @@ enum PhysicsInstruction{
|
|||||||
Input(PhysicsInputInstruction),
|
Input(PhysicsInputInstruction),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone,Copy,Debug,Default,Hash)]
|
#[derive(Clone,Copy,Debug,Hash)]
|
||||||
pub struct Body{
|
pub struct Body{
|
||||||
pub position:Planar64Vec3,//I64 where 2^32 = 1 u
|
pub position:Planar64Vec3,//I64 where 2^32 = 1 u
|
||||||
pub velocity:Planar64Vec3,//I64 where 2^32 = 1 u/s
|
pub velocity:Planar64Vec3,//I64 where 2^32 = 1 u/s
|
||||||
@ -124,13 +124,13 @@ struct ContactMoveState{
|
|||||||
}
|
}
|
||||||
impl TransientAcceleration{
|
impl TransientAcceleration{
|
||||||
fn with_target_diff(target_diff:Planar64Vec3,accel:Planar64,time:Time)->Self{
|
fn with_target_diff(target_diff:Planar64Vec3,accel:Planar64,time:Time)->Self{
|
||||||
if target_diff==Planar64Vec3::ZERO{
|
if target_diff==vec3::ZERO{
|
||||||
TransientAcceleration::Reached
|
TransientAcceleration::Reached
|
||||||
}else{
|
}else{
|
||||||
//normal friction acceleration is clippedAcceleration.dot(normal)*friction
|
//normal friction acceleration is clippedAcceleration.dot(normal)*friction
|
||||||
TransientAcceleration::Reachable{
|
TransientAcceleration::Reachable{
|
||||||
acceleration:target_diff.with_length(accel),
|
acceleration:target_diff.with_length(accel).divide().fix_1(),
|
||||||
time:time+Time::from(target_diff.length()/accel)
|
time:time+Time::from((target_diff.length()/accel).divide().fix_1())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -147,7 +147,7 @@ impl TransientAcceleration{
|
|||||||
}
|
}
|
||||||
fn acceleration(&self)->Planar64Vec3{
|
fn acceleration(&self)->Planar64Vec3{
|
||||||
match self{
|
match self{
|
||||||
TransientAcceleration::Reached=>Planar64Vec3::ZERO,
|
TransientAcceleration::Reached=>vec3::ZERO,
|
||||||
&TransientAcceleration::Reachable{acceleration,time:_}=>acceleration,
|
&TransientAcceleration::Reachable{acceleration,time:_}=>acceleration,
|
||||||
&TransientAcceleration::Unreachable{acceleration}=>acceleration,
|
&TransientAcceleration::Unreachable{acceleration}=>acceleration,
|
||||||
}
|
}
|
||||||
@ -158,7 +158,7 @@ impl ContactMoveState{
|
|||||||
Self{
|
Self{
|
||||||
target:TransientAcceleration::ground(walk_settings,body,gravity,target_velocity),
|
target:TransientAcceleration::ground(walk_settings,body,gravity,target_velocity),
|
||||||
contact,
|
contact,
|
||||||
jump_direction:JumpDirection::Exactly(Planar64Vec3::Y),
|
jump_direction:JumpDirection::Exactly(vec3::Y),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn ladder(ladder_settings:&gameplay_style::LadderSettings,body:&Body,gravity:Planar64Vec3,target_velocity:Planar64Vec3,contact:ContactCollision)->Self{
|
fn ladder(ladder_settings:&gameplay_style::LadderSettings,body:&Body,gravity:Planar64Vec3,target_velocity:Planar64Vec3,contact:ContactCollision)->Self{
|
||||||
@ -296,7 +296,7 @@ impl PhysicsCamera{
|
|||||||
let ay=Angle32::clamp_from_i64(a.y)
|
let ay=Angle32::clamp_from_i64(a.y)
|
||||||
//clamp to actual vertical cam limit
|
//clamp to actual vertical cam limit
|
||||||
.clamp(Self::ANGLE_PITCH_LOWER_LIMIT,Self::ANGLE_PITCH_UPPER_LIMIT);
|
.clamp(Self::ANGLE_PITCH_LOWER_LIMIT,Self::ANGLE_PITCH_UPPER_LIMIT);
|
||||||
Planar64Mat3::from_rotation_yx(ax,ay)
|
mat3::from_rotation_yx(ax,ay)
|
||||||
}
|
}
|
||||||
fn rotation(&self)->Planar64Mat3{
|
fn rotation(&self)->Planar64Mat3{
|
||||||
self.get_rotation(self.clamped_mouse_pos)
|
self.get_rotation(self.clamped_mouse_pos)
|
||||||
@ -306,7 +306,7 @@ impl PhysicsCamera{
|
|||||||
}
|
}
|
||||||
fn get_rotation_y(&self,mouse_pos_x:i32)->Planar64Mat3{
|
fn get_rotation_y(&self,mouse_pos_x:i32)->Planar64Mat3{
|
||||||
let ax=-self.sensitivity.x.mul_int(mouse_pos_x as i64);
|
let ax=-self.sensitivity.x.mul_int(mouse_pos_x as i64);
|
||||||
Planar64Mat3::from_rotation_y(Angle32::wrap_from_i64(ax))
|
mat3::from_rotation_y(Angle32::wrap_from_i64(ax))
|
||||||
}
|
}
|
||||||
fn rotation_y(&self)->Planar64Mat3{
|
fn rotation_y(&self)->Planar64Mat3{
|
||||||
self.get_rotation_y(self.clamped_mouse_pos.x)
|
self.get_rotation_y(self.clamped_mouse_pos.x)
|
||||||
@ -410,10 +410,10 @@ impl HitboxMesh{
|
|||||||
let transform=PhysicsMeshTransform::new(transform);
|
let transform=PhysicsMeshTransform::new(transform);
|
||||||
let transformed_mesh=TransformedMesh::new(mesh.complete_mesh_view(),&transform);
|
let transformed_mesh=TransformedMesh::new(mesh.complete_mesh_view(),&transform);
|
||||||
for vert in transformed_mesh.verts(){
|
for vert in transformed_mesh.verts(){
|
||||||
aabb.grow(vert);
|
aabb.grow(vert.fix_1());
|
||||||
}
|
}
|
||||||
Self{
|
Self{
|
||||||
halfsize:aabb.size()/2,
|
halfsize:aabb.size()>>1,
|
||||||
mesh,
|
mesh,
|
||||||
transform,
|
transform,
|
||||||
}
|
}
|
||||||
@ -438,7 +438,7 @@ impl StyleHelper for StyleModifiers{
|
|||||||
|
|
||||||
fn get_control_dir(&self,controls:Controls)->Planar64Vec3{
|
fn get_control_dir(&self,controls:Controls)->Planar64Vec3{
|
||||||
//don't get fancy just do it
|
//don't get fancy just do it
|
||||||
let mut control_dir:Planar64Vec3 = Planar64Vec3::ZERO;
|
let mut control_dir:Planar64Vec3=vec3::ZERO;
|
||||||
//Apply mask after held check so you can require non-allowed keys to be held for some reason
|
//Apply mask after held check so you can require non-allowed keys to be held for some reason
|
||||||
let controls=controls.intersection(self.controls_mask);
|
let controls=controls.intersection(self.controls_mask);
|
||||||
if controls.contains(Controls::MoveForward){
|
if controls.contains(Controls::MoveForward){
|
||||||
@ -463,19 +463,22 @@ impl StyleHelper for StyleModifiers{
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_y_control_dir(&self,camera:&PhysicsCamera,controls:Controls)->Planar64Vec3{
|
fn get_y_control_dir(&self,camera:&PhysicsCamera,controls:Controls)->Planar64Vec3{
|
||||||
camera.rotation_y()*self.get_control_dir(controls)
|
(camera.rotation_y()*self.get_control_dir(controls)).fix_1()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_propulsion_control_dir(&self,camera:&PhysicsCamera,controls:Controls)->Planar64Vec3{
|
fn get_propulsion_control_dir(&self,camera:&PhysicsCamera,controls:Controls)->Planar64Vec3{
|
||||||
//don't interpolate this! discrete mouse movement, constant acceleration
|
//don't interpolate this! discrete mouse movement, constant acceleration
|
||||||
camera.rotation()*self.get_control_dir(controls)
|
(camera.rotation()*self.get_control_dir(controls)).fix_1()
|
||||||
}
|
}
|
||||||
fn calculate_mesh(&self)->HitboxMesh{
|
fn calculate_mesh(&self)->HitboxMesh{
|
||||||
let mesh=match self.hitbox.mesh{
|
let mesh=match self.hitbox.mesh{
|
||||||
gameplay_style::HitboxMesh::Box=>PhysicsMesh::unit_cube(),
|
gameplay_style::HitboxMesh::Box=>PhysicsMesh::unit_cube(),
|
||||||
gameplay_style::HitboxMesh::Cylinder=>PhysicsMesh::unit_cylinder(),
|
gameplay_style::HitboxMesh::Cylinder=>PhysicsMesh::unit_cylinder(),
|
||||||
};
|
};
|
||||||
let transform=integer::Planar64Affine3::new(Planar64Mat3::from_diagonal(self.hitbox.halfsize),Planar64Vec3::ZERO);
|
let transform=integer::Planar64Affine3::new(
|
||||||
|
mat3::from_diagonal(self.hitbox.halfsize),
|
||||||
|
vec3::ZERO
|
||||||
|
);
|
||||||
HitboxMesh::new(mesh,transform)
|
HitboxMesh::new(mesh,transform)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -491,7 +494,7 @@ impl MoveState{
|
|||||||
//call this after state.move_state is changed
|
//call this after state.move_state is changed
|
||||||
fn apply_enum(&self,body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){
|
fn apply_enum(&self,body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){
|
||||||
match self{
|
match self{
|
||||||
MoveState::Fly=>body.acceleration=Planar64Vec3::ZERO,
|
MoveState::Fly=>body.acceleration=vec3::ZERO,
|
||||||
MoveState::Air=>{
|
MoveState::Air=>{
|
||||||
//calculate base acceleration
|
//calculate base acceleration
|
||||||
let a=touching.base_acceleration(models,style,camera,input_state);
|
let a=touching.base_acceleration(models,style,camera,input_state);
|
||||||
@ -766,9 +769,9 @@ impl TouchingState{
|
|||||||
//TODO: trey push solve
|
//TODO: trey push solve
|
||||||
for contact in &self.contacts{
|
for contact in &self.contacts{
|
||||||
let n=contact_normal(models,hitbox_mesh,contact);
|
let n=contact_normal(models,hitbox_mesh,contact);
|
||||||
let d=n.dot128(*velocity);
|
let d=n.dot(*velocity);
|
||||||
if d<0{
|
if d.is_negative(){
|
||||||
*velocity-=n*Planar64::raw(((d<<32)/n.dot128(n)) as i64);
|
*velocity-=(n*d/n.length_squared()).divide().fix_1();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -776,23 +779,24 @@ impl TouchingState{
|
|||||||
//TODO: trey push solve
|
//TODO: trey push solve
|
||||||
for contact in &self.contacts{
|
for contact in &self.contacts{
|
||||||
let n=contact_normal(models,hitbox_mesh,contact);
|
let n=contact_normal(models,hitbox_mesh,contact);
|
||||||
let d=n.dot128(*acceleration);
|
let d=n.dot(*acceleration);
|
||||||
if d<0{
|
if d.is_negative(){
|
||||||
*acceleration-=n*Planar64::raw(((d<<32)/n.dot128(n)) as i64);
|
*acceleration-=(n*d/n.length_squared()).divide().fix_1();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn predict_collision_end(&self,collector:&mut instruction::InstructionCollector<PhysicsInternalInstruction>,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,body:&Body,time:Time){
|
fn predict_collision_end(&self,collector:&mut instruction::InstructionCollector<PhysicsInternalInstruction>,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,body:&Body,time:Time){
|
||||||
let relative_body=VirtualBody::relative(&Body::default(),body).body(time);
|
let relative_body=VirtualBody::relative(&Body::ZERO,body).body(time);
|
||||||
for contact in &self.contacts{
|
for contact in &self.contacts{
|
||||||
//detect face slide off
|
//detect face slide off
|
||||||
let model_mesh=models.contact_mesh(contact);
|
let model_mesh=models.contact_mesh(contact);
|
||||||
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh());
|
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh());
|
||||||
collector.collect(minkowski.predict_collision_face_out(&relative_body,collector.time(),contact.face_id).map(|(_face,time)|{
|
collector.collect(minkowski.predict_collision_face_out(&relative_body,collector.time(),contact.face_id).map(|(_face,time)|{
|
||||||
TimedInstruction{
|
TimedInstruction{
|
||||||
time,
|
time:relative_body.time+time.into(),
|
||||||
instruction:PhysicsInternalInstruction::CollisionEnd(
|
instruction:PhysicsInternalInstruction::CollisionEnd(
|
||||||
Collision::Contact(*contact)
|
Collision::Contact(*contact),
|
||||||
|
time
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
@ -803,9 +807,10 @@ impl TouchingState{
|
|||||||
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh());
|
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh());
|
||||||
collector.collect(minkowski.predict_collision_out(&relative_body,collector.time()).map(|(_face,time)|{
|
collector.collect(minkowski.predict_collision_out(&relative_body,collector.time()).map(|(_face,time)|{
|
||||||
TimedInstruction{
|
TimedInstruction{
|
||||||
time,
|
time:relative_body.time+time.into(),
|
||||||
instruction:PhysicsInternalInstruction::CollisionEnd(
|
instruction:PhysicsInternalInstruction::CollisionEnd(
|
||||||
Collision::Intersect(*intersect)
|
Collision::Intersect(*intersect),
|
||||||
|
time
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
@ -814,6 +819,7 @@ impl TouchingState{
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Body{
|
impl Body{
|
||||||
|
pub const ZERO:Self=Self::new(vec3::ZERO,vec3::ZERO,vec3::ZERO,Time::ZERO);
|
||||||
pub const fn new(position:Planar64Vec3,velocity:Planar64Vec3,acceleration:Planar64Vec3,time:Time)->Self{
|
pub const fn new(position:Planar64Vec3,velocity:Planar64Vec3,acceleration:Planar64Vec3,time:Time)->Self{
|
||||||
Self{
|
Self{
|
||||||
position,
|
position,
|
||||||
@ -824,20 +830,64 @@ impl Body{
|
|||||||
}
|
}
|
||||||
pub fn extrapolated_position(&self,time:Time)->Planar64Vec3{
|
pub fn extrapolated_position(&self,time:Time)->Planar64Vec3{
|
||||||
let dt=time-self.time;
|
let dt=time-self.time;
|
||||||
self.position+self.velocity*dt+self.acceleration*(dt*dt/2)
|
self.position
|
||||||
|
+(self.velocity*dt).map(|elem|elem.divide().fix_1())
|
||||||
|
+self.acceleration.map(|elem|(dt*dt*elem/2).divide().fix_1())
|
||||||
}
|
}
|
||||||
pub fn extrapolated_velocity(&self,time:Time)->Planar64Vec3{
|
pub fn extrapolated_velocity(&self,time:Time)->Planar64Vec3{
|
||||||
let dt=time-self.time;
|
let dt=time-self.time;
|
||||||
self.velocity+self.acceleration*dt
|
self.velocity+(self.acceleration*dt).map(|elem|elem.divide().fix_1())
|
||||||
}
|
}
|
||||||
pub fn advance_time(&mut self,time:Time){
|
pub fn advance_time(&mut self,time:Time){
|
||||||
self.position=self.extrapolated_position(time);
|
self.position=self.extrapolated_position(time);
|
||||||
self.velocity=self.extrapolated_velocity(time);
|
self.velocity=self.extrapolated_velocity(time);
|
||||||
self.time=time;
|
self.time=time;
|
||||||
}
|
}
|
||||||
|
pub fn extrapolated_position_ratio_dt<Num,Den,N1,D1,N2,N3,D2,N4,T1>(&self,dt:integer::Ratio<Num,Den>)->Planar64Vec3
|
||||||
|
where
|
||||||
|
// Why?
|
||||||
|
// All of this can be removed with const generics because the type can be specified as
|
||||||
|
// Ratio<Fixed<N,NF>,Fixed<D,DF>>
|
||||||
|
// which is known to implement all the necessary traits
|
||||||
|
Num:Copy,
|
||||||
|
Den:Copy+core::ops::Mul<i64,Output=D1>,
|
||||||
|
D1:Copy,
|
||||||
|
Num:core::ops::Mul<Planar64,Output=N1>,
|
||||||
|
Planar64:core::ops::Mul<D1,Output=N2>,
|
||||||
|
N1:core::ops::Add<N2,Output=N3>,
|
||||||
|
Num:core::ops::Mul<N3,Output=N4>,
|
||||||
|
Den:core::ops::Mul<D1,Output=D2>,
|
||||||
|
D2:Copy,
|
||||||
|
Planar64:core::ops::Mul<D2,Output=N4>,
|
||||||
|
N4:integer::Divide<D2,Output=T1>,
|
||||||
|
T1:integer::Fix<Planar64>,
|
||||||
|
{
|
||||||
|
// a*dt^2/2 + v*dt + p
|
||||||
|
// (a*dt/2+v)*dt+p
|
||||||
|
(self.acceleration.map(|elem|dt*elem/2)+self.velocity).map(|elem|dt.mul_ratio(elem))
|
||||||
|
.map(|elem|elem.divide().fix())+self.position
|
||||||
|
}
|
||||||
|
pub fn extrapolated_velocity_ratio_dt<Num,Den,N1,T1>(&self,dt:integer::Ratio<Num,Den>)->Planar64Vec3
|
||||||
|
where
|
||||||
|
Num:Copy,
|
||||||
|
Den:Copy,
|
||||||
|
Num:core::ops::Mul<Planar64,Output=N1>,
|
||||||
|
Planar64:core::ops::Mul<Den,Output=N1>,
|
||||||
|
N1:integer::Divide<Den,Output=T1>,
|
||||||
|
T1:integer::Fix<Planar64>,
|
||||||
|
{
|
||||||
|
// a*dt + v
|
||||||
|
self.acceleration.map(|elem|dt*elem)
|
||||||
|
.map(|elem|elem.divide().fix())+self.velocity
|
||||||
|
}
|
||||||
|
pub fn advance_time_ratio_dt(&mut self,dt:model_physics::GigaTime){
|
||||||
|
self.position=self.extrapolated_position_ratio_dt(dt);
|
||||||
|
self.velocity=self.extrapolated_velocity_ratio_dt(dt);
|
||||||
|
self.time+=dt.into();
|
||||||
|
}
|
||||||
pub fn infinity_dir(&self)->Option<Planar64Vec3>{
|
pub fn infinity_dir(&self)->Option<Planar64Vec3>{
|
||||||
if self.velocity==Planar64Vec3::ZERO{
|
if self.velocity==vec3::ZERO{
|
||||||
if self.acceleration==Planar64Vec3::ZERO{
|
if self.acceleration==vec3::ZERO{
|
||||||
None
|
None
|
||||||
}else{
|
}else{
|
||||||
Some(self.acceleration)
|
Some(self.acceleration)
|
||||||
@ -851,22 +901,22 @@ impl Body{
|
|||||||
aabb.grow(self.extrapolated_position(t1));
|
aabb.grow(self.extrapolated_position(t1));
|
||||||
//v+a*t==0
|
//v+a*t==0
|
||||||
//goober code
|
//goober code
|
||||||
if self.acceleration.x()!=Planar64::ZERO{
|
if !self.acceleration.x.is_zero(){
|
||||||
let t=Time::from(-self.velocity.x()/self.acceleration.x());
|
let t=-self.velocity.x/self.acceleration.x;
|
||||||
if t0<t&&t<t1{
|
if t0.to_ratio().lt_ratio(t)&&t.lt_ratio(t1.to_ratio()){
|
||||||
aabb.grow(self.extrapolated_position(t));
|
aabb.grow(self.extrapolated_position_ratio_dt(t));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.acceleration.y()!=Planar64::ZERO{
|
if !self.acceleration.y.is_zero(){
|
||||||
let t=Time::from(-self.velocity.y()/self.acceleration.y());
|
let t=-self.velocity.y/self.acceleration.y;
|
||||||
if t0<t&&t<t1{
|
if t0.to_ratio().lt_ratio(t)&&t.lt_ratio(t1.to_ratio()){
|
||||||
aabb.grow(self.extrapolated_position(t));
|
aabb.grow(self.extrapolated_position_ratio_dt(t));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.acceleration.z()!=Planar64::ZERO{
|
if !self.acceleration.z.is_zero(){
|
||||||
let t=Time::from(-self.velocity.z()/self.acceleration.z());
|
let t=-self.velocity.z/self.acceleration.z;
|
||||||
if t0<t&&t<t1{
|
if t0.to_ratio().lt_ratio(t)&&t.lt_ratio(t1.to_ratio()){
|
||||||
aabb.grow(self.extrapolated_position(t));
|
aabb.grow(self.extrapolated_position_ratio_dt(t));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -939,7 +989,7 @@ pub struct PhysicsData{
|
|||||||
impl Default for PhysicsState{
|
impl Default for PhysicsState{
|
||||||
fn default()->Self{
|
fn default()->Self{
|
||||||
Self{
|
Self{
|
||||||
body:Body::new(Planar64Vec3::int(0,50,0),Planar64Vec3::int(0,0,0),Planar64Vec3::int(0,-100,0),Time::ZERO),
|
body:Body::new(vec3::int(0,50,0),vec3::int(0,0,0),vec3::int(0,-100,0),Time::ZERO),
|
||||||
time:Time::ZERO,
|
time:Time::ZERO,
|
||||||
style:StyleModifiers::default(),
|
style:StyleModifiers::default(),
|
||||||
touching:TouchingState::default(),
|
touching:TouchingState::default(),
|
||||||
@ -1154,7 +1204,7 @@ impl PhysicsContext{
|
|||||||
let mut aabb=aabb::Aabb::default();
|
let mut aabb=aabb::Aabb::default();
|
||||||
let transformed_mesh=TransformedMesh::new(view,transform);
|
let transformed_mesh=TransformedMesh::new(view,transform);
|
||||||
for v in transformed_mesh.verts(){
|
for v in transformed_mesh.verts(){
|
||||||
aabb.grow(v);
|
aabb.grow(v.fix_1());
|
||||||
}
|
}
|
||||||
(ConvexMeshId{
|
(ConvexMeshId{
|
||||||
model_id,
|
model_id,
|
||||||
@ -1229,12 +1279,15 @@ impl PhysicsContext{
|
|||||||
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,data.hitbox_mesh.transformed_mesh());
|
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,data.hitbox_mesh.transformed_mesh());
|
||||||
collector.collect(minkowski.predict_collision_in(relative_body,collector.time())
|
collector.collect(minkowski.predict_collision_in(relative_body,collector.time())
|
||||||
//temp (?) code to avoid collision loops
|
//temp (?) code to avoid collision loops
|
||||||
.map_or(None,|(face,time)|if time<=state.time{None}else{Some((face,time))})
|
.map_or(None,|(face,dt)|{
|
||||||
.map(|(face,time)|
|
let time=relative_body.time+dt.into();
|
||||||
|
if time<=state.time{None}else{Some((time,face,dt))}})
|
||||||
|
.map(|(time,face,dt)|
|
||||||
TimedInstruction{
|
TimedInstruction{
|
||||||
time,
|
time,
|
||||||
instruction:PhysicsInternalInstruction::CollisionStart(
|
instruction:PhysicsInternalInstruction::CollisionStart(
|
||||||
Collision::new(convex_mesh_id,face)
|
Collision::new(convex_mesh_id,face),
|
||||||
|
dt
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -1247,7 +1300,8 @@ impl PhysicsContext{
|
|||||||
fn contact_normal(models:&PhysicsModels,hitbox_mesh:&HitboxMesh,contact:&ContactCollision)->Planar64Vec3{
|
fn contact_normal(models:&PhysicsModels,hitbox_mesh:&HitboxMesh,contact:&ContactCollision)->Planar64Vec3{
|
||||||
let model_mesh=models.contact_mesh(contact);
|
let model_mesh=models.contact_mesh(contact);
|
||||||
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh());
|
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh());
|
||||||
minkowski.face_nd(contact.face_id).0
|
// TODO: normalize to i64::MAX>>1
|
||||||
|
minkowski.face_nd(contact.face_id).0.fix_1()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn recalculate_touching(
|
fn recalculate_touching(
|
||||||
@ -1331,12 +1385,12 @@ fn set_velocity_cull(body:&mut Body,touching:&mut TouchingState,models:&PhysicsM
|
|||||||
let mut culled=false;
|
let mut culled=false;
|
||||||
touching.contacts.retain(|contact|{
|
touching.contacts.retain(|contact|{
|
||||||
let n=contact_normal(models,hitbox_mesh,contact);
|
let n=contact_normal(models,hitbox_mesh,contact);
|
||||||
let r=n.dot(v)<=Planar64::ZERO;
|
let r=n.dot(v).is_positive();
|
||||||
if !r{
|
if r{
|
||||||
culled=true;
|
culled=true;
|
||||||
println!("set_velocity_cull contact={:?}",contact);
|
println!("set_velocity_cull contact={:?}",contact);
|
||||||
}
|
}
|
||||||
r
|
!r
|
||||||
});
|
});
|
||||||
set_velocity(body,touching,models,hitbox_mesh,v);
|
set_velocity(body,touching,models,hitbox_mesh,v);
|
||||||
culled
|
culled
|
||||||
@ -1350,12 +1404,12 @@ fn set_acceleration_cull(body:&mut Body,touching:&mut TouchingState,models:&Phys
|
|||||||
let mut culled=false;
|
let mut culled=false;
|
||||||
touching.contacts.retain(|contact|{
|
touching.contacts.retain(|contact|{
|
||||||
let n=contact_normal(models,hitbox_mesh,contact);
|
let n=contact_normal(models,hitbox_mesh,contact);
|
||||||
let r=n.dot(a)<=Planar64::ZERO;
|
let r=n.dot(a).is_positive();
|
||||||
if !r{
|
if r{
|
||||||
culled=true;
|
culled=true;
|
||||||
println!("set_acceleration_cull contact={:?}",contact);
|
println!("set_acceleration_cull contact={:?}",contact);
|
||||||
}
|
}
|
||||||
r
|
!r
|
||||||
});
|
});
|
||||||
set_acceleration(body,touching,models,hitbox_mesh,a);
|
set_acceleration(body,touching,models,hitbox_mesh,a);
|
||||||
culled
|
culled
|
||||||
@ -1403,8 +1457,10 @@ fn teleport_to_spawn(
|
|||||||
input_state:&InputState,
|
input_state:&InputState,
|
||||||
time:Time,
|
time:Time,
|
||||||
)->Result<(),TeleportToSpawnError>{
|
)->Result<(),TeleportToSpawnError>{
|
||||||
|
const EPSILON:Planar64=Planar64::raw((1<<32)/16);
|
||||||
let transform=models.get_model_transform(stage.spawn()).ok_or(TeleportToSpawnError::NoModel)?;
|
let transform=models.get_model_transform(stage.spawn()).ok_or(TeleportToSpawnError::NoModel)?;
|
||||||
let point=transform.vertex.transform_point3(Planar64Vec3::Y)+Planar64Vec3::Y*(style.hitbox.halfsize.y()+Planar64::ONE/16);
|
//TODO: transform.vertex.matrix3.col(1)+transform.vertex.translation
|
||||||
|
let point=transform.vertex.transform_point3(vec3::Y).fix_1()+Planar64Vec3::new([Planar64::ZERO,style.hitbox.halfsize.y+EPSILON,Planar64::ZERO]);
|
||||||
teleport(point,move_state,body,touching,run,mode_state,Some(mode),models,hitbox_mesh,bvh,style,camera,input_state,time);
|
teleport(point,move_state,body,touching,run,mode_state,Some(mode),models,hitbox_mesh,bvh,style,camera,input_state,time);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -1525,7 +1581,7 @@ fn collision_start_contact(
|
|||||||
Some(gameplay_attributes::ContactingBehaviour::Surf)=>println!("I'm surfing!"),
|
Some(gameplay_attributes::ContactingBehaviour::Surf)=>println!("I'm surfing!"),
|
||||||
Some(gameplay_attributes::ContactingBehaviour::Cling)=>println!("Unimplemented!"),
|
Some(gameplay_attributes::ContactingBehaviour::Cling)=>println!("Unimplemented!"),
|
||||||
&Some(gameplay_attributes::ContactingBehaviour::Elastic(elasticity))=>{
|
&Some(gameplay_attributes::ContactingBehaviour::Elastic(elasticity))=>{
|
||||||
let reflected_velocity=body.velocity+(body.velocity-incident_velocity)*Planar64::raw(elasticity as i64+1);
|
let reflected_velocity=body.velocity+((body.velocity-incident_velocity)*Planar64::raw(elasticity as i64+1)).fix_1();
|
||||||
set_velocity(body,touching,models,hitbox_mesh,reflected_velocity);
|
set_velocity(body,touching,models,hitbox_mesh,reflected_velocity);
|
||||||
},
|
},
|
||||||
Some(gameplay_attributes::ContactingBehaviour::Ladder(contacting_ladder))=>
|
Some(gameplay_attributes::ContactingBehaviour::Ladder(contacting_ladder))=>
|
||||||
@ -1534,7 +1590,7 @@ fn collision_start_contact(
|
|||||||
//kill v
|
//kill v
|
||||||
//actually you could do this with a booster attribute :thinking:
|
//actually you could do this with a booster attribute :thinking:
|
||||||
//it's a little bit different because maybe you want to chain ladders together
|
//it's a little bit different because maybe you want to chain ladders together
|
||||||
set_velocity(body,touching,models,hitbox_mesh,Planar64Vec3::ZERO);//model.velocity
|
set_velocity(body,touching,models,hitbox_mesh,vec3::ZERO);//model.velocity
|
||||||
}
|
}
|
||||||
//ladder walkstate
|
//ladder walkstate
|
||||||
let (gravity,target_velocity)=ladder_things(ladder_settings,&contact,touching,models,hitbox_mesh,style,camera,input_state);
|
let (gravity,target_velocity)=ladder_things(ladder_settings,&contact,touching,models,hitbox_mesh,style,camera,input_state);
|
||||||
@ -1543,7 +1599,7 @@ fn collision_start_contact(
|
|||||||
},
|
},
|
||||||
Some(gameplay_attributes::ContactingBehaviour::NoJump)=>todo!("nyi"),
|
Some(gameplay_attributes::ContactingBehaviour::NoJump)=>todo!("nyi"),
|
||||||
None=>if let Some(walk_settings)=&style.walk{
|
None=>if let Some(walk_settings)=&style.walk{
|
||||||
if walk_settings.is_slope_walkable(contact_normal(models,hitbox_mesh,&contact),Planar64Vec3::Y){
|
if walk_settings.is_slope_walkable(contact_normal(models,hitbox_mesh,&contact),vec3::Y){
|
||||||
//ground
|
//ground
|
||||||
let (gravity,target_velocity)=ground_things(walk_settings,&contact,touching,models,hitbox_mesh,style,camera,input_state);
|
let (gravity,target_velocity)=ground_things(walk_settings,&contact,touching,models,hitbox_mesh,style,camera,input_state);
|
||||||
let walk_state=ContactMoveState::ground(walk_settings,body,gravity,target_velocity,contact);
|
let walk_state=ContactMoveState::ground(walk_settings,body,gravity,target_velocity,contact);
|
||||||
@ -1671,17 +1727,20 @@ fn collision_end_intersect(
|
|||||||
}
|
}
|
||||||
fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedInstruction<PhysicsInternalInstruction>){
|
fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedInstruction<PhysicsInternalInstruction>){
|
||||||
state.time=ins.time;
|
state.time=ins.time;
|
||||||
let should_advance_body=match ins.instruction{
|
let (should_advance_body,goober_time)=match ins.instruction{
|
||||||
PhysicsInternalInstruction::CollisionStart(_)
|
PhysicsInternalInstruction::CollisionStart(_,dt)
|
||||||
|PhysicsInternalInstruction::CollisionEnd(_)
|
|PhysicsInternalInstruction::CollisionEnd(_,dt)=>(true,Some(dt)),
|
||||||
|PhysicsInternalInstruction::StrafeTick
|
PhysicsInternalInstruction::StrafeTick
|
||||||
|PhysicsInternalInstruction::ReachWalkTargetVelocity=>true,
|
|PhysicsInternalInstruction::ReachWalkTargetVelocity=>(true,None),
|
||||||
};
|
};
|
||||||
if should_advance_body{
|
if should_advance_body{
|
||||||
state.body.advance_time(state.time);
|
match goober_time{
|
||||||
|
Some(dt)=>state.body.advance_time_ratio_dt(dt),
|
||||||
|
None=>state.body.advance_time(state.time),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
match ins.instruction{
|
match ins.instruction{
|
||||||
PhysicsInternalInstruction::CollisionStart(collision)=>{
|
PhysicsInternalInstruction::CollisionStart(collision,_)=>{
|
||||||
let mode=data.modes.get_mode(state.mode_state.get_mode_id());
|
let mode=data.modes.get_mode(state.mode_state.get_mode_id());
|
||||||
match collision{
|
match collision{
|
||||||
Collision::Contact(contact)=>collision_start_contact(
|
Collision::Contact(contact)=>collision_start_contact(
|
||||||
@ -1702,7 +1761,7 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
PhysicsInternalInstruction::CollisionEnd(collision)=>match collision{
|
PhysicsInternalInstruction::CollisionEnd(collision,_)=>match collision{
|
||||||
Collision::Contact(contact)=>collision_end_contact(
|
Collision::Contact(contact)=>collision_end_contact(
|
||||||
&mut state.move_state,&mut state.body,&mut state.touching,&data.models,&data.hitbox_mesh,&state.style,&state.camera,&state.input_state,
|
&mut state.move_state,&mut state.body,&mut state.touching,&data.models,&data.hitbox_mesh,&state.style,&state.camera,&state.input_state,
|
||||||
data.models.contact_attr(contact.model_id),
|
data.models.contact_attr(contact.model_id),
|
||||||
@ -1724,9 +1783,9 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
|
|||||||
if strafe_settings.activates(controls){
|
if strafe_settings.activates(controls){
|
||||||
let masked_controls=strafe_settings.mask(controls);
|
let masked_controls=strafe_settings.mask(controls);
|
||||||
let control_dir=state.style.get_control_dir(masked_controls);
|
let control_dir=state.style.get_control_dir(masked_controls);
|
||||||
if control_dir!=Planar64Vec3::ZERO{
|
if control_dir!=vec3::ZERO{
|
||||||
let camera_mat=state.camera.simulate_move_rotation_y(state.input_state.lerp_delta(state.time).x);
|
let camera_mat=state.camera.simulate_move_rotation_y(state.input_state.lerp_delta(state.time).x);
|
||||||
if let Some(ticked_velocity)=strafe_settings.tick_velocity(state.body.velocity,(camera_mat*control_dir).with_length(Planar64::ONE)){
|
if let Some(ticked_velocity)=strafe_settings.tick_velocity(state.body.velocity,(camera_mat*control_dir).with_length(Planar64::ONE).divide().fix_1()){
|
||||||
//this is wrong but will work ig
|
//this is wrong but will work ig
|
||||||
//need to note which push planes activate in push solve and keep those
|
//need to note which push planes activate in push solve and keep those
|
||||||
state.cull_velocity(data,ticked_velocity);
|
state.cull_velocity(data,ticked_velocity);
|
||||||
@ -1750,7 +1809,7 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
|
|||||||
//we know that the acceleration is precisely zero because the walk target is known to be reachable
|
//we know that the acceleration is precisely zero because the walk target is known to be reachable
|
||||||
//which means that gravity can be fully cancelled
|
//which means that gravity can be fully cancelled
|
||||||
//ignore moving platforms for now
|
//ignore moving platforms for now
|
||||||
set_acceleration(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,Planar64Vec3::ZERO);
|
set_acceleration(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,vec3::ZERO);
|
||||||
walk_state.target=TransientAcceleration::Reached;
|
walk_state.target=TransientAcceleration::Reached;
|
||||||
},
|
},
|
||||||
//you are not supposed to reach an unreachable walk target!
|
//you are not supposed to reach an unreachable walk target!
|
||||||
@ -1845,9 +1904,9 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
|
|||||||
data.models.get_model_transform(mode.get_start().into()).map(|transform|
|
data.models.get_model_transform(mode.get_start().into()).map(|transform|
|
||||||
transform.vertex.translation
|
transform.vertex.translation
|
||||||
)
|
)
|
||||||
).unwrap_or(Planar64Vec3::ZERO);
|
).unwrap_or(vec3::ZERO);
|
||||||
set_position(spawn_point,&mut state.move_state,&mut state.body,&mut state.touching,&mut state.run,&mut state.mode_state,mode,&data.models,&data.hitbox_mesh,&data.bvh,&state.style,&state.camera,&state.input_state,state.time);
|
set_position(spawn_point,&mut state.move_state,&mut state.body,&mut state.touching,&mut state.run,&mut state.mode_state,mode,&data.models,&data.hitbox_mesh,&data.bvh,&state.style,&state.camera,&state.input_state,state.time);
|
||||||
set_velocity(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,Planar64Vec3::ZERO);
|
set_velocity(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,vec3::ZERO);
|
||||||
state.set_move_state(data,MoveState::Air);
|
state.set_move_state(data,MoveState::Air);
|
||||||
b_refresh_walk_target=false;
|
b_refresh_walk_target=false;
|
||||||
}
|
}
|
||||||
@ -1898,7 +1957,7 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
|
|||||||
_=>println!("{}|{:?}",ins.time,ins.instruction),
|
_=>println!("{}|{:?}",ins.time,ins.instruction),
|
||||||
}
|
}
|
||||||
if ins.time<state.time{
|
if ins.time<state.time{
|
||||||
println!("@@@@ Time travel warning! {:?}",ins);
|
println!("@@@@ Time travel warning! state.time={} ins.time={}\nInstruction={:?}",state.time,ins.time,ins.instruction);
|
||||||
}
|
}
|
||||||
//idle is special, it is specifically a no-op to get Internal events to catch up to real time
|
//idle is special, it is specifically a no-op to get Internal events to catch up to real time
|
||||||
match ins.instruction{
|
match ins.instruction{
|
||||||
@ -1910,215 +1969,216 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test{
|
mod test{
|
||||||
|
use strafesnet_common::integer::{vec3::{self,int as int3},mat3};
|
||||||
use super::*;
|
use super::*;
|
||||||
fn test_collision_axis_aligned(relative_body:Body,expected_collision_time:Option<Time>){
|
fn test_collision_axis_aligned(relative_body:Body,expected_collision_time:Option<Time>){
|
||||||
let h0=HitboxMesh::new(PhysicsMesh::unit_cube(),integer::Planar64Affine3::new(Planar64Mat3::from_diagonal(Planar64Vec3::int(5,1,5)/2),Planar64Vec3::ZERO));
|
let h0=HitboxMesh::new(PhysicsMesh::unit_cube(),integer::Planar64Affine3::new(mat3::from_diagonal(int3(5,1,5)>>1),vec3::ZERO));
|
||||||
let h1=StyleModifiers::roblox_bhop().calculate_mesh();
|
let h1=StyleModifiers::roblox_bhop().calculate_mesh();
|
||||||
let hitbox_mesh=h1.transformed_mesh();
|
let hitbox_mesh=h1.transformed_mesh();
|
||||||
let platform_mesh=h0.transformed_mesh();
|
let platform_mesh=h0.transformed_mesh();
|
||||||
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(platform_mesh,hitbox_mesh);
|
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(platform_mesh,hitbox_mesh);
|
||||||
let collision=minkowski.predict_collision_in(&relative_body,Time::MAX);
|
let collision=minkowski.predict_collision_in(&relative_body,Time::from_secs(10));
|
||||||
assert_eq!(collision.map(|tup|tup.1),expected_collision_time,"Incorrect time of collision");
|
assert_eq!(collision.map(|tup|relative_body.time+tup.1.into()),expected_collision_time,"Incorrect time of collision");
|
||||||
}
|
}
|
||||||
fn test_collision_rotated(relative_body:Body,expected_collision_time:Option<Time>){
|
fn test_collision_rotated(relative_body:Body,expected_collision_time:Option<Time>){
|
||||||
let h0=HitboxMesh::new(PhysicsMesh::unit_cube(),
|
let h0=HitboxMesh::new(PhysicsMesh::unit_cube(),
|
||||||
integer::Planar64Affine3::new(
|
integer::Planar64Affine3::new(
|
||||||
integer::Planar64Mat3::from_cols(
|
integer::Planar64Mat3::from_cols([
|
||||||
Planar64Vec3::int(5,0,1)/2,
|
int3(5,0,1)>>1,
|
||||||
Planar64Vec3::int(0,1,0)/2,
|
int3(0,1,0)>>1,
|
||||||
Planar64Vec3::int(-1,0,5)/2,
|
int3(-1,0,5)>>1,
|
||||||
|
]),
|
||||||
|
vec3::ZERO
|
||||||
),
|
),
|
||||||
Planar64Vec3::ZERO,
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
let h1=StyleModifiers::roblox_bhop().calculate_mesh();
|
let h1=StyleModifiers::roblox_bhop().calculate_mesh();
|
||||||
let hitbox_mesh=h1.transformed_mesh();
|
let hitbox_mesh=h1.transformed_mesh();
|
||||||
let platform_mesh=h0.transformed_mesh();
|
let platform_mesh=h0.transformed_mesh();
|
||||||
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(platform_mesh,hitbox_mesh);
|
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(platform_mesh,hitbox_mesh);
|
||||||
let collision=minkowski.predict_collision_in(&relative_body,Time::MAX);
|
let collision=minkowski.predict_collision_in(&relative_body,Time::from_secs(10));
|
||||||
assert_eq!(collision.map(|tup|tup.1),expected_collision_time,"Incorrect time of collision");
|
assert_eq!(collision.map(|tup|relative_body.time+tup.1.into()),expected_collision_time,"Incorrect time of collision");
|
||||||
}
|
}
|
||||||
fn test_collision(relative_body:Body,expected_collision_time:Option<Time>){
|
fn test_collision(relative_body:Body,expected_collision_time:Option<Time>){
|
||||||
test_collision_axis_aligned(relative_body.clone(),expected_collision_time);
|
test_collision_axis_aligned(relative_body.clone(),expected_collision_time);
|
||||||
test_collision_rotated(relative_body,expected_collision_time);
|
test_collision_rotated(relative_body,expected_collision_time);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_collision_degenerate(){
|
fn test_collision_degenerate_straight_down(){
|
||||||
test_collision(Body::new(
|
test_collision(Body::new(
|
||||||
Planar64Vec3::int(0,5,0),
|
int3(0,5,0),
|
||||||
Planar64Vec3::int(0,-1,0),
|
int3(0,-1,0),
|
||||||
Planar64Vec3::ZERO,
|
vec3::ZERO,
|
||||||
Time::ZERO
|
Time::ZERO
|
||||||
),Some(Time::from_secs(2)));
|
),Some(Time::from_secs(2)));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_collision_degenerate_east(){
|
fn test_collision_degenerate_east(){
|
||||||
test_collision(Body::new(
|
test_collision(Body::new(
|
||||||
Planar64Vec3::int(3,5,0),
|
int3(3,5,0),
|
||||||
Planar64Vec3::int(0,-1,0),
|
int3(0,-1,0),
|
||||||
Planar64Vec3::ZERO,
|
vec3::ZERO,
|
||||||
Time::ZERO
|
Time::ZERO
|
||||||
),Some(Time::from_secs(2)));
|
),Some(Time::from_secs(2)));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_collision_degenerate_south(){
|
fn test_collision_degenerate_south(){
|
||||||
test_collision(Body::new(
|
test_collision(Body::new(
|
||||||
Planar64Vec3::int(0,5,3),
|
int3(0,5,3),
|
||||||
Planar64Vec3::int(0,-1,0),
|
int3(0,-1,0),
|
||||||
Planar64Vec3::ZERO,
|
vec3::ZERO,
|
||||||
Time::ZERO
|
Time::ZERO
|
||||||
),Some(Time::from_secs(2)));
|
),Some(Time::from_secs(2)));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_collision_degenerate_west(){
|
fn test_collision_degenerate_west(){
|
||||||
test_collision(Body::new(
|
test_collision(Body::new(
|
||||||
Planar64Vec3::int(-3,5,0),
|
int3(-3,5,0),
|
||||||
Planar64Vec3::int(0,-1,0),
|
int3(0,-1,0),
|
||||||
Planar64Vec3::ZERO,
|
vec3::ZERO,
|
||||||
Time::ZERO
|
Time::ZERO
|
||||||
),Some(Time::from_secs(2)));
|
),Some(Time::from_secs(2)));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_collision_degenerate_north(){
|
fn test_collision_degenerate_north(){
|
||||||
test_collision(Body::new(
|
test_collision(Body::new(
|
||||||
Planar64Vec3::int(0,5,-3),
|
int3(0,5,-3),
|
||||||
Planar64Vec3::int(0,-1,0),
|
int3(0,-1,0),
|
||||||
Planar64Vec3::ZERO,
|
vec3::ZERO,
|
||||||
Time::ZERO
|
Time::ZERO
|
||||||
),Some(Time::from_secs(2)));
|
),Some(Time::from_secs(2)));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_collision_parabola_edge_east_from_west(){
|
fn test_collision_parabola_edge_east_from_west(){
|
||||||
test_collision(VirtualBody::relative(&Body::default(),&Body::new(
|
test_collision(VirtualBody::relative(&Body::ZERO,&Body::new(
|
||||||
Planar64Vec3::int(3,3,0),
|
int3(3,3,0),
|
||||||
Planar64Vec3::int(100,-1,0),
|
int3(100,-1,0),
|
||||||
Planar64Vec3::int(0,-1,0),
|
int3(0,-1,0),
|
||||||
Time::ZERO
|
Time::ZERO
|
||||||
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_collision_parabola_edge_south_from_north(){
|
fn test_collision_parabola_edge_south_from_north(){
|
||||||
test_collision(VirtualBody::relative(&Body::default(),&Body::new(
|
test_collision(VirtualBody::relative(&Body::ZERO,&Body::new(
|
||||||
Planar64Vec3::int(0,3,3),
|
int3(0,3,3),
|
||||||
Planar64Vec3::int(0,-1,100),
|
int3(0,-1,100),
|
||||||
Planar64Vec3::int(0,-1,0),
|
int3(0,-1,0),
|
||||||
Time::ZERO
|
Time::ZERO
|
||||||
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_collision_parabola_edge_west_from_east(){
|
fn test_collision_parabola_edge_west_from_east(){
|
||||||
test_collision(VirtualBody::relative(&Body::default(),&Body::new(
|
test_collision(VirtualBody::relative(&Body::ZERO,&Body::new(
|
||||||
Planar64Vec3::int(-3,3,0),
|
int3(-3,3,0),
|
||||||
Planar64Vec3::int(-100,-1,0),
|
int3(-100,-1,0),
|
||||||
Planar64Vec3::int(0,-1,0),
|
int3(0,-1,0),
|
||||||
Time::ZERO
|
Time::ZERO
|
||||||
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_collision_parabola_edge_north_from_south(){
|
fn test_collision_parabola_edge_north_from_south(){
|
||||||
test_collision(VirtualBody::relative(&Body::default(),&Body::new(
|
test_collision(VirtualBody::relative(&Body::ZERO,&Body::new(
|
||||||
Planar64Vec3::int(0,3,-3),
|
int3(0,3,-3),
|
||||||
Planar64Vec3::int(0,-1,-100),
|
int3(0,-1,-100),
|
||||||
Planar64Vec3::int(0,-1,0),
|
int3(0,-1,0),
|
||||||
Time::ZERO
|
Time::ZERO
|
||||||
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_collision_parabola_edge_north_from_ne(){
|
fn test_collision_parabola_edge_north_from_ne(){
|
||||||
test_collision(VirtualBody::relative(&Body::default(),&Body::new(
|
test_collision(VirtualBody::relative(&Body::ZERO,&Body::new(
|
||||||
Planar64Vec3::int(0,6,-7)/2,
|
int3(0,6,-7)>>1,
|
||||||
Planar64Vec3::int(-10,-1,1),
|
int3(-10,-1,1),
|
||||||
Planar64Vec3::int(0,-1,0),
|
int3(0,-1,0),
|
||||||
Time::ZERO
|
Time::ZERO
|
||||||
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_collision_parabola_edge_north_from_nw(){
|
fn test_collision_parabola_edge_north_from_nw(){
|
||||||
test_collision(VirtualBody::relative(&Body::default(),&Body::new(
|
test_collision(VirtualBody::relative(&Body::ZERO,&Body::new(
|
||||||
Planar64Vec3::int(0,6,-7)/2,
|
int3(0,6,-7)>>1,
|
||||||
Planar64Vec3::int(10,-1,1),
|
int3(10,-1,1),
|
||||||
Planar64Vec3::int(0,-1,0),
|
int3(0,-1,0),
|
||||||
Time::ZERO
|
Time::ZERO
|
||||||
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_collision_parabola_edge_east_from_se(){
|
fn test_collision_parabola_edge_east_from_se(){
|
||||||
test_collision(VirtualBody::relative(&Body::default(),&Body::new(
|
test_collision(VirtualBody::relative(&Body::ZERO,&Body::new(
|
||||||
Planar64Vec3::int(7,6,0)/2,
|
int3(7,6,0)>>1,
|
||||||
Planar64Vec3::int(-1,-1,-10),
|
int3(-1,-1,-10),
|
||||||
Planar64Vec3::int(0,-1,0),
|
int3(0,-1,0),
|
||||||
Time::ZERO
|
Time::ZERO
|
||||||
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_collision_parabola_edge_east_from_ne(){
|
fn test_collision_parabola_edge_east_from_ne(){
|
||||||
test_collision(VirtualBody::relative(&Body::default(),&Body::new(
|
test_collision(VirtualBody::relative(&Body::ZERO,&Body::new(
|
||||||
Planar64Vec3::int(7,6,0)/2,
|
int3(7,6,0)>>1,
|
||||||
Planar64Vec3::int(-1,-1,10),
|
int3(-1,-1,10),
|
||||||
Planar64Vec3::int(0,-1,0),
|
int3(0,-1,0),
|
||||||
Time::ZERO
|
Time::ZERO
|
||||||
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_collision_parabola_edge_south_from_se(){
|
fn test_collision_parabola_edge_south_from_se(){
|
||||||
test_collision(VirtualBody::relative(&Body::default(),&Body::new(
|
test_collision(VirtualBody::relative(&Body::ZERO,&Body::new(
|
||||||
Planar64Vec3::int(0,6,7)/2,
|
int3(0,6,7)>>1,
|
||||||
Planar64Vec3::int(-10,-1,-1),
|
int3(-10,-1,-1),
|
||||||
Planar64Vec3::int(0,-1,0),
|
int3(0,-1,0),
|
||||||
Time::ZERO
|
Time::ZERO
|
||||||
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_collision_parabola_edge_south_from_sw(){
|
fn test_collision_parabola_edge_south_from_sw(){
|
||||||
test_collision(VirtualBody::relative(&Body::default(),&Body::new(
|
test_collision(VirtualBody::relative(&Body::ZERO,&Body::new(
|
||||||
Planar64Vec3::int(0,6,7)/2,
|
int3(0,6,7)>>1,
|
||||||
Planar64Vec3::int(10,-1,-1),
|
int3(10,-1,-1),
|
||||||
Planar64Vec3::int(0,-1,0),
|
int3(0,-1,0),
|
||||||
Time::ZERO
|
Time::ZERO
|
||||||
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_collision_parabola_edge_west_from_se(){
|
fn test_collision_parabola_edge_west_from_se(){
|
||||||
test_collision(VirtualBody::relative(&Body::default(),&Body::new(
|
test_collision(VirtualBody::relative(&Body::ZERO,&Body::new(
|
||||||
Planar64Vec3::int(-7,6,0)/2,
|
int3(-7,6,0)>>1,
|
||||||
Planar64Vec3::int(1,-1,-10),
|
int3(1,-1,-10),
|
||||||
Planar64Vec3::int(0,-1,0),
|
int3(0,-1,0),
|
||||||
Time::ZERO
|
Time::ZERO
|
||||||
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_collision_parabola_edge_west_from_ne(){
|
fn test_collision_parabola_edge_west_from_ne(){
|
||||||
test_collision(VirtualBody::relative(&Body::default(),&Body::new(
|
test_collision(VirtualBody::relative(&Body::ZERO,&Body::new(
|
||||||
Planar64Vec3::int(-7,6,0)/2,
|
int3(-7,6,0)>>1,
|
||||||
Planar64Vec3::int(1,-1,10),
|
int3(1,-1,10),
|
||||||
Planar64Vec3::int(0,-1,0),
|
int3(0,-1,0),
|
||||||
Time::ZERO
|
Time::ZERO
|
||||||
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_collision_oblique(){
|
fn test_collision_oblique(){
|
||||||
test_collision(Body::new(
|
test_collision(Body::new(
|
||||||
Planar64Vec3::int(0,5,0),
|
int3(0,5,0),
|
||||||
Planar64Vec3::int(1,-64,2)/64,
|
int3(1,-64,2)>>6,// /64
|
||||||
Planar64Vec3::ZERO,
|
vec3::ZERO,
|
||||||
Time::ZERO
|
Time::ZERO
|
||||||
),Some(Time::from_secs(2)));
|
),Some(Time::from_secs(2)));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn zoom_hit_nothing(){
|
fn zoom_hit_nothing(){
|
||||||
test_collision(Body::new(
|
test_collision(Body::new(
|
||||||
Planar64Vec3::int(0,10,0),
|
int3(0,10,0),
|
||||||
Planar64Vec3::int(1,0,0),
|
int3(1,0,0),
|
||||||
Planar64Vec3::int(0,1,0),
|
int3(0,1,0),
|
||||||
Time::ZERO
|
Time::ZERO
|
||||||
),None);
|
),None);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn already_inside_hit_nothing(){
|
fn already_inside_hit_nothing(){
|
||||||
test_collision(Body::new(
|
test_collision(Body::new(
|
||||||
Planar64Vec3::ZERO,
|
vec3::ZERO,
|
||||||
Planar64Vec3::int(1,0,0),
|
int3(1,0,0),
|
||||||
Planar64Vec3::int(0,1,0),
|
int3(0,1,0),
|
||||||
Time::ZERO
|
Time::ZERO
|
||||||
),None);
|
),None);
|
||||||
}
|
}
|
||||||
|
@ -181,9 +181,9 @@ 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
|
||||||
|
Loading…
Reference in New Issue
Block a user