Compare commits

..

5 Commits

Author SHA1 Message Date
6d382e8165 sad api 2024-09-18 18:48:47 -07:00
08c8b59bd9 run_scripts is now a member function 2024-09-18 15:34:56 -07:00
74b7265527 no 2024-09-17 17:59:34 -07:00
e68d15f4e3 run scripts 2024-09-17 17:44:18 -07:00
8d9b1eeb04 update specific deps 2024-09-17 17:44:18 -07:00
8 changed files with 514 additions and 889 deletions

685
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,33 +1,32 @@
use crate::physics::Body; use crate::physics::Body;
use crate::model_physics::{GigaTime,FEV,MeshQuery,DirectedEdge,MinkowskiMesh,MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert}; use crate::model_physics::{FEV,MeshQuery,DirectedEdge};
use strafesnet_common::integer::{Time,Fixed,Ratio}; use strafesnet_common::integer::{Time,Planar64};
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>,GigaTime), Next(FEV<F,E,V>,Time),
Hit(F,GigaTime), Hit(F,Time),
} }
type MinkowskiFEV=FEV<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert>; 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 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_transition=MinkowskiTransition::Miss; let mut best_time=time_limit;
let mut best_transtition=Transition::Miss;
match fev{ match fev{
&MinkowskiFEV::Face(face_id)=>{ &FEV::<F,E,V>::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 dt in Fixed::<4,128>::zeroes2((n.dot(body.position)-d)*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){ for t in zeroes2((n.dot(body.position)-d)*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){ let t=body.time+Time::from(t);
best_time=dt; if time<=t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{
best_transition=MinkowskiTransition::Hit(face_id,dt); best_time=t;
best_transtition=Transition::Hit(face_id,t);
break; break;
} }
} }
@ -37,18 +36,18 @@ type MinkowskiTransition=Transition<MinkowskiFace,MinkowskiDirectedEdge,Minkowsk
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!
//WARNING: precision is swept under the rug! 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)){
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()){ 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::Edge(directed_edge_id.as_undirected()),dt); best_transtition=Transition::Next(FEV::<F,E,V>::Edge(directed_edge_id.as_undirected()),t);
break; break;
} }
} }
} }
//if none: //if none:
}, },
&MinkowskiFEV::Edge(edge_id)=>{ &FEV::<F,E,V>::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);
@ -58,10 +57,11 @@ type MinkowskiTransition=Transition<MinkowskiFace,MinkowskiDirectedEdge,Minkowsk
//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 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()){ for t in zeroes2(n.dot(delta_pos),n.dot(body.velocity)*2,n.dot(body.acceleration)){
if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){ let t=body.time+Time::from(t);
best_time=dt; if time<=t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{
best_transition=MinkowskiTransition::Next(MinkowskiFEV::Face(edge_face_id),dt); best_time=t;
best_transtition=Transition::Next(FEV::<F,E,V>::Face(edge_face_id),t);
break; break;
} }
} }
@ -70,27 +70,27 @@ type MinkowskiTransition=Transition<MinkowskiFace,MinkowskiDirectedEdge,Minkowsk
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 dt in Fixed::<2,64>::zeroes2((n.dot(body.position-mesh.vert(vert_id)))*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){ for t in zeroes2((n.dot(body.position-mesh.vert(vert_id)))*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){ let t=body.time+Time::from(t);
let dt=Ratio::new(dt.num.fix_4(),dt.den.fix_4()); 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::Vert(vert_id),dt); best_transtition=Transition::Next(FEV::<F,E,V>::Vert(vert_id),t);
break; break;
} }
} }
} }
//if none: //if none:
}, },
&MinkowskiFEV::Vert(vert_id)=>{ &FEV::<F,E,V>::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 dt in Fixed::<2,64>::zeroes2((n.dot(body.position-mesh.vert(vert_id)))*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){ for t in zeroes2((n.dot(body.position-mesh.vert(vert_id)))*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){ let t=body.time+Time::from(t);
let dt=Ratio::new(dt.num.fix_4(),dt.den.fix_4()); 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::Edge(directed_edge_id.as_undirected()),dt); best_transtition=Transition::Next(FEV::<F,E,V>::Edge(directed_edge_id.as_undirected()),t);
break; break;
} }
} }
@ -98,26 +98,18 @@ type MinkowskiTransition=Transition<MinkowskiFace,MinkowskiDirectedEdge,Minkowsk
//if none: //if none:
}, },
} }
best_transition best_transtition
} }
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,GigaTime), Hit(F,Time),
} }
type MinkowskiCrawlResult=CrawlResult<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert>; 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>{
pub fn crawl_fev(mut fev:MinkowskiFEV,mesh:&MinkowskiMesh,relative_body:&Body,start_time:Time,time_limit:Time)->MinkowskiCrawlResult{ let mut time=start_time;
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,body_time,mesh,relative_body,time_limit){ match next_transition(&fev,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,body_time)=(next_fev,next_time), Transition::Next(next_fev,next_time)=>(fev,time)=(next_fev,next_time),
Transition::Hit(face,time)=>return CrawlResult::Hit(face,time), Transition::Hit(face,time)=>return CrawlResult::Hit(face,time),
} }
} }

