Compare commits

..

119 Commits

Author SHA1 Message Date
4c05c01b6e fix big wrong 2023-11-15 19:48:13 -08:00
b20f573f1d missing semicolons 2023-11-15 19:45:38 -08:00
21e29aa63e implement MinkowskiMesh::face_edges MinkowskiFace::EdgeEdge case 2023-11-15 18:00:14 -08:00
48870b8a76 implement MinkowskiMesh::edge_faces 2023-11-15 17:59:47 -08:00
020c704968 fix edge faces ordering 2023-11-15 17:59:24 -08:00
8f63699d45 consistent ordering 2023-11-15 16:05:42 -08:00
6071e23be1 style 2023-11-11 21:32:59 -08:00
46d6c31957 avoid double rocket boost 2023-11-11 21:27:20 -08:00
5b26304e95 project walk 2023-11-10 19:13:41 -08:00
d3cf75e0d5 fix ladder crashes 2023-11-10 19:13:41 -08:00
2acf740898 MinkowskiMesh::edge_faces is wrong 2023-11-10 19:13:41 -08:00
816f786763 implement MinkowskiMesh::face_nd for MinkowskiFace::EdgeEdge case 2023-11-10 19:13:41 -08:00
f7c613dfa6 correct MinkowskiMesh::face_edges FaceVert + VertFace cases 2023-11-10 19:13:41 -08:00
4fc09806f6 minkowski vert_edges 2023-11-10 19:13:41 -08:00
3cbefbab03 introduce DirectedEdge 2023-11-10 19:13:41 -08:00
717cf2290d vert_faces 2023-11-10 19:13:41 -08:00
756648c2fb probably need a better fix for this 2023-11-10 19:13:41 -08:00
44540853dd use alternate quadratic equation in poor accuracy cases 2023-11-10 19:13:41 -08:00
43e62e8f1d implement dot128 for face_nd 2023-11-10 19:13:41 -08:00
4af4dc669b update move state if acceleration culls surface 2023-11-10 19:13:41 -08:00
24f6472e9f acceleration was not being refreshed in air move (the bug) 2023-11-10 19:13:41 -08:00
cff920ddcd tweak elasticity calc 2023-11-10 19:13:41 -08:00
053514fa4a exit strafe tick early if zero control dir 2023-11-10 19:13:41 -08:00
27a46093ae introduce dot128 2023-11-10 19:13:41 -08:00
6049aba716 do not overshadow variable 2023-11-10 19:13:41 -08:00
b7998c7bd4 break walking 2023-11-10 19:13:41 -08:00
63cfbbfa9c fix ladders 2023-11-10 19:13:41 -08:00
f7072be5b4 separate culling type setters 2023-11-10 19:13:41 -08:00
4456ee29ec surf test was wrong 2023-11-10 19:13:41 -08:00
2d14e8ac8e cross product was wrong 💀x7 2023-11-10 19:13:41 -08:00
176aaa069c fix walk friction 2023-11-10 19:13:41 -08:00
a6dfa1d72b elasticity calc 2023-11-10 19:13:41 -08:00
516510faa8 add set body functions + jump query functions 2023-11-10 19:13:41 -08:00
1d2de93b9e normalize normal 2023-11-10 19:13:41 -08:00
2988175436 important 2023-11-10 19:13:41 -08:00
03b21aa27f work 2023-11-10 19:13:41 -08:00
910d520c2b wip TouchingState::get_move_state 2023-11-10 19:13:41 -08:00
c321814b9b normal_determinant is not real 2023-11-10 19:13:41 -08:00
ce1e9df334 TouchingState::base_acceleration 2023-11-10 19:13:41 -08:00
e6e7366e66 fix idiot 2023-11-10 19:13:41 -08:00
535ae1a5f0 fix constrain 2023-11-10 19:13:41 -08:00
432a38718c fix slope divide by zero 2023-11-10 19:13:41 -08:00
9aa7656278 mesh gen test 2023-11-10 19:13:41 -08:00
823a16e08d fix determinant 2023-11-10 19:13:41 -08:00
82d84ee07c fix mesh gen algorithm 2023-11-10 19:13:41 -08:00
ffc19020b9 unused var 2023-11-10 19:13:41 -08:00
e0581390bd fix transformed face_nd 2023-11-10 19:13:41 -08:00
4b4ac0de19 test stop taking so long 2023-11-10 19:13:41 -08:00
d94ad475eb debug message 2023-11-10 19:13:41 -08:00
60eadd3156 ref faster no copy 2023-11-10 19:13:41 -08:00
ebca761dbc whoops 2023-11-10 19:13:41 -08:00
ab60fb2cf7 idiot 2023-11-10 19:13:41 -08:00
ec9b720138 make build 2023-11-10 19:13:41 -08:00
7ad0270d2f TransformedMesh::new to avoid pub 2023-11-10 19:13:41 -08:00
d8e605d203 fix compiler nits 2023-11-10 19:13:41 -08:00
dc46cb3d45 delete unused aabb face code 2023-11-10 19:13:41 -08:00
a53d90f74b tape up last stragglers 2023-11-10 19:13:41 -08:00
d9be4b8105 checkpoints don't actually exist 2023-11-10 19:13:41 -08:00
58c35ce364 water 2023-11-10 19:13:41 -08:00
2a033b87e0 rethink ordered checkpoints - do not support non-teleporting (backtracking) 2023-11-10 19:13:41 -08:00
d374a3d4c6 SpawnAt comment 2023-11-10 19:13:41 -08:00
c37b990389 tweak water 2023-11-10 19:13:41 -08:00
7311b1ae88 checkpoints are stage elements 2023-11-10 19:13:41 -08:00
af7fcdd5cb add GameMechanicAttributes::is_wrcp() function 2023-11-10 19:13:41 -08:00
bf51afcf1d fix normal code 2023-11-10 19:13:41 -08:00
fd9ca6cd3f Planar64Vec3::{inverse,transpose,determinant} 2023-11-10 19:13:41 -08:00
d71f143d68 point hitbox work 2023-11-10 19:13:41 -08:00
769f88eb9f real functions 2023-11-10 19:13:41 -08:00
cf9063bffc PhysicsModels idea 2023-11-10 19:13:41 -08:00
fc3d72393f rename enum 2023-11-10 19:13:41 -08:00
a5ef0195e2 tweak minkowski code + fix edge_verts 2023-11-10 19:13:41 -08:00
6fec6b9f59 cling thing 2023-11-10 19:13:41 -08:00
895f9b82f8 tabs 2023-11-10 19:13:41 -08:00
6aa0e790d8 brute force t for plain mesh 2023-11-10 19:13:41 -08:00
b897d4a662 I need to rethink Collision vs Attributes 2023-11-10 19:13:41 -08:00
49931a40fc make aabb_list with silly iterator to avoid pub-ing Vert 2023-11-10 19:13:41 -08:00
e92eaed2d3 work on indexing meshes and attributes 2023-11-10 19:13:41 -08:00
1ec9412b49 closest_fev for TransformedMesh 2023-11-10 19:13:41 -08:00
d9966a4eb0 write mesh closest_fev 2023-11-10 19:13:41 -08:00
cee6835af6 decide 2023-11-10 19:13:41 -08:00
f1e0514ecb cross product!!!! 2023-11-10 19:13:41 -08:00
86d45252c7 use edge_n instead of ambiguous face 2023-11-10 19:13:41 -08:00
9f948744d4 add edge_n to MeshQuery 2023-11-10 19:13:41 -08:00
5e28c02640 TODOs 2023-11-10 19:13:41 -08:00
5f1b93d9c4 next_instruction face crawler implementation 2023-11-10 19:13:41 -08:00
d63a76269f ignore face 2023-11-10 19:13:41 -08:00
354b96d98f collector.time() 2023-11-10 19:13:41 -08:00
d155517587 delete aabb physics 2023-11-10 19:13:41 -08:00
3c443b6b6a mesh generation 2023-11-10 19:13:41 -08:00
f73b4a0c52 Planar64Vec3::new 2023-11-10 19:13:41 -08:00
c37194daf6 AddAssign for Planar64 2023-11-10 19:13:41 -08:00
5317a0fafe rename VirtualMesh to TransformedMesh 2023-11-10 19:13:41 -08:00
941faf199d vert and edge does not have an unambiguous face 2023-11-10 19:13:41 -08:00
053bab9e24 wip: physics work 2023-11-10 19:13:41 -08:00
89f7a2b9b9 wip: start brute force closest_fev 2023-11-10 19:13:41 -08:00
dff2648cd3 add Planar64::MAX 2023-11-10 19:13:41 -08:00
d2d5a62458 implement vert 2023-11-10 19:13:41 -08:00
89ea7f45f9 wip 2023-11-10 19:13:41 -08:00
01d74102e3 MinkowskiMesh::edge_verts 2023-11-10 19:13:41 -08:00
ee4e6dbbaf MinkowskiMesh::edge_faces 2023-11-10 19:13:41 -08:00
19d4fed2a2 MinkowskiMesh::face_edges 2023-11-10 19:13:41 -08:00
a9f6cc7c29 rename functions 2023-11-10 19:13:41 -08:00
64657e718d cow: utter brilliance 2023-11-10 19:13:41 -08:00
3d5ac0cd61 Minkowski face_nd 2023-11-10 19:13:41 -08:00
72ebe5c9c8 generic generic generic 2023-11-10 19:13:41 -08:00
e1fde9b507 there is another 2023-11-10 19:13:41 -08:00
0cb6236035 todo 2023-11-10 19:13:41 -08:00
a8b1ced5cd implement face crawler algorithm using functions that don't exist 2023-11-10 19:13:41 -08:00
bb46801610 functions 2023-11-10 19:13:41 -08:00
06eeac6043 data structures 2023-11-10 19:13:41 -08:00
558febabc3 face_normal 2023-11-10 19:13:41 -08:00
1b51e21ecd implement PhysicsMesh 2023-11-10 19:13:41 -08:00
7ce08b0bbe move stuff to model_physics 2023-11-10 19:13:41 -08:00
19c7a8924e face crawler work 2023-11-10 19:13:41 -08:00
b215578c8d virtual body 2023-11-10 19:13:41 -08:00
2bea7e210e pub body 2023-11-10 19:13:41 -08:00
f8a8cbf12a replace Body::with_pva with Body::new 2023-11-10 19:13:41 -08:00
9f76611c18 algorithm 2023-11-10 19:13:41 -08:00
4da3d78057 Face Crawler™ 2023-11-10 19:13:41 -08:00
12 changed files with 797 additions and 1225 deletions

