Compare commits
11 Commits
run-timer-
...
master
Author | SHA1 | Date | |
---|---|---|---|
a67aa71fb0 | |||
7cc0fd59c8 | |||
bded9fccdf | |||
4cd0114567 | |||
991e01d530 | |||
e2bd9ba692 | |||
383df8637f | |||
1a6831cf8b | |||
ccae1a45e1 | |||
1e299b7b9c | |||
f3d4d8dbda |
707
Cargo.lock
generated
707
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
18
Cargo.toml
18
Cargo.toml
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "strafe-client"
|
||||
version = "0.10.3"
|
||||
version = "0.10.5"
|
||||
edition = "2021"
|
||||
repository = "https://git.itzana.me/StrafesNET/strafe-client"
|
||||
license = "Custom"
|
||||
@ -18,17 +18,17 @@ roblox = ["dep:strafesnet_deferred_loader", "dep:strafesnet_rbx_loader"]
|
||||
bytemuck = { version = "1.13.1", features = ["derive"] }
|
||||
configparser = "3.0.2"
|
||||
ddsfile = "0.5.1"
|
||||
glam = "0.28.0"
|
||||
glam = "0.29.0"
|
||||
id = { version = "0.1.0", registry = "strafesnet" }
|
||||
parking_lot = "0.12.1"
|
||||
pollster = "0.3.0"
|
||||
strafesnet_bsp_loader = { version = "0.1.3", registry = "strafesnet", optional = true }
|
||||
strafesnet_common = { version = "0.4.0", registry = "strafesnet" }
|
||||
strafesnet_deferred_loader = { version = "0.3.1", features = ["legacy"], registry = "strafesnet", optional = true }
|
||||
strafesnet_rbx_loader = { version = "0.3.2", registry = "strafesnet", optional = true }
|
||||
strafesnet_snf = { version = "0.1.2", registry = "strafesnet", optional = true }
|
||||
wgpu = "22.0.0"
|
||||
winit = "0.30.4"
|
||||
strafesnet_bsp_loader = { version = "0.2.1", registry = "strafesnet", optional = true }
|
||||
strafesnet_common = { version = "0.5.2", registry = "strafesnet" }
|
||||
strafesnet_deferred_loader = { version = "0.4.0", features = ["legacy"], registry = "strafesnet", optional = true }
|
||||
strafesnet_rbx_loader = { version = "0.5.1", registry = "strafesnet", optional = true }
|
||||
strafesnet_snf = { version = "0.2.0", registry = "strafesnet", optional = true }
|
||||
wgpu = "22.1.0"
|
||||
winit = "0.30.5"
|
||||
|
||||
[profile.release]
|
||||
#lto = true
|
||||
|
@ -1,32 +1,33 @@
|
||||
use crate::physics::Body;
|
||||
use crate::model_physics::{FEV,MeshQuery,DirectedEdge};
|
||||
use strafesnet_common::integer::{Time,Planar64};
|
||||
use strafesnet_common::zeroes::zeroes2;
|
||||
use crate::model_physics::{GigaTime,FEV,MeshQuery,DirectedEdge,MinkowskiMesh,MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert};
|
||||
use strafesnet_common::integer::{Time,Fixed,Ratio};
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Transition<F,E:DirectedEdge,V>{
|
||||
Miss,
|
||||
Next(FEV<F,E,V>,Time),
|
||||
Hit(F,Time),
|
||||
Next(FEV<F,E,V>,GigaTime),
|
||||
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.
|
||||
//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_transtition=Transition::Miss;
|
||||
let mut best_transition=MinkowskiTransition::Miss;
|
||||
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
|
||||
//n=face.normal d=face.dot
|
||||
//n.a t^2+n.v t+n.p-d==0
|
||||
let (n,d)=mesh.face_nd(face_id);
|
||||
//TODO: use higher precision 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)){
|
||||
let t=body.time+Time::from(t);
|
||||
if time<=t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{
|
||||
best_time=t;
|
||||
best_transtition=Transition::Hit(face_id,t);
|
||||
for dt in Fixed::<4,128>::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(){
|
||||
best_time=dt;
|
||||
best_transition=MinkowskiTransition::Hit(face_id,dt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -36,18 +37,18 @@ enum Transition<F,E:DirectedEdge,V>{
|
||||
let n=n.cross(edge_n);
|
||||
let verts=mesh.edge_verts(directed_edge_id.as_undirected());
|
||||
//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)){
|
||||
let t=body.time+Time::from(t);
|
||||
if time<=t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{
|
||||
best_time=t;
|
||||
best_transtition=Transition::Next(FEV::<F,E,V>::Edge(directed_edge_id.as_undirected()),t);
|
||||
//WARNING: precision is swept under the rug!
|
||||
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 body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
|
||||
best_time=dt;
|
||||
best_transition=MinkowskiTransition::Next(MinkowskiFEV::Edge(directed_edge_id.as_undirected()),dt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//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
|
||||
let edge_n=mesh.edge_n(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
|
||||
let n=face_n.cross(edge_n)*((i as i64)*2-1);
|
||||
//WARNING yada yada d *2
|
||||
for t in zeroes2(n.dot(delta_pos),n.dot(body.velocity)*2,n.dot(body.acceleration)){
|
||||
let t=body.time+Time::from(t);
|
||||
if time<=t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{
|
||||
best_time=t;
|
||||
best_transtition=Transition::Next(FEV::<F,E,V>::Face(edge_face_id),t);
|
||||
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()){
|
||||
if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
|
||||
best_time=dt;
|
||||
best_transition=MinkowskiTransition::Next(MinkowskiFEV::Face(edge_face_id),dt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -70,27 +70,27 @@ enum Transition<F,E:DirectedEdge,V>{
|
||||
for (i,&vert_id) in edge_verts.iter().enumerate(){
|
||||
//vertex normal gets parity from vert index
|
||||
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)){
|
||||
let t=body.time+Time::from(t);
|
||||
if time<=t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{
|
||||
best_time=t;
|
||||
best_transtition=Transition::Next(FEV::<F,E,V>::Vert(vert_id),t);
|
||||
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)){
|
||||
if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
|
||||
let dt=Ratio::new(dt.num.fix_4(),dt.den.fix_4());
|
||||
best_time=dt;
|
||||
best_transition=MinkowskiTransition::Next(MinkowskiFEV::Vert(vert_id),dt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//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
|
||||
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
|
||||
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)){
|
||||
let t=body.time+Time::from(t);
|
||||
if time<=t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{
|
||||
best_time=t;
|
||||
best_transtition=Transition::Next(FEV::<F,E,V>::Edge(directed_edge_id.as_undirected()),t);
|
||||
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)){
|
||||
if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
|
||||
let dt=Ratio::new(dt.num.fix_4(),dt.den.fix_4());
|
||||
best_time=dt;
|
||||
best_transition=MinkowskiTransition::Next(MinkowskiFEV::Edge(directed_edge_id.as_undirected()),dt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -98,18 +98,26 @@ enum Transition<F,E:DirectedEdge,V>{
|
||||
//if none:
|
||||
},
|
||||
}
|
||||
best_transtition
|
||||
best_transition
|
||||
}
|
||||
pub enum CrawlResult<F,E:DirectedEdge,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>{
|
||||
let mut time=start_time;
|
||||
type MinkowskiCrawlResult=CrawlResult<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert>;
|
||||
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{
|
||||
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::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),
|
||||
}
|
||||
}
|
||||
|
11
src/file.rs
11
src/file.rs
@ -22,7 +22,7 @@ impl std::error::Error for ReadError{}
|
||||
|
||||
pub enum DataStructure{
|
||||
#[cfg(feature="roblox")]
|
||||
Roblox(strafesnet_rbx_loader::Dom),
|
||||
Roblox(strafesnet_rbx_loader::Model),
|
||||
#[cfg(feature="source")]
|
||||
Source(strafesnet_bsp_loader::Bsp),
|
||||
#[cfg(feature="snf")]
|
||||
@ -66,13 +66,16 @@ pub fn load<P:AsRef<std::path::Path>>(path:P)->Result<strafesnet_common::map::Co
|
||||
#[cfg(feature="snf")]
|
||||
DataStructure::StrafesNET(map)=>Ok(map),
|
||||
#[cfg(feature="roblox")]
|
||||
DataStructure::Roblox(dom)=>{
|
||||
DataStructure::Roblox(model)=>{
|
||||
let mut place=model.into_place();
|
||||
place.run_scripts();
|
||||
|
||||
let mut loader=strafesnet_deferred_loader::roblox_legacy();
|
||||
|
||||
let (texture_loader,mesh_loader)=loader.get_inner_mut();
|
||||
|
||||
let map_step1=strafesnet_rbx_loader::convert(
|
||||
&dom,
|
||||
&place,
|
||||
|name|texture_loader.acquire_render_config_id(name),
|
||||
|name|mesh_loader.acquire_mesh_id(name),
|
||||
);
|
||||
@ -110,7 +113,7 @@ pub fn load<P:AsRef<std::path::Path>>(path:P)->Result<strafesnet_common::map::Co
|
||||
|name|mesh_loader.acquire_mesh_id(name),
|
||||
);
|
||||
|
||||
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(
|
||||
//the type conflagulator 9000
|
||||
|
@ -219,7 +219,7 @@ impl GraphicsState{
|
||||
//wow
|
||||
let instance=GraphicsModelOwned{
|
||||
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),
|
||||
};
|
||||
//get or create owned mesh map
|
||||
@ -238,9 +238,9 @@ impl GraphicsState{
|
||||
//create
|
||||
let owned_mesh_id=IndexedGraphicsMeshOwnedRenderConfigId::new(unique_render_config_models.len() as u32);
|
||||
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_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_vertices:mesh.unique_vertices.clone(),
|
||||
render_config:graphics_group.render,
|
||||
@ -890,7 +890,7 @@ impl GraphicsState{
|
||||
|
||||
// update rotation
|
||||
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)
|
||||
);
|
||||
self.staging_belt
|
||||
|
@ -1,15 +1,15 @@
|
||||
use std::borrow::{Borrow,Cow};
|
||||
use std::collections::{HashSet,HashMap};
|
||||
use strafesnet_common::integer::vec3::Vector3;
|
||||
use strafesnet_common::model::{self,MeshId,PolygonIter};
|
||||
use strafesnet_common::zeroes;
|
||||
use strafesnet_common::integer::{self,Planar64,Planar64Vec3};
|
||||
use strafesnet_common::integer::{self,vec3,Fixed,Planar64,Planar64Vec3,Ratio};
|
||||
|
||||
pub trait UndirectedEdge{
|
||||
type DirectedEdge:Copy+DirectedEdge;
|
||||
fn as_directed(&self,parity:bool)->Self::DirectedEdge;
|
||||
}
|
||||
pub trait DirectedEdge{
|
||||
type UndirectedEdge:Copy+UndirectedEdge;
|
||||
type UndirectedEdge:Copy+std::fmt::Debug+UndirectedEdge;
|
||||
fn as_undirected(&self)->Self::UndirectedEdge;
|
||||
fn parity(&self)->bool;
|
||||
//this is stupid but may work fine
|
||||
@ -50,6 +50,7 @@ impl DirectedEdge for SubmeshDirectedEdgeId{
|
||||
}
|
||||
|
||||
//Vertex <-> Edge <-> Face -> Collide
|
||||
#[derive(Debug)]
|
||||
pub enum FEV<F,E:DirectedEdge,V>{
|
||||
Face(F),
|
||||
Edge(E::UndirectedEdge),
|
||||
@ -64,6 +65,9 @@ struct Face{
|
||||
}
|
||||
struct Vert(Planar64Vec3);
|
||||
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{
|
||||
let verts=self.edge_verts(edge_id);
|
||||
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)
|
||||
}
|
||||
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 edge_faces(&self,edge_id:EDGE::UndirectedEdge)->Cow<[FACE;2]>;
|
||||
fn edge_verts(&self,edge_id:EDGE::UndirectedEdge)->Cow<[VERT;2]>;
|
||||
@ -137,22 +141,22 @@ impl PhysicsMesh{
|
||||
//go go gadget debug print mesh
|
||||
let data=PhysicsMeshData{
|
||||
faces:vec![
|
||||
Face{normal:Planar64Vec3::raw_xyz( 4294967296, 0, 0),dot:Planar64::raw(4294967296)},
|
||||
Face{normal:Planar64Vec3::raw_xyz( 0, 4294967296, 0),dot:Planar64::raw(4294967296)},
|
||||
Face{normal:Planar64Vec3::raw_xyz( 0, 0, 4294967296),dot:Planar64::raw(4294967296)},
|
||||
Face{normal:Planar64Vec3::raw_xyz(-4294967296, 0, 0),dot:Planar64::raw(4294967296)},
|
||||
Face{normal:Planar64Vec3::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( 4294967296, 0, 0),dot:Planar64::raw(4294967296)},
|
||||
Face{normal:vec3::raw_xyz( 0, 4294967296, 0),dot:Planar64::raw(4294967296)},
|
||||
Face{normal:vec3::raw_xyz( 0, 0, 4294967296),dot:Planar64::raw(4294967296)},
|
||||
Face{normal:vec3::raw_xyz(-4294967296, 0, 0),dot:Planar64::raw(4294967296)},
|
||||
Face{normal:vec3::raw_xyz( 0,-4294967296, 0),dot:Planar64::raw(4294967296)},
|
||||
Face{normal:vec3::raw_xyz( 0, 0,-4294967296),dot:Planar64::raw(4294967296)}
|
||||
],
|
||||
verts:vec![
|
||||
Vert(Planar64Vec3::raw_xyz( 4294967296,-4294967296,-4294967296)),
|
||||
Vert(Planar64Vec3::raw_xyz( 4294967296, 4294967296,-4294967296)),
|
||||
Vert(Planar64Vec3::raw_xyz( 4294967296, 4294967296, 4294967296)),
|
||||
Vert(Planar64Vec3::raw_xyz( 4294967296,-4294967296, 4294967296)),
|
||||
Vert(Planar64Vec3::raw_xyz(-4294967296, 4294967296,-4294967296)),
|
||||
Vert(Planar64Vec3::raw_xyz(-4294967296, 4294967296, 4294967296)),
|
||||
Vert(Planar64Vec3::raw_xyz(-4294967296,-4294967296, 4294967296)),
|
||||
Vert(Planar64Vec3::raw_xyz(-4294967296,-4294967296,-4294967296))
|
||||
Vert(vec3::raw_xyz( 4294967296,-4294967296,-4294967296)),
|
||||
Vert(vec3::raw_xyz( 4294967296, 4294967296,-4294967296)),
|
||||
Vert(vec3::raw_xyz( 4294967296, 4294967296, 4294967296)),
|
||||
Vert(vec3::raw_xyz( 4294967296,-4294967296, 4294967296)),
|
||||
Vert(vec3::raw_xyz(-4294967296, 4294967296,-4294967296)),
|
||||
Vert(vec3::raw_xyz(-4294967296, 4294967296, 4294967296)),
|
||||
Vert(vec3::raw_xyz(-4294967296,-4294967296, 4294967296)),
|
||||
Vert(vec3::raw_xyz(-4294967296,-4294967296,-4294967296))
|
||||
]
|
||||
};
|
||||
let mesh_topology=PhysicsMeshTopology{
|
||||
@ -330,7 +334,7 @@ impl TryFrom<&model::Mesh> for PhysicsMesh{
|
||||
for poly_vertices in polygon_group.polys(){
|
||||
let submesh_face_id=SubmeshFaceId::new(submesh_faces.len() as u32);
|
||||
//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 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);
|
||||
@ -341,11 +345,11 @@ impl TryFrom<&model::Mesh> for PhysicsMesh{
|
||||
//https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal (Newell's Method)
|
||||
let v0=mesh.unique_pos[vert0_id.get() as usize];
|
||||
let v1=mesh.unique_pos[vert1_id.get() as usize];
|
||||
normal+=Planar64Vec3::new(
|
||||
(v0.y()-v1.y())*(v0.z()+v1.z()),
|
||||
(v0.z()-v1.z())*(v0.x()+v1.x()),
|
||||
(v0.x()-v1.x())*(v0.y()+v1.y()),
|
||||
);
|
||||
normal+=Vector3::new([
|
||||
(v0.y-v1.y)*(v0.z+v1.z),
|
||||
(v0.z-v1.z)*(v0.x+v1.x),
|
||||
(v0.x-v1.x)*(v0.y+v1.y),
|
||||
]);
|
||||
//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_faces,edge_id)=edge_pool.push(edge_ref_verts);
|
||||
@ -362,14 +366,16 @@ impl TryFrom<&model::Mesh> for PhysicsMesh{
|
||||
//return directed_edge_id
|
||||
edge_id.as_directed(is_sorted)
|
||||
}).collect();
|
||||
//choose precision loss randomly idk
|
||||
normal=normal/len as i64;
|
||||
let mut dot=Planar64::ZERO;
|
||||
let mut dot=Fixed::ZERO;
|
||||
// find the average dot
|
||||
for &v in poly_vertices{
|
||||
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...
|
||||
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){
|
||||
Some(&face_id)=>face_id,
|
||||
None=>{
|
||||
@ -416,6 +422,8 @@ pub struct PhysicsMeshView<'a>{
|
||||
topology:&'a PhysicsMeshTopology,
|
||||
}
|
||||
impl MeshQuery<SubmeshFaceId,SubmeshDirectedEdgeId,SubmeshVertId> for PhysicsMeshView<'_>{
|
||||
type Normal=Planar64Vec3;
|
||||
type Offset=Planar64;
|
||||
fn face_nd(&self,face_id:SubmeshFaceId)->(Planar64Vec3,Planar64){
|
||||
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)
|
||||
@ -444,14 +452,14 @@ impl MeshQuery<SubmeshFaceId,SubmeshDirectedEdgeId,SubmeshVertId> for PhysicsMes
|
||||
|
||||
pub struct PhysicsMeshTransform{
|
||||
pub vertex:integer::Planar64Affine3,
|
||||
pub normal:integer::Planar64Mat3,
|
||||
pub det:Planar64,
|
||||
pub normal:integer::mat3::Matrix3<Fixed<2,64>>,
|
||||
pub det:Fixed<3,96>,
|
||||
}
|
||||
impl PhysicsMeshTransform{
|
||||
pub const fn new(transform:integer::Planar64Affine3)->Self{
|
||||
pub fn new(transform:integer::Planar64Affine3)->Self{
|
||||
Self{
|
||||
normal:transform.matrix3.inverse_times_det().transpose(),
|
||||
det:transform.matrix3.determinant(),
|
||||
normal:transform.matrix3.adjugate().transpose(),
|
||||
det:transform.matrix3.det(),
|
||||
vertex:transform,
|
||||
}
|
||||
}
|
||||
@ -471,33 +479,33 @@ impl TransformedMesh<'_>{
|
||||
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))
|
||||
}
|
||||
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
|
||||
for (i,vert_id) in self.view.topology.verts.iter().enumerate(){
|
||||
let p=self.transform.vertex.transform_point3(self.view.data.verts[vert_id.get() as usize].0);
|
||||
let d=dir.dot(p);
|
||||
if best_dot<d{
|
||||
best_dot=d;
|
||||
best_vert=SubmeshVertId::new(i as u32);
|
||||
}
|
||||
}
|
||||
best_vert
|
||||
SubmeshVertId::new(
|
||||
self.view.topology.verts.iter()
|
||||
.enumerate()
|
||||
.max_by_key(|(_,&vert_id)|
|
||||
dir.dot(self.transform.vertex.transform_point3(self.view.data.verts[vert_id.get() as usize].0))
|
||||
)
|
||||
//assume there is more than zero vertices.
|
||||
.unwrap().0 as u32
|
||||
)
|
||||
}
|
||||
}
|
||||
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 transformed_n=self.transform.normal*n;
|
||||
let transformed_d=d+transformed_n.dot(self.transform.vertex.translation)/self.transform.det;
|
||||
(transformed_n/self.transform.det,transformed_d)
|
||||
let transformed_d=d*self.transform.det+transformed_n.dot(self.transform.vertex.translation);
|
||||
(transformed_n,transformed_d)
|
||||
}
|
||||
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]
|
||||
fn face_edges(&self,face_id:SubmeshFaceId)->Cow<Vec<SubmeshDirectedEdgeId>>{
|
||||
@ -525,11 +533,11 @@ impl MeshQuery<SubmeshFaceId,SubmeshDirectedEdgeId,SubmeshVertId> for Transforme
|
||||
//(face,vertex)
|
||||
//(edge,edge)
|
||||
//(vertex,face)
|
||||
#[derive(Clone,Copy)]
|
||||
#[derive(Clone,Copy,Debug)]
|
||||
pub enum MinkowskiVert{
|
||||
VertVert(SubmeshVertId,SubmeshVertId),
|
||||
}
|
||||
#[derive(Clone,Copy)]
|
||||
#[derive(Clone,Copy,Debug)]
|
||||
pub enum MinkowskiEdge{
|
||||
VertEdge(SubmeshVertId,SubmeshEdgeId),
|
||||
EdgeVert(SubmeshEdgeId,SubmeshVertId),
|
||||
@ -544,7 +552,7 @@ impl UndirectedEdge for MinkowskiEdge{
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Clone,Copy)]
|
||||
#[derive(Clone,Copy,Debug)]
|
||||
pub enum MinkowskiDirectedEdge{
|
||||
VertEdge(SubmeshVertId,SubmeshDirectedEdgeId),
|
||||
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{
|
||||
VertFace(SubmeshVertId,SubmeshFaceId),
|
||||
EdgeEdge(SubmeshEdgeId,SubmeshEdgeId,bool),
|
||||
@ -581,6 +589,7 @@ pub struct MinkowskiMesh<'a>{
|
||||
}
|
||||
|
||||
//infinity fev algorithm state transition
|
||||
#[derive(Debug)]
|
||||
enum Transition{
|
||||
Done,//found closest vert, no edges are better
|
||||
Vert(MinkowskiVert),//transition to vert
|
||||
@ -590,6 +599,8 @@ enum EV{
|
||||
Edge(MinkowskiEdge),
|
||||
}
|
||||
|
||||
pub type GigaTime=Ratio<Fixed<4,128>,Fixed<4,128>>;
|
||||
|
||||
impl MinkowskiMesh<'_>{
|
||||
pub fn minkowski_sum<'a>(mesh0:TransformedMesh<'a>,mesh1:TransformedMesh<'a>)->MinkowskiMesh<'a>{
|
||||
MinkowskiMesh{
|
||||
@ -600,7 +611,7 @@ impl MinkowskiMesh<'_>{
|
||||
fn farthest_vert(&self,dir:Planar64Vec3)->MinkowskiVert{
|
||||
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;
|
||||
for &directed_edge_id in self.vert_edges(vert_id).iter(){
|
||||
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];
|
||||
//test if it's closer
|
||||
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);
|
||||
if distance_squared<*best_distance_squared{
|
||||
best_transition=Transition::Vert(test_vert_id);
|
||||
@ -620,21 +631,21 @@ impl MinkowskiMesh<'_>{
|
||||
}
|
||||
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 diff=point-self.vert(vert_id);
|
||||
for &directed_edge_id in self.vert_edges(vert_id).iter(){
|
||||
let edge_n=self.directed_edge_n(directed_edge_id);
|
||||
//is boundary uncrossable by a crawl from infinity
|
||||
//check if time of collision is outside Time::MIN..Time::MAX
|
||||
let d=edge_n.dot(diff);
|
||||
if zeroes::zeroes1(d,edge_n.dot(infinity_dir)).len()==0{
|
||||
if edge_n.dot(infinity_dir).is_zero(){
|
||||
let d=edge_n.dot(diff);
|
||||
//test the edge
|
||||
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 c=diff.cross(edge_n);
|
||||
c.dot(c)/edge_nn
|
||||
(c.dot(c)/edge_nn).divide().fix_2()
|
||||
};
|
||||
if distance_squared<=*best_distance_squared{
|
||||
best_transition=EV::Edge(directed_edge_id.as_undirected());
|
||||
@ -680,7 +691,7 @@ impl MinkowskiMesh<'_>{
|
||||
let boundary_d=boundary_n.dot(delta_pos);
|
||||
//check if time of collision is outside Time::MIN..Time::MAX
|
||||
//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.
|
||||
return FEV::<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert>::Face(face_id);
|
||||
}
|
||||
@ -694,15 +705,16 @@ impl MinkowskiMesh<'_>{
|
||||
let infinity_fev=self.infinity_fev(-dir,infinity_body.position);
|
||||
//a line is simpler to solve than a parabola
|
||||
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
|
||||
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::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|{
|
||||
//continue forwards along the body parabola
|
||||
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
|
||||
let infinity_body=crate::physics::Body::new(
|
||||
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)
|
||||
//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 face_n=self.face_nd(contact_face_id).0;
|
||||
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 d=n.dot(self.vert(verts[0])+self.vert(verts[1]));
|
||||
//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)){
|
||||
let t=relative_body.time+integer::Time::from(t);
|
||||
if relative_body.time<t&&t<best_time&&n.dot(relative_body.extrapolated_velocity(t))<Planar64::ZERO{
|
||||
best_time=t;
|
||||
//WARNING: truncated precision
|
||||
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 Ratio::new(Planar64::ZERO,Planar64::EPSILON).le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(relative_body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
|
||||
best_time=dt;
|
||||
best_edge=Some(directed_edge_id);
|
||||
break;
|
||||
}
|
||||
@ -751,15 +766,15 @@ impl MinkowskiMesh<'_>{
|
||||
}
|
||||
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);
|
||||
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::Hit(face,time)=>Some((face,time)),
|
||||
}
|
||||
}
|
||||
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,
|
||||
//otherwise the point is not inside the mesh
|
||||
self.infinity_in(infinity_body)
|
||||
@ -770,7 +785,9 @@ impl 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{
|
||||
MinkowskiFace::VertFace(v0,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 e0d=n.dot(self.mesh0.vert(e0v0)+self.mesh0.vert(e0v1));
|
||||
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)=>{
|
||||
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();
|
||||
Cow::Owned([(e1f1,false),(e1f0,true)].map(|(edge_face_id1,face_parity)|{
|
||||
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_nn=edge_face1_n.dot(edge_face1_n);
|
||||
for &directed_edge_id0 in v0e.iter(){
|
||||
let edge0_n=self.mesh0.directed_edge_n(directed_edge_id0);
|
||||
//must be behind other face.
|
||||
let d=edge_face1_n.dot(edge0_n);
|
||||
if d<Planar64::ZERO{
|
||||
if d.is_negative(){
|
||||
let edge0_nn=edge0_n.dot(edge0_n);
|
||||
//divide by zero???
|
||||
let dd=d*d/(edge_face1_nn*edge0_nn);
|
||||
// Assume not every number is huge
|
||||
// TODO: revisit this
|
||||
let dd=(d*d)/(edge_face1_nn*edge0_nn);
|
||||
if best_d<dd{
|
||||
best_d=dd;
|
||||
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();
|
||||
Cow::Owned([(e0f0,true),(e0f1,false)].map(|(edge_face_id0,face_parity)|{
|
||||
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_nn=edge_face0_n.dot(edge_face0_n);
|
||||
for &directed_edge_id1 in v1e.iter(){
|
||||
let edge1_n=self.mesh1.directed_edge_n(directed_edge_id1);
|
||||
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 dd=d*d/(edge_face0_nn*edge1_nn);
|
||||
let dd=(d*d)/(edge_face0_nn*edge1_nn);
|
||||
if best_d<dd{
|
||||
best_d=dd;
|
||||
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
|
||||
let v0f=self.mesh0.vert_faces(v0);
|
||||
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 v1f_n:Vec<Planar64Vec3>=v1f.iter().map(|&face_id|self.mesh1.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<_>=v1f.iter().map(|&face_id|self.mesh1.face_nd(face_id).0).collect();
|
||||
let the_len=v0f.len()+v1f.len();
|
||||
for &directed_edge_id in self.mesh0.vert_edges(v0).iter(){
|
||||
let n=self.mesh0.directed_edge_n(directed_edge_id);
|
||||
let nn=n.dot(n);
|
||||
// TODO: there's gotta be a better way to do this
|
||||
//make a set of faces
|
||||
let mut face_normals=Vec::with_capacity(the_len);
|
||||
//add mesh0 faces as-is
|
||||
face_normals.clone_from(&v0f_n);
|
||||
for face_n in &v1f_n{
|
||||
//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){
|
||||
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);
|
||||
face_normals.clone_from(&v1f_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){
|
||||
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();
|
||||
for i in 0..len-1{
|
||||
for j in i+1..len{
|
||||
@ -953,9 +972,10 @@ fn is_empty_volume(normals:Vec<Planar64Vec3>)->bool{
|
||||
let mut d_comp=None;
|
||||
for k in 0..len{
|
||||
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 *comp*d<Planar64::ZERO{
|
||||
// This is testing if d_comp*d < 0
|
||||
if comp^d{
|
||||
return true;
|
||||
}
|
||||
}else{
|
||||
@ -970,8 +990,8 @@ fn is_empty_volume(normals:Vec<Planar64Vec3>)->bool{
|
||||
|
||||
#[test]
|
||||
fn test_is_empty_volume(){
|
||||
assert!(!is_empty_volume([Planar64Vec3::X,Planar64Vec3::Y,Planar64Vec3::Z].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()].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]
|
||||
|
379
src/physics.rs
379
src/physics.rs
@ -11,7 +11,7 @@ use strafesnet_common::gameplay_modes::{self,StageId};
|
||||
use strafesnet_common::gameplay_style::{self,StyleModifiers};
|
||||
use strafesnet_common::controls_bitflag::Controls;
|
||||
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;
|
||||
|
||||
//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
|
||||
#[derive(Debug)]
|
||||
enum PhysicsInternalInstruction{
|
||||
CollisionStart(Collision),
|
||||
CollisionEnd(Collision),
|
||||
CollisionStart(Collision,model_physics::GigaTime),
|
||||
CollisionEnd(Collision,model_physics::GigaTime),
|
||||
StrafeTick,
|
||||
ReachWalkTargetVelocity,
|
||||
// Water,
|
||||
@ -36,7 +36,7 @@ enum PhysicsInstruction{
|
||||
Input(PhysicsInputInstruction),
|
||||
}
|
||||
|
||||
#[derive(Clone,Copy,Debug,Default,Hash)]
|
||||
#[derive(Clone,Copy,Debug,Hash)]
|
||||
pub struct Body{
|
||||
pub position:Planar64Vec3,//I64 where 2^32 = 1 u
|
||||
pub velocity:Planar64Vec3,//I64 where 2^32 = 1 u/s
|
||||
@ -124,13 +124,13 @@ struct ContactMoveState{
|
||||
}
|
||||
impl TransientAcceleration{
|
||||
fn with_target_diff(target_diff:Planar64Vec3,accel:Planar64,time:Time)->Self{
|
||||
if target_diff==Planar64Vec3::ZERO{
|
||||
if target_diff==vec3::ZERO{
|
||||
TransientAcceleration::Reached
|
||||
}else{
|
||||
//normal friction acceleration is clippedAcceleration.dot(normal)*friction
|
||||
TransientAcceleration::Reachable{
|
||||
acceleration:target_diff.with_length(accel),
|
||||
time:time+Time::from(target_diff.length()/accel)
|
||||
acceleration:target_diff.with_length(accel).divide().fix_1(),
|
||||
time:time+Time::from((target_diff.length()/accel).divide().fix_1())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -147,7 +147,7 @@ impl TransientAcceleration{
|
||||
}
|
||||
fn acceleration(&self)->Planar64Vec3{
|
||||
match self{
|
||||
TransientAcceleration::Reached=>Planar64Vec3::ZERO,
|
||||
TransientAcceleration::Reached=>vec3::ZERO,
|
||||
&TransientAcceleration::Reachable{acceleration,time:_}=>acceleration,
|
||||
&TransientAcceleration::Unreachable{acceleration}=>acceleration,
|
||||
}
|
||||
@ -158,7 +158,7 @@ impl ContactMoveState{
|
||||
Self{
|
||||
target:TransientAcceleration::ground(walk_settings,body,gravity,target_velocity),
|
||||
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{
|
||||
@ -296,7 +296,7 @@ impl PhysicsCamera{
|
||||
let ay=Angle32::clamp_from_i64(a.y)
|
||||
//clamp to actual vertical cam 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{
|
||||
self.get_rotation(self.clamped_mouse_pos)
|
||||
@ -306,7 +306,7 @@ impl PhysicsCamera{
|
||||
}
|
||||
fn get_rotation_y(&self,mouse_pos_x:i32)->Planar64Mat3{
|
||||
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{
|
||||
self.get_rotation_y(self.clamped_mouse_pos.x)
|
||||
@ -410,10 +410,10 @@ impl HitboxMesh{
|
||||
let transform=PhysicsMeshTransform::new(transform);
|
||||
let transformed_mesh=TransformedMesh::new(mesh.complete_mesh_view(),&transform);
|
||||
for vert in transformed_mesh.verts(){
|
||||
aabb.grow(vert);
|
||||
aabb.grow(vert.fix_1());
|
||||
}
|
||||
Self{
|
||||
halfsize:aabb.size()/2,
|
||||
halfsize:aabb.size()>>1,
|
||||
mesh,
|
||||
transform,
|
||||
}
|
||||
@ -438,7 +438,7 @@ impl StyleHelper for StyleModifiers{
|
||||
|
||||
fn get_control_dir(&self,controls:Controls)->Planar64Vec3{
|
||||
//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
|
||||
let controls=controls.intersection(self.controls_mask);
|
||||
if controls.contains(Controls::MoveForward){
|
||||
@ -463,19 +463,22 @@ impl StyleHelper for StyleModifiers{
|
||||
}
|
||||
|
||||
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{
|
||||
//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{
|
||||
let mesh=match self.hitbox.mesh{
|
||||
gameplay_style::HitboxMesh::Box=>PhysicsMesh::unit_cube(),
|
||||
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)
|
||||
}
|
||||
}
|
||||
@ -491,7 +494,7 @@ impl MoveState{
|
||||
//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){
|
||||
match self{
|
||||
MoveState::Fly=>body.acceleration=Planar64Vec3::ZERO,
|
||||
MoveState::Fly=>body.acceleration=vec3::ZERO,
|
||||
MoveState::Air=>{
|
||||
//calculate base acceleration
|
||||
let a=touching.base_acceleration(models,style,camera,input_state);
|
||||
@ -766,9 +769,9 @@ impl TouchingState{
|
||||
//TODO: trey push solve
|
||||
for contact in &self.contacts{
|
||||
let n=contact_normal(models,hitbox_mesh,contact);
|
||||
let d=n.dot128(*velocity);
|
||||
if d<0{
|
||||
*velocity-=n*Planar64::raw(((d<<32)/n.dot128(n)) as i64);
|
||||
let d=n.dot(*velocity);
|
||||
if d.is_negative(){
|
||||
*velocity-=(n*d/n.length_squared()).divide().fix_1();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -776,23 +779,24 @@ impl TouchingState{
|
||||
//TODO: trey push solve
|
||||
for contact in &self.contacts{
|
||||
let n=contact_normal(models,hitbox_mesh,contact);
|
||||
let d=n.dot128(*acceleration);
|
||||
if d<0{
|
||||
*acceleration-=n*Planar64::raw(((d<<32)/n.dot128(n)) as i64);
|
||||
let d=n.dot(*acceleration);
|
||||
if d.is_negative(){
|
||||
*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){
|
||||
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{
|
||||
//detect face slide off
|
||||
let model_mesh=models.contact_mesh(contact);
|
||||
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)|{
|
||||
TimedInstruction{
|
||||
time,
|
||||
time:relative_body.time+time.into(),
|
||||
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());
|
||||
collector.collect(minkowski.predict_collision_out(&relative_body,collector.time()).map(|(_face,time)|{
|
||||
TimedInstruction{
|
||||
time,
|
||||
time:relative_body.time+time.into(),
|
||||
instruction:PhysicsInternalInstruction::CollisionEnd(
|
||||
Collision::Intersect(*intersect)
|
||||
Collision::Intersect(*intersect),
|
||||
time
|
||||
),
|
||||
}
|
||||
}));
|
||||
@ -814,6 +819,7 @@ impl TouchingState{
|
||||
}
|
||||
|
||||
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{
|
||||
Self{
|
||||
position,
|
||||
@ -824,20 +830,63 @@ impl Body{
|
||||
}
|
||||
pub fn extrapolated_position(&self,time:Time)->Planar64Vec3{
|
||||
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{
|
||||
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){
|
||||
self.position=self.extrapolated_position(time);
|
||||
self.velocity=self.extrapolated_velocity(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>{
|
||||
if self.velocity==Planar64Vec3::ZERO{
|
||||
if self.acceleration==Planar64Vec3::ZERO{
|
||||
if self.velocity==vec3::ZERO{
|
||||
if self.acceleration==vec3::ZERO{
|
||||
None
|
||||
}else{
|
||||
Some(self.acceleration)
|
||||
@ -851,22 +900,22 @@ impl Body{
|
||||
aabb.grow(self.extrapolated_position(t1));
|
||||
//v+a*t==0
|
||||
//goober code
|
||||
if self.acceleration.x()!=Planar64::ZERO{
|
||||
let t=Time::from(-self.velocity.x()/self.acceleration.x());
|
||||
if t0<t&&t<t1{
|
||||
aabb.grow(self.extrapolated_position(t));
|
||||
if !self.acceleration.x.is_zero(){
|
||||
let t=-self.velocity.x/self.acceleration.x;
|
||||
if t0.to_ratio().lt_ratio(t)&&t.lt_ratio(t1.to_ratio()){
|
||||
aabb.grow(self.extrapolated_position_ratio_dt(t));
|
||||
}
|
||||
}
|
||||
if self.acceleration.y()!=Planar64::ZERO{
|
||||
let t=Time::from(-self.velocity.y()/self.acceleration.y());
|
||||
if t0<t&&t<t1{
|
||||
aabb.grow(self.extrapolated_position(t));
|
||||
if !self.acceleration.y.is_zero(){
|
||||
let t=-self.velocity.y/self.acceleration.y;
|
||||
if t0.to_ratio().lt_ratio(t)&&t.lt_ratio(t1.to_ratio()){
|
||||
aabb.grow(self.extrapolated_position_ratio_dt(t));
|
||||
}
|
||||
}
|
||||
if self.acceleration.z()!=Planar64::ZERO{
|
||||
let t=Time::from(-self.velocity.z()/self.acceleration.z());
|
||||
if t0<t&&t<t1{
|
||||
aabb.grow(self.extrapolated_position(t));
|
||||
if !self.acceleration.z.is_zero(){
|
||||
let t=-self.velocity.z/self.acceleration.z;
|
||||
if t0.to_ratio().lt_ratio(t)&&t.lt_ratio(t1.to_ratio()){
|
||||
aabb.grow(self.extrapolated_position_ratio_dt(t));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -939,7 +988,7 @@ pub struct PhysicsData{
|
||||
impl Default for PhysicsState{
|
||||
fn default()->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,
|
||||
style:StyleModifiers::default(),
|
||||
touching:TouchingState::default(),
|
||||
@ -1154,7 +1203,7 @@ impl PhysicsContext{
|
||||
let mut aabb=aabb::Aabb::default();
|
||||
let transformed_mesh=TransformedMesh::new(view,transform);
|
||||
for v in transformed_mesh.verts(){
|
||||
aabb.grow(v);
|
||||
aabb.grow(v.fix_1());
|
||||
}
|
||||
(ConvexMeshId{
|
||||
model_id,
|
||||
@ -1229,12 +1278,15 @@ impl PhysicsContext{
|
||||
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,data.hitbox_mesh.transformed_mesh());
|
||||
collector.collect(minkowski.predict_collision_in(relative_body,collector.time())
|
||||
//temp (?) code to avoid collision loops
|
||||
.map_or(None,|(face,time)|if time<=state.time{None}else{Some((face,time))})
|
||||
.map(|(face,time)|
|
||||
.map_or(None,|(face,dt)|{
|
||||
let time=relative_body.time+dt.into();
|
||||
if time<=state.time{None}else{Some((time,face,dt))}})
|
||||
.map(|(time,face,dt)|
|
||||
TimedInstruction{
|
||||
time,
|
||||
instruction:PhysicsInternalInstruction::CollisionStart(
|
||||
Collision::new(convex_mesh_id,face)
|
||||
Collision::new(convex_mesh_id,face),
|
||||
dt
|
||||
)
|
||||
}
|
||||
)
|
||||
@ -1247,7 +1299,8 @@ impl PhysicsContext{
|
||||
fn contact_normal(models:&PhysicsModels,hitbox_mesh:&HitboxMesh,contact:&ContactCollision)->Planar64Vec3{
|
||||
let model_mesh=models.contact_mesh(contact);
|
||||
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(
|
||||
@ -1331,12 +1384,12 @@ fn set_velocity_cull(body:&mut Body,touching:&mut TouchingState,models:&PhysicsM
|
||||
let mut culled=false;
|
||||
touching.contacts.retain(|contact|{
|
||||
let n=contact_normal(models,hitbox_mesh,contact);
|
||||
let r=n.dot(v)<=Planar64::ZERO;
|
||||
if !r{
|
||||
let r=n.dot(v).is_positive();
|
||||
if r{
|
||||
culled=true;
|
||||
println!("set_velocity_cull contact={:?}",contact);
|
||||
}
|
||||
r
|
||||
!r
|
||||
});
|
||||
set_velocity(body,touching,models,hitbox_mesh,v);
|
||||
culled
|
||||
@ -1350,12 +1403,12 @@ fn set_acceleration_cull(body:&mut Body,touching:&mut TouchingState,models:&Phys
|
||||
let mut culled=false;
|
||||
touching.contacts.retain(|contact|{
|
||||
let n=contact_normal(models,hitbox_mesh,contact);
|
||||
let r=n.dot(a)<=Planar64::ZERO;
|
||||
if !r{
|
||||
let r=n.dot(a).is_positive();
|
||||
if r{
|
||||
culled=true;
|
||||
println!("set_acceleration_cull contact={:?}",contact);
|
||||
}
|
||||
r
|
||||
!r
|
||||
});
|
||||
set_acceleration(body,touching,models,hitbox_mesh,a);
|
||||
culled
|
||||
@ -1403,8 +1456,10 @@ fn teleport_to_spawn(
|
||||
input_state:&InputState,
|
||||
time:Time,
|
||||
)->Result<(),TeleportToSpawnError>{
|
||||
const EPSILON:Planar64=Planar64::raw((1<<32)/16);
|
||||
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);
|
||||
Ok(())
|
||||
}
|
||||
@ -1525,7 +1580,7 @@ fn collision_start_contact(
|
||||
Some(gameplay_attributes::ContactingBehaviour::Surf)=>println!("I'm surfing!"),
|
||||
Some(gameplay_attributes::ContactingBehaviour::Cling)=>println!("Unimplemented!"),
|
||||
&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);
|
||||
},
|
||||
Some(gameplay_attributes::ContactingBehaviour::Ladder(contacting_ladder))=>
|
||||
@ -1534,7 +1589,7 @@ fn collision_start_contact(
|
||||
//kill v
|
||||
//actually you could do this with a booster attribute :thinking:
|
||||
//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
|
||||
let (gravity,target_velocity)=ladder_things(ladder_settings,&contact,touching,models,hitbox_mesh,style,camera,input_state);
|
||||
@ -1543,7 +1598,7 @@ fn collision_start_contact(
|
||||
},
|
||||
Some(gameplay_attributes::ContactingBehaviour::NoJump)=>todo!("nyi"),
|
||||
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
|
||||
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);
|
||||
@ -1671,17 +1726,20 @@ fn collision_end_intersect(
|
||||
}
|
||||
fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedInstruction<PhysicsInternalInstruction>){
|
||||
state.time=ins.time;
|
||||
let should_advance_body=match ins.instruction{
|
||||
PhysicsInternalInstruction::CollisionStart(_)
|
||||
|PhysicsInternalInstruction::CollisionEnd(_)
|
||||
|PhysicsInternalInstruction::StrafeTick
|
||||
|PhysicsInternalInstruction::ReachWalkTargetVelocity=>true,
|
||||
let (should_advance_body,goober_time)=match ins.instruction{
|
||||
PhysicsInternalInstruction::CollisionStart(_,dt)
|
||||
|PhysicsInternalInstruction::CollisionEnd(_,dt)=>(true,Some(dt)),
|
||||
PhysicsInternalInstruction::StrafeTick
|
||||
|PhysicsInternalInstruction::ReachWalkTargetVelocity=>(true,None),
|
||||
};
|
||||
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{
|
||||
PhysicsInternalInstruction::CollisionStart(collision)=>{
|
||||
PhysicsInternalInstruction::CollisionStart(collision,_)=>{
|
||||
let mode=data.modes.get_mode(state.mode_state.get_mode_id());
|
||||
match collision{
|
||||
Collision::Contact(contact)=>collision_start_contact(
|
||||
@ -1702,7 +1760,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(
|
||||
&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),
|
||||
@ -1724,9 +1782,9 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
|
||||
if strafe_settings.activates(controls){
|
||||
let masked_controls=strafe_settings.mask(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);
|
||||
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
|
||||
//need to note which push planes activate in push solve and keep those
|
||||
state.cull_velocity(data,ticked_velocity);
|
||||
@ -1750,7 +1808,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
|
||||
//which means that gravity can be fully cancelled
|
||||
//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;
|
||||
},
|
||||
//you are not supposed to reach an unreachable walk target!
|
||||
@ -1845,9 +1903,9 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
|
||||
data.models.get_model_transform(mode.get_start().into()).map(|transform|
|
||||
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_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);
|
||||
b_refresh_walk_target=false;
|
||||
}
|
||||
@ -1898,7 +1956,7 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
|
||||
_=>println!("{}|{:?}",ins.time,ins.instruction),
|
||||
}
|
||||
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
|
||||
match ins.instruction{
|
||||
@ -1910,215 +1968,216 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
|
||||
|
||||
#[cfg(test)]
|
||||
mod test{
|
||||
use strafesnet_common::integer::{vec3::{self,int as int3},mat3};
|
||||
use super::*;
|
||||
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 hitbox_mesh=h1.transformed_mesh();
|
||||
let platform_mesh=h0.transformed_mesh();
|
||||
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(platform_mesh,hitbox_mesh);
|
||||
let collision=minkowski.predict_collision_in(&relative_body,Time::MAX);
|
||||
assert_eq!(collision.map(|tup|tup.1),expected_collision_time,"Incorrect time of collision");
|
||||
let collision=minkowski.predict_collision_in(&relative_body,Time::from_secs(10));
|
||||
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>){
|
||||
let h0=HitboxMesh::new(PhysicsMesh::unit_cube(),
|
||||
integer::Planar64Affine3::new(
|
||||
integer::Planar64Mat3::from_cols(
|
||||
Planar64Vec3::int(5,0,1)/2,
|
||||
Planar64Vec3::int(0,1,0)/2,
|
||||
Planar64Vec3::int(-1,0,5)/2,
|
||||
),
|
||||
Planar64Vec3::ZERO,
|
||||
)
|
||||
integer::Planar64Mat3::from_cols([
|
||||
int3(5,0,1)>>1,
|
||||
int3(0,1,0)>>1,
|
||||
int3(-1,0,5)>>1,
|
||||
]),
|
||||
vec3::ZERO
|
||||
),
|
||||
);
|
||||
let h1=StyleModifiers::roblox_bhop().calculate_mesh();
|
||||
let hitbox_mesh=h1.transformed_mesh();
|
||||
let platform_mesh=h0.transformed_mesh();
|
||||
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(platform_mesh,hitbox_mesh);
|
||||
let collision=minkowski.predict_collision_in(&relative_body,Time::MAX);
|
||||
assert_eq!(collision.map(|tup|tup.1),expected_collision_time,"Incorrect time of collision");
|
||||
let collision=minkowski.predict_collision_in(&relative_body,Time::from_secs(10));
|
||||
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>){
|
||||
test_collision_axis_aligned(relative_body.clone(),expected_collision_time);
|
||||
test_collision_rotated(relative_body,expected_collision_time);
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_degenerate(){
|
||||
fn test_collision_degenerate_straight_down(){
|
||||
test_collision(Body::new(
|
||||
Planar64Vec3::int(0,5,0),
|
||||
Planar64Vec3::int(0,-1,0),
|
||||
Planar64Vec3::ZERO,
|
||||
int3(0,5,0),
|
||||
int3(0,-1,0),
|
||||
vec3::ZERO,
|
||||
Time::ZERO
|
||||
),Some(Time::from_secs(2)));
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_degenerate_east(){
|
||||
test_collision(Body::new(
|
||||
Planar64Vec3::int(3,5,0),
|
||||
Planar64Vec3::int(0,-1,0),
|
||||
Planar64Vec3::ZERO,
|
||||
int3(3,5,0),
|
||||
int3(0,-1,0),
|
||||
vec3::ZERO,
|
||||
Time::ZERO
|
||||
),Some(Time::from_secs(2)));
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_degenerate_south(){
|
||||
test_collision(Body::new(
|
||||
Planar64Vec3::int(0,5,3),
|
||||
Planar64Vec3::int(0,-1,0),
|
||||
Planar64Vec3::ZERO,
|
||||
int3(0,5,3),
|
||||
int3(0,-1,0),
|
||||
vec3::ZERO,
|
||||
Time::ZERO
|
||||
),Some(Time::from_secs(2)));
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_degenerate_west(){
|
||||
test_collision(Body::new(
|
||||
Planar64Vec3::int(-3,5,0),
|
||||
Planar64Vec3::int(0,-1,0),
|
||||
Planar64Vec3::ZERO,
|
||||
int3(-3,5,0),
|
||||
int3(0,-1,0),
|
||||
vec3::ZERO,
|
||||
Time::ZERO
|
||||
),Some(Time::from_secs(2)));
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_degenerate_north(){
|
||||
test_collision(Body::new(
|
||||
Planar64Vec3::int(0,5,-3),
|
||||
Planar64Vec3::int(0,-1,0),
|
||||
Planar64Vec3::ZERO,
|
||||
int3(0,5,-3),
|
||||
int3(0,-1,0),
|
||||
vec3::ZERO,
|
||||
Time::ZERO
|
||||
),Some(Time::from_secs(2)));
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_parabola_edge_east_from_west(){
|
||||
test_collision(VirtualBody::relative(&Body::default(),&Body::new(
|
||||
Planar64Vec3::int(3,3,0),
|
||||
Planar64Vec3::int(100,-1,0),
|
||||
Planar64Vec3::int(0,-1,0),
|
||||
test_collision(VirtualBody::relative(&Body::ZERO,&Body::new(
|
||||
int3(3,3,0),
|
||||
int3(100,-1,0),
|
||||
int3(0,-1,0),
|
||||
Time::ZERO
|
||||
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_parabola_edge_south_from_north(){
|
||||
test_collision(VirtualBody::relative(&Body::default(),&Body::new(
|
||||
Planar64Vec3::int(0,3,3),
|
||||
Planar64Vec3::int(0,-1,100),
|
||||
Planar64Vec3::int(0,-1,0),
|
||||
test_collision(VirtualBody::relative(&Body::ZERO,&Body::new(
|
||||
int3(0,3,3),
|
||||
int3(0,-1,100),
|
||||
int3(0,-1,0),
|
||||
Time::ZERO
|
||||
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_parabola_edge_west_from_east(){
|
||||
test_collision(VirtualBody::relative(&Body::default(),&Body::new(
|
||||
Planar64Vec3::int(-3,3,0),
|
||||
Planar64Vec3::int(-100,-1,0),
|
||||
Planar64Vec3::int(0,-1,0),
|
||||
test_collision(VirtualBody::relative(&Body::ZERO,&Body::new(
|
||||
int3(-3,3,0),
|
||||
int3(-100,-1,0),
|
||||
int3(0,-1,0),
|
||||
Time::ZERO
|
||||
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_parabola_edge_north_from_south(){
|
||||
test_collision(VirtualBody::relative(&Body::default(),&Body::new(
|
||||
Planar64Vec3::int(0,3,-3),
|
||||
Planar64Vec3::int(0,-1,-100),
|
||||
Planar64Vec3::int(0,-1,0),
|
||||
test_collision(VirtualBody::relative(&Body::ZERO,&Body::new(
|
||||
int3(0,3,-3),
|
||||
int3(0,-1,-100),
|
||||
int3(0,-1,0),
|
||||
Time::ZERO
|
||||
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_parabola_edge_north_from_ne(){
|
||||
test_collision(VirtualBody::relative(&Body::default(),&Body::new(
|
||||
Planar64Vec3::int(0,6,-7)/2,
|
||||
Planar64Vec3::int(-10,-1,1),
|
||||
Planar64Vec3::int(0,-1,0),
|
||||
test_collision(VirtualBody::relative(&Body::ZERO,&Body::new(
|
||||
int3(0,6,-7)>>1,
|
||||
int3(-10,-1,1),
|
||||
int3(0,-1,0),
|
||||
Time::ZERO
|
||||
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_parabola_edge_north_from_nw(){
|
||||
test_collision(VirtualBody::relative(&Body::default(),&Body::new(
|
||||
Planar64Vec3::int(0,6,-7)/2,
|
||||
Planar64Vec3::int(10,-1,1),
|
||||
Planar64Vec3::int(0,-1,0),
|
||||
test_collision(VirtualBody::relative(&Body::ZERO,&Body::new(
|
||||
int3(0,6,-7)>>1,
|
||||
int3(10,-1,1),
|
||||
int3(0,-1,0),
|
||||
Time::ZERO
|
||||
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_parabola_edge_east_from_se(){
|
||||
test_collision(VirtualBody::relative(&Body::default(),&Body::new(
|
||||
Planar64Vec3::int(7,6,0)/2,
|
||||
Planar64Vec3::int(-1,-1,-10),
|
||||
Planar64Vec3::int(0,-1,0),
|
||||
test_collision(VirtualBody::relative(&Body::ZERO,&Body::new(
|
||||
int3(7,6,0)>>1,
|
||||
int3(-1,-1,-10),
|
||||
int3(0,-1,0),
|
||||
Time::ZERO
|
||||
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_parabola_edge_east_from_ne(){
|
||||
test_collision(VirtualBody::relative(&Body::default(),&Body::new(
|
||||
Planar64Vec3::int(7,6,0)/2,
|
||||
Planar64Vec3::int(-1,-1,10),
|
||||
Planar64Vec3::int(0,-1,0),
|
||||
test_collision(VirtualBody::relative(&Body::ZERO,&Body::new(
|
||||
int3(7,6,0)>>1,
|
||||
int3(-1,-1,10),
|
||||
int3(0,-1,0),
|
||||
Time::ZERO
|
||||
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_parabola_edge_south_from_se(){
|
||||
test_collision(VirtualBody::relative(&Body::default(),&Body::new(
|
||||
Planar64Vec3::int(0,6,7)/2,
|
||||
Planar64Vec3::int(-10,-1,-1),
|
||||
Planar64Vec3::int(0,-1,0),
|
||||
test_collision(VirtualBody::relative(&Body::ZERO,&Body::new(
|
||||
int3(0,6,7)>>1,
|
||||
int3(-10,-1,-1),
|
||||
int3(0,-1,0),
|
||||
Time::ZERO
|
||||
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_parabola_edge_south_from_sw(){
|
||||
test_collision(VirtualBody::relative(&Body::default(),&Body::new(
|
||||
Planar64Vec3::int(0,6,7)/2,
|
||||
Planar64Vec3::int(10,-1,-1),
|
||||
Planar64Vec3::int(0,-1,0),
|
||||
test_collision(VirtualBody::relative(&Body::ZERO,&Body::new(
|
||||
int3(0,6,7)>>1,
|
||||
int3(10,-1,-1),
|
||||
int3(0,-1,0),
|
||||
Time::ZERO
|
||||
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_parabola_edge_west_from_se(){
|
||||
test_collision(VirtualBody::relative(&Body::default(),&Body::new(
|
||||
Planar64Vec3::int(-7,6,0)/2,
|
||||
Planar64Vec3::int(1,-1,-10),
|
||||
Planar64Vec3::int(0,-1,0),
|
||||
test_collision(VirtualBody::relative(&Body::ZERO,&Body::new(
|
||||
int3(-7,6,0)>>1,
|
||||
int3(1,-1,-10),
|
||||
int3(0,-1,0),
|
||||
Time::ZERO
|
||||
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_parabola_edge_west_from_ne(){
|
||||
test_collision(VirtualBody::relative(&Body::default(),&Body::new(
|
||||
Planar64Vec3::int(-7,6,0)/2,
|
||||
Planar64Vec3::int(1,-1,10),
|
||||
Planar64Vec3::int(0,-1,0),
|
||||
test_collision(VirtualBody::relative(&Body::ZERO,&Body::new(
|
||||
int3(-7,6,0)>>1,
|
||||
int3(1,-1,10),
|
||||
int3(0,-1,0),
|
||||
Time::ZERO
|
||||
)).body(Time::from_secs(-1)),Some(Time::from_secs(0)));
|
||||
}
|
||||
#[test]
|
||||
fn test_collision_oblique(){
|
||||
test_collision(Body::new(
|
||||
Planar64Vec3::int(0,5,0),
|
||||
Planar64Vec3::int(1,-64,2)/64,
|
||||
Planar64Vec3::ZERO,
|
||||
int3(0,5,0),
|
||||
int3(1,-64,2)>>6,// /64
|
||||
vec3::ZERO,
|
||||
Time::ZERO
|
||||
),Some(Time::from_secs(2)));
|
||||
}
|
||||
#[test]
|
||||
fn zoom_hit_nothing(){
|
||||
test_collision(Body::new(
|
||||
Planar64Vec3::int(0,10,0),
|
||||
Planar64Vec3::int(1,0,0),
|
||||
Planar64Vec3::int(0,1,0),
|
||||
int3(0,10,0),
|
||||
int3(1,0,0),
|
||||
int3(0,1,0),
|
||||
Time::ZERO
|
||||
),None);
|
||||
}
|
||||
#[test]
|
||||
fn already_inside_hit_nothing(){
|
||||
test_collision(Body::new(
|
||||
Planar64Vec3::ZERO,
|
||||
Planar64Vec3::int(1,0,0),
|
||||
Planar64Vec3::int(0,1,0),
|
||||
vec3::ZERO,
|
||||
int3(1,0,0),
|
||||
int3(0,1,0),
|
||||
Time::ZERO
|
||||
),None);
|
||||
}
|
||||
|
@ -181,9 +181,9 @@ mod test{
|
||||
#[test]//How to run this test with printing: cargo test --release -- --nocapture
|
||||
fn test_worker() {
|
||||
// Create the worker thread
|
||||
let test_body=physics::Body::new(integer::Planar64Vec3::ONE,integer::Planar64Vec3::ONE,integer::Planar64Vec3::ONE,integer::Time::ZERO);
|
||||
let worker=QRWorker::new(physics::Body::default(),
|
||||
|_|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::ZERO,
|
||||
|_|physics::Body::new(integer::vec3::ONE,integer::vec3::ONE,integer::vec3::ONE,integer::Time::ZERO)
|
||||
);
|
||||
|
||||
// Send tasks to the worker
|
||||
|
Loading…
Reference in New Issue
Block a user