View File

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

View File

@ -219,7 +219,7 @@ impl GraphicsState{
//wow //wow
let instance=GraphicsModelOwned{ let instance=GraphicsModelOwned{
transform:model.transform.into(), transform:model.transform.into(),
normal_transform:glam::Mat3::from_cols_array_2d(&model.transform.matrix3.to_array().map(|row|row.map(Into::into))).inverse().transpose(), normal_transform:Into::<glam::Mat3>::into(model.transform.matrix3).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|v.to_array().map(Into::into)).collect(), unique_pos:mesh.unique_pos.iter().map(|&v|*Into::<glam::Vec3>::into(v).as_ref()).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|v.to_array().map(Into::into)).collect(), unique_normal:mesh.unique_normal.iter().map(|&v|*Into::<glam::Vec3>::into(v).as_ref()).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).map(Into::<f32>::into).to_array().into(), frame_state.body.extrapolated_position(frame_state.time).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

View File

@ -1,15 +1,15 @@
use std::borrow::{Borrow,Cow}; use std::borrow::{Borrow,Cow};
use std::collections::{HashSet,HashMap}; use std::collections::{HashSet,HashMap};
use strafesnet_common::integer::vec3::Vector3;
use strafesnet_common::model::{self,MeshId,PolygonIter}; use strafesnet_common::model::{self,MeshId,PolygonIter};
use strafesnet_common::integer::{self,vec3,Fixed,Planar64,Planar64Vec3,Ratio}; use strafesnet_common::zeroes;
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+std::fmt::Debug+UndirectedEdge; type UndirectedEdge:Copy+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,7 +50,6 @@ 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),
@ -65,9 +64,6 @@ 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())
@ -77,7 +73,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)->(Self::Normal,Self::Offset); fn face_nd(&self,face_id:FACE)->(Planar64Vec3,Planar64);
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]>;
@ -141,22 +137,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:vec3::raw_xyz( 4294967296, 0, 0),dot:Planar64::raw(4294967296)}, Face{normal:Planar64Vec3::raw_xyz( 4294967296, 0, 0),dot:Planar64::raw(4294967296)},
Face{normal:vec3::raw_xyz( 0, 4294967296, 0),dot:Planar64::raw(4294967296)}, Face{normal:Planar64Vec3::raw_xyz( 0, 4294967296, 0),dot:Planar64::raw(4294967296)},
Face{normal:vec3::raw_xyz( 0, 0, 4294967296),dot:Planar64::raw(4294967296)}, Face{normal:Planar64Vec3::raw_xyz( 0, 0, 4294967296),dot:Planar64::raw(4294967296)},
Face{normal:vec3::raw_xyz(-4294967296, 0, 0),dot:Planar64::raw(4294967296)}, Face{normal:Planar64Vec3::raw_xyz(-4294967296, 0, 0),dot:Planar64::raw(4294967296)},
Face{normal:vec3::raw_xyz( 0,-4294967296, 0),dot:Planar64::raw(4294967296)}, Face{normal:Planar64Vec3::raw_xyz( 0,-4294967296, 0),dot:Planar64::raw(4294967296)},
Face{normal:vec3::raw_xyz( 0, 0,-4294967296),dot:Planar64::raw(4294967296)} Face{normal:Planar64Vec3::raw_xyz( 0, 0,-4294967296),dot:Planar64::raw(4294967296)}
], ],
verts:vec![ verts:vec![
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)) Vert(Planar64Vec3::raw_xyz(-4294967296,-4294967296,-4294967296))
] ]
}; };
let mesh_topology=PhysicsMeshTopology{ let mesh_topology=PhysicsMeshTopology{
@ -334,7 +330,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=Vector3::new([Fixed::ZERO,Fixed::ZERO,Fixed::ZERO]); let mut normal=Planar64Vec3::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);
@ -345,11 +341,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+=Vector3::new([ normal+=Planar64Vec3::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);
@ -366,16 +362,14 @@ 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();
let mut dot=Fixed::ZERO; //choose precision loss randomly idk
// find the average dot normal=normal/len as i64;
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{ let face=Face{normal,dot:dot/len as i64};
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=>{
@ -422,8 +416,6 @@ 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)
@ -452,14 +444,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::mat3::Matrix3<Fixed<2,64>>, pub normal:integer::Planar64Mat3,
pub det:Fixed<3,96>, pub det:Planar64,
} }
impl PhysicsMeshTransform{ impl PhysicsMeshTransform{
pub fn new(transform:integer::Planar64Affine3)->Self{ pub const fn new(transform:integer::Planar64Affine3)->Self{
Self{ Self{
normal:transform.matrix3.adjugate().transpose(), normal:transform.matrix3.inverse_times_det().transpose(),
det:transform.matrix3.det(), det:transform.matrix3.determinant(),
vertex:transform, vertex:transform,
} }
} }
@ -479,33 +471,33 @@ impl TransformedMesh<'_>{
transform, transform,
} }
} }
pub fn verts<'a>(&'a self)->impl Iterator<Item=vec3::Vector3<Fixed<2,64>>>+'a{ pub fn verts<'a>(&'a self)->impl Iterator<Item=Planar64Vec3>+'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
SubmeshVertId::new( for (i,vert_id) in self.view.topology.verts.iter().enumerate(){
self.view.topology.verts.iter() let p=self.transform.vertex.transform_point3(self.view.data.verts[vert_id.get() as usize].0);
.enumerate() let d=dir.dot(p);
.max_by_key(|(_,&vert_id)| if best_dot<d{
dir.dot(self.transform.vertex.transform_point3(self.view.data.verts[vert_id.get() as usize].0)) best_dot=d;
) 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<'_>{
type Normal=Vector3<Fixed<3,96>>; fn face_nd(&self,face_id:SubmeshFaceId)->(Planar64Vec3,Planar64){
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*self.transform.det+transformed_n.dot(self.transform.vertex.translation); let transformed_d=d+transformed_n.dot(self.transform.vertex.translation)/self.transform.det;
(transformed_n,transformed_d) (transformed_n/self.transform.det,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)).fix_1() self.transform.vertex.transform_point3(self.view.vert(vert_id))
} }
#[inline] #[inline]
fn face_edges(&self,face_id:SubmeshFaceId)->Cow<Vec<SubmeshDirectedEdgeId>>{ fn face_edges(&self,face_id:SubmeshFaceId)->Cow<Vec<SubmeshDirectedEdgeId>>{
@ -533,11 +525,11 @@ impl MeshQuery<SubmeshFaceId,SubmeshDirectedEdgeId,SubmeshVertId> for Transforme
//(face,vertex) //(face,vertex)
//(edge,edge) //(edge,edge)
//(vertex,face) //(vertex,face)
#[derive(Clone,Copy,Debug)] #[derive(Clone,Copy)]
pub enum MinkowskiVert{ pub enum MinkowskiVert{
VertVert(SubmeshVertId,SubmeshVertId), VertVert(SubmeshVertId,SubmeshVertId),
} }
#[derive(Clone,Copy,Debug)] #[derive(Clone,Copy)]
pub enum MinkowskiEdge{ pub enum MinkowskiEdge{
VertEdge(SubmeshVertId,SubmeshEdgeId), VertEdge(SubmeshVertId,SubmeshEdgeId),
EdgeVert(SubmeshEdgeId,SubmeshVertId), EdgeVert(SubmeshEdgeId,SubmeshVertId),
@ -552,7 +544,7 @@ impl UndirectedEdge for MinkowskiEdge{
} }
} }
} }
#[derive(Clone,Copy,Debug)] #[derive(Clone,Copy)]
pub enum MinkowskiDirectedEdge{ pub enum MinkowskiDirectedEdge{
VertEdge(SubmeshVertId,SubmeshDirectedEdgeId), VertEdge(SubmeshVertId,SubmeshDirectedEdgeId),
EdgeVert(SubmeshDirectedEdgeId,SubmeshVertId), EdgeVert(SubmeshDirectedEdgeId,SubmeshVertId),
@ -573,7 +565,7 @@ impl DirectedEdge for MinkowskiDirectedEdge{
} }
} }
} }
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq)] #[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
pub enum MinkowskiFace{ pub enum MinkowskiFace{
VertFace(SubmeshVertId,SubmeshFaceId), VertFace(SubmeshVertId,SubmeshFaceId),
EdgeEdge(SubmeshEdgeId,SubmeshEdgeId,bool), EdgeEdge(SubmeshEdgeId,SubmeshEdgeId,bool),
@ -589,7 +581,6 @@ 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
@ -599,8 +590,6 @@ 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{
@ -611,7 +600,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 Fixed<2,64>,infinity_dir:Planar64Vec3,point:Planar64Vec3)->Transition{ fn next_transition_vert(&self,vert_id:MinkowskiVert,best_distance_squared:&mut Planar64,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);
@ -621,7 +610,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 edge_n.dot(infinity_dir).is_zero(){ if zeroes::zeroes1(edge_n.dot(diff),edge_n.dot(infinity_dir)).len()==0{
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);
@ -631,21 +620,21 @@ impl MinkowskiMesh<'_>{
} }
best_transition best_transition
} }
fn final_ev(&self,vert_id:MinkowskiVert,best_distance_squared:&mut Fixed<2,64>,infinity_dir:Planar64Vec3,point:Planar64Vec3)->EV{ fn final_ev(&self,vert_id:MinkowskiVert,best_distance_squared:&mut Planar64,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 !d.is_negative()&&d<=edge_nn{ if Planar64::ZERO<=d&&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).divide().fix_2() c.dot(c)/edge_nn
}; };
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());
@ -691,7 +680,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.is_positive()&&boundary_n.dot(infinity_dir).is_zero(){ if (boundary_d)<=Planar64::ZERO&&zeroes::zeroes1(boundary_d,boundary_n.dot(infinity_dir)*2).len()==0{
//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);
} }
@ -705,16 +694,15 @@ 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=vec3::ZERO; infinity_body.acceleration=Planar64Vec3::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
// TODO: change crawl_fev args to delta time? Optional values? 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(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,GigaTime)>{ pub fn predict_collision_in(&self,relative_body:&crate::physics::Body,time_limit:integer::Time)->Option<(MinkowskiFace,integer::Time)>{
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){
@ -723,7 +711,7 @@ impl MinkowskiMesh<'_>{
} }
}) })
} }
pub fn predict_collision_out(&self,relative_body:&crate::physics::Body,time_limit:integer::Time)->Option<(MinkowskiFace,GigaTime)>{ pub fn predict_collision_out(&self,relative_body:&crate::physics::Body,time_limit:integer::Time)->Option<(MinkowskiFace,integer::Time)>{
//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),
@ -739,13 +727,10 @@ impl MinkowskiMesh<'_>{
} }
}) })
} }
pub fn predict_collision_face_out(&self,relative_body:&crate::physics::Body,time_limit:integer::Time,contact_face_id:MinkowskiFace)->Option<(MinkowskiEdge,GigaTime)>{ pub fn predict_collision_face_out(&self,relative_body:&crate::physics::Body,time_limit:integer::Time,contact_face_id:MinkowskiFace)->Option<(MinkowskiEdge,integer::Time)>{
//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={ let mut best_time=time_limit;
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(){
@ -755,10 +740,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
//WARNING: truncated precision for t in zeroes::zeroes2((n.dot(relative_body.position))*2-d,n.dot(relative_body.velocity)*2,n.dot(relative_body.acceleration)){
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()){ let t=relative_body.time+integer::Time::from(t);
if Ratio::new(Planar64::ZERO,Planar64::EPSILON).le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(relative_body.extrapolated_velocity_ratio_dt(dt)).is_negative(){ if relative_body.time<t&&t<best_time&&n.dot(relative_body.extrapolated_velocity(t))<Planar64::ZERO{
best_time=dt; best_time=t;
best_edge=Some(directed_edge_id); best_edge=Some(directed_edge_id);
break; break;
} }
@ -766,15 +751,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,GigaTime)>{ fn infinity_in(&self,infinity_body:crate::physics::Body)->Option<(MinkowskiFace,integer::Time)>{
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/4,infinity_body.time){ match crate::face_crawler::crawl_fev(infinity_fev,self,&infinity_body,integer::Time::MIN,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,vec3::Y,vec3::ZERO,integer::Time::ZERO); let infinity_body=crate::physics::Body::new(point,Planar64Vec3::Y,Planar64Vec3::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)
@ -785,9 +770,7 @@ impl MinkowskiMesh<'_>{
} }
} }
impl MeshQuery<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert> for MinkowskiMesh<'_>{ impl MeshQuery<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert> for MinkowskiMesh<'_>{
type Normal=Vector3<Fixed<3,96>>; fn face_nd(&self,face_id:MinkowskiFace)->(Planar64Vec3,Planar64){
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);
@ -801,7 +784,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)).fix_3(),((e0d-e1d)*(parity as i64*2-1)).fix_4()) (n*(parity as i64*4-2),(e0d-e1d)*(parity as i64*2-1))
}, },
MinkowskiFace::FaceVert(f0,v1)=>{ MinkowskiFace::FaceVert(f0,v1)=>{
let (n,d)=self.mesh0.face_nd(f0); let (n,d)=self.mesh0.face_nd(f0);
@ -850,18 +833,17 @@ 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:Ratio<Fixed<8,256>,Fixed<8,256>>=Ratio::new(Fixed::ZERO,Fixed::ONE); let mut best_d=Planar64::ZERO;
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.is_negative(){ if d<Planar64::ZERO{
let edge0_nn=edge0_n.dot(edge0_n); let edge0_nn=edge0_n.dot(edge0_n);
// Assume not every number is huge //divide by zero???
// TODO: revisit this let dd=d*d/(edge_face1_nn*edge0_nn);
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);
@ -880,15 +862,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:Ratio<Fixed<8,256>,Fixed<8,256>>=Ratio::new(Fixed::ZERO,Fixed::ONE); let mut best_d=Planar64::ZERO;
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.is_negative(){ if d<Planar64::ZERO{
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);
@ -924,20 +906,19 @@ 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<_>=v0f.iter().map(|&face_id|self.mesh0.face_nd(face_id).0).collect(); let v0f_n:Vec<Planar64Vec3>=v0f.iter().map(|&face_id|self.mesh0.face_nd(face_id).0).collect();
let v1f_n:Vec<_>=v1f.iter().map(|&face_id|self.mesh1.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 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).divide().fix_3()); face_normals.push(*face_n-n*(face_n.dot(n)*2/nn));
} }
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));
@ -949,7 +930,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).divide().fix_3()); face_normals.push(*face_n-n*(face_n.dot(n)*2/nn));
} }
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));
@ -964,7 +945,7 @@ impl MeshQuery<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert> for MinkowskiM
} }
} }
fn is_empty_volume(normals:Vec<Vector3<Fixed<3,96>>>)->bool{ fn is_empty_volume(normals:Vec<Planar64Vec3>)->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{
@ -972,10 +953,9 @@ fn is_empty_volume(normals:Vec<Vector3<Fixed<3,96>>>)->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]).is_negative(); let d=n.dot(normals[k]);
if let Some(comp)=&d_comp{ if let Some(comp)=&d_comp{
// This is testing if d_comp*d < 0 if *comp*d<Planar64::ZERO{
if comp^d{
return true; return true;
} }
}else{ }else{
@ -990,8 +970,8 @@ fn is_empty_volume(normals:Vec<Vector3<Fixed<3,96>>>)->bool{
#[test] #[test]
fn test_is_empty_volume(){ fn test_is_empty_volume(){
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].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())); assert!(is_empty_volume([Planar64Vec3::X,Planar64Vec3::Y,Planar64Vec3::Z,Planar64Vec3::NEG_X].to_vec()));
} }
#[test] #[test]