419
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package]
name = "strafe-client"
version = "0.9.0"
version = "0.8.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@@ -2,8 +2,8 @@ use crate::integer::Planar64Vec3;
#[derive(Clone)]
pub struct Aabb{
min:Planar64Vec3,
max:Planar64Vec3,
pub min:Planar64Vec3,
pub max:Planar64Vec3,
}
impl Default for Aabb {
@@ -28,11 +28,8 @@ impl Aabb{
pub fn intersects(&self,aabb:&Aabb)->bool{
(self.min.cmplt(aabb.max)&aabb.min.cmplt(self.max)).all()
}
pub fn size(&self)->Planar64Vec3{
self.max-self.min
}
pub fn center(&self)->Planar64Vec3{
self.min.midpoint(self.max)
return self.min.midpoint(self.max)
}
//probably use floats for area & volume because we don't care about precision
// pub fn area_weight(&self)->f32{

View File

@@ -72,9 +72,9 @@ fn generate_bvh_node(boxen:Vec<(usize,Aabb)>)->BvhNode{
sort_y.push((*i,center.y()));
sort_z.push((*i,center.z()));
}
sort_x.sort_by(|tup0,tup1|tup0.1.cmp(&tup1.1));
sort_y.sort_by(|tup0,tup1|tup0.1.cmp(&tup1.1));
sort_z.sort_by(|tup0,tup1|tup0.1.cmp(&tup1.1));
sort_x.sort_by(|tup0,tup1|tup0.1.partial_cmp(&tup1.1).unwrap());
sort_y.sort_by(|tup0,tup1|tup0.1.partial_cmp(&tup1.1).unwrap());
sort_z.sort_by(|tup0,tup1|tup0.1.partial_cmp(&tup1.1).unwrap());
let h=n/2;
let median_x=sort_x[h].1;
let median_y=sort_y[h].1;

View File

@@ -1,46 +1,49 @@
use crate::physics::Body;
use crate::model_physics::{FEV,MeshQuery,DirectedEdge};
use crate::integer::{Time,Planar64};
use crate::model_physics::{FEV,MeshQuery};
use crate::integer::{Time,Planar64,Planar64Vec3};
use crate::zeroes::zeroes2;
enum Transition<F,E:DirectedEdge,V>{
struct State<FEV>{
fev:FEV,
time:Time,
}
enum Transition<F,E,V>{
Miss,
Next(FEV<F,E,V>,Time),
Hit(F,Time),
}
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>{
impl<F:Copy,E:Copy,V:Copy> State<FEV<F,E,V>>{
fn next_transition(&self,mesh:&impl MeshQuery<F,E,V>,body:&Body,time_limit:Time)->Transition<F,E,V>{
//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;
match fev{
match &self.fev{
&FEV::<F,E,V>::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{
if self.time<t&&t<best_time&&n.dot(body.extrapolated_velocity(t))<Planar64::ZERO{
best_time=t;
best_transtition=Transition::Hit(face_id,t);
break;
}
}
//test each edge collision time, ignoring roots with zero or conflicting derivative
for &directed_edge_id in mesh.face_edges(face_id).iter(){
let edge_n=mesh.directed_edge_n(directed_edge_id);
for &(edge_id,test_face_id) in mesh.face_edges(face_id).iter(){
let edge_n=mesh.edge_n(edge_id);
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)){
//picking a vert randomly is terrible
let d=n.dot(mesh.vert(mesh.edge_verts(edge_id)[0]));
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{
if self.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);
best_transtition=Transition::Next(FEV::<F,E,V>::Edge(edge_id),t);
break;
}
}
@@ -50,29 +53,26 @@ enum Transition<F,E:DirectedEdge,V>{
&FEV::<F,E,V>::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);
let delta_pos=body.position*2-(mesh.vert(edge_verts[0])+mesh.vert(edge_verts[1]));
for (i,&edge_face_id) in mesh.edge_faces(edge_id).iter().enumerate(){
let face_n=mesh.face_nd(edge_face_id).0;
//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)){
for &test_face_id in mesh.edge_faces(edge_id).iter(){
let face_n=mesh.face_nd(test_face_id).0;
let n=edge_n.cross(face_n);
let d=n.dot(mesh.vert(mesh.edge_verts(edge_id)[0]));
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{
if self.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);
best_transtition=Transition::Next(FEV::<F,E,V>::Face(test_face_id),t);
break;
}
}
}
//test each vertex collision time, ignoring roots with zero or conflicting derivative
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 n=mesh.edge_n(edge_id);
for &vert_id in mesh.edge_verts(edge_id).iter(){
let d=n.dot(mesh.vert(vert_id));
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{
if self.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);
break;
@@ -83,14 +83,14 @@ enum Transition<F,E:DirectedEdge,V>{
},
&FEV::<F,E,V>::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)){
for &edge_id in mesh.vert_edges(vert_id).iter(){
let n=mesh.edge_n(edge_id);
let d=n.dot(mesh.vert(vert_id));
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{
if self.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);
best_transtition=Transition::Next(FEV::<F,E,V>::Edge(edge_id),t);
break;
}
}
@@ -100,20 +100,27 @@ enum Transition<F,E:DirectedEdge,V>{
}
best_transtition
}
pub enum CrawlResult<F,E:DirectedEdge,V>{
Miss(FEV<F,E,V>),
Hit(F,Time),
}
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;
for _ in 0..20{
match next_transition(&fev,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::Hit(face,time)=>return CrawlResult::Hit(face,time),
pub fn predict_collision<F:Copy,E:Copy,V:Copy>(mesh:&impl MeshQuery<F,E,V>,relative_body:&Body,time_limit:Time)->Option<(F,Time)>{
let mut state=State{
fev:mesh.closest_fev(relative_body.position),
time:relative_body.time,
};
//it would be possible to write down the point of closest approach...
loop{
match state.next_transition(mesh,relative_body,time_limit){
Transition::Miss=>return None,
Transition::Next(fev,time)=>(state.fev,state.time)=(fev,time),
Transition::Hit(face,time)=>return Some((face,time)),
}
}
//TODO: fix all bugs
println!("Too many iterations! Using default behaviour instead of crashing...");
CrawlResult::Miss(fev)
}
pub fn predict_collision_end<F:Copy,E:Copy,V:Copy>(mesh:&impl MeshQuery<F,E,V>,relative_body:&Body,time_limit:Time,ignore_face_id:F)->Option<(F,Time)>{
//imagine the mesh without the collision face
//no algorithm needed, there is only one state and three cases (Face,Edge,None)
//determine when it passes an edge ("sliding off" case) or if it leaves the surface directly
//the state can be constructed from the ContactCollision directly
None
}

View File

@@ -2,8 +2,6 @@
#[derive(Clone,Copy,Hash,Eq,PartialEq,PartialOrd,Debug)]
pub struct Time(i64);
impl Time{
pub const MIN:Self=Self(i64::MIN);
pub const MAX:Self=Self(i64::MAX);
pub const ZERO:Self=Self(0);
pub const ONE_SECOND:Self=Self(1_000_000_000);
pub const ONE_MILLISECOND:Self=Self(1_000_000);
@@ -105,23 +103,23 @@ impl Ratio64{
None
}else{
let d=gcd(num.unsigned_abs(),den);
Some(Self{num:num/(d as i64),den:den/d})
Some(Self{num:num/d as i64,den:den/d})
}
}
#[inline]
pub fn mul_int(&self,rhs:i64)->i64{
rhs*self.num/(self.den as i64)
rhs*self.num/self.den as i64
}
#[inline]
pub fn rhs_div_int(&self,rhs:i64)->i64{
rhs*(self.den as i64)/self.num
rhs*self.den as i64/self.num
}
#[inline]
pub fn mul_ref(&self,rhs:&Ratio64)->Ratio64{
let (num,den)=(self.num*rhs.num,self.den*rhs.den);
let d=gcd(num.unsigned_abs(),den);
Self{
num:num/(d as i64),
num:num/d as i64,
den:den/d,
}
}
@@ -209,8 +207,8 @@ impl TryFrom<f32> for Ratio64{
std::num::FpCategory::Nan=>Err(Self::Error::Nan),
std::num::FpCategory::Infinite=>Err(Self::Error::Infinite),
std::num::FpCategory::Zero=>Ok(Self::ZERO),
std::num::FpCategory::Subnormal
|std::num::FpCategory::Normal=>ratio64_from_mes(integer_decode_f32(value)),
std::num::FpCategory::Subnormal=>Err(Self::Error::Subnormal),
std::num::FpCategory::Normal=>ratio64_from_mes(integer_decode_f32(value)),
}
}
}
@@ -222,8 +220,8 @@ impl TryFrom<f64> for Ratio64{
std::num::FpCategory::Nan=>Err(Self::Error::Nan),
std::num::FpCategory::Infinite=>Err(Self::Error::Infinite),
std::num::FpCategory::Zero=>Ok(Self::ZERO),
std::num::FpCategory::Subnormal
|std::num::FpCategory::Normal=>ratio64_from_mes(integer_decode_f64(value)),
std::num::FpCategory::Subnormal=>Err(Self::Error::Subnormal),
std::num::FpCategory::Normal=>ratio64_from_mes(integer_decode_f64(value)),
}
}
}
@@ -234,7 +232,7 @@ impl std::ops::Mul<Ratio64> for Ratio64{
let (num,den)=(self.num*rhs.num,self.den*rhs.den);
let d=gcd(num.unsigned_abs(),den);
Self{
num:num/(d as i64),
num:num/d as i64,
den:den/d,
}
}
@@ -417,7 +415,6 @@ impl Planar64{
pub const ZERO:Self=Self(0);
pub const ONE:Self=Self(1<<32);
pub const MAX:Self=Self(i64::MAX);
pub const MIN:Self=Self(i64::MIN);
#[inline]
pub const fn int(num:i32)->Self{
Self(Self::ONE.0*num as i64)
@@ -451,7 +448,7 @@ impl Into<f32> for Planar64{
impl From<Ratio64> for Planar64{
#[inline]
fn from(ratio:Ratio64)->Self{
Self((((ratio.num as i128)<<32)/(ratio.den as i128)) as i64)
Self((((ratio.num as i128)<<32)/ratio.den as i128) as i64)
}
}
#[derive(Debug)]
@@ -459,8 +456,26 @@ pub enum Planar64TryFromFloatError{
Nan,
Infinite,
Subnormal,
HighlyNegativeExponent,
HighlyPositiveExponent,
HighlyNegativeExponent(i16),
HighlyPositiveExponent(i16),
}
#[inline]
fn planar64_from_mes((m,e,s):(u64,i16,i8))->Result<Planar64,Planar64TryFromFloatError>{
let e32=e+32;
if e32<0&&(m>>-e32)==0{//shifting m will underflow to 0
Ok(Planar64::ZERO)
// println!("m{} e{} s{}",m,e,s);
// println!("f={}",(m as f64)*(2.0f64.powf(e as f64))*(s as f64));
// Err(Planar64TryFromFloatError::HighlyNegativeExponent(e))
}else if (64-m.leading_zeros() as i16)+e32<64{//shifting m will not overflow
if e32<0{
Ok(Planar64((m as i64)*(s as i64)>>-e32))
}else{
Ok(Planar64((m as i64)*(s as i64)<<e32))
}
}else{//if shifting m will overflow (prev check failed)
Err(Planar64TryFromFloatError::HighlyPositiveExponent(e))
}
}
impl TryFrom<f32> for Planar64{
type Error=Planar64TryFromFloatError;
@@ -470,15 +485,8 @@ impl TryFrom<f32> for Planar64{
std::num::FpCategory::Nan=>Err(Self::Error::Nan),
std::num::FpCategory::Infinite=>Err(Self::Error::Infinite),
std::num::FpCategory::Zero=>Ok(Self::ZERO),
std::num::FpCategory::Subnormal
|std::num::FpCategory::Normal=>{
let planar=value*PLANAR64_ONE_FLOAT32;
if planar<(i64::MIN as f32)||(i64::MAX as f32)<planar{
Err(Self::Error::HighlyPositiveExponent)
}else{
Ok(Planar64(unsafe{planar.to_int_unchecked()}))
}
}
std::num::FpCategory::Subnormal=>Err(Self::Error::Subnormal),
std::num::FpCategory::Normal=>planar64_from_mes(integer_decode_f32(value)),
}
}
}
@@ -490,15 +498,8 @@ impl TryFrom<f64> for Planar64{
std::num::FpCategory::Nan=>Err(Self::Error::Nan),
std::num::FpCategory::Infinite=>Err(Self::Error::Infinite),
std::num::FpCategory::Zero=>Ok(Self::ZERO),
std::num::FpCategory::Subnormal
|std::num::FpCategory::Normal=>{
let planar=value*PLANAR64_ONE_FLOAT64;
if planar<(i64::MIN as f64)||(i64::MAX as f64)<planar{
Err(Self::Error::HighlyPositiveExponent)
}else{
Ok(Planar64(unsafe{planar.to_int_unchecked()}))
}
}
std::num::FpCategory::Subnormal=>Err(Self::Error::Subnormal),
std::num::FpCategory::Normal=>planar64_from_mes(integer_decode_f64(value)),
}
}
}
@@ -568,7 +569,7 @@ impl std::ops::Div<Planar64> for Planar64{
type Output=Planar64;
#[inline]
fn div(self, rhs: Planar64) -> Self::Output {
Planar64((((self.0 as i128)<<32)/(rhs.0 as i128)) as i64)
Planar64((((self.0 as i128)<<32)/rhs.0 as i128) as i64)
}
}
// impl PartialOrd<i64> for Planar64{
@@ -803,17 +804,6 @@ impl std::ops::Mul<Time> for Planar64Vec3{
))
}
}
impl std::ops::Div<Planar64> for Planar64Vec3{
type Output=Planar64Vec3;
#[inline]
fn div(self,rhs:Planar64)->Self::Output{
Planar64Vec3(glam::i64vec3(
(((self.0.x as i128)<<32)/(rhs.0 as i128)) as i64,
(((self.0.y as i128)<<32)/(rhs.0 as i128)) as i64,
(((self.0.z as i128)<<32)/(rhs.0 as i128)) as i64,
))
}
}
impl std::ops::Div<i64> for Planar64Vec3{
type Output=Planar64Vec3;
#[inline]
@@ -843,6 +833,16 @@ impl Default for Planar64Mat3{
}
}
}
impl std::ops::Mul<Planar64Vec3> for Planar64Mat3{
type Output=Planar64Vec3;
#[inline]
fn mul(self,rhs:Planar64Vec3) -> Self::Output {
self.x_axis*rhs.x()
+self.y_axis*rhs.y()
+self.z_axis*rhs.z()
}
}
impl Planar64Mat3{
#[inline]
pub fn from_cols(x_axis:Planar64Vec3,y_axis:Planar64Vec3,z_axis:Planar64Vec3)->Self{
@@ -860,14 +860,6 @@ impl Planar64Mat3{
}
}
#[inline]
pub const fn from_diagonal(diagonal:Planar64Vec3)->Self{
Self{
x_axis:Planar64Vec3::raw(diagonal.0.x,0,0),
y_axis:Planar64Vec3::raw(0,diagonal.0.y,0),
z_axis:Planar64Vec3::raw(0,0,diagonal.0.z),
}
}
#[inline]
pub fn from_rotation_yx(yaw:Angle32,pitch:Angle32)->Self{
let xtheta=yaw.0 as f64*ANGLE32_TO_FLOAT64_RADIANS;
let (xs,xc)=xtheta.sin_cos();
@@ -914,14 +906,6 @@ impl Planar64Mat3{
}
}
#[inline]
pub const fn inverse_times_det(&self)->Self{
Self{
x_axis:Planar64Vec3::raw(((-(self.y_axis.0.z as i128*self.z_axis.0.y as i128)+self.y_axis.0.y as i128*self.z_axis.0.z as i128)>>32) as i64,((self.x_axis.0.z as i128*self.z_axis.0.y as i128-self.x_axis.0.y as i128*self.z_axis.0.z as i128)>>32) as i64,((-(self.x_axis.0.z as i128*self.y_axis.0.y as i128)+self.x_axis.0.y as i128*self.y_axis.0.z as i128)>>32) as i64),
y_axis:Planar64Vec3::raw(((self.y_axis.0.z as i128*self.z_axis.0.x as i128-self.y_axis.0.x as i128*self.z_axis.0.z as i128)>>32) as i64,((-(self.x_axis.0.z as i128*self.z_axis.0.x as i128)+self.x_axis.0.x as i128*self.z_axis.0.z as i128)>>32) as i64,((self.x_axis.0.z as i128*self.y_axis.0.x as i128-self.x_axis.0.x as i128*self.y_axis.0.z as i128)>>32) as i64),
z_axis:Planar64Vec3::raw(((-(self.y_axis.0.y as i128*self.z_axis.0.x as i128)+self.y_axis.0.x as i128*self.z_axis.0.y as i128)>>32) as i64,((self.x_axis.0.y as i128*self.z_axis.0.x as i128-self.x_axis.0.x as i128*self.z_axis.0.y as i128)>>32) as i64,((-(self.x_axis.0.y as i128*self.y_axis.0.x as i128)+self.x_axis.0.x as i128*self.y_axis.0.y as i128)>>32) as i64),
}
}
#[inline]
pub const fn transpose(&self)->Self{
Self{
x_axis:Planar64Vec3::raw(self.x_axis.0.x,self.y_axis.0.x,self.z_axis.0.x),
@@ -971,15 +955,6 @@ impl std::fmt::Display for Planar64Mat3{
)
}
}
impl std::ops::Mul<Planar64Vec3> for Planar64Mat3{
type Output=Planar64Vec3;
#[inline]
fn mul(self,rhs:Planar64Vec3) -> Self::Output {
self.x_axis*rhs.x()
+self.y_axis*rhs.y()
+self.z_axis*rhs.z()
}
}
impl std::ops::Div<i64> for Planar64Mat3{
type Output=Planar64Mat3;
#[inline]
@@ -1048,7 +1023,7 @@ impl std::fmt::Display for Planar64Affine3{
#[test]
fn test_sqrt(){
let r=Planar64::int(400);
assert_eq!(1717986918400,r.get());
println!("r{}",r.get());
let s=r.sqrt();
assert_eq!(85899345920,s.get());
println!("s{}",s.get());
}

View File

@@ -14,15 +14,12 @@ fn class_is_a(class: &str, superclass: &str) -> bool {
return false
}
fn recursive_collect_superclass(objects: &mut std::vec::Vec<rbx_dom_weak::types::Ref>,dom: &rbx_dom_weak::WeakDom, instance: &rbx_dom_weak::Instance, superclass: &str){
let mut stack=vec![instance];
while let Some(item)=stack.pop(){
for &referent in item.children(){
if let Some(c)=dom.get_by_ref(referent){
if class_is_a(c.class.as_str(),superclass){
objects.push(c.referent());//copy ref
}
stack.push(c);
for &referent in instance.children() {
if let Some(c) = dom.get_by_ref(referent) {
if class_is_a(c.class.as_str(), superclass) {
objects.push(c.referent());//copy ref
}
recursive_collect_superclass(objects,dom,c,superclass);
}
}
}
@@ -265,18 +262,6 @@ pub fn generate_indexed_models(dom:rbx_dom_weak::WeakDom) -> crate::model::Index
{
let model_transform=planar64_affine3_from_roblox(cf,size);
if model_transform.matrix3.determinant()==Planar64::ZERO{
let mut parent_ref=object.parent();
let mut full_path=object.name.clone();
while let Some(parent)=dom.get_by_ref(parent_ref){
full_path=format!("{}.{}",parent.name,full_path);
parent_ref=parent.parent();
}
println!("Zero determinant CFrame at location {}",full_path);
println!("matrix3:{}",model_transform.matrix3);
continue;
}
//push TempIndexedAttributes
let mut force_intersecting=false;
let mut temp_indexing_attributes=Vec::new();

View File

@@ -11,33 +11,8 @@ pub struct IndexedVertex{
pub struct IndexedPolygon{
pub vertices:Vec<u32>,
}
pub type TextureId=u32;
pub type ShaderId=u32;
pub enum ShaderResource{
Model,//pass in model attributes
Texture(TextureId),
Depth,//current draw depth
Normal,
BehindTexture,//what is drawn behind this pixel
}
pub enum RenderType{
NoTexture,//simply draw the vertex color or model color or something
Texture(TextureId),
Material{
texture:TextureId,
normal:TextureId,
},
//GUI,
Portal{
transform:glam::Mat4,
},
Shader{
id:ShaderId,
resources:Vec<ShaderResource>,
},
}
pub struct IndexedGroup{
pub texture:Option<u32>,//TODO: RenderType
pub texture:Option<u32>,//RenderPattern? material/texture/shader/flat color
pub polys:Vec<IndexedPolygon>,
}
pub struct IndexedModel{
@@ -284,6 +259,7 @@ impl IntersectingAttributes{
}
}
//Spawn(u32) NO! spawns are indexed in the map header instead of marked with attibutes
//TODO: deduplicate attributes
pub enum CollisionAttributes{
Decoration,//visual only
Contact{//track whether you are contacting the object

File diff suppressed because it is too large Load Diff

View File

@@ -43,17 +43,6 @@ pub struct Body{
pub acceleration:Planar64Vec3,//I64 where 2^32 = 1 u/s/s
pub time:Time,//nanoseconds x xxxxD!
}
impl std::ops::Neg for Body{
type Output=Self;
fn neg(self)->Self::Output{
Self{
position:self.position,
velocity:-self.velocity,
acceleration:self.acceleration,
time:-self.time,
}
}
}
//hey dumbass just use a delta
#[derive(Clone,Debug)]
@@ -202,7 +191,6 @@ impl PhysicsModels{
&self.meshes[self.models[model_id].mesh_id],
&self.models[model_id].transform,
&self.models[model_id].normal_transform,
self.models[model_id].transform_det,
)
}
fn model(&self,model_id:usize)->&PhysicsModel{
@@ -339,60 +327,6 @@ struct StrafeSettings{
tick_rate:Ratio64,
}
struct Hitbox{
halfsize:Planar64Vec3,
mesh:PhysicsMesh,
transform:crate::integer::Planar64Affine3,
normal_transform:Planar64Mat3,
transform_det:Planar64,
}
impl Hitbox{
fn new(mesh:PhysicsMesh,transform:crate::integer::Planar64Affine3)->Self{
//calculate extents
let mut aabb=crate::aabb::Aabb::default();
for vert in mesh.verts(){
aabb.grow(transform.transform_point3(vert));
}
Self{
halfsize:aabb.size()/2,
mesh,
transform,
normal_transform:transform.matrix3.inverse_times_det().transpose(),
transform_det:transform.matrix3.determinant(),
}
}
fn from_mesh_scale(mesh:PhysicsMesh,scale:Planar64Vec3)->Self{
let matrix3=Planar64Mat3::from_diagonal(scale);
Self{
halfsize:scale,
mesh,
normal_transform:matrix3.inverse_times_det().transpose(),
transform:crate::integer::Planar64Affine3::new(matrix3,Planar64Vec3::ZERO),
transform_det:matrix3.determinant(),//scale.x*scale.y*scale.z but whatever
}
}
fn from_mesh_scale_offset(mesh:PhysicsMesh,scale:Planar64Vec3,offset:Planar64Vec3)->Self{
let matrix3=Planar64Mat3::from_diagonal(scale);
Self{
halfsize:scale,
mesh,
normal_transform:matrix3.inverse_times_det().transpose(),
transform:crate::integer::Planar64Affine3::new(matrix3,offset),
transform_det:matrix3.determinant(),
}
}
fn roblox()->Self{
Self::from_mesh_scale(PhysicsMesh::from(&crate::primitives::unit_cylinder()),Planar64Vec3::int(2,5,2)/2)
}
fn source()->Self{
Self::from_mesh_scale(PhysicsMesh::from(&crate::primitives::unit_cube()),Planar64Vec3::raw(33<<28,73<<28,33<<28)/2)
}
#[inline]
fn transformed_mesh(&self)->TransformedMesh{
TransformedMesh::new(&self.mesh,&self.transform,&self.normal_transform,self.transform_det)
}
}
struct StyleModifiers{
controls_used:u32,//controls which are allowed to pass into gameplay
controls_mask:u32,//controls which are masked from control state (e.g. jump in scroll style)
@@ -412,7 +346,7 @@ struct StyleModifiers{
surf_slope:Option<Planar64>,
rocket_force:Option<Planar64>,
gravity:Planar64Vec3,
hitbox:Hitbox,
hitbox_halfsize:Planar64Vec3,
camera_offset:Planar64Vec3,
}
impl std::default::Default for StyleModifiers{
@@ -434,14 +368,14 @@ impl StyleModifiers{
const UP_DIR:Planar64Vec3=Planar64Vec3::Y;
const FORWARD_DIR:Planar64Vec3=Planar64Vec3::NEG_Z;
fn neo()->Self{
fn new()->Self{
Self{
controls_used:!0,
controls_mask:!0,//&!(Self::CONTROL_MOVEUP|Self::CONTROL_MOVEDOWN),
strafe:Some(StrafeSettings{
enable:EnableStrafe::Always,
air_accel_limit:None,
tick_rate:Ratio64::new(64,Time::ONE_SECOND.nanos() as u64).unwrap(),
tick_rate:Ratio64::new(128,Time::ONE_SECOND.nanos() as u64).unwrap(),
}),
jump_impulse:JumpImpulse::FromEnergy(Planar64::int(512)),
jump_calculation:JumpCalculation::Energy,
@@ -449,7 +383,7 @@ impl StyleModifiers{
static_friction:Planar64::int(2),
kinetic_friction:Planar64::int(3),//unrealistic: kinetic friction is typically lower than static
mass:Planar64::int(1),
mv:Planar64::int(3),
mv:Planar64::int(2),
rocket_force:None,
walk_speed:Planar64::int(16),
walk_accel:Planar64::int(80),
@@ -458,7 +392,7 @@ impl StyleModifiers{
ladder_dot:(Planar64::int(1)/2).sqrt(),
swim_speed:Planar64::int(12),
surf_slope:Some(Planar64::raw(7)/8),
hitbox:Hitbox::roblox(),
hitbox_halfsize:Planar64Vec3::int(2,5,2)/2,
camera_offset:Planar64Vec3::int(0,2,0),//4.5-2.5=2
}
}
@@ -487,7 +421,7 @@ impl StyleModifiers{
ladder_dot:(Planar64::int(1)/2).sqrt(),
swim_speed:Planar64::int(12),
surf_slope:Some(Planar64::raw(3787805118)),// normal.y=0.75
hitbox:Hitbox::roblox(),
hitbox_halfsize:Planar64Vec3::int(2,5,2)/2,
camera_offset:Planar64Vec3::int(0,2,0),//4.5-2.5=2
}
}
@@ -515,7 +449,7 @@ impl StyleModifiers{
ladder_dot:(Planar64::int(1)/2).sqrt(),
swim_speed:Planar64::int(12),
surf_slope:Some(Planar64::raw(3787805118)),// normal.y=0.75
hitbox:Hitbox::roblox(),
hitbox_halfsize:Planar64Vec3::int(2,5,2)/2,
camera_offset:Planar64Vec3::int(0,2,0),//4.5-2.5=2
}
}
@@ -526,7 +460,7 @@ impl StyleModifiers{
controls_mask:!0,//&!(Self::CONTROL_MOVEUP|Self::CONTROL_MOVEDOWN),
strafe:Some(StrafeSettings{
enable:EnableStrafe::Always,
air_accel_limit:Some(Planar64::raw(150<<28)*100),
air_accel_limit:Some(Planar64::raw(150<<28)*66),
tick_rate:Ratio64::new(100,Time::ONE_SECOND.nanos() as u64).unwrap(),
}),
jump_impulse:JumpImpulse::FromHeight(Planar64::raw(52<<28)),
@@ -544,7 +478,7 @@ impl StyleModifiers{
ladder_dot:(Planar64::int(1)/2).sqrt(),//?
swim_speed:Planar64::int(12),//?
surf_slope:Some(Planar64::raw(3787805118)),// normal.y=0.75
hitbox:Hitbox::source(),
hitbox_halfsize:Planar64Vec3::raw(33<<28,73<<28,33<<28)/2,
camera_offset:Planar64Vec3::raw(0,(64<<28)-(73<<27),0),
}
}
@@ -572,7 +506,7 @@ impl StyleModifiers{
ladder_dot:(Planar64::int(1)/2).sqrt(),//?
swim_speed:Planar64::int(12),//?
surf_slope:Some(Planar64::raw(3787805118)),// normal.y=0.75
hitbox:Hitbox::source(),
hitbox_halfsize:Planar64Vec3::raw(33<<28,73<<28,33<<28)/2,
camera_offset:Planar64Vec3::raw(0,(64<<28)-(73<<27),0),
}
}
@@ -596,7 +530,7 @@ impl StyleModifiers{
ladder_dot:(Planar64::int(1)/2).sqrt(),
swim_speed:Planar64::int(12),
surf_slope:Some(Planar64::raw(3787805118)),// normal.y=0.75
hitbox:Hitbox::roblox(),
hitbox_halfsize:Planar64Vec3::int(2,5,2)/2,
camera_offset:Planar64Vec3::int(0,2,0),//4.5-2.5=2
}
}
@@ -710,10 +644,6 @@ impl StyleModifiers{
let camera_mat=camera.simulate_move_rotation(camera.mouse.lerp(&next_mouse,time));
camera_mat*self.get_control_dir(controls)
}
#[inline]
fn mesh(&self)->TransformedMesh{
self.hitbox.transformed_mesh()
}
}
enum MoveState{
@@ -785,24 +715,24 @@ pub struct PhysicsModel{
attr_id:usize,
transform:crate::integer::Planar64Affine3,
normal_transform:crate::integer::Planar64Mat3,
transform_det:Planar64,
}
impl PhysicsModel{
pub fn new(mesh_id:usize,attr_id:usize,transform:crate::integer::Planar64Affine3)->Self{
let normal_transform=transform.matrix3.inverse().transpose();
Self{
mesh_id,
attr_id,
transform,
normal_transform:transform.matrix3.inverse_times_det().transpose(),
transform_det:transform.matrix3.determinant(),
normal_transform,
}
}
}
#[derive(Debug,Clone,Eq,Hash,PartialEq)]
struct ContactCollision{
face_id:crate::model_physics::MinkowskiFace,
//face_id:crate::model_physics::MinkowskiFace,
face_id:crate::model_physics::FaceId,
model_id:usize,//using id to avoid lifetimes
}
#[derive(Debug,Clone,Eq,Hash,PartialEq)]
@@ -821,7 +751,7 @@ impl Collision{
|&Collision::Intersect(IntersectCollision{model_id})=>model_id,
}
}
fn face_id(&self)->Option<crate::model_physics::MinkowskiFace>{
fn face_id(&self)->Option<crate::model_physics::FaceId>{
match self{
&Collision::Contact(ContactCollision{model_id:_,face_id})=>Some(face_id),
&Collision::Intersect(IntersectCollision{model_id:_})=>None,
@@ -881,20 +811,20 @@ impl TouchingState{
//add water../?
a
}
fn constrain_velocity(&self,models:&PhysicsModels,style_mesh:&TransformedMesh,velocity:&mut Planar64Vec3){
fn constrain_velocity(&self,models:&PhysicsModels,velocity:&mut Planar64Vec3){
//TODO: trey push solve
for contact in &self.contacts{
let n=contact_normal(models,style_mesh,contact);
let n=models.mesh(contact.model_id).face_nd(contact.face_id).0;
let d=n.dot128(*velocity);
if d<0{
*velocity-=n*Planar64::raw(((d<<32)/n.dot128(n)) as i64);
}
}
}
fn constrain_acceleration(&self,models:&PhysicsModels,style_mesh:&TransformedMesh,acceleration:&mut Planar64Vec3){
fn constrain_acceleration(&self,models:&PhysicsModels,acceleration:&mut Planar64Vec3){
//TODO: trey push solve
for contact in &self.contacts{
let n=contact_normal(models,style_mesh,contact);
let n=models.mesh(contact.model_id).face_nd(contact.face_id).0;
let d=n.dot128(*acceleration);
if d<0{
*acceleration-=n*Planar64::raw(((d<<32)/n.dot128(n)) as i64);
@@ -905,31 +835,30 @@ impl TouchingState{
//check current move conditions and use heuristics to determine
//which ladder to climb on, which ground to walk on, etc
//collect move state affecting objects from contacts (accelerator,water,ladder,ground)
let style_mesh=style.mesh();
let gravity=self.base_acceleration(models,style,camera,controls,next_mouse,time);
let mut move_state=MoveState::Air;
let mut a=gravity;
for contact in &self.contacts{
match models.attr(contact.model_id){
PhysicsCollisionAttributes::Contact{contacting,general}=>{
let normal=contact_normal(models,&style_mesh,contact);
let normal=models.mesh(contact.model_id).face_nd(contact.face_id).0;
match &contacting.contact_behaviour{
Some(crate::model::ContactingBehaviour::Ladder(_))=>{
//ladder walkstate
let mut target_velocity=style.get_ladder_target_velocity(camera,controls,next_mouse,time,&normal);
self.constrain_velocity(models,&style_mesh,&mut target_velocity);
self.constrain_velocity(models,&mut target_velocity);
let (walk_state,mut acceleration)=WalkState::ladder(body,style,gravity,target_velocity,contact.clone(),&normal);
move_state=MoveState::Ladder(walk_state);
self.constrain_acceleration(models,&style_mesh,&mut acceleration);
self.constrain_acceleration(models,&mut acceleration);
a=acceleration;
},
None=>if style.surf_slope.map_or(true,|s|normal.walkable(s,Planar64Vec3::Y)){
//check ground
let mut target_velocity=style.get_walk_target_velocity(camera,controls,next_mouse,time,&normal);
self.constrain_velocity(models,&style_mesh,&mut target_velocity);
self.constrain_velocity(models,&mut target_velocity);
let (walk_state,mut acceleration)=WalkState::ground(body,style,gravity,target_velocity,contact.clone(),&normal);
move_state=MoveState::Walk(walk_state);
self.constrain_acceleration(models,&style_mesh,&mut acceleration);
self.constrain_acceleration(models,&mut acceleration);
a=acceleration;
},
_=>(),
@@ -939,18 +868,15 @@ impl TouchingState{
}
}
for intersect in &self.intersects{
//water
//
}
self.constrain_acceleration(models,&style_mesh,&mut a);
self.constrain_acceleration(models,&mut a);
(move_state,a)
}
fn predict_collision_end(&self,collector:&mut crate::instruction::InstructionCollector<PhysicsInstruction>,models:&PhysicsModels,style_mesh:&TransformedMesh,body:&Body,time:Time){
let relative_body=VirtualBody::relative(&Body::default(),body).body(time);
fn predict_collision_end(&self,collector:&mut crate::instruction::InstructionCollector<PhysicsInstruction>,models:&PhysicsModels,body:&Body,time:Time){
for contact in &self.contacts{
//detect face slide off
let model_mesh=models.mesh(contact.model_id);
let minkowski=crate::model_physics::MinkowskiMesh::minkowski_sum(&model_mesh,&style_mesh);
collector.collect(minkowski.predict_collision_face_out(&relative_body,collector.time(),contact.face_id).map(|(face,time)|{
collector.collect(models.mesh(contact.model_id).brute_out_face(body,collector.time(),contact.face_id).map(|(face,time)|{
TimedInstruction{
time,
instruction:PhysicsInstruction::CollisionEnd(
@@ -959,11 +885,10 @@ impl TouchingState{
}
}));
}
let relative_body=VirtualBody::relative(&Body::default(),body).body(time);
for intersect in &self.intersects{
//detect model collision in reverse
let model_mesh=models.mesh(intersect.model_id);
let minkowski=crate::model_physics::MinkowskiMesh::minkowski_sum(&model_mesh,&style_mesh);
collector.collect(minkowski.predict_collision_out(&relative_body,collector.time()).map(|(face,time)|{
collector.collect(models.mesh(intersect.model_id).brute_out(&relative_body,collector.time()).map(|(face,time)|{
TimedInstruction{
time,
instruction:PhysicsInstruction::CollisionEnd(
@@ -997,42 +922,6 @@ impl Body{
self.velocity=self.extrapolated_velocity(time);
self.time=time;
}
pub fn infinity_dir(&self)->Option<Planar64Vec3>{
if self.velocity==Planar64Vec3::ZERO{
if self.acceleration==Planar64Vec3::ZERO{
None
}else{
Some(self.acceleration)
}
}else{
Some(self.velocity)
}
}
pub fn grow_aabb(&self,aabb:&mut crate::aabb::Aabb,t0:Time,t1:Time){
aabb.grow(self.extrapolated_position(t0));
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.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.z()!=Planar64::ZERO{
let t=Time::from(-self.velocity.z()/self.acceleration.z());
if t0<t&&t<t1{
aabb.grow(self.extrapolated_position(t));
}
}
}
}
impl std::fmt::Display for Body{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
@@ -1243,23 +1132,21 @@ impl PhysicsState {
match &mut self.move_state{
MoveState::Air|MoveState::Water=>self.touching.base_acceleration(&self.models,&self.style,&self.camera,self.controls,&self.next_mouse,self.time),
MoveState::Walk(WalkState{state,contact,jump_direction:_})=>{
let style_mesh=self.style.mesh();
let n=contact_normal(&self.models,&style_mesh,contact);
let n=self.models.mesh(contact.model_id).face_nd(contact.face_id).0;
let gravity=self.touching.base_acceleration(&self.models,&self.style,&self.camera,self.controls,&self.next_mouse,self.time);
let mut a;
let mut v=self.style.get_walk_target_velocity(&self.camera,self.controls,&self.next_mouse,self.time,&n);
self.touching.constrain_velocity(&self.models,&style_mesh,&mut v);
self.touching.constrain_velocity(&self.models,&mut v);
let normal_accel=-n.dot(gravity)/n.length();
(*state,a)=WalkEnum::with_target_velocity(&self.body,&self.style,v,&n,self.style.walk_speed,normal_accel);
a
},
MoveState::Ladder(WalkState{state,contact,jump_direction:_})=>{
let style_mesh=self.style.mesh();
let n=contact_normal(&self.models,&style_mesh,contact);
let n=self.models.mesh(contact.model_id).face_nd(contact.face_id).0;
let gravity=self.touching.base_acceleration(&self.models,&self.style,&self.camera,self.controls,&self.next_mouse,self.time);
let mut a;
let mut v=self.style.get_ladder_target_velocity(&self.camera,self.controls,&self.next_mouse,self.time,&n);
self.touching.constrain_velocity(&self.models,&style_mesh,&mut v);
self.touching.constrain_velocity(&self.models,&mut v);
(*state,a)=WalkEnum::with_target_velocity(&self.body,&self.style,v,&n,self.style.ladder_speed,self.style.ladder_accel);
a
},
@@ -1289,23 +1176,20 @@ impl crate::instruction::InstructionEmitter<PhysicsInstruction> for PhysicsState
collector.collect(self.next_move_instruction());
let style_mesh=self.style.mesh();
//check for collision ends
self.touching.predict_collision_end(&mut collector,&self.models,&style_mesh,&self.body,self.time);
self.touching.predict_collision_end(&mut collector,&self.models,&self.body,self.time);
//check for collision starts
let mut aabb=crate::aabb::Aabb::default();
self.body.grow_aabb(&mut aabb,self.time,collector.time());
aabb.inflate(self.style.hitbox.halfsize);
aabb.grow(self.body.extrapolated_position(self.time));
aabb.grow(self.body.extrapolated_position(collector.time()));
aabb.inflate(self.style.hitbox_halfsize);
//common body
let relative_body=VirtualBody::relative(&Body::default(),&self.body).body(self.time);
self.bvh.the_tester(&aabb,&mut |id|{
//no checks are needed because of the time limits.
let model_mesh=self.models.mesh(id);
let minkowski=crate::model_physics::MinkowskiMesh::minkowski_sum(&model_mesh,&style_mesh);
collector.collect(minkowski.predict_collision_in(&relative_body,collector.time())
//temp (?) code to avoid collision loops
.map_or(None,|(face,time)|if time==self.time{None}else{Some((face,time))})
.map(|(face,time)|{
//let minkowski=crate::model_physics::MinkowskiMesh::minkowski_sum(self.style.mesh,&self.models.mesh(id));
//collector.collect(crate::face_crawler::predict_collision(&minkowski,&relative_body,collector.time()).map(|(face,time)|{
collector.collect(self.models.mesh(id).brute_in(&relative_body,collector.time()).map(|(face,time)|{
TimedInstruction{time,instruction:PhysicsInstruction::CollisionStart(match self.models.attr(id){
PhysicsCollisionAttributes::Contact{contacting:_,general:_}=>Collision::Contact(ContactCollision{model_id:id,face_id:face}),
PhysicsCollisionAttributes::Intersect{intersecting:_,general:_}=>Collision::Intersect(IntersectCollision{model_id:id}),
@@ -1325,18 +1209,12 @@ fn get_walk_state(move_state:&MoveState)->Option<&WalkState>{
fn jumped_velocity(models:&PhysicsModels,style:&StyleModifiers,walk_state:&WalkState,v:&mut Planar64Vec3){
let jump_dir=match &walk_state.jump_direction{
JumpDirection::FromContactNormal=>contact_normal(models,&style.mesh(),&walk_state.contact),
JumpDirection::FromContactNormal=>models.mesh(walk_state.contact.model_id).face_nd(walk_state.contact.face_id).0,
&JumpDirection::Exactly(dir)=>dir,
};
*v=*v+jump_dir*(style.get_jump_deltav()/jump_dir.length());
}
fn contact_normal(models:&PhysicsModels,style_mesh:&TransformedMesh,contact:&ContactCollision)->Planar64Vec3{
let model_mesh=models.mesh(contact.model_id);
let minkowski=crate::model_physics::MinkowskiMesh::minkowski_sum(&model_mesh,style_mesh);
minkowski.face_nd(contact.face_id).0
}
fn set_position(body:&mut Body,touching:&mut TouchingState,point:Planar64Vec3)->Planar64Vec3{
//test intersections at new position
//hovering above the surface 0 units is not intersecting. you will fall into it just fine
@@ -1347,11 +1225,11 @@ fn set_position(body:&mut Body,touching:&mut TouchingState,point:Planar64Vec3)->
//touching.recalculate(body);
point
}
fn set_velocity_cull(body:&mut Body,touching:&mut TouchingState,models:&PhysicsModels,style_mesh:&TransformedMesh,v:Planar64Vec3)->bool{
fn set_velocity_cull(body:&mut Body,touching:&mut TouchingState,models:&PhysicsModels,v:Planar64Vec3)->bool{
//This is not correct but is better than what I have
let mut culled=false;
touching.contacts.retain(|contact|{
let n=contact_normal(models,style_mesh,contact);
let n=models.mesh(contact.model_id).face_nd(contact.face_id).0;
let r=n.dot(v)<=Planar64::ZERO;
if !r{
culled=true;
@@ -1359,19 +1237,19 @@ fn set_velocity_cull(body:&mut Body,touching:&mut TouchingState,models:&PhysicsM
}
r
});
set_velocity(body,touching,models,style_mesh,v);
set_velocity(body,touching,models,v);
culled
}
fn set_velocity(body:&mut Body,touching:&TouchingState,models:&PhysicsModels,style_mesh:&TransformedMesh,mut v:Planar64Vec3)->Planar64Vec3{
touching.constrain_velocity(models,style_mesh,&mut v);
fn set_velocity(body:&mut Body,touching:&TouchingState,models:&PhysicsModels,mut v:Planar64Vec3)->Planar64Vec3{
touching.constrain_velocity(&models,&mut v);
body.velocity=v;
v
}
fn set_acceleration_cull(body:&mut Body,touching:&mut TouchingState,models:&PhysicsModels,style_mesh:&TransformedMesh,a:Planar64Vec3)->bool{
fn set_acceleration_cull(body:&mut Body,touching:&mut TouchingState,models:&PhysicsModels,a:Planar64Vec3)->bool{
//This is not correct but is better than what I have
let mut culled=false;
touching.contacts.retain(|contact|{
let n=contact_normal(models,style_mesh,contact);
let n=models.mesh(contact.model_id).face_nd(contact.face_id).0;
let r=n.dot(a)<=Planar64::ZERO;
if !r{
culled=true;
@@ -1379,23 +1257,23 @@ fn set_acceleration_cull(body:&mut Body,touching:&mut TouchingState,models:&Phys
}
r
});
set_acceleration(body,touching,models,style_mesh,a);
set_acceleration(body,touching,models,a);
culled
}
fn set_acceleration(body:&mut Body,touching:&TouchingState,models:&PhysicsModels,style_mesh:&TransformedMesh,mut a:Planar64Vec3)->Planar64Vec3{
touching.constrain_acceleration(models,style_mesh,&mut a);
fn set_acceleration(body:&mut Body,touching:&TouchingState,models:&PhysicsModels,mut a:Planar64Vec3)->Planar64Vec3{
touching.constrain_acceleration(&models,&mut a);
body.acceleration=a;
a
}
fn teleport(body:&mut Body,touching:&mut TouchingState,models:&PhysicsModels,style:&StyleModifiers,point:Planar64Vec3)->MoveState{
set_position(body,touching,point);
set_acceleration(body,touching,models,&style.mesh(),style.gravity);
set_acceleration(body,touching,models,style.gravity);
MoveState::Air
}
fn teleport_to_spawn(body:&mut Body,touching:&mut TouchingState,style:&StyleModifiers,mode:&crate::model::ModeDescription,models:&PhysicsModels,stage_id:u32)->Option<MoveState>{
let model=models.model(*mode.get_spawn_model_id(stage_id)? as usize);
let point=model.transform.transform_point3(Planar64Vec3::Y)+Planar64Vec3::Y*(style.hitbox.halfsize.y()+Planar64::ONE/16);
let point=model.transform.transform_point3(Planar64Vec3::Y)+Planar64Vec3::Y*(style.hitbox_halfsize.y()+Planar64::ONE/16);
Some(teleport(body,touching,models,style,point))
}
@@ -1484,42 +1362,41 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
}
match ins.instruction{
PhysicsInstruction::CollisionStart(c)=>{
let style_mesh=self.style.mesh();
let model_id=c.model_id();
match (self.models.attr(model_id),&c){
(PhysicsCollisionAttributes::Contact{contacting,general},Collision::Contact(contact))=>{
let mut v=self.body.velocity;
let normal=contact_normal(&self.models,&style_mesh,contact);
let normal=self.models.mesh(model_id).face_nd(contact.face_id).0;
match &contacting.contact_behaviour{
Some(crate::model::ContactingBehaviour::Surf)=>println!("I'm surfing!"),
Some(crate::model::ContactingBehaviour::Cling)=>println!("Unimplemented!"),
&Some(crate::model::ContactingBehaviour::Elastic(elasticity))=>{
let n=self.models.mesh(contact.model_id).face_nd(contact.face_id).0;
//velocity and normal are facing opposite directions so this is inherently negative.
let d=normal.dot(v)*(Planar64::ONE+Planar64::raw(elasticity as i64+1));
v+=normal*(d/normal.dot(normal));
let d=n.dot(v)*(Planar64::ONE+Planar64::raw(elasticity as i64+1));
v+=n*(d/n.dot(n));
},
Some(crate::model::ContactingBehaviour::Ladder(contacting_ladder))=>{
if contacting_ladder.sticky{
//kill v
//actually you could do this with a booster attribute :thinking:
v=Planar64Vec3::ZERO;//model.velocity
}
//ladder walkstate
let gravity=self.touching.base_acceleration(&self.models,&self.style,&self.camera,self.controls,&self.next_mouse,self.time);
let mut target_velocity=self.style.get_ladder_target_velocity(&self.camera,self.controls,&self.next_mouse,self.time,&normal);
self.touching.constrain_velocity(&self.models,&style_mesh,&mut target_velocity);
self.touching.constrain_velocity(&self.models,&mut target_velocity);
let (walk_state,a)=WalkState::ladder(&self.body,&self.style,gravity,target_velocity,contact.clone(),&normal);
self.move_state=MoveState::Ladder(walk_state);
set_acceleration(&mut self.body,&self.touching,&self.models,&style_mesh,a);
set_acceleration(&mut self.body,&self.touching,&self.models,a);
}
None=>if self.style.surf_slope.map_or(true,|s|contact_normal(&self.models,&style_mesh,contact).walkable(s,Planar64Vec3::Y)){
None=>if self.style.surf_slope.map_or(true,|s|self.models.mesh(model_id).face_nd(contact.face_id).0.walkable(s,Planar64Vec3::Y)){
//ground
let gravity=self.touching.base_acceleration(&self.models,&self.style,&self.camera,self.controls,&self.next_mouse,self.time);
let mut target_velocity=self.style.get_walk_target_velocity(&self.camera,self.controls,&self.next_mouse,self.time,&normal);
self.touching.constrain_velocity(&self.models,&style_mesh,&mut target_velocity);
self.touching.constrain_velocity(&self.models,&mut target_velocity);
let (walk_state,a)=WalkState::ground(&self.body,&self.style,gravity,target_velocity,contact.clone(),&normal);
self.move_state=MoveState::Walk(walk_state);
set_acceleration(&mut self.body,&self.touching,&self.models,&style_mesh,a);
set_acceleration(&mut self.body,&self.touching,&self.models,a);
},
}
//check ground
@@ -1527,7 +1404,7 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
//I love making functions with 10 arguments to dodge the borrow checker
run_teleport_behaviour(&general.teleport_behaviour,&mut self.game,&self.models,&self.modes,&self.style,&mut self.touching,&mut self.body,model_id);
//flatten v
self.touching.constrain_velocity(&self.models,&style_mesh,&mut v);
self.touching.constrain_velocity(&self.models,&mut v);
match &general.booster{
Some(booster)=>{
//DELETE THIS when boosters get converted to height machines
@@ -1542,7 +1419,7 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
let calc_move=if self.style.get_control(StyleModifiers::CONTROL_JUMP,self.controls){
if let Some(walk_state)=get_walk_state(&self.move_state){
jumped_velocity(&self.models,&self.style,walk_state,&mut v);
set_velocity_cull(&mut self.body,&mut self.touching,&self.models,&style_mesh,v)
set_velocity_cull(&mut self.body,&mut self.touching,&self.models,v)
}else{false}
}else{false};
match &general.trajectory{
@@ -1558,13 +1435,13 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
},
None=>(),
}
set_velocity(&mut self.body,&self.touching,&self.models,&style_mesh,v);
set_velocity(&mut self.body,&self.touching,&self.models,v);
//not sure if or is correct here
if calc_move||Planar64::ZERO<normal.dot(v){
(self.move_state,self.body.acceleration)=self.touching.get_move_state(&self.body,&self.models,&self.style,&self.camera,self.controls,&self.next_mouse,self.time);
}
let a=self.refresh_walk_target();
set_acceleration(&mut self.body,&self.touching,&self.models,&self.style.mesh(),a);
set_acceleration(&mut self.body,&self.touching,&self.models,a);
},
(PhysicsCollisionAttributes::Intersect{intersecting: _,general},Collision::Intersect(intersect))=>{
//I think that setting the velocity to 0 was preventing surface contacts from entering an infinite loop
@@ -1597,7 +1474,7 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
let v=self.body.velocity+control_dir*(self.style.mv-d);
//this is wrong but will work ig
//need to note which push planes activate in push solve and keep those
if set_velocity_cull(&mut self.body,&mut self.touching,&self.models,&self.style.mesh(),v){
if set_velocity_cull(&mut self.body,&mut self.touching,&self.models,v){
(self.move_state,self.body.acceleration)=self.touching.get_move_state(&self.body,&self.models,&self.style,&self.camera,self.controls,&self.next_mouse,self.time);
}
}
@@ -1610,12 +1487,11 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
match &mut walk_state.state{
WalkEnum::Reached=>(),
WalkEnum::Transient(walk_target)=>{
let style_mesh=self.style.mesh();
//precisely set velocity
let a=Planar64Vec3::ZERO;//ignore gravity for now.
set_acceleration(&mut self.body,&self.touching,&self.models,&style_mesh,a);
set_acceleration(&mut self.body,&self.touching,&self.models,a);
let v=walk_target.velocity;
set_velocity(&mut self.body,&self.touching,&self.models,&style_mesh,v);
set_velocity(&mut self.body,&self.touching,&self.models,v);
walk_state.state=WalkEnum::Reached;
},
}
@@ -1644,7 +1520,7 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
if let Some(walk_state)=get_walk_state(&self.move_state){
let mut v=self.body.velocity;
jumped_velocity(&self.models,&self.style,walk_state,&mut v);
if set_velocity_cull(&mut self.body,&mut self.touching,&self.models,&self.style.mesh(),v){
if set_velocity_cull(&mut self.body,&mut self.touching,&self.models,v){
(self.move_state,self.body.acceleration)=self.touching.get_move_state(&self.body,&self.models,&self.style,&self.camera,self.controls,&self.next_mouse,self.time);
}
}
@@ -1657,7 +1533,7 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
PhysicsInputInstruction::Reset => {
//it matters which of these runs first, but I have not thought it through yet as it doesn't matter yet
set_position(&mut self.body,&mut self.touching,self.spawn_point);
set_velocity(&mut self.body,&self.touching,&self.models,&self.style.mesh(),Planar64Vec3::ZERO);
set_velocity(&mut self.body,&self.touching,&self.models,Planar64Vec3::ZERO);
(self.move_state,self.body.acceleration)=self.touching.get_move_state(&self.body,&self.models,&self.style,&self.camera,self.controls,&self.next_mouse,self.time);
refresh_walk_target=false;
},
@@ -1665,7 +1541,7 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
}
if refresh_walk_target{
let a=self.refresh_walk_target();
if set_acceleration_cull(&mut self.body,&mut self.touching,&self.models,&self.style.mesh(),a){
if set_acceleration_cull(&mut self.body,&mut self.touching,&self.models,a){
(self.move_state,self.body.acceleration)=self.touching.get_move_state(&self.body,&self.models,&self.style,&self.camera,self.controls,&self.next_mouse,self.time);
}
}
@@ -1673,218 +1549,3 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
}
}
}
#[allow(dead_code)]
fn test_collision_axis_aligned(relative_body:Body,expected_collision_time:Option<Time>){
let h0=Hitbox::from_mesh_scale(PhysicsMesh::from(&crate::primitives::unit_cube()),Planar64Vec3::int(5,1,5)/2);
let h1=Hitbox::roblox();
let hitbox_mesh=h1.transformed_mesh();
let platform_mesh=h0.transformed_mesh();
let minkowski=crate::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");
}
#[allow(dead_code)]
fn test_collision_rotated(relative_body:Body,expected_collision_time:Option<Time>){
let h0=Hitbox::new(PhysicsMesh::from(&crate::primitives::unit_cube()),
crate::integer::Planar64Affine3::new(
crate::integer::Planar64Mat3::from_cols(
Planar64Vec3::int(5,0,1)/2,
Planar64Vec3::int(0,1,0)/2,
Planar64Vec3::int(-1,0,5)/2,
),
Planar64Vec3::ZERO,
)
);
let h1=Hitbox::roblox();
let hitbox_mesh=h1.transformed_mesh();
let platform_mesh=h0.transformed_mesh();
let minkowski=crate::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");
}
#[allow(dead_code)]
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(){
test_collision(Body::new(
Planar64Vec3::int(0,5,0),
Planar64Vec3::int(0,-1,0),
Planar64Vec3::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,
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,
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,
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,
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),
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),
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),
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),
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),
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),
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),
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),
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),
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),
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),
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),
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,
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),
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),
Time::ZERO
),None);
}

View File

@@ -175,8 +175,8 @@ impl<'a,Task:Send+'a> INWorker<'a,Task>{
#[test]//How to run this test with printing: cargo test --release -- --nocapture
fn test_worker() {
println!("hiiiii");
// Create the worker thread
let test_body=crate::physics::Body::new(crate::integer::Planar64Vec3::ONE,crate::integer::Planar64Vec3::ONE,crate::integer::Planar64Vec3::ONE,crate::integer::Time::ZERO);
let worker=QRWorker::new(crate::physics::Body::default(),
|_|crate::physics::Body::new(crate::integer::Planar64Vec3::ONE,crate::integer::Planar64Vec3::ONE,crate::integer::Planar64Vec3::ONE,crate::integer::Time::ZERO)
);
@@ -203,7 +203,7 @@ fn test_worker() {
};
worker.send(task).unwrap();
//assert_eq!(test_body,worker.grab_clone());
println!("value={}",worker.grab_clone());
// wait long enough to see print from final task
thread::sleep(std::time::Duration::from_millis(10));

View File

@@ -9,7 +9,6 @@ pub fn zeroes2(a0:Planar64,a1:Planar64,a2:Planar64) -> Vec<Planar64>{
let radicand=a1.get() as i128*a1.get() as i128-a2.get() as i128*a0.get() as i128*4;
if 0<radicand {
//start with f64 sqrt
//failure case: 2^63 < sqrt(2^127)
let planar_radicand=Planar64::raw(unsafe{(radicand as f64).sqrt().to_int_unchecked()});
//TODO: one or two newtons
//sort roots ascending and avoid taking the difference of large numbers
@@ -29,12 +28,7 @@ pub fn zeroes2(a0:Planar64,a1:Planar64,a2:Planar64) -> Vec<Planar64>{
pub fn zeroes1(a0:Planar64,a1:Planar64) -> Vec<Planar64> {
if a1==Planar64::ZERO{
return vec![];
}else{
let q=((-a0.get() as i128)<<32)/(a1.get() as i128);
if i64::MIN as i128<=q&&q<=i64::MAX as i128{
return vec![Planar64::raw(q as i64)];
}else{
return vec![];
}
} else {
return vec![-a0/a1];
}
}