View File

@ -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,vec3,mat3,Time,Planar64,Planar64Vec3,Planar64Mat3,Angle32,Ratio64Vec2}; use strafesnet_common::integer::{self,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,model_physics::GigaTime), CollisionStart(Collision),
CollisionEnd(Collision,model_physics::GigaTime), CollisionEnd(Collision),
StrafeTick, StrafeTick,
ReachWalkTargetVelocity, ReachWalkTargetVelocity,
// Water, // Water,
@ -36,7 +36,7 @@ enum PhysicsInstruction{
Input(PhysicsInputInstruction), Input(PhysicsInputInstruction),
} }
#[derive(Clone,Copy,Debug,Hash)] #[derive(Clone,Copy,Debug,Default,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==vec3::ZERO{ if target_diff==Planar64Vec3::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).divide().fix_1(), acceleration:target_diff.with_length(accel),
time:time+Time::from((target_diff.length()/accel).divide().fix_1()) time:time+Time::from(target_diff.length()/accel)
} }
} }
} }
@ -147,7 +147,7 @@ impl TransientAcceleration{
} }
fn acceleration(&self)->Planar64Vec3{ fn acceleration(&self)->Planar64Vec3{
match self{ match self{
TransientAcceleration::Reached=>vec3::ZERO, TransientAcceleration::Reached=>Planar64Vec3::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(vec3::Y), jump_direction:JumpDirection::Exactly(Planar64Vec3::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);
mat3::from_rotation_yx(ax,ay) Planar64Mat3::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);
mat3::from_rotation_y(Angle32::wrap_from_i64(ax)) Planar64Mat3::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.fix_1()); aabb.grow(vert);
} }
Self{ Self{
halfsize:aabb.size()>>1, halfsize:aabb.size()/2,
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=vec3::ZERO; let mut control_dir:Planar64Vec3 = Planar64Vec3::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,22 +463,19 @@ 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)).fix_1() camera.rotation_y()*self.get_control_dir(controls)
} }
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)).fix_1() camera.rotation()*self.get_control_dir(controls)
} }
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( let transform=integer::Planar64Affine3::new(Planar64Mat3::from_diagonal(self.hitbox.halfsize),Planar64Vec3::ZERO);
mat3::from_diagonal(self.hitbox.halfsize),
vec3::ZERO
);
HitboxMesh::new(mesh,transform) HitboxMesh::new(mesh,transform)
} }
} }
@ -494,7 +491,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=vec3::ZERO, MoveState::Fly=>body.acceleration=Planar64Vec3::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);
@ -769,9 +766,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.dot(*velocity); let d=n.dot128(*velocity);
if d.is_negative(){ if d<0{
*velocity-=(n*d/n.length_squared()).divide().fix_1(); *velocity-=n*Planar64::raw(((d<<32)/n.dot128(n)) as i64);
} }
} }
} }
@ -779,24 +776,23 @@ 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.dot(*acceleration); let d=n.dot128(*acceleration);
if d.is_negative(){ if d<0{
*acceleration-=(n*d/n.length_squared()).divide().fix_1(); *acceleration-=n*Planar64::raw(((d<<32)/n.dot128(n)) as i64);
} }
} }
} }
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::ZERO,body).body(time); let relative_body=VirtualBody::relative(&Body::default(),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:relative_body.time+time.into(), time,
instruction:PhysicsInternalInstruction::CollisionEnd( instruction:PhysicsInternalInstruction::CollisionEnd(
Collision::Contact(*contact), Collision::Contact(*contact)
time
), ),
} }
})); }));
@ -807,10 +803,9 @@ 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:relative_body.time+time.into(), time,
instruction:PhysicsInternalInstruction::CollisionEnd( instruction:PhysicsInternalInstruction::CollisionEnd(
Collision::Intersect(*intersect), Collision::Intersect(*intersect)
time
), ),
} }
})); }));
@ -819,7 +814,6 @@ 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,
@ -830,63 +824,20 @@ 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.position+self.velocity*dt+self.acceleration*(dt*dt/2)
+(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).map(|elem|elem.divide().fix_1()) self.velocity+self.acceleration*dt
} }
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).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==vec3::ZERO{ if self.velocity==Planar64Vec3::ZERO{
if self.acceleration==vec3::ZERO{ if self.acceleration==Planar64Vec3::ZERO{
None None
}else{ }else{
Some(self.acceleration) Some(self.acceleration)
@ -900,22 +851,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.is_zero(){ if self.acceleration.x()!=Planar64::ZERO{
let t=-self.velocity.x/self.acceleration.x; let t=Time::from(-self.velocity.x()/self.acceleration.x());
if t0.to_ratio().lt_ratio(t)&&t.lt_ratio(t1.to_ratio()){ if t0<t&&t<t1{
aabb.grow(self.extrapolated_position_ratio_dt(t)); aabb.grow(self.extrapolated_position(t));
} }
} }
if !self.acceleration.y.is_zero(){ if self.acceleration.y()!=Planar64::ZERO{
let t=-self.velocity.y/self.acceleration.y; let t=Time::from(-self.velocity.y()/self.acceleration.y());
if t0.to_ratio().lt_ratio(t)&&t.lt_ratio(t1.to_ratio()){ if t0<t&&t<t1{
aabb.grow(self.extrapolated_position_ratio_dt(t)); aabb.grow(self.extrapolated_position(t));
} }
} }
if !self.acceleration.z.is_zero(){ if self.acceleration.z()!=Planar64::ZERO{
let t=-self.velocity.z/self.acceleration.z; let t=Time::from(-self.velocity.z()/self.acceleration.z());
if t0.to_ratio().lt_ratio(t)&&t.lt_ratio(t1.to_ratio()){ if t0<t&&t<t1{
aabb.grow(self.extrapolated_position_ratio_dt(t)); aabb.grow(self.extrapolated_position(t));
} }
} }
} }
@ -988,7 +939,7 @@ pub struct PhysicsData{
impl Default for PhysicsState{ impl Default for PhysicsState{
fn default()->Self{ fn default()->Self{
Self{ Self{
body:Body::new(vec3::int(0,50,0),vec3::int(0,0,0),vec3::int(0,-100,0),Time::ZERO), body:Body::new(Planar64Vec3::int(0,50,0),Planar64Vec3::int(0,0,0),Planar64Vec3::int(0,-100,0),Time::ZERO),
time:Time::ZERO, time:Time::ZERO,
style:StyleModifiers::default(), style:StyleModifiers::default(),
touching:TouchingState::default(), touching:TouchingState::default(),
@ -1203,7 +1154,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.fix_1()); aabb.grow(v);
} }
(ConvexMeshId{ (ConvexMeshId{
model_id, model_id,
@ -1278,15 +1229,12 @@ 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,dt)|{ .map_or(None,|(face,time)|if time<=state.time{None}else{Some((face,time))})
let time=relative_body.time+dt.into(); .map(|(face,time)|
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
) )
} }
) )
@ -1299,8 +1247,7 @@ 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());
// TODO: normalize to i64::MAX>>1 minkowski.face_nd(contact.face_id).0
minkowski.face_nd(contact.face_id).0.fix_1()
} }
fn recalculate_touching( fn recalculate_touching(
@ -1384,12 +1331,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).is_positive(); let r=n.dot(v)<=Planar64::ZERO;
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
@ -1403,12 +1350,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).is_positive(); let r=n.dot(a)<=Planar64::ZERO;
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
@ -1456,10 +1403,8 @@ 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)?;
//TODO: transform.vertex.matrix3.col(1)+transform.vertex.translation let point=transform.vertex.transform_point3(Planar64Vec3::Y)+Planar64Vec3::Y*(style.hitbox.halfsize.y()+Planar64::ONE/16);
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(())
} }
@ -1580,7 +1525,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)).fix_1(); let reflected_velocity=body.velocity+(body.velocity-incident_velocity)*Planar64::raw(elasticity as i64+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))=>
@ -1589,7 +1534,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,vec3::ZERO);//model.velocity set_velocity(body,touching,models,hitbox_mesh,Planar64Vec3::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);
@ -1598,7 +1543,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),vec3::Y){ if walk_settings.is_slope_walkable(contact_normal(models,hitbox_mesh,&contact),Planar64Vec3::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);
@ -1726,20 +1671,17 @@ 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,goober_time)=match ins.instruction{ let should_advance_body=match ins.instruction{
PhysicsInternalInstruction::CollisionStart(_,dt) PhysicsInternalInstruction::CollisionStart(_)
|PhysicsInternalInstruction::CollisionEnd(_,dt)=>(true,Some(dt)), |PhysicsInternalInstruction::CollisionEnd(_)
PhysicsInternalInstruction::StrafeTick |PhysicsInternalInstruction::StrafeTick
|PhysicsInternalInstruction::ReachWalkTargetVelocity=>(true,None), |PhysicsInternalInstruction::ReachWalkTargetVelocity=>true,
}; };
if should_advance_body{ if should_advance_body{
match goober_time{ state.body.advance_time(state.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(
@ -1760,7 +1702,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),
@ -1782,9 +1724,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!=vec3::ZERO{ if control_dir!=Planar64Vec3::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).divide().fix_1()){ if let Some(ticked_velocity)=strafe_settings.tick_velocity(state.body.velocity,(camera_mat*control_dir).with_length(Planar64::ONE)){
//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);
@ -1808,7 +1750,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,vec3::ZERO); set_acceleration(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,Planar64Vec3::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!
@ -1903,9 +1845,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(vec3::ZERO); ).unwrap_or(Planar64Vec3::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,vec3::ZERO); set_velocity(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,Planar64Vec3::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;
} }
@ -1956,7 +1898,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! state.time={} ins.time={}\nInstruction={:?}",state.time,ins.time,ins.instruction); println!("@@@@ Time travel warning! {:?}",ins);
} }
//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{
@ -1968,216 +1910,215 @@ 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(mat3::from_diagonal(int3(5,1,5)>>1),vec3::ZERO)); let h0=HitboxMesh::new(PhysicsMesh::unit_cube(),integer::Planar64Affine3::new(Planar64Mat3::from_diagonal(Planar64Vec3::int(5,1,5)/2),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::from_secs(10)); let collision=minkowski.predict_collision_in(&relative_body,Time::MAX);
assert_eq!(collision.map(|tup|relative_body.time+tup.1.into()),expected_collision_time,"Incorrect time of collision"); assert_eq!(collision.map(|tup|tup.1),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(
int3(5,0,1)>>1, Planar64Vec3::int(5,0,1)/2,
int3(0,1,0)>>1, Planar64Vec3::int(0,1,0)/2,
int3(-1,0,5)>>1, Planar64Vec3::int(-1,0,5)/2,
]), ),
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::from_secs(10)); let collision=minkowski.predict_collision_in(&relative_body,Time::MAX);
assert_eq!(collision.map(|tup|relative_body.time+tup.1.into()),expected_collision_time,"Incorrect time of collision"); assert_eq!(collision.map(|tup|tup.1),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_straight_down(){ fn test_collision_degenerate(){
test_collision(Body::new( test_collision(Body::new(
int3(0,5,0), Planar64Vec3::int(0,5,0),
int3(0,-1,0), Planar64Vec3::int(0,-1,0),
vec3::ZERO, Planar64Vec3::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(
int3(3,5,0), Planar64Vec3::int(3,5,0),
int3(0,-1,0), Planar64Vec3::int(0,-1,0),
vec3::ZERO, Planar64Vec3::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(
int3(0,5,3), Planar64Vec3::int(0,5,3),
int3(0,-1,0), Planar64Vec3::int(0,-1,0),
vec3::ZERO, Planar64Vec3::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(
int3(-3,5,0), Planar64Vec3::int(-3,5,0),
int3(0,-1,0), Planar64Vec3::int(0,-1,0),
vec3::ZERO, Planar64Vec3::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(
int3(0,5,-3), Planar64Vec3::int(0,5,-3),
int3(0,-1,0), Planar64Vec3::int(0,-1,0),
vec3::ZERO, Planar64Vec3::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::ZERO,&Body::new( test_collision(VirtualBody::relative(&Body::default(),&Body::new(
int3(3,3,0), Planar64Vec3::int(3,3,0),
int3(100,-1,0), Planar64Vec3::int(100,-1,0),
int3(0,-1,0), Planar64Vec3::int(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::ZERO,&Body::new( test_collision(VirtualBody::relative(&Body::default(),&Body::new(
int3(0,3,3), Planar64Vec3::int(0,3,3),
int3(0,-1,100), Planar64Vec3::int(0,-1,100),
int3(0,-1,0), Planar64Vec3::int(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::ZERO,&Body::new( test_collision(VirtualBody::relative(&Body::default(),&Body::new(
int3(-3,3,0), Planar64Vec3::int(-3,3,0),
int3(-100,-1,0), Planar64Vec3::int(-100,-1,0),
int3(0,-1,0), Planar64Vec3::int(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::ZERO,&Body::new( test_collision(VirtualBody::relative(&Body::default(),&Body::new(
int3(0,3,-3), Planar64Vec3::int(0,3,-3),
int3(0,-1,-100), Planar64Vec3::int(0,-1,-100),
int3(0,-1,0), Planar64Vec3::int(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::ZERO,&Body::new( test_collision(VirtualBody::relative(&Body::default(),&Body::new(
int3(0,6,-7)>>1, Planar64Vec3::int(0,6,-7)/2,
int3(-10,-1,1), Planar64Vec3::int(-10,-1,1),
int3(0,-1,0), Planar64Vec3::int(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::ZERO,&Body::new( test_collision(VirtualBody::relative(&Body::default(),&Body::new(
int3(0,6,-7)>>1, Planar64Vec3::int(0,6,-7)/2,
int3(10,-1,1), Planar64Vec3::int(10,-1,1),
int3(0,-1,0), Planar64Vec3::int(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::ZERO,&Body::new( test_collision(VirtualBody::relative(&Body::default(),&Body::new(
int3(7,6,0)>>1, Planar64Vec3::int(7,6,0)/2,
int3(-1,-1,-10), Planar64Vec3::int(-1,-1,-10),
int3(0,-1,0), Planar64Vec3::int(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::ZERO,&Body::new( test_collision(VirtualBody::relative(&Body::default(),&Body::new(
int3(7,6,0)>>1, Planar64Vec3::int(7,6,0)/2,
int3(-1,-1,10), Planar64Vec3::int(-1,-1,10),
int3(0,-1,0), Planar64Vec3::int(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::ZERO,&Body::new( test_collision(VirtualBody::relative(&Body::default(),&Body::new(
int3(0,6,7)>>1, Planar64Vec3::int(0,6,7)/2,
int3(-10,-1,-1), Planar64Vec3::int(-10,-1,-1),
int3(0,-1,0), Planar64Vec3::int(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::ZERO,&Body::new( test_collision(VirtualBody::relative(&Body::default(),&Body::new(
int3(0,6,7)>>1, Planar64Vec3::int(0,6,7)/2,
int3(10,-1,-1), Planar64Vec3::int(10,-1,-1),
int3(0,-1,0), Planar64Vec3::int(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::ZERO,&Body::new( test_collision(VirtualBody::relative(&Body::default(),&Body::new(
int3(-7,6,0)>>1, Planar64Vec3::int(-7,6,0)/2,
int3(1,-1,-10), Planar64Vec3::int(1,-1,-10),
int3(0,-1,0), Planar64Vec3::int(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::ZERO,&Body::new( test_collision(VirtualBody::relative(&Body::default(),&Body::new(
int3(-7,6,0)>>1, Planar64Vec3::int(-7,6,0)/2,
int3(1,-1,10), Planar64Vec3::int(1,-1,10),
int3(0,-1,0), Planar64Vec3::int(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(
int3(0,5,0), Planar64Vec3::int(0,5,0),
int3(1,-64,2)>>6,// /64 Planar64Vec3::int(1,-64,2)/64,
vec3::ZERO, Planar64Vec3::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(
int3(0,10,0), Planar64Vec3::int(0,10,0),
int3(1,0,0), Planar64Vec3::int(1,0,0),
int3(0,1,0), Planar64Vec3::int(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(
vec3::ZERO, Planar64Vec3::ZERO,
int3(1,0,0), Planar64Vec3::int(1,0,0),
int3(0,1,0), Planar64Vec3::int(0,1,0),
Time::ZERO Time::ZERO
),None); ),None);
} }

View File

@ -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::vec3::ONE,integer::vec3::ONE,integer::vec3::ONE,integer::Time::ZERO); let test_body=physics::Body::new(integer::Planar64Vec3::ONE,integer::Planar64Vec3::ONE,integer::Planar64Vec3::ONE,integer::Time::ZERO);
let worker=QRWorker::new(physics::Body::ZERO, let worker=QRWorker::new(physics::Body::default(),
|_|physics::Body::new(integer::vec3::ONE,integer::vec3::ONE,integer::vec3::ONE,integer::Time::ZERO) |_|physics::Body::new(integer::Planar64Vec3::ONE,integer::Planar64Vec3::ONE,integer::Planar64Vec3::ONE,integer::Time::ZERO)
); );
// Send tasks to the worker // Send tasks to the worker