Compare commits

...

22 Commits

Author SHA1 Message Date
05999f73f4 failed attempt to bypass bug (edge_faces returning two equivalent faces? [FaceVert,EdgeEdge]) 2025-03-19 16:34:53 -07:00
dcdf57b389 wrong parity (?) 2025-03-19 16:34:53 -07:00
b8a0175686 physics: flush face transfer 2025-03-19 16:34:53 -07:00
2a5f610650 physics: predict_collision_face_out returns directed edge 2025-03-19 16:34:53 -07:00
02615af1fd common: integer: angle constructor 2025-03-19 16:34:42 -07:00
6594ffa87e common: integer: more zero constants 2025-03-19 16:34:38 -07:00
6295dc68ef physics: fix lint 2025-03-19 12:52:54 -07:00
8fb0d30e1d graphics: move get_instances_buffer_data away from the cool render code 2025-03-14 15:38:35 -07:00
0fb0230cb1 tools: create dev profile 2025-03-14 15:24:47 -07:00
bdb1090664 physics: implement precise width conversion 2025-03-13 17:06:23 -07:00
3ce7746489 snf: implement precise width conversion 2025-03-13 17:06:23 -07:00
7978050f8a rbx_loader: implement precise width conversion 2025-03-13 17:06:23 -07:00
77ee36fa72 bsp_loader: implement precise width conversion 2025-03-13 17:06:23 -07:00
0fc1ec3086 common: implement precise width conversion 2025-03-13 16:57:36 -07:00
f60c03ac56 common: update deps 2025-03-13 16:57:36 -07:00
b570d8809d linear_ops: v0.1.1 unwrap vector of results 2025-03-13 16:57:36 -07:00
3f268ec034 linear_ops: allow unwraping vector of results 2025-03-13 16:57:36 -07:00
ba161ef6d1 fixed_wide: v0.2.0 precise width conversion 2025-03-13 16:57:36 -07:00
0755d4413c fixed_wide: expand upon width conversion 2025-03-13 16:13:03 -07:00
459cb13957 physics: monomorphise MeshQuery over AsRef 2025-03-12 19:34:45 -07:00
a5ad7d4d6e it: cli 2025-03-12 17:23:21 -07:00
3d5e76f078 bsp_loader: fix utf8 brush model 2025-03-11 18:13:39 -07:00
25 changed files with 469 additions and 309 deletions

4
Cargo.lock generated
View File

@@ -911,7 +911,7 @@ checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da"
[[package]] [[package]]
name = "fixed_wide" name = "fixed_wide"
version = "0.1.2" version = "0.2.0"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
"bnum", "bnum",
@@ -1860,7 +1860,7 @@ dependencies = [
[[package]] [[package]]
name = "linear_ops" name = "linear_ops"
version = "0.1.0" version = "0.1.1"
dependencies = [ dependencies = [
"fixed_wide", "fixed_wide",
"paste", "paste",

View File

@@ -24,3 +24,7 @@ resolver = "2"
#lto = true #lto = true
strip = true strip = true
codegen-units = 1 codegen-units = 1
[profile.dev]
strip = false
opt-level = 3

View File

@@ -103,6 +103,26 @@ impl std::default::Default for GraphicsCamera{
} }
} }
const MODEL_BUFFER_SIZE:usize=4*4 + 12 + 4;//let size=std::mem::size_of::<ModelInstance>();
const MODEL_BUFFER_SIZE_BYTES:usize=MODEL_BUFFER_SIZE*4;
fn get_instances_buffer_data(instances:&[GraphicsModelOwned])->Vec<f32>{
let mut raw=Vec::with_capacity(MODEL_BUFFER_SIZE*instances.len());
for mi in instances{
//model transform
raw.extend_from_slice(&AsRef::<[f32; 4*4]>::as_ref(&mi.transform)[..]);
//normal transform
raw.extend_from_slice(AsRef::<[f32; 3]>::as_ref(&mi.normal_transform.x_axis));
raw.extend_from_slice(&[0.0]);
raw.extend_from_slice(AsRef::<[f32; 3]>::as_ref(&mi.normal_transform.y_axis));
raw.extend_from_slice(&[0.0]);
raw.extend_from_slice(AsRef::<[f32; 3]>::as_ref(&mi.normal_transform.z_axis));
raw.extend_from_slice(&[0.0]);
//color
raw.extend_from_slice(AsRef::<[f32; 4]>::as_ref(&mi.color.get()));
}
raw
}
pub struct GraphicsState{ pub struct GraphicsState{
pipelines:GraphicsPipelines, pipelines:GraphicsPipelines,
bind_groups:GraphicsBindGroups, bind_groups:GraphicsBindGroups,
@@ -967,22 +987,3 @@ impl GraphicsState{
self.staging_belt.recall(); self.staging_belt.recall();
} }
} }
const MODEL_BUFFER_SIZE:usize=4*4 + 12 + 4;//let size=std::mem::size_of::<ModelInstance>();
const MODEL_BUFFER_SIZE_BYTES:usize=MODEL_BUFFER_SIZE*4;
fn get_instances_buffer_data(instances:&[GraphicsModelOwned])->Vec<f32>{
let mut raw=Vec::with_capacity(MODEL_BUFFER_SIZE*instances.len());
for mi in instances{
//model transform
raw.extend_from_slice(&AsRef::<[f32; 4*4]>::as_ref(&mi.transform)[..]);
//normal transform
raw.extend_from_slice(AsRef::<[f32; 3]>::as_ref(&mi.normal_transform.x_axis));
raw.extend_from_slice(&[0.0]);
raw.extend_from_slice(AsRef::<[f32; 3]>::as_ref(&mi.normal_transform.y_axis));
raw.extend_from_slice(&[0.0]);
raw.extend_from_slice(AsRef::<[f32; 3]>::as_ref(&mi.normal_transform.z_axis));
raw.extend_from_slice(&[0.0]);
//color
raw.extend_from_slice(AsRef::<[f32; 4]>::as_ref(&mi.color.get()));
}
raw
}

View File

@@ -42,12 +42,12 @@ impl<T> Body<T>
pub fn extrapolated_position(&self,time:Time<T>)->Planar64Vec3{ pub fn extrapolated_position(&self,time:Time<T>)->Planar64Vec3{
let dt=time-self.time; let dt=time-self.time;
self.position self.position
+(self.velocity*dt).map(|elem|elem.divide().fix_1()) +(self.velocity*dt).map(|elem|elem.divide().clamp_1())
+self.acceleration.map(|elem|(dt*dt*elem/2).divide().fix_1()) +self.acceleration.map(|elem|(dt*dt*elem/2).divide().clamp_1())
} }
pub fn extrapolated_velocity(&self,time:Time<T>)->Planar64Vec3{ pub fn extrapolated_velocity(&self,time:Time<T>)->Planar64Vec3{
let dt=time-self.time; let dt=time-self.time;
self.velocity+(self.acceleration*dt).map(|elem|elem.divide().fix_1()) self.velocity+(self.acceleration*dt).map(|elem|elem.divide().clamp_1())
} }
pub fn advance_time(&mut self,time:Time<T>){ pub fn advance_time(&mut self,time:Time<T>){
self.position=self.extrapolated_position(time); self.position=self.extrapolated_position(time);
@@ -71,12 +71,12 @@ impl<T> Body<T>
D2:Copy, D2:Copy,
Planar64:core::ops::Mul<D2,Output=N4>, Planar64:core::ops::Mul<D2,Output=N4>,
N4:integer::Divide<D2,Output=T1>, N4:integer::Divide<D2,Output=T1>,
T1:integer::Fix<Planar64>, T1:integer::Clamp<Planar64>,
{ {
// a*dt^2/2 + v*dt + p // a*dt^2/2 + v*dt + p
// (a*dt/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)) (self.acceleration.map(|elem|dt*elem/2)+self.velocity).map(|elem|dt.mul_ratio(elem))
.map(|elem|elem.divide().fix())+self.position .map(|elem|elem.divide().clamp())+self.position
} }
pub fn extrapolated_velocity_ratio_dt<Num,Den,N1,T1>(&self,dt:integer::Ratio<Num,Den>)->Planar64Vec3 pub fn extrapolated_velocity_ratio_dt<Num,Den,N1,T1>(&self,dt:integer::Ratio<Num,Den>)->Planar64Vec3
where where
@@ -85,10 +85,10 @@ impl<T> Body<T>
Num:core::ops::Mul<Planar64,Output=N1>, Num:core::ops::Mul<Planar64,Output=N1>,
Planar64:core::ops::Mul<Den,Output=N1>, Planar64:core::ops::Mul<Den,Output=N1>,
N1:integer::Divide<Den,Output=T1>, N1:integer::Divide<Den,Output=T1>,
T1:integer::Fix<Planar64>, T1:integer::Clamp<Planar64>,
{ {
// a*dt + v // a*dt + v
self.acceleration.map(|elem|(dt*elem).divide().fix())+self.velocity self.acceleration.map(|elem|(dt*elem).divide().clamp())+self.velocity
} }
pub fn advance_time_ratio_dt(&mut self,dt:crate::model::GigaTime){ pub fn advance_time_ratio_dt(&mut self,dt:crate::model::GigaTime){
self.position=self.extrapolated_position_ratio_dt(dt); self.position=self.extrapolated_position_ratio_dt(dt);

View File

@@ -57,13 +57,14 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
} }
} }
//test each edge collision time, ignoring roots with zero or conflicting derivative //test each edge collision time, ignoring roots with zero or conflicting derivative
for &directed_edge_id in mesh.face_edges(face_id).iter(){ for &directed_edge_id in mesh.face_edges(face_id).as_ref(){
let edge_n=mesh.directed_edge_n(directed_edge_id); let edge_n=mesh.directed_edge_n(directed_edge_id);
let n=n.cross(edge_n); let n=n.cross(edge_n);
let verts=mesh.edge_verts(directed_edge_id.as_undirected()); let &[v0,v1]=mesh.edge_verts(directed_edge_id.as_undirected()).as_ref();
//WARNING: d is moved out of the *2 block because of adding two vertices! //WARNING: d is moved out of the *2 block because of adding two vertices!
//WARNING: precision is swept under the rug! //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()){ //wrap for speed
for dt in Fixed::<4,128>::zeroes2(n.dot(body.position*2-(mesh.vert(v0)+mesh.vert(v1))).wrap_4(),n.dot(body.velocity).wrap_4()*2,n.dot(body.acceleration).wrap_4()){
if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){ 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_time=dt;
best_transition=Transition::Next(FEV::Edge(directed_edge_id.as_undirected()),dt); best_transition=Transition::Next(FEV::Edge(directed_edge_id.as_undirected()),dt);
@@ -77,13 +78,15 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
//test each face collision time, ignoring roots with zero or conflicting derivative //test each face collision time, ignoring roots with zero or conflicting derivative
let edge_n=mesh.edge_n(edge_id); let edge_n=mesh.edge_n(edge_id);
let edge_verts=mesh.edge_verts(edge_id); let edge_verts=mesh.edge_verts(edge_id);
let delta_pos=body.position*2-(mesh.vert(edge_verts[0])+mesh.vert(edge_verts[1])); let &[ev0,ev1]=edge_verts.as_ref();
for (i,&edge_face_id) in mesh.edge_faces(edge_id).iter().enumerate(){ let delta_pos=body.position*2-(mesh.vert(ev0)+mesh.vert(ev1));
for (i,&edge_face_id) in mesh.edge_faces(edge_id).as_ref().iter().enumerate(){
let face_n=mesh.face_nd(edge_face_id).0; let face_n=mesh.face_nd(edge_face_id).0;
//edge_n gets parity from the order of edge_faces //edge_n gets parity from the order of edge_faces
let n=face_n.cross(edge_n)*((i as i64)*2-1); let n=face_n.cross(edge_n)*((i as i64)*2-1);
//WARNING yada yada d *2 //WARNING yada yada d *2
for dt in Fixed::<4,128>::zeroes2(n.dot(delta_pos).fix_4(),n.dot(body.velocity).fix_4()*2,n.dot(body.acceleration).fix_4()){ //wrap for speed
for dt in Fixed::<4,128>::zeroes2(n.dot(delta_pos).wrap_4(),n.dot(body.velocity).wrap_4()*2,n.dot(body.acceleration).wrap_4()){
if body_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){ 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_time=dt;
best_transition=Transition::Next(FEV::Face(edge_face_id),dt); best_transition=Transition::Next(FEV::Face(edge_face_id),dt);
@@ -92,12 +95,12 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
} }
} }
//test each vertex collision time, ignoring roots with zero or conflicting derivative //test each vertex collision time, ignoring roots with zero or conflicting derivative
for (i,&vert_id) in edge_verts.iter().enumerate(){ for (i,&vert_id) in edge_verts.as_ref().iter().enumerate(){
//vertex normal gets parity from vert index //vertex normal gets parity from vert index
let n=edge_n*(1-2*(i as i64)); let n=edge_n*(1-2*(i as i64));
for dt in Fixed::<2,64>::zeroes2((n.dot(body.position-mesh.vert(vert_id)))*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){ for 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(){ 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()); let dt=Ratio::new(dt.num.widen_4(),dt.den.widen_4());
best_time=dt; best_time=dt;
best_transition=Transition::Next(FEV::Vert(vert_id),dt); best_transition=Transition::Next(FEV::Vert(vert_id),dt);
break; break;
@@ -108,12 +111,12 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
}, },
&FEV::Vert(vert_id)=>{ &FEV::Vert(vert_id)=>{
//test each edge collision time, ignoring roots with zero or conflicting derivative //test each edge collision time, ignoring roots with zero or conflicting derivative
for &directed_edge_id in mesh.vert_edges(vert_id).iter(){ for &directed_edge_id in mesh.vert_edges(vert_id).as_ref(){
//edge is directed away from vertex, but we want the dot product to turn out negative //edge is directed away from vertex, but we want the dot product to turn out negative
let n=-mesh.directed_edge_n(directed_edge_id); let n=-mesh.directed_edge_n(directed_edge_id);
for dt in Fixed::<2,64>::zeroes2((n.dot(body.position-mesh.vert(vert_id)))*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){ for 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(){ 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()); let dt=Ratio::new(dt.num.widen_4(),dt.den.widen_4());
best_time=dt; best_time=dt;
best_transition=Transition::Next(FEV::Edge(directed_edge_id.as_undirected()),dt); best_transition=Transition::Next(FEV::Edge(directed_edge_id.as_undirected()),dt);
break; break;
@@ -128,11 +131,11 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
pub fn crawl(mut self,mesh:&M,relative_body:&Body,start_time:Time,time_limit:Time)->CrawlResult<M>{ pub fn crawl(mut self,mesh:&M,relative_body:&Body,start_time:Time,time_limit:Time)->CrawlResult<M>{
let mut body_time={ let mut body_time={
let r=(start_time-relative_body.time).to_ratio(); let r=(start_time-relative_body.time).to_ratio();
Ratio::new(r.num.fix_4(),r.den.fix_4()) Ratio::new(r.num.widen_4(),r.den.widen_4())
}; };
let time_limit={ let time_limit={
let r=(time_limit-relative_body.time).to_ratio(); let r=(time_limit-relative_body.time).to_ratio();
Ratio::new(r.num.fix_4(),r.den.fix_4()) Ratio::new(r.num.widen_4(),r.den.widen_4())
}; };
for _ in 0..20{ for _ in 0..20{
match self.next_transition(body_time,mesh,relative_body,time_limit){ match self.next_transition(body_time,mesh,relative_body,time_limit){

View File

@@ -1,4 +1,3 @@
use std::borrow::{Borrow,Cow};
use std::collections::{HashSet,HashMap}; use std::collections::{HashSet,HashMap};
use core::ops::Range; use core::ops::Range;
use strafesnet_common::integer::vec3::Vector3; use strafesnet_common::integer::vec3::Vector3;
@@ -8,6 +7,13 @@ use strafesnet_common::physics::Time;
type Body=crate::body::Body<strafesnet_common::physics::TimeInner>; type Body=crate::body::Body<strafesnet_common::physics::TimeInner>;
struct AsRefHelper<T>(T);
impl<T> AsRef<T> for AsRefHelper<T>{
fn as_ref(&self)->&T{
&self.0
}
}
pub trait UndirectedEdge{ pub trait UndirectedEdge{
type DirectedEdge:Copy+DirectedEdge; type DirectedEdge:Copy+DirectedEdge;
fn as_directed(&self,parity:bool)->Self::DirectedEdge; fn as_directed(&self,parity:bool)->Self::DirectedEdge;
@@ -69,27 +75,27 @@ struct Face{
} }
struct Vert(Planar64Vec3); struct Vert(Planar64Vec3);
pub trait MeshQuery{ pub trait MeshQuery{
type Face:Clone; type Face:Copy;
type Edge:Clone+DirectedEdge; type Edge:Copy+DirectedEdge;
type Vert:Clone; type Vert:Copy;
// Vertex must be Planar64Vec3 because it represents an actual position // Vertex must be Planar64Vec3 because it represents an actual position
type Normal; type Normal;
type Offset; type Offset;
fn edge_n(&self,edge_id:<Self::Edge as DirectedEdge>::UndirectedEdge)->Planar64Vec3{ fn edge_n(&self,edge_id:<Self::Edge as DirectedEdge>::UndirectedEdge)->Planar64Vec3{
let verts=self.edge_verts(edge_id); let &[v0,v1]=self.edge_verts(edge_id).as_ref();
self.vert(verts[1].clone())-self.vert(verts[0].clone()) self.vert(v1)-self.vert(v0)
} }
fn directed_edge_n(&self,directed_edge_id:Self::Edge)->Planar64Vec3{ fn directed_edge_n(&self,directed_edge_id:Self::Edge)->Planar64Vec3{
let verts=self.edge_verts(directed_edge_id.as_undirected()); let &[v0,v1]=self.edge_verts(directed_edge_id.as_undirected()).as_ref();
(self.vert(verts[1].clone())-self.vert(verts[0].clone()))*((directed_edge_id.parity() as i64)*2-1) (self.vert(v1)-self.vert(v0))*((directed_edge_id.parity() as i64)*2-1)
} }
fn vert(&self,vert_id:Self::Vert)->Planar64Vec3; fn vert(&self,vert_id:Self::Vert)->Planar64Vec3;
fn face_nd(&self,face_id:Self::Face)->(Self::Normal,Self::Offset); fn face_nd(&self,face_id:Self::Face)->(Self::Normal,Self::Offset);
fn face_edges(&self,face_id:Self::Face)->Cow<[Self::Edge]>; fn face_edges(&self,face_id:Self::Face)->impl AsRef<[Self::Edge]>;
fn edge_faces(&self,edge_id:<Self::Edge as DirectedEdge>::UndirectedEdge)->Cow<[Self::Face;2]>; fn edge_faces(&self,edge_id:<Self::Edge as DirectedEdge>::UndirectedEdge)->impl AsRef<[Self::Face;2]>;
fn edge_verts(&self,edge_id:<Self::Edge as DirectedEdge>::UndirectedEdge)->Cow<[Self::Vert;2]>; fn edge_verts(&self,edge_id:<Self::Edge as DirectedEdge>::UndirectedEdge)->impl AsRef<[Self::Vert;2]>;
fn vert_edges(&self,vert_id:Self::Vert)->Cow<[Self::Edge]>; fn vert_edges(&self,vert_id:Self::Vert)->impl AsRef<[Self::Edge]>;
fn vert_faces(&self,vert_id:Self::Vert)->Cow<[Self::Face]>; fn vert_faces(&self,vert_id:Self::Vert)->impl AsRef<[Self::Face]>;
} }
struct FaceRefs{ struct FaceRefs{
edges:Vec<SubmeshDirectedEdgeId>, edges:Vec<SubmeshDirectedEdgeId>,
@@ -372,8 +378,8 @@ impl TryFrom<&model::Mesh> for PhysicsMesh{
} }
//assume face hash is stable, and there are no flush faces... //assume face hash is stable, and there are no flush faces...
let face=Face{ let face=Face{
normal:(normal/len as i64).divide().fix_1(), normal:(normal/len as i64).divide().narrow_1().unwrap(),
dot:(dot/(len*len) as i64).fix_1(), dot:(dot/(len*len) as i64).narrow_1().unwrap(),
}; };
let face_id=match face_id_from_face.get(&face){ let face_id=match face_id_from_face.get(&face){
Some(&face_id)=>face_id, Some(&face_id)=>face_id,
@@ -435,20 +441,20 @@ impl MeshQuery for PhysicsMeshView<'_>{
let vert_idx=self.topology.verts[vert_id.get() as usize].get() as usize; let vert_idx=self.topology.verts[vert_id.get() as usize].get() as usize;
self.data.verts[vert_idx].0 self.data.verts[vert_idx].0
} }
fn face_edges(&self,face_id:SubmeshFaceId)->Cow<[SubmeshDirectedEdgeId]>{ fn face_edges(&self,face_id:SubmeshFaceId)->impl AsRef<[SubmeshDirectedEdgeId]>{
Cow::Borrowed(&self.topology.face_topology[face_id.get() as usize].edges) self.topology.face_topology[face_id.get() as usize].edges.as_slice()
} }
fn edge_faces(&self,edge_id:SubmeshEdgeId)->Cow<[SubmeshFaceId;2]>{ fn edge_faces(&self,edge_id:SubmeshEdgeId)->impl AsRef<[SubmeshFaceId;2]>{
Cow::Borrowed(&self.topology.edge_topology[edge_id.get() as usize].faces) AsRefHelper(self.topology.edge_topology[edge_id.get() as usize].faces)
} }
fn edge_verts(&self,edge_id:SubmeshEdgeId)->Cow<[SubmeshVertId;2]>{ fn edge_verts(&self,edge_id:SubmeshEdgeId)->impl AsRef<[SubmeshVertId;2]>{
Cow::Borrowed(&self.topology.edge_topology[edge_id.get() as usize].verts) AsRefHelper(self.topology.edge_topology[edge_id.get() as usize].verts)
} }
fn vert_edges(&self,vert_id:SubmeshVertId)->Cow<[SubmeshDirectedEdgeId]>{ fn vert_edges(&self,vert_id:SubmeshVertId)->impl AsRef<[SubmeshDirectedEdgeId]>{
Cow::Borrowed(&self.topology.vert_topology[vert_id.get() as usize].edges) self.topology.vert_topology[vert_id.get() as usize].edges.as_slice()
} }
fn vert_faces(&self,vert_id:SubmeshVertId)->Cow<[SubmeshFaceId]>{ fn vert_faces(&self,vert_id:SubmeshVertId)->impl AsRef<[SubmeshFaceId]>{
Cow::Borrowed(&self.topology.vert_topology[vert_id.get() as usize].faces) self.topology.vert_topology[vert_id.get() as usize].faces.as_slice()
} }
} }
@@ -513,26 +519,27 @@ impl MeshQuery for TransformedMesh<'_>{
(transformed_n,transformed_d) (transformed_n,transformed_d)
} }
fn vert(&self,vert_id:SubmeshVertId)->Planar64Vec3{ fn vert(&self,vert_id:SubmeshVertId)->Planar64Vec3{
self.transform.vertex.transform_point3(self.view.vert(vert_id)).fix_1() // wrap for speed
self.transform.vertex.transform_point3(self.view.vert(vert_id)).wrap_1()
} }
#[inline] #[inline]
fn face_edges(&self,face_id:SubmeshFaceId)->Cow<[SubmeshDirectedEdgeId]>{ fn face_edges(&self,face_id:SubmeshFaceId)->impl AsRef<[SubmeshDirectedEdgeId]>{
self.view.face_edges(face_id) self.view.face_edges(face_id)
} }
#[inline] #[inline]
fn edge_faces(&self,edge_id:SubmeshEdgeId)->Cow<[SubmeshFaceId;2]>{ fn edge_faces(&self,edge_id:SubmeshEdgeId)->impl AsRef<[SubmeshFaceId;2]>{
self.view.edge_faces(edge_id) self.view.edge_faces(edge_id)
} }
#[inline] #[inline]
fn edge_verts(&self,edge_id:SubmeshEdgeId)->Cow<[SubmeshVertId;2]>{ fn edge_verts(&self,edge_id:SubmeshEdgeId)->impl AsRef<[SubmeshVertId;2]>{
self.view.edge_verts(edge_id) self.view.edge_verts(edge_id)
} }
#[inline] #[inline]
fn vert_edges(&self,vert_id:SubmeshVertId)->Cow<[SubmeshDirectedEdgeId]>{ fn vert_edges(&self,vert_id:SubmeshVertId)->impl AsRef<[SubmeshDirectedEdgeId]>{
self.view.vert_edges(vert_id) self.view.vert_edges(vert_id)
} }
#[inline] #[inline]
fn vert_faces(&self,vert_id:SubmeshVertId)->Cow<[SubmeshFaceId]>{ fn vert_faces(&self,vert_id:SubmeshVertId)->impl AsRef<[SubmeshFaceId]>{
self.view.vert_faces(vert_id) self.view.vert_faces(vert_id)
} }
} }
@@ -621,12 +628,12 @@ impl MinkowskiMesh<'_>{
} }
fn next_transition_vert(&self,vert_id:MinkowskiVert,best_distance_squared:&mut Fixed<2,64>,infinity_dir:Planar64Vec3,point:Planar64Vec3)->Transition{ fn next_transition_vert(&self,vert_id:MinkowskiVert,best_distance_squared:&mut Fixed<2,64>,infinity_dir:Planar64Vec3,point:Planar64Vec3)->Transition{
let mut best_transition=Transition::Done; let mut best_transition=Transition::Done;
for &directed_edge_id in self.vert_edges(vert_id).iter(){ for &directed_edge_id in self.vert_edges(vert_id).as_ref(){
let edge_n=self.directed_edge_n(directed_edge_id); let edge_n=self.directed_edge_n(directed_edge_id);
//is boundary uncrossable by a crawl from infinity //is boundary uncrossable by a crawl from infinity
let edge_verts=self.edge_verts(directed_edge_id.as_undirected()); let edge_verts=self.edge_verts(directed_edge_id.as_undirected());
//select opposite vertex //select opposite vertex
let test_vert_id=edge_verts[directed_edge_id.parity() as usize]; let test_vert_id=edge_verts.as_ref()[directed_edge_id.parity() as usize];
//test if it's closer //test if it's closer
let diff=point-self.vert(test_vert_id); let diff=point-self.vert(test_vert_id);
if edge_n.dot(infinity_dir).is_zero(){ if edge_n.dot(infinity_dir).is_zero(){
@@ -642,7 +649,7 @@ impl MinkowskiMesh<'_>{
fn final_ev(&self,vert_id:MinkowskiVert,best_distance_squared:&mut Fixed<2,64>,infinity_dir:Planar64Vec3,point:Planar64Vec3)->EV{ fn final_ev(&self,vert_id:MinkowskiVert,best_distance_squared:&mut Fixed<2,64>,infinity_dir:Planar64Vec3,point:Planar64Vec3)->EV{
let mut best_transition=EV::Vert(vert_id); let mut best_transition=EV::Vert(vert_id);
let diff=point-self.vert(vert_id); let diff=point-self.vert(vert_id);
for &directed_edge_id in self.vert_edges(vert_id).iter(){ for &directed_edge_id in self.vert_edges(vert_id).as_ref(){
let edge_n=self.directed_edge_n(directed_edge_id); let edge_n=self.directed_edge_n(directed_edge_id);
//is boundary uncrossable by a crawl from infinity //is boundary uncrossable by a crawl from infinity
//check if time of collision is outside Time::MIN..Time::MAX //check if time of collision is outside Time::MIN..Time::MAX
@@ -653,7 +660,8 @@ impl MinkowskiMesh<'_>{
if !d.is_negative()&&d<=edge_nn{ if !d.is_negative()&&d<=edge_nn{
let distance_squared={ let distance_squared={
let c=diff.cross(edge_n); let c=diff.cross(edge_n);
(c.dot(c)/edge_nn).divide().fix_2() //wrap for speed
(c.dot(c)/edge_nn).divide().wrap_2()
}; };
if distance_squared<=*best_distance_squared{ if distance_squared<=*best_distance_squared{
best_transition=EV::Edge(directed_edge_id.as_undirected()); best_transition=EV::Edge(directed_edge_id.as_undirected());
@@ -689,10 +697,10 @@ impl MinkowskiMesh<'_>{
let edge_n=self.edge_n(edge_id); let edge_n=self.edge_n(edge_id);
// point is multiplied by two because vert_sum sums two vertices. // point is multiplied by two because vert_sum sums two vertices.
let delta_pos=point*2-{ let delta_pos=point*2-{
let &[v0,v1]=self.edge_verts(edge_id).borrow(); let &[v0,v1]=self.edge_verts(edge_id).as_ref();
self.vert(v0)+self.vert(v1) self.vert(v0)+self.vert(v1)
}; };
for (i,&face_id) in self.edge_faces(edge_id).iter().enumerate(){ for (i,&face_id) in self.edge_faces(edge_id).as_ref().iter().enumerate(){
let face_n=self.face_nd(face_id).0; let face_n=self.face_nd(face_id).0;
//edge-face boundary nd, n facing out of the face towards the edge //edge-face boundary nd, n facing out of the face towards the edge
let boundary_n=face_n.cross(edge_n)*(i as i64*2-1); let boundary_n=face_n.cross(edge_n)*(i as i64*2-1);
@@ -749,7 +757,7 @@ impl MinkowskiMesh<'_>{
.map(|(face,time)|(face,-time)) .map(|(face,time)|(face,-time))
}) })
} }
pub fn predict_collision_face_out(&self,relative_body:&Body,Range{start:start_time,end:time_limit}:Range<Time>,contact_face_id:MinkowskiFace)->Option<(MinkowskiEdge,GigaTime)>{ pub fn predict_collision_face_out(&self,relative_body:&Body,Range{start:start_time,end:time_limit}:Range<Time>,contact_face_id:MinkowskiFace)->Option<(MinkowskiDirectedEdge,GigaTime)>{
//no algorithm needed, there is only one state and two cases (Edge,None) //no algorithm needed, there is only one state and two cases (Edge,None)
//determine when it passes an edge ("sliding off" case) //determine when it passes an edge ("sliding off" case)
let start_time={ let start_time={
@@ -758,19 +766,20 @@ impl MinkowskiMesh<'_>{
}; };
let mut best_time={ let mut best_time={
let r=(time_limit-relative_body.time).to_ratio(); let r=(time_limit-relative_body.time).to_ratio();
Ratio::new(r.num.fix_4(),r.den.fix_4()) Ratio::new(r.num.widen_4(),r.den.widen_4())
}; };
let mut best_edge=None; let mut best_edge=None;
let face_n=self.face_nd(contact_face_id).0; let face_n=self.face_nd(contact_face_id).0;
for &directed_edge_id in self.face_edges(contact_face_id).iter(){ for &directed_edge_id in self.face_edges(contact_face_id).as_ref(){
let edge_n=self.directed_edge_n(directed_edge_id); let edge_n=self.directed_edge_n(directed_edge_id);
//f x e points in //f x e points in
let n=face_n.cross(edge_n); let n=face_n.cross(edge_n);
let verts=self.edge_verts(directed_edge_id.as_undirected()); let &[v0,v1]=self.edge_verts(directed_edge_id.as_undirected()).as_ref();
let d=n.dot(self.vert(verts[0])+self.vert(verts[1])); let d=n.dot(self.vert(v0)+self.vert(v1));
//WARNING! d outside of *2 //WARNING! d outside of *2
//WARNING: truncated precision //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()){ //wrap for speed
for dt in Fixed::<4,128>::zeroes2(((n.dot(relative_body.position))*2-d).wrap_4(),n.dot(relative_body.velocity).wrap_4()*2,n.dot(relative_body.acceleration).wrap_4()){
if start_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(relative_body.extrapolated_velocity_ratio_dt(dt)).is_negative(){ if start_time.le_ratio(dt)&&dt.lt_ratio(best_time)&&n.dot(relative_body.extrapolated_velocity_ratio_dt(dt)).is_negative(){
best_time=dt; best_time=dt;
best_edge=Some(directed_edge_id); best_edge=Some(directed_edge_id);
@@ -778,7 +787,7 @@ impl MinkowskiMesh<'_>{
} }
} }
} }
best_edge.map(|e|(e.as_undirected(),best_time)) best_edge.map(|e|(e,best_time))
} }
fn infinity_in(&self,infinity_body:Body)->Option<(MinkowskiFace,GigaTime)>{ fn infinity_in(&self,infinity_body:Body)->Option<(MinkowskiFace,GigaTime)>{
let infinity_fev=self.infinity_fev(-infinity_body.velocity,infinity_body.position); let infinity_fev=self.infinity_fev(-infinity_body.velocity,infinity_body.position);
@@ -811,12 +820,12 @@ impl MeshQuery for MinkowskiMesh<'_>{
MinkowskiFace::EdgeEdge(e0,e1,parity)=>{ MinkowskiFace::EdgeEdge(e0,e1,parity)=>{
let edge0_n=self.mesh0.edge_n(e0); let edge0_n=self.mesh0.edge_n(e0);
let edge1_n=self.mesh1.edge_n(e1); let edge1_n=self.mesh1.edge_n(e1);
let &[e0v0,e0v1]=self.mesh0.edge_verts(e0).borrow(); let &[e0v0,e0v1]=self.mesh0.edge_verts(e0).as_ref();
let &[e1v0,e1v1]=self.mesh1.edge_verts(e1).borrow(); let &[e1v0,e1v1]=self.mesh1.edge_verts(e1).as_ref();
let n=edge0_n.cross(edge1_n); let n=edge0_n.cross(edge1_n);
let e0d=n.dot(self.mesh0.vert(e0v0)+self.mesh0.vert(e0v1)); let e0d=n.dot(self.mesh0.vert(e0v0)+self.mesh0.vert(e0v1));
let e1d=n.dot(self.mesh1.vert(e1v0)+self.mesh1.vert(e1v1)); let e1d=n.dot(self.mesh1.vert(e1v0)+self.mesh1.vert(e1v1));
((n*(parity as i64*4-2)).fix_3(),((e0d-e1d)*(parity as i64*2-1)).fix_4()) ((n*(parity as i64*4-2)).widen_3(),((e0d-e1d)*(parity as i64*2-1)).widen_4())
}, },
MinkowskiFace::FaceVert(f0,v1)=>{ MinkowskiFace::FaceVert(f0,v1)=>{
let (n,d)=self.mesh0.face_nd(f0); let (n,d)=self.mesh0.face_nd(f0);
@@ -831,44 +840,44 @@ impl MeshQuery for MinkowskiMesh<'_>{
}, },
} }
} }
fn face_edges(&self,face_id:MinkowskiFace)->Cow<[MinkowskiDirectedEdge]>{ fn face_edges(&self,face_id:MinkowskiFace)->impl AsRef<[MinkowskiDirectedEdge]>{
match face_id{ match face_id{
MinkowskiFace::VertFace(v0,f1)=>{ MinkowskiFace::VertFace(v0,f1)=>{
Cow::Owned(self.mesh1.face_edges(f1).iter().map(|&edge_id1|{ self.mesh1.face_edges(f1).as_ref().iter().map(|&edge_id1|
MinkowskiDirectedEdge::VertEdge(v0,edge_id1.reverse()) MinkowskiDirectedEdge::VertEdge(v0,edge_id1.reverse())
}).collect()) ).collect()
}, },
MinkowskiFace::EdgeEdge(e0,e1,parity)=>{ MinkowskiFace::EdgeEdge(e0,e1,parity)=>{
let e0v=self.mesh0.edge_verts(e0); let &[e0v0,e0v1]=self.mesh0.edge_verts(e0).as_ref();
let e1v=self.mesh1.edge_verts(e1); let &[e1v0,e1v1]=self.mesh1.edge_verts(e1).as_ref();
//could sort this if ordered edges are needed //could sort this if ordered edges are needed
//probably just need to reverse this list according to parity //probably just need to reverse this list according to parity
Cow::Owned(vec![ vec![
MinkowskiDirectedEdge::VertEdge(e0v[0],e1.as_directed(parity)), MinkowskiDirectedEdge::VertEdge(e0v0,e1.as_directed(parity)),
MinkowskiDirectedEdge::EdgeVert(e0.as_directed(!parity),e1v[0]), MinkowskiDirectedEdge::EdgeVert(e0.as_directed(!parity),e1v0),
MinkowskiDirectedEdge::VertEdge(e0v[1],e1.as_directed(!parity)), MinkowskiDirectedEdge::VertEdge(e0v1,e1.as_directed(!parity)),
MinkowskiDirectedEdge::EdgeVert(e0.as_directed(parity),e1v[1]), MinkowskiDirectedEdge::EdgeVert(e0.as_directed(parity),e1v1),
]) ]
}, },
MinkowskiFace::FaceVert(f0,v1)=>{ MinkowskiFace::FaceVert(f0,v1)=>{
Cow::Owned(self.mesh0.face_edges(f0).iter().map(|&edge_id0|{ self.mesh0.face_edges(f0).as_ref().iter().map(|&edge_id0|
MinkowskiDirectedEdge::EdgeVert(edge_id0,v1) MinkowskiDirectedEdge::EdgeVert(edge_id0,v1)
}).collect()) ).collect()
}, },
} }
} }
fn edge_faces(&self,edge_id:MinkowskiEdge)->Cow<[MinkowskiFace;2]>{ fn edge_faces(&self,edge_id:MinkowskiEdge)->impl AsRef<[MinkowskiFace;2]>{
match edge_id{ match edge_id{
MinkowskiEdge::VertEdge(v0,e1)=>{ MinkowskiEdge::VertEdge(v0,e1)=>{
//faces are listed backwards from the minkowski mesh //faces are listed backwards from the minkowski mesh
let v0e=self.mesh0.vert_edges(v0); let v0e=self.mesh0.vert_edges(v0);
let &[e1f0,e1f1]=self.mesh1.edge_faces(e1).borrow(); let &[e1f0,e1f1]=self.mesh1.edge_faces(e1).as_ref();
Cow::Owned([(e1f1,false),(e1f0,true)].map(|(edge_face_id1,face_parity)|{ AsRefHelper([(e1f1,false),(e1f0,true)].map(|(edge_face_id1,face_parity)|{
let mut best_edge=None; let mut best_edge=None;
let mut best_d:Ratio<Fixed<8,256>,Fixed<8,256>>=Ratio::new(Fixed::ZERO,Fixed::ONE); let mut best_d:Ratio<Fixed<8,256>,Fixed<8,256>>=Ratio::new(Fixed::ZERO,Fixed::ONE);
let edge_face1_n=self.mesh1.face_nd(edge_face_id1).0; let edge_face1_n=self.mesh1.face_nd(edge_face_id1).0;
let edge_face1_nn=edge_face1_n.dot(edge_face1_n); let edge_face1_nn=edge_face1_n.dot(edge_face1_n);
for &directed_edge_id0 in v0e.iter(){ for &directed_edge_id0 in v0e.as_ref(){
let edge0_n=self.mesh0.directed_edge_n(directed_edge_id0); let edge0_n=self.mesh0.directed_edge_n(directed_edge_id0);
//must be behind other face. //must be behind other face.
let d=edge_face1_n.dot(edge0_n); let d=edge_face1_n.dot(edge0_n);
@@ -892,13 +901,13 @@ impl MeshQuery for MinkowskiMesh<'_>{
MinkowskiEdge::EdgeVert(e0,v1)=>{ MinkowskiEdge::EdgeVert(e0,v1)=>{
//tracking index with an external variable because .enumerate() is not available //tracking index with an external variable because .enumerate() is not available
let v1e=self.mesh1.vert_edges(v1); let v1e=self.mesh1.vert_edges(v1);
let &[e0f0,e0f1]=self.mesh0.edge_faces(e0).borrow(); let &[e0f0,e0f1]=self.mesh0.edge_faces(e0).as_ref();
Cow::Owned([(e0f0,true),(e0f1,false)].map(|(edge_face_id0,face_parity)|{ AsRefHelper([(e0f0,true),(e0f1,false)].map(|(edge_face_id0,face_parity)|{
let mut best_edge=None; let mut best_edge=None;
let mut best_d:Ratio<Fixed<8,256>,Fixed<8,256>>=Ratio::new(Fixed::ZERO,Fixed::ONE); let mut best_d:Ratio<Fixed<8,256>,Fixed<8,256>>=Ratio::new(Fixed::ZERO,Fixed::ONE);
let edge_face0_n=self.mesh0.face_nd(edge_face_id0).0; let edge_face0_n=self.mesh0.face_nd(edge_face_id0).0;
let edge_face0_nn=edge_face0_n.dot(edge_face0_n); let edge_face0_nn=edge_face0_n.dot(edge_face0_n);
for &directed_edge_id1 in v1e.iter(){ for &directed_edge_id1 in v1e.as_ref(){
let edge1_n=self.mesh1.directed_edge_n(directed_edge_id1); let edge1_n=self.mesh1.directed_edge_n(directed_edge_id1);
let d=edge_face0_n.dot(edge1_n); let d=edge_face0_n.dot(edge1_n);
if d.is_negative(){ if d.is_negative(){
@@ -918,31 +927,27 @@ impl MeshQuery for MinkowskiMesh<'_>{
}, },
} }
} }
fn edge_verts(&self,edge_id:MinkowskiEdge)->Cow<[MinkowskiVert;2]>{ fn edge_verts(&self,edge_id:MinkowskiEdge)->impl AsRef<[MinkowskiVert;2]>{
match edge_id{ AsRefHelper(match edge_id{
MinkowskiEdge::VertEdge(v0,e1)=>{ MinkowskiEdge::VertEdge(v0,e1)=>(*self.mesh1.edge_verts(e1).as_ref()).map(|vert_id1|
Cow::Owned(self.mesh1.edge_verts(e1).map(|vert_id1|{ MinkowskiVert::VertVert(v0,vert_id1)
MinkowskiVert::VertVert(v0,vert_id1) ),
})) MinkowskiEdge::EdgeVert(e0,v1)=>(*self.mesh0.edge_verts(e0).as_ref()).map(|vert_id0|
}, MinkowskiVert::VertVert(vert_id0,v1)
MinkowskiEdge::EdgeVert(e0,v1)=>{ ),
Cow::Owned(self.mesh0.edge_verts(e0).map(|vert_id0|{ })
MinkowskiVert::VertVert(vert_id0,v1)
}))
},
}
} }
fn vert_edges(&self,vert_id:MinkowskiVert)->Cow<[MinkowskiDirectedEdge]>{ fn vert_edges(&self,vert_id:MinkowskiVert)->impl AsRef<[MinkowskiDirectedEdge]>{
match vert_id{ match vert_id{
MinkowskiVert::VertVert(v0,v1)=>{ MinkowskiVert::VertVert(v0,v1)=>{
let mut edges=Vec::new(); let mut edges=Vec::new();
//detect shared volume when the other mesh is mirrored along a test edge dir //detect shared volume when the other mesh is mirrored along a test edge dir
let v0f=self.mesh0.vert_faces(v0); let v0f=self.mesh0.vert_faces(v0);
let v1f=self.mesh1.vert_faces(v1); let v1f=self.mesh1.vert_faces(v1);
let v0f_n:Vec<_>=v0f.iter().map(|&face_id|self.mesh0.face_nd(face_id).0).collect(); let v0f_n:Vec<_>=v0f.as_ref().iter().map(|&face_id|self.mesh0.face_nd(face_id).0).collect();
let v1f_n:Vec<_>=v1f.iter().map(|&face_id|self.mesh1.face_nd(face_id).0).collect(); let v1f_n:Vec<_>=v1f.as_ref().iter().map(|&face_id|self.mesh1.face_nd(face_id).0).collect();
let the_len=v0f.len()+v1f.len(); let the_len=v0f.as_ref().len()+v1f.as_ref().len();
for &directed_edge_id in self.mesh0.vert_edges(v0).iter(){ for &directed_edge_id in self.mesh0.vert_edges(v0).as_ref(){
let n=self.mesh0.directed_edge_n(directed_edge_id); let n=self.mesh0.directed_edge_n(directed_edge_id);
let nn=n.dot(n); let nn=n.dot(n);
// TODO: there's gotta be a better way to do this // TODO: there's gotta be a better way to do this
@@ -952,30 +957,33 @@ impl MeshQuery for MinkowskiMesh<'_>{
face_normals.clone_from(&v0f_n); face_normals.clone_from(&v0f_n);
for face_n in &v1f_n{ for face_n in &v1f_n{
//add reflected mesh1 faces //add reflected mesh1 faces
face_normals.push(*face_n-(n*face_n.dot(n)*2/nn).divide().fix_3()); //wrap for speed
face_normals.push(*face_n-(n*face_n.dot(n)*2/nn).divide().wrap_3());
} }
if is_empty_volume(face_normals){ if is_empty_volume(face_normals){
edges.push(MinkowskiDirectedEdge::EdgeVert(directed_edge_id,v1)); edges.push(MinkowskiDirectedEdge::EdgeVert(directed_edge_id,v1));
} }
} }
for &directed_edge_id in self.mesh1.vert_edges(v1).iter(){ for &directed_edge_id in self.mesh1.vert_edges(v1).as_ref(){
let n=self.mesh1.directed_edge_n(directed_edge_id); let n=self.mesh1.directed_edge_n(directed_edge_id);
let nn=n.dot(n); let nn=n.dot(n);
let mut face_normals=Vec::with_capacity(the_len); let mut face_normals=Vec::with_capacity(the_len);
face_normals.clone_from(&v1f_n); face_normals.clone_from(&v1f_n);
for face_n in &v0f_n{ for face_n in &v0f_n{
face_normals.push(*face_n-(n*face_n.dot(n)*2/nn).divide().fix_3()); //wrap for speed
face_normals.push(*face_n-(n*face_n.dot(n)*2/nn).divide().wrap_3());
} }
if is_empty_volume(face_normals){ if is_empty_volume(face_normals){
edges.push(MinkowskiDirectedEdge::VertEdge(v0,directed_edge_id)); edges.push(MinkowskiDirectedEdge::VertEdge(v0,directed_edge_id));
} }
} }
Cow::Owned(edges) edges
}, },
} }
} }
fn vert_faces(&self,_vert_id:MinkowskiVert)->Cow<[MinkowskiFace]>{ fn vert_faces(&self,_vert_id:MinkowskiVert)->impl AsRef<[MinkowskiFace]>{
unimplemented!() unimplemented!();
vec![]
} }
} }
@@ -1005,6 +1013,6 @@ fn is_empty_volume(normals:Vec<Vector3<Fixed<3,96>>>)->bool{
#[test] #[test]
fn test_is_empty_volume(){ fn test_is_empty_volume(){
assert!(!is_empty_volume([vec3::X.fix_3(),vec3::Y.fix_3(),vec3::Z.fix_3()].to_vec())); assert!(!is_empty_volume([vec3::X.widen_3(),vec3::Y.widen_3(),vec3::Z.widen_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())); assert!(is_empty_volume([vec3::X.widen_3(),vec3::Y.widen_3(),vec3::Z.widen_3(),vec3::NEG_X.widen_3()].to_vec()));
} }

View File

@@ -26,6 +26,8 @@ use strafesnet_common::physics::{Instruction,MouseInstruction,ModeInstruction,Mi
#[derive(Debug)] #[derive(Debug)]
pub enum InternalInstruction{ pub enum InternalInstruction{
CollisionStart(Collision,model_physics::GigaTime), CollisionStart(Collision,model_physics::GigaTime),
// transfer to a flush minkowski face
CollisionTransfer(ContactCollision,model_physics::MinkowskiFace),
CollisionEnd(Collision,model_physics::GigaTime), CollisionEnd(Collision,model_physics::GigaTime),
StrafeTick, StrafeTick,
ReachWalkTargetVelocity, ReachWalkTargetVelocity,
@@ -117,8 +119,8 @@ impl TransientAcceleration{
}else{ }else{
//normal friction acceleration is clippedAcceleration.dot(normal)*friction //normal friction acceleration is clippedAcceleration.dot(normal)*friction
TransientAcceleration::Reachable{ TransientAcceleration::Reachable{
acceleration:target_diff.with_length(accel).divide().fix_1(), acceleration:target_diff.with_length(accel).divide().wrap_1(),
time:time+Time::from((target_diff.length()/accel).divide().fix_1()) time:time+Time::from((target_diff.length()/accel).divide().clamp_1())
} }
} }
} }
@@ -407,7 +409,7 @@ impl HitboxMesh{
let transform=PhysicsMeshTransform::new(transform); let transform=PhysicsMeshTransform::new(transform);
let transformed_mesh=TransformedMesh::new(mesh.complete_mesh_view(),&transform); let transformed_mesh=TransformedMesh::new(mesh.complete_mesh_view(),&transform);
for vert in transformed_mesh.verts(){ for vert in transformed_mesh.verts(){
aabb.grow(vert.fix_1()); aabb.grow(vert.narrow_1().unwrap());
} }
Self{ Self{
halfsize:aabb.size()>>1, halfsize:aabb.size()>>1,
@@ -460,12 +462,12 @@ impl StyleHelper for StyleModifiers{
} }
fn get_y_control_dir(&self,camera:&PhysicsCamera,controls:Controls)->Planar64Vec3{ fn get_y_control_dir(&self,camera:&PhysicsCamera,controls:Controls)->Planar64Vec3{
(camera.rotation_y()*self.get_control_dir(controls)).fix_1() (camera.rotation_y()*self.get_control_dir(controls)).wrap_1()
} }
fn get_propulsion_control_dir(&self,camera:&PhysicsCamera,controls:Controls)->Planar64Vec3{ fn get_propulsion_control_dir(&self,camera:&PhysicsCamera,controls:Controls)->Planar64Vec3{
//don't interpolate this! discrete mouse movement, constant acceleration //don't interpolate this! discrete mouse movement, constant acceleration
(camera.rotation()*self.get_control_dir(controls)).fix_1() (camera.rotation()*self.get_control_dir(controls)).wrap_1()
} }
fn calculate_mesh(&self)->HitboxMesh{ fn calculate_mesh(&self)->HitboxMesh{
let mesh=match self.hitbox.mesh{ let mesh=match self.hitbox.mesh{
@@ -785,19 +787,39 @@ impl TouchingState{
crate::push_solve::push_solve(&contacts,acceleration) crate::push_solve::push_solve(&contacts,acceleration)
} }
fn predict_collision_end(&self,collector:&mut instruction::InstructionCollector<InternalInstruction,Time>,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,body:&Body,start_time:Time){ fn predict_collision_end(&self,collector:&mut instruction::InstructionCollector<InternalInstruction,Time>,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,body:&Body,start_time:Time){
use model_physics::DirectedEdge;
// let relative_body=body.relative_to(&Body::ZERO); // let relative_body=body.relative_to(&Body::ZERO);
let relative_body=body; let relative_body=body;
for contact in &self.contacts{ for contact in &self.contacts{
//detect face slide off //detect face slide off
let model_mesh=models.contact_mesh(contact); let model_mesh=models.contact_mesh(contact);
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh()); let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh());
collector.collect(minkowski.predict_collision_face_out(&relative_body,start_time..collector.time(),contact.face_id).map(|(_face,time)|{ collector.collect(minkowski.predict_collision_face_out(&relative_body,start_time..collector.time(),contact.face_id).map(|(out_edge,time)|{
TimedInstruction{ // TODO: determine if this code should go inside predict_collision_face_out
time:relative_body.time+time.into(), // the face across may be left or right depending on the directed edge parity
instruction:InternalInstruction::CollisionEnd( let &[f0,f1]=minkowski.edge_faces(out_edge.as_undirected()).as_ref();
Collision::Contact(*contact), let (f0n,_f0d)=minkowski.face_nd(f0);
time let (f1n,_f1d)=minkowski.face_nd(f1);
), // are they exactly flush
if f0n.cross(f1n)==vec3::ZERO_6
// implicitly don't need to check d values because faces share two vertices
// && n0*d1==n1*d0
{
TimedInstruction{
time:relative_body.time+time.into(),
instruction:InternalInstruction::CollisionTransfer(
*contact,
if out_edge.parity(){f1}else{f0},
),
}
}else{
TimedInstruction{
time:relative_body.time+time.into(),
instruction:InternalInstruction::CollisionEnd(
Collision::Contact(*contact),
time
),
}
} }
})); }));
} }
@@ -982,7 +1004,7 @@ impl PhysicsContext<'_>{
impl PhysicsData{ impl PhysicsData{
/// use with caution, this is the only non-instruction way to mess with physics /// use with caution, this is the only non-instruction way to mess with physics
pub fn generate_models(&mut self,map:&map::CompleteMap){ pub fn generate_models(&mut self,map:&map::CompleteMap){
let mut modes=map.modes.clone().denormalize(); let modes=map.modes.clone().denormalize();
let mut used_contact_attributes=Vec::new(); let mut used_contact_attributes=Vec::new();
let mut used_intersect_attributes=Vec::new(); let mut used_intersect_attributes=Vec::new();
@@ -1084,7 +1106,7 @@ impl PhysicsData{
let mut aabb=aabb::Aabb::default(); let mut aabb=aabb::Aabb::default();
let transformed_mesh=TransformedMesh::new(view,transform); let transformed_mesh=TransformedMesh::new(view,transform);
for v in transformed_mesh.verts(){ for v in transformed_mesh.verts(){
aabb.grow(v.fix_1()); aabb.grow(v.narrow_1().unwrap());
} }
(ConvexMeshId{ (ConvexMeshId{
model_id, model_id,
@@ -1162,7 +1184,8 @@ fn contact_normal(models:&PhysicsModels,hitbox_mesh:&HitboxMesh,contact:&Contact
let model_mesh=models.contact_mesh(contact); let model_mesh=models.contact_mesh(contact);
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh()); let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh());
// TODO: normalize to i64::MAX>>1 // TODO: normalize to i64::MAX>>1
minkowski.face_nd(contact.face_id).0.fix_1() // wrap for speed
minkowski.face_nd(contact.face_id).0.wrap_1()
} }
fn recalculate_touching( fn recalculate_touching(
@@ -1321,7 +1344,7 @@ fn teleport_to_spawn(
const EPSILON:Planar64=Planar64::raw((1<<32)/16); const EPSILON:Planar64=Planar64::raw((1<<32)/16);
let transform=models.get_model_transform(spawn_model_id).ok_or(TeleportToSpawnError::NoModel)?; let transform=models.get_model_transform(spawn_model_id).ok_or(TeleportToSpawnError::NoModel)?;
//TODO: transform.vertex.matrix3.col(1)+transform.vertex.translation //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]); let point=transform.vertex.transform_point3(vec3::Y).clamp_1()+Planar64Vec3::new([Planar64::ZERO,style.hitbox.halfsize.y+EPSILON,Planar64::ZERO]);
teleport(point,move_state,body,touching,run,mode_state,Some(mode),models,hitbox_mesh,bvh,style,camera,input_state,time); teleport(point,move_state,body,touching,run,mode_state,Some(mode),models,hitbox_mesh,bvh,style,camera,input_state,time);
Ok(()) Ok(())
} }
@@ -1485,7 +1508,7 @@ fn collision_start_contact(
Some(gameplay_attributes::ContactingBehaviour::Surf)=>(), Some(gameplay_attributes::ContactingBehaviour::Surf)=>(),
Some(gameplay_attributes::ContactingBehaviour::Cling)=>println!("Unimplemented!"), Some(gameplay_attributes::ContactingBehaviour::Cling)=>println!("Unimplemented!"),
&Some(gameplay_attributes::ContactingBehaviour::Elastic(elasticity))=>{ &Some(gameplay_attributes::ContactingBehaviour::Elastic(elasticity))=>{
let reflected_velocity=body.velocity+((body.velocity-incident_velocity)*Planar64::raw(elasticity as i64+1)).fix_1(); let reflected_velocity=body.velocity+((body.velocity-incident_velocity)*Planar64::raw(elasticity as i64+1)).wrap_1();
set_velocity(body,touching,models,hitbox_mesh,reflected_velocity); set_velocity(body,touching,models,hitbox_mesh,reflected_velocity);
}, },
Some(gameplay_attributes::ContactingBehaviour::Ladder(contacting_ladder))=> Some(gameplay_attributes::ContactingBehaviour::Ladder(contacting_ladder))=>
@@ -1655,6 +1678,7 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
|InternalInstruction::CollisionEnd(_,dt)=>(true,Some(dt)), |InternalInstruction::CollisionEnd(_,dt)=>(true,Some(dt)),
InternalInstruction::StrafeTick InternalInstruction::StrafeTick
|InternalInstruction::ReachWalkTargetVelocity=>(true,None), |InternalInstruction::ReachWalkTargetVelocity=>(true,None),
InternalInstruction::CollisionTransfer(..)=>(false,None),
}; };
if should_advance_body{ if should_advance_body{
match goober_time{ match goober_time{
@@ -1699,6 +1723,15 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
state.time state.time
), ),
}, },
InternalInstruction::CollisionTransfer(collision,dst_face)=>{
// transfer to a flush surface of the same object
state.touching.contacts.remove(&collision);
state.touching.contacts.insert(ContactCollision{
face_id:dst_face,
model_id:collision.model_id,
submesh_id:collision.submesh_id,
});
},
InternalInstruction::StrafeTick=>{ InternalInstruction::StrafeTick=>{
//TODO make this less huge //TODO make this less huge
if let Some(strafe_settings)=&state.style.strafe{ if let Some(strafe_settings)=&state.style.strafe{
@@ -1708,7 +1741,7 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
let control_dir=state.style.get_control_dir(masked_controls); let control_dir=state.style.get_control_dir(masked_controls);
if control_dir!=vec3::ZERO{ if control_dir!=vec3::ZERO{
let camera_mat=state.camera.simulate_move_rotation_y(state.input_state.lerp_delta(state.time).x); let camera_mat=state.camera.simulate_move_rotation_y(state.input_state.lerp_delta(state.time).x);
if let Some(ticked_velocity)=strafe_settings.tick_velocity(state.body.velocity,(camera_mat*control_dir).with_length(Planar64::ONE).divide().fix_1()){ if let Some(ticked_velocity)=strafe_settings.tick_velocity(state.body.velocity,(camera_mat*control_dir).with_length(Planar64::ONE).divide().wrap_1()){
//this is wrong but will work ig //this is wrong but will work ig
//need to note which push planes activate in push solve and keep those //need to note which push planes activate in push solve and keep those
state.cull_velocity(data,ticked_velocity); state.cull_velocity(data,ticked_velocity);

View File

@@ -152,18 +152,19 @@ const fn get_push_ray_0(point:Planar64Vec3)->Ray{
Ray{origin:point,direction:vec3::ZERO} Ray{origin:point,direction:vec3::ZERO}
} }
fn get_push_ray_1(point:Planar64Vec3,c0:&Contact)->Option<Ray>{ fn get_push_ray_1(point:Planar64Vec3,c0:&Contact)->Option<Ray>{
let direction=solve1(c0)?.divide().fix_1(); //wrap for speed
let direction=solve1(c0)?.divide().wrap_1();
let [s0]=decompose1(direction,c0.velocity)?; let [s0]=decompose1(direction,c0.velocity)?;
if s0.lt_ratio(RATIO_ZERO){ if s0.lt_ratio(RATIO_ZERO){
return None; return None;
} }
let origin=point+solve1( let origin=point+solve1(
&c0.relative_to(point), &c0.relative_to(point),
)?.divide().fix_1(); )?.divide().wrap_1();
Some(Ray{origin,direction}) Some(Ray{origin,direction})
} }
fn get_push_ray_2(point:Planar64Vec3,c0:&Contact,c1:&Contact)->Option<Ray>{ fn get_push_ray_2(point:Planar64Vec3,c0:&Contact,c1:&Contact)->Option<Ray>{
let direction=solve2(c0,c1)?.divide().fix_1(); let direction=solve2(c0,c1)?.divide().wrap_1();
let [s0,s1]=decompose2(direction,c0.velocity,c1.velocity)?; let [s0,s1]=decompose2(direction,c0.velocity,c1.velocity)?;
if s0.lt_ratio(RATIO_ZERO)||s1.lt_ratio(RATIO_ZERO){ if s0.lt_ratio(RATIO_ZERO)||s1.lt_ratio(RATIO_ZERO){
return None; return None;
@@ -171,11 +172,11 @@ fn get_push_ray_2(point:Planar64Vec3,c0:&Contact,c1:&Contact)->Option<Ray>{
let origin=point+solve2( let origin=point+solve2(
&c0.relative_to(point), &c0.relative_to(point),
&c1.relative_to(point), &c1.relative_to(point),
)?.divide().fix_1(); )?.divide().wrap_1();
Some(Ray{origin,direction}) Some(Ray{origin,direction})
} }
fn get_push_ray_3(point:Planar64Vec3,c0:&Contact,c1:&Contact,c2:&Contact)->Option<Ray>{ fn get_push_ray_3(point:Planar64Vec3,c0:&Contact,c1:&Contact,c2:&Contact)->Option<Ray>{
let direction=solve3(c0,c1,c2)?.divide().fix_1(); let direction=solve3(c0,c1,c2)?.divide().wrap_1();
let [s0,s1,s2]=decompose3(direction,c0.velocity,c1.velocity,c2.velocity)?; let [s0,s1,s2]=decompose3(direction,c0.velocity,c1.velocity,c2.velocity)?;
if s0.lt_ratio(RATIO_ZERO)||s1.lt_ratio(RATIO_ZERO)||s2.lt_ratio(RATIO_ZERO){ if s0.lt_ratio(RATIO_ZERO)||s1.lt_ratio(RATIO_ZERO)||s2.lt_ratio(RATIO_ZERO){
return None; return None;
@@ -184,7 +185,7 @@ fn get_push_ray_3(point:Planar64Vec3,c0:&Contact,c1:&Contact,c2:&Contact)->Optio
&c0.relative_to(point), &c0.relative_to(point),
&c1.relative_to(point), &c1.relative_to(point),
&c2.relative_to(point), &c2.relative_to(point),
)?.divide().fix_1(); )?.divide().wrap_1();
Some(Ray{origin,direction}) Some(Ray{origin,direction})
} }

View File

@@ -1,4 +1,3 @@
use std::io::Cursor; use std::io::Cursor;
use std::path::Path; use std::path::Path;
use std::time::Instant; use std::time::Instant;
@@ -6,7 +5,12 @@ use std::time::Instant;
use strafesnet_physics::physics::{PhysicsData,PhysicsState,PhysicsContext}; use strafesnet_physics::physics::{PhysicsData,PhysicsState,PhysicsContext};
fn main(){ fn main(){
test_determinism().unwrap(); let arg=std::env::args().skip(1).next();
match arg.as_deref(){
Some("determinism")|None=>test_determinism().unwrap(),
Some("replay")=>run_replay().unwrap(),
_=>println!("invalid argument"),
}
} }
#[allow(unused)] #[allow(unused)]

View File

@@ -34,6 +34,7 @@ pub enum PlanesToFacesError{
InitFace2, InitFace2,
InitIntersection, InitIntersection,
FindNewIntersection, FindNewIntersection,
// Narrow(strafesnet_common::integer::NarrowError),
EmptyFaces, EmptyFaces,
InfiniteLoop1, InfiniteLoop1,
InfiniteLoop2, InfiniteLoop2,
@@ -81,12 +82,12 @@ fn planes_to_faces(face_list:std::collections::HashSet<Face>)->Result<Faces,Plan
// test if any *other* faces occlude the intersection // test if any *other* faces occlude the intersection
for new_face in &face_list{ for new_face in &face_list{
// new face occludes intersection point // new face occludes intersection point
if (new_face.dot.fix_2()/Planar64::ONE).lt_ratio(new_face.normal.dot(intersection.num)/intersection.den){ if (new_face.dot.widen_2()/Planar64::ONE).lt_ratio(new_face.normal.dot(intersection.num)/intersection.den){
// replace one of the faces with the new face // replace one of the faces with the new face
// dont' try to replace face0 because we are exploring that face in particular // dont' try to replace face0 because we are exploring that face in particular
if let Some(new_intersection)=solve3(face0,new_face,face2){ if let Some(new_intersection)=solve3(face0,new_face,face2){
// face1 does not occlude (or intersect) the new intersection // face1 does not occlude (or intersect) the new intersection
if (face1.dot.fix_2()/Planar64::ONE).gt_ratio(face1.normal.dot(new_intersection.num)/new_intersection.den){ if (face1.dot.widen_2()/Planar64::ONE).gt_ratio(face1.normal.dot(new_intersection.num)/new_intersection.den){
face1=new_face; face1=new_face;
intersection=new_intersection; intersection=new_intersection;
continue 'find; continue 'find;
@@ -94,7 +95,7 @@ fn planes_to_faces(face_list:std::collections::HashSet<Face>)->Result<Faces,Plan
} }
if let Some(new_intersection)=solve3(face0,face1,new_face){ if let Some(new_intersection)=solve3(face0,face1,new_face){
// face2 does not occlude (or intersect) the new intersection // face2 does not occlude (or intersect) the new intersection
if (face2.dot.fix_2()/Planar64::ONE).gt_ratio(face2.normal.dot(new_intersection.num)/new_intersection.den){ if (face2.dot.widen_2()/Planar64::ONE).gt_ratio(face2.normal.dot(new_intersection.num)/new_intersection.den){
face2=new_face; face2=new_face;
intersection=new_intersection; intersection=new_intersection;
continue 'find; continue 'find;
@@ -119,7 +120,7 @@ fn planes_to_faces(face_list:std::collections::HashSet<Face>)->Result<Faces,Plan
continue; continue;
} }
// new_face occludes intersection meaning intersection is not on convex solid and face0 is degenrate // new_face occludes intersection meaning intersection is not on convex solid and face0 is degenrate
if (new_face.dot.fix_2()/Planar64::ONE).lt_ratio(new_face.normal.dot(intersection.num)/intersection.den){ if (new_face.dot.widen_2()/Planar64::ONE).lt_ratio(new_face.normal.dot(intersection.num)/intersection.den){
// abort! reject face0 entirely // abort! reject face0 entirely
continue 'face; continue 'face;
} }
@@ -137,7 +138,7 @@ fn planes_to_faces(face_list:std::collections::HashSet<Face>)->Result<Faces,Plan
loop{ loop{
// push point onto vertices // push point onto vertices
// problem: this may push a vertex that does not fit in the fixed point range and is thus meaningless // problem: this may push a vertex that does not fit in the fixed point range and is thus meaningless
face.push(intersection.divide().fix_1()); face.push(intersection.divide().narrow_1().unwrap());
// we looped back around to face1, we're done! // we looped back around to face1, we're done!
if core::ptr::eq(face1,face2){ if core::ptr::eq(face1,face2){

View File

@@ -53,9 +53,9 @@ fn add_brush<'a>(
rendercolor.b as f32 rendercolor.b as f32
])/255.0).extend(1.0); ])/255.0).extend(1.0);
match model.split_at(1){ match model.chars().next(){
// The first character of brush.model is '*' // The first character of brush.model is '*'
("*",id_str)=>match id_str.parse(){ Some('*')=>match model[1..].parse(){
Ok(mesh_id)=>{ Ok(mesh_id)=>{
let mesh=model::MeshId::new(mesh_id); let mesh=model::MeshId::new(mesh_id);
world_models.push( world_models.push(

View File

@@ -12,8 +12,8 @@ authors = ["Rhys Lloyd <krakow20@gmail.com>"]
[dependencies] [dependencies]
arrayvec = "0.7.4" arrayvec = "0.7.4"
bitflags = "2.6.0" bitflags = "2.6.0"
fixed_wide = { version = "0.1.2", path = "../fixed_wide", registry = "strafesnet", features = ["deferred-division","zeroes","wide-mul"] } fixed_wide = { version = "0.2.0", path = "../fixed_wide", registry = "strafesnet", features = ["deferred-division","zeroes","wide-mul"] }
linear_ops = { version = "0.1.0", path = "../linear_ops", registry = "strafesnet", features = ["deferred-division","named-fields"] } linear_ops = { version = "0.1.1", path = "../linear_ops", registry = "strafesnet", features = ["deferred-division","named-fields"] }
ratio_ops = { version = "0.1.0", path = "../ratio_ops", registry = "strafesnet" } ratio_ops = { version = "0.1.0", path = "../ratio_ops", registry = "strafesnet" }
glam = "0.30.0" glam = "0.30.0"
id = { version = "0.1.0", registry = "strafesnet" } id = { version = "0.1.0", registry = "strafesnet" }

View File

@@ -66,18 +66,18 @@ impl JumpImpulse{
_mass:Planar64, _mass:Planar64,
)->Planar64Vec3{ )->Planar64Vec3{
match self{ match self{
&JumpImpulse::Time(time)=>velocity-(*gravity*time).map(|t|t.divide().fix_1()), &JumpImpulse::Time(time)=>velocity-(*gravity*time).map(|t|t.divide().clamp_1()),
&JumpImpulse::Height(height)=>{ &JumpImpulse::Height(height)=>{
//height==-v.y*v.y/(2*g.y); //height==-v.y*v.y/(2*g.y);
//use energy to determine max height //use energy to determine max height
let gg=gravity.length_squared(); let gg=gravity.length_squared();
let g=gg.sqrt().fix_1(); let g=gg.sqrt();
let v_g=gravity.dot(velocity); let v_g=gravity.dot(velocity);
//do it backwards //do it backwards
let radicand=v_g*v_g+(g*height*2).fix_4(); let radicand=v_g*v_g+(g*height*2).widen_4();
velocity-(*gravity*(radicand.sqrt().fix_2()+v_g)/gg).divide().fix_1() velocity-(*gravity*(radicand.sqrt().wrap_2()+v_g)/gg).divide().clamp_1()
}, },
&JumpImpulse::Linear(jump_speed)=>velocity+(jump_dir*jump_speed/jump_dir.length()).divide().fix_1(), &JumpImpulse::Linear(jump_speed)=>velocity+(jump_dir*jump_speed/jump_dir.length()).divide().clamp_1(),
&JumpImpulse::Energy(_energy)=>{ &JumpImpulse::Energy(_energy)=>{
//calculate energy //calculate energy
//let e=gravity.dot(velocity); //let e=gravity.dot(velocity);
@@ -91,10 +91,10 @@ impl JumpImpulse{
pub fn get_jump_deltav(&self,gravity:&Planar64Vec3,mass:Planar64)->Planar64{ pub fn get_jump_deltav(&self,gravity:&Planar64Vec3,mass:Planar64)->Planar64{
//gravity.length() is actually the proper calculation because the jump is always opposite the gravity direction //gravity.length() is actually the proper calculation because the jump is always opposite the gravity direction
match self{ match self{
&JumpImpulse::Time(time)=>(gravity.length().fix_1()*time/2).divide().fix_1(), &JumpImpulse::Time(time)=>(gravity.length().wrap_1()*time/2).divide().clamp_1(),
&JumpImpulse::Height(height)=>(gravity.length()*height*2).sqrt().fix_1(), &JumpImpulse::Height(height)=>(gravity.length()*height*2).sqrt().wrap_1(),
&JumpImpulse::Linear(deltav)=>deltav, &JumpImpulse::Linear(deltav)=>deltav,
&JumpImpulse::Energy(energy)=>(energy.sqrt()*2/mass.sqrt()).divide().fix_1(), &JumpImpulse::Energy(energy)=>(energy.sqrt()*2/mass.sqrt()).divide().clamp_1(),
} }
} }
} }
@@ -126,10 +126,10 @@ impl JumpSettings{
None=>rel_velocity, None=>rel_velocity,
}; };
let j=boost_vel.dot(jump_dir); let j=boost_vel.dot(jump_dir);
let js=jump_speed.fix_2(); let js=jump_speed.widen_2();
if j<js{ if j<js{
//weak booster: just do a regular jump //weak booster: just do a regular jump
boost_vel+jump_dir.with_length(js-j).divide().fix_1() boost_vel+jump_dir.with_length(js-j).divide().wrap_1()
}else{ }else{
//activate booster normally, jump does nothing //activate booster normally, jump does nothing
boost_vel boost_vel
@@ -142,13 +142,13 @@ impl JumpSettings{
None=>rel_velocity, None=>rel_velocity,
}; };
let j=boost_vel.dot(jump_dir); let j=boost_vel.dot(jump_dir);
let js=jump_speed.fix_2(); let js=jump_speed.widen_2();
if j<js{ if j<js{
//speed in direction of jump cannot be lower than amount //speed in direction of jump cannot be lower than amount
boost_vel+jump_dir.with_length(js-j).divide().fix_1() boost_vel+jump_dir.with_length(js-j).divide().wrap_1()
}else{ }else{
//boost and jump add together //boost and jump add together
boost_vel+jump_dir.with_length(js).divide().fix_1() boost_vel+jump_dir.with_length(js).divide().wrap_1()
} }
} }
(false,JumpCalculation::Max)=>{ (false,JumpCalculation::Max)=>{
@@ -159,10 +159,10 @@ impl JumpSettings{
None=>rel_velocity, None=>rel_velocity,
}; };
let boost_dot=boost_vel.dot(jump_dir); let boost_dot=boost_vel.dot(jump_dir);
let js=jump_speed.fix_2(); let js=jump_speed.widen_2();
if boost_dot<js{ if boost_dot<js{
//weak boost is extended to jump speed //weak boost is extended to jump speed
boost_vel+jump_dir.with_length(js-boost_dot).divide().fix_1() boost_vel+jump_dir.with_length(js-boost_dot).divide().wrap_1()
}else{ }else{
//activate booster normally, jump does nothing //activate booster normally, jump does nothing
boost_vel boost_vel
@@ -174,7 +174,7 @@ impl JumpSettings{
Some(booster)=>booster.boost(rel_velocity), Some(booster)=>booster.boost(rel_velocity),
None=>rel_velocity, None=>rel_velocity,
}; };
boost_vel+jump_dir.with_length(jump_speed).divide().fix_1() boost_vel+jump_dir.with_length(jump_speed).divide().wrap_1()
}, },
} }
} }
@@ -267,9 +267,9 @@ pub struct StrafeSettings{
impl StrafeSettings{ impl StrafeSettings{
pub fn tick_velocity(&self,velocity:Planar64Vec3,control_dir:Planar64Vec3)->Option<Planar64Vec3>{ pub fn tick_velocity(&self,velocity:Planar64Vec3,control_dir:Planar64Vec3)->Option<Planar64Vec3>{
let d=velocity.dot(control_dir); let d=velocity.dot(control_dir);
let mv=self.mv.fix_2(); let mv=self.mv.widen_2();
match d<mv{ match d<mv{
true=>Some(velocity+(control_dir*self.air_accel_limit.map_or(mv-d,|limit|limit.fix_2().min(mv-d))).fix_1()), true=>Some(velocity+(control_dir*self.air_accel_limit.map_or(mv-d,|limit|limit.widen_2().min(mv-d))).wrap_1()),
false=>None, false=>None,
} }
} }
@@ -290,7 +290,7 @@ pub struct PropulsionSettings{
} }
impl PropulsionSettings{ impl PropulsionSettings{
pub fn acceleration(&self,control_dir:Planar64Vec3)->Planar64Vec3{ pub fn acceleration(&self,control_dir:Planar64Vec3)->Planar64Vec3{
(control_dir*self.magnitude).fix_1() (control_dir*self.magnitude).clamp_1()
} }
} }
@@ -310,13 +310,13 @@ pub struct WalkSettings{
impl WalkSettings{ impl WalkSettings{
pub fn accel(&self,target_diff:Planar64Vec3,gravity:Planar64Vec3)->Planar64{ pub fn accel(&self,target_diff:Planar64Vec3,gravity:Planar64Vec3)->Planar64{
//TODO: fallible walk accel //TODO: fallible walk accel
let diff_len=target_diff.length().fix_1(); let diff_len=target_diff.length().wrap_1();
let friction=if diff_len<self.accelerate.topspeed{ let friction=if diff_len<self.accelerate.topspeed{
self.static_friction self.static_friction
}else{ }else{
self.kinetic_friction self.kinetic_friction
}; };
self.accelerate.accel.min((-gravity.y*friction).fix_1()) self.accelerate.accel.min((-gravity.y*friction).clamp_1())
} }
pub fn get_walk_target_velocity(&self,control_dir:Planar64Vec3,normal:Planar64Vec3)->Planar64Vec3{ pub fn get_walk_target_velocity(&self,control_dir:Planar64Vec3,normal:Planar64Vec3)->Planar64Vec3{
if control_dir==crate::integer::vec3::ZERO{ if control_dir==crate::integer::vec3::ZERO{
@@ -332,7 +332,7 @@ impl WalkSettings{
if cr==crate::integer::vec3::ZERO_2{ if cr==crate::integer::vec3::ZERO_2{
crate::integer::vec3::ZERO crate::integer::vec3::ZERO
}else{ }else{
(cr.cross(normal)*self.accelerate.topspeed/((nn*(nnmm-dd)).sqrt())).divide().fix_1() (cr.cross(normal)*self.accelerate.topspeed/((nn*(nnmm-dd)).sqrt())).divide().clamp_1()
} }
}else{ }else{
crate::integer::vec3::ZERO crate::integer::vec3::ZERO
@@ -341,7 +341,7 @@ impl WalkSettings{
pub fn is_slope_walkable(&self,normal:Planar64Vec3,up:Planar64Vec3)->bool{ pub fn is_slope_walkable(&self,normal:Planar64Vec3,up:Planar64Vec3)->bool{
//normal is not guaranteed to be unit length //normal is not guaranteed to be unit length
let ny=normal.dot(up); let ny=normal.dot(up);
let h=normal.length().fix_1(); let h=normal.length().wrap_1();
//remember this is a normal vector //remember this is a normal vector
ny.is_positive()&&h*self.surf_dot<ny ny.is_positive()&&h*self.surf_dot<ny
} }
@@ -368,13 +368,13 @@ impl LadderSettings{
let nnmm=nn*mm; let nnmm=nn*mm;
let d=normal.dot(control_dir); let d=normal.dot(control_dir);
let mut dd=d*d; let mut dd=d*d;
if (self.dot*self.dot*nnmm).fix_4()<dd{ if (self.dot*self.dot*nnmm).clamp_4()<dd{
if d.is_negative(){ if d.is_negative(){
control_dir=Planar64Vec3::new([Planar64::ZERO,mm.fix_1(),Planar64::ZERO]); control_dir=Planar64Vec3::new([Planar64::ZERO,mm.clamp_1(),Planar64::ZERO]);
}else{ }else{
control_dir=Planar64Vec3::new([Planar64::ZERO,-mm.fix_1(),Planar64::ZERO]); control_dir=Planar64Vec3::new([Planar64::ZERO,-mm.clamp_1(),Planar64::ZERO]);
} }
dd=(normal.y*normal.y).fix_4(); dd=(normal.y*normal.y).widen_4();
} }
//n=d if you are standing on top of a ladder and press E. //n=d if you are standing on top of a ladder and press E.
//two fixes: //two fixes:
@@ -385,7 +385,7 @@ impl LadderSettings{
if cr==crate::integer::vec3::ZERO_2{ if cr==crate::integer::vec3::ZERO_2{
crate::integer::vec3::ZERO crate::integer::vec3::ZERO
}else{ }else{
(cr.cross(normal)*self.accelerate.topspeed/((nn*(nnmm-dd)).sqrt())).divide().fix_1() (cr.cross(normal)*self.accelerate.topspeed/((nn*(nnmm-dd)).sqrt())).divide().clamp_1()
} }
}else{ }else{
crate::integer::vec3::ZERO crate::integer::vec3::ZERO
@@ -417,7 +417,7 @@ impl Hitbox{
} }
pub fn source()->Self{ pub fn source()->Self{
Self{ Self{
halfsize:((int3(33,73,33)>>1)*VALVE_SCALE).fix_1(), halfsize:((int3(33,73,33)>>1)*VALVE_SCALE).narrow_1().unwrap(),
mesh:HitboxMesh::Box, mesh:HitboxMesh::Box,
} }
} }
@@ -538,11 +538,11 @@ impl StyleModifiers{
tick_rate:Ratio64::new(100,AbsoluteTime::ONE_SECOND.get() as u64).unwrap(), tick_rate:Ratio64::new(100,AbsoluteTime::ONE_SECOND.get() as u64).unwrap(),
}), }),
jump:Some(JumpSettings{ jump:Some(JumpSettings{
impulse:JumpImpulse::Height((int(52)*VALVE_SCALE).fix_1()), impulse:JumpImpulse::Height((int(52)*VALVE_SCALE).narrow_1().unwrap()),
calculation:JumpCalculation::JumpThenBoost, calculation:JumpCalculation::JumpThenBoost,
limit_minimum:true, limit_minimum:true,
}), }),
gravity:(int3(0,-800,0)*VALVE_SCALE).fix_1(), gravity:(int3(0,-800,0)*VALVE_SCALE).narrow_1().unwrap(),
mass:int(1), mass:int(1),
rocket:None, rocket:None,
walk:Some(WalkSettings{ walk:Some(WalkSettings{
@@ -565,7 +565,7 @@ impl StyleModifiers{
magnitude:int(12),//? magnitude:int(12),//?
}), }),
hitbox:Hitbox::source(), hitbox:Hitbox::source(),
camera_offset:((int3(0,64,0)-(int3(0,73,0)>>1))*VALVE_SCALE).fix_1(), camera_offset:((int3(0,64,0)-(int3(0,73,0)>>1))*VALVE_SCALE).narrow_1().unwrap(),
} }
} }
pub fn source_surf()->Self{ pub fn source_surf()->Self{
@@ -574,16 +574,16 @@ impl StyleModifiers{
controls_mask_state:Controls::all(), controls_mask_state:Controls::all(),
strafe:Some(StrafeSettings{ strafe:Some(StrafeSettings{
enable:ControlsActivation::full_2d(), enable:ControlsActivation::full_2d(),
air_accel_limit:Some((int(150)*66*VALVE_SCALE).fix_1()), air_accel_limit:Some((int(150)*66*VALVE_SCALE).narrow_1().unwrap()),
mv:Planar64::raw(30<<28), mv:Planar64::raw(30<<28),
tick_rate:Ratio64::new(66,AbsoluteTime::ONE_SECOND.get() as u64).unwrap(), tick_rate:Ratio64::new(66,AbsoluteTime::ONE_SECOND.get() as u64).unwrap(),
}), }),
jump:Some(JumpSettings{ jump:Some(JumpSettings{
impulse:JumpImpulse::Height((int(52)*VALVE_SCALE).fix_1()), impulse:JumpImpulse::Height((int(52)*VALVE_SCALE).narrow_1().unwrap()),
calculation:JumpCalculation::JumpThenBoost, calculation:JumpCalculation::JumpThenBoost,
limit_minimum:true, limit_minimum:true,
}), }),
gravity:(int3(0,-800,0)*VALVE_SCALE).fix_1(), gravity:(int3(0,-800,0)*VALVE_SCALE).narrow_1().unwrap(),
mass:int(1), mass:int(1),
rocket:None, rocket:None,
walk:Some(WalkSettings{ walk:Some(WalkSettings{
@@ -606,7 +606,7 @@ impl StyleModifiers{
magnitude:int(12),//? magnitude:int(12),//?
}), }),
hitbox:Hitbox::source(), hitbox:Hitbox::source(),
camera_offset:((int3(0,64,0)-(int3(0,73,0)>>1))*VALVE_SCALE).fix_1(), camera_offset:((int3(0,64,0)-(int3(0,73,0)>>1))*VALVE_SCALE).narrow_1().unwrap(),
} }
} }
} }

View File

@@ -1,4 +1,4 @@
pub use fixed_wide::fixed::{Fixed,Fix}; pub use fixed_wide::fixed::*;
pub use ratio_ops::ratio::{Ratio,Divide}; pub use ratio_ops::ratio::{Ratio,Divide};
//integer units //integer units
@@ -60,7 +60,7 @@ impl<T> Time<T>{
impl<T> From<Planar64> for Time<T>{ impl<T> From<Planar64> for Time<T>{
#[inline] #[inline]
fn from(value:Planar64)->Self{ fn from(value:Planar64)->Self{
Self::raw((value*Planar64::raw(1_000_000_000)).fix_1().to_raw()) Self::raw((value*Planar64::raw(1_000_000_000)).clamp_1().to_raw())
} }
} }
impl<T> From<Time<T>> for Ratio<Planar64,Planar64>{ impl<T> From<Time<T>> for Ratio<Planar64,Planar64>{
@@ -73,11 +73,11 @@ impl<T,Num,Den,N1,T1> From<Ratio<Num,Den>> for Time<T>
where where
Num:core::ops::Mul<Planar64,Output=N1>, Num:core::ops::Mul<Planar64,Output=N1>,
N1:Divide<Den,Output=T1>, N1:Divide<Den,Output=T1>,
T1:Fix<Planar64>, T1:Clamp<Planar64>,
{ {
#[inline] #[inline]
fn from(value:Ratio<Num,Den>)->Self{ fn from(value:Ratio<Num,Den>)->Self{
Self::raw((value*Planar64::raw(1_000_000_000)).divide().fix().to_raw()) Self::raw((value*Planar64::raw(1_000_000_000)).divide().clamp().to_raw())
} }
} }
impl<T> std::fmt::Display for Time<T>{ impl<T> std::fmt::Display for Time<T>{
@@ -401,6 +401,10 @@ impl Angle32{
pub const NEG_FRAC_PI_2:Self=Self(-1<<30); pub const NEG_FRAC_PI_2:Self=Self(-1<<30);
pub const PI:Self=Self(-1<<31); pub const PI:Self=Self(-1<<31);
#[inline] #[inline]
pub const fn raw(num:i32)->Self{
Self(num)
}
#[inline]
pub const fn wrap_from_i64(theta:i64)->Self{ pub const fn wrap_from_i64(theta:i64)->Self{
//take lower bits //take lower bits
//note: this was checked on compiler explorer and compiles to 1 instruction! //note: this was checked on compiler explorer and compiles to 1 instruction!
@@ -515,8 +519,8 @@ fn angle_sin_cos(){
println!("cordic s={} c={}",(s/h).divide(),(c/h).divide()); println!("cordic s={} c={}",(s/h).divide(),(c/h).divide());
let (fs,fc)=f.sin_cos(); let (fs,fc)=f.sin_cos();
println!("float s={} c={}",fs,fc); println!("float s={} c={}",fs,fc);
assert!(close_enough((c/h).divide().fix_1(),Planar64::raw((fc*((1u64<<32) as f64)) as i64))); assert!(close_enough((c/h).divide().wrap_1(),Planar64::raw((fc*((1u64<<32) as f64)) as i64)));
assert!(close_enough((s/h).divide().fix_1(),Planar64::raw((fs*((1u64<<32) as f64)) as i64))); assert!(close_enough((s/h).divide().wrap_1(),Planar64::raw((fs*((1u64<<32) as f64)) as i64)));
} }
test_angle(1.0); test_angle(1.0);
test_angle(std::f64::consts::PI/4.0); test_angle(std::f64::consts::PI/4.0);
@@ -558,6 +562,10 @@ pub mod vec3{
pub const MAX:Planar64Vec3=Planar64Vec3::new([Planar64::MAX;3]); pub const MAX:Planar64Vec3=Planar64Vec3::new([Planar64::MAX;3]);
pub const ZERO:Planar64Vec3=Planar64Vec3::new([Planar64::ZERO;3]); pub const ZERO:Planar64Vec3=Planar64Vec3::new([Planar64::ZERO;3]);
pub const ZERO_2:linear_ops::types::Vector3<Fixed::<2,64>>=linear_ops::types::Vector3::new([Fixed::<2,64>::ZERO;3]); pub const ZERO_2:linear_ops::types::Vector3<Fixed::<2,64>>=linear_ops::types::Vector3::new([Fixed::<2,64>::ZERO;3]);
pub const ZERO_3:linear_ops::types::Vector3<Fixed::<3,96>>=linear_ops::types::Vector3::new([Fixed::<3,96>::ZERO;3]);
pub const ZERO_4:linear_ops::types::Vector3<Fixed::<4,128>>=linear_ops::types::Vector3::new([Fixed::<4,128>::ZERO;3]);
pub const ZERO_5:linear_ops::types::Vector3<Fixed::<5,160>>=linear_ops::types::Vector3::new([Fixed::<5,160>::ZERO;3]);
pub const ZERO_6:linear_ops::types::Vector3<Fixed::<6,192>>=linear_ops::types::Vector3::new([Fixed::<6,192>::ZERO;3]);
pub const X:Planar64Vec3=Planar64Vec3::new([Planar64::ONE,Planar64::ZERO,Planar64::ZERO]); pub const X:Planar64Vec3=Planar64Vec3::new([Planar64::ONE,Planar64::ZERO,Planar64::ZERO]);
pub const Y:Planar64Vec3=Planar64Vec3::new([Planar64::ZERO,Planar64::ONE,Planar64::ZERO]); pub const Y:Planar64Vec3=Planar64Vec3::new([Planar64::ZERO,Planar64::ONE,Planar64::ZERO]);
pub const Z:Planar64Vec3=Planar64Vec3::new([Planar64::ZERO,Planar64::ZERO,Planar64::ONE]); pub const Z:Planar64Vec3=Planar64Vec3::new([Planar64::ZERO,Planar64::ZERO,Planar64::ONE]);
@@ -625,8 +633,8 @@ pub mod mat3{
let (yc,ys)=y.cos_sin(); let (yc,ys)=y.cos_sin();
Planar64Mat3::from_cols([ Planar64Mat3::from_cols([
Planar64Vec3::new([xc,Planar64::ZERO,-xs]), Planar64Vec3::new([xc,Planar64::ZERO,-xs]),
Planar64Vec3::new([(xs*ys).fix_1(),yc,(xc*ys).fix_1()]), Planar64Vec3::new([(xs*ys).wrap_1(),yc,(xc*ys).wrap_1()]),
Planar64Vec3::new([(xs*yc).fix_1(),-ys,(xc*yc).fix_1()]), Planar64Vec3::new([(xs*yc).wrap_1(),-ys,(xc*yc).wrap_1()]),
]) ])
} }
#[inline] #[inline]
@@ -668,7 +676,7 @@ impl Planar64Affine3{
} }
#[inline] #[inline]
pub fn transform_point3(&self,point:Planar64Vec3)->vec3::Vector3<Fixed<2,64>>{ pub fn transform_point3(&self,point:Planar64Vec3)->vec3::Vector3<Fixed<2,64>>{
self.translation.fix_2()+self.matrix3*point self.translation.widen_2()+self.matrix3*point
} }
} }
impl Into<glam::Mat4> for Planar64Affine3{ impl Into<glam::Mat4> for Planar64Affine3{

View File

@@ -13,8 +13,8 @@ impl Ray{
Num:core::ops::Mul<Planar64,Output=N1>, Num:core::ops::Mul<Planar64,Output=N1>,
Planar64:core::ops::Mul<Den,Output=N1>, Planar64:core::ops::Mul<Den,Output=N1>,
N1:integer::Divide<Den,Output=T1>, N1:integer::Divide<Den,Output=T1>,
T1:integer::Fix<Planar64>, T1:integer::Clamp<Planar64>,
{ {
self.origin+self.direction.map(|elem|(t*elem).divide().fix()) self.origin+self.direction.map(|elem|(t*elem).divide().clamp())
} }
} }

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "fixed_wide" name = "fixed_wide"
version = "0.1.2" version = "0.2.0"
edition = "2024" edition = "2024"
repository = "https://git.itzana.me/StrafesNET/strafe-project" repository = "https://git.itzana.me/StrafesNET/strafe-project"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"

View File

@@ -663,74 +663,94 @@ macro_repeated!(
1,2,3,4,5,6,7,8 1,2,3,4,5,6,7,8
); );
pub trait Fix<Out>{ #[derive(Debug,Eq,PartialEq)]
fn fix(self)->Out; pub enum NarrowError{
Overflow,
Underflow,
} }
macro_rules! impl_fix_rhs_lt_lhs_not_const_generic{ pub trait Wrap<Output>{
fn wrap(self)->Output;
}
pub trait Clamp<Output>{
fn clamp(self)->Output;
}
impl<const N:usize,const F:usize> Clamp<Fixed<N,F>> for Result<Fixed<N,F>,NarrowError>{
fn clamp(self)->Fixed<N,F>{
match self{
Ok(fixed)=>fixed,
Err(NarrowError::Overflow)=>Fixed::MAX,
Err(NarrowError::Underflow)=>Fixed::MIN,
}
}
}
macro_rules! impl_narrow_not_const_generic{
( (
(), (),
($lhs:expr,$rhs:expr) ($lhs:expr,$rhs:expr)
)=>{ )=>{
impl Fixed<$lhs,{$lhs*32}> paste::item!{
{ impl Fixed<$lhs,{$lhs*32}>
paste::item!{ {
#[inline] #[inline]
pub fn [<fix_ $rhs>](self)->Fixed<$rhs,{$rhs*32}>{ pub fn [<wrap_ $rhs>](self)->Fixed<$rhs,{$rhs*32}>{
Fixed::from_bits(bnum::cast::As::as_::<BInt::<$rhs>>(self.bits.shr(($lhs-$rhs)*32))) Fixed::from_bits(bnum::cast::As::as_::<BInt::<$rhs>>(self.bits.shr(($lhs-$rhs)*32)))
} }
#[inline]
pub fn [<narrow_ $rhs>](self)->Result<Fixed<$rhs,{$rhs*32}>,NarrowError>{
if Fixed::<$rhs,{$rhs*32}>::MAX.[<widen_ $lhs>]().bits<self.bits{
return Err(NarrowError::Overflow);
}
if self.bits<Fixed::<$rhs,{$rhs*32}>::MIN.[<widen_ $lhs>]().bits{
return Err(NarrowError::Underflow);
}
Ok(self.[<wrap_ $rhs>]())
}
#[inline]
pub fn [<clamp_ $rhs>](self)->Fixed<$rhs,{$rhs*32}>{
self.[<narrow_ $rhs>]().clamp()
}
} }
} impl Wrap<Fixed<$rhs,{$rhs*32}>> for Fixed<$lhs,{$lhs*32}>{
impl Fix<Fixed<$rhs,{$rhs*32}>> for Fixed<$lhs,{$lhs*32}>{ #[inline]
fn fix(self)->Fixed<$rhs,{$rhs*32}>{ fn wrap(self)->Fixed<$rhs,{$rhs*32}>{
paste::item!{ self.[<wrap_ $rhs>]()
self.[<fix_ $rhs>]() }
}
impl TryInto<Fixed<$rhs,{$rhs*32}>> for Fixed<$lhs,{$lhs*32}>{
type Error=NarrowError;
#[inline]
fn try_into(self)->Result<Fixed<$rhs,{$rhs*32}>,Self::Error>{
self.[<narrow_ $rhs>]()
}
}
impl Clamp<Fixed<$rhs,{$rhs*32}>> for Fixed<$lhs,{$lhs*32}>{
#[inline]
fn clamp(self)->Fixed<$rhs,{$rhs*32}>{
self.[<clamp_ $rhs>]()
} }
} }
} }
} }
} }
macro_rules! impl_fix_lhs_lt_rhs_not_const_generic{ macro_rules! impl_widen_not_const_generic{
( (
(), (),
($lhs:expr,$rhs:expr) ($lhs:expr,$rhs:expr)
)=>{ )=>{
impl Fixed<$lhs,{$lhs*32}> paste::item!{
{ impl Fixed<$lhs,{$lhs*32}>
paste::item!{ {
#[inline] #[inline]
pub fn [<fix_ $rhs>](self)->Fixed<$rhs,{$rhs*32}>{ pub fn [<widen_ $rhs>](self)->Fixed<$rhs,{$rhs*32}>{
Fixed::from_bits(bnum::cast::As::as_::<BInt::<$rhs>>(self.bits).shl(($rhs-$lhs)*32)) Fixed::from_bits(bnum::cast::As::as_::<BInt::<$rhs>>(self.bits).shl(($rhs-$lhs)*32))
} }
} }
} impl Into<Fixed<$rhs,{$rhs*32}>> for Fixed<$lhs,{$lhs*32}>{
impl Fix<Fixed<$rhs,{$rhs*32}>> for Fixed<$lhs,{$lhs*32}>{
fn fix(self)->Fixed<$rhs,{$rhs*32}>{
paste::item!{
self.[<fix_ $rhs>]()
}
}
}
}
}
macro_rules! impl_fix_lhs_eq_rhs_not_const_generic{
(
(),
($lhs:expr,$rhs:expr)
)=>{
impl Fixed<$lhs,{$lhs*32}>
{
paste::item!{
#[inline] #[inline]
pub fn [<fix_ $rhs>](self)->Fixed<$rhs,{$rhs*32}>{ fn into(self)->Fixed<$rhs,{$rhs*32}>{
self self.[<widen_ $rhs>]()
}
}
}
impl Fix<Fixed<$rhs,{$rhs*32}>> for Fixed<$lhs,{$lhs*32}>{
fn fix(self)->Fixed<$rhs,{$rhs*32}>{
paste::item!{
self.[<fix_ $rhs>]()
} }
} }
} }
@@ -740,7 +760,7 @@ macro_rules! impl_fix_lhs_eq_rhs_not_const_generic{
// I LOVE NOT BEING ABLE TO USE CONST GENERICS // I LOVE NOT BEING ABLE TO USE CONST GENERICS
macro_repeated!( macro_repeated!(
impl_fix_rhs_lt_lhs_not_const_generic,(), impl_narrow_not_const_generic,(),
(2,1),(3,1),(4,1),(5,1),(6,1),(7,1),(8,1),(9,1),(10,1),(11,1),(12,1),(13,1),(14,1),(15,1),(16,1),(17,1), (2,1),(3,1),(4,1),(5,1),(6,1),(7,1),(8,1),(9,1),(10,1),(11,1),(12,1),(13,1),(14,1),(15,1),(16,1),(17,1),
(3,2),(4,2),(5,2),(6,2),(7,2),(8,2),(9,2),(10,2),(11,2),(12,2),(13,2),(14,2),(15,2),(16,2), (3,2),(4,2),(5,2),(6,2),(7,2),(8,2),(9,2),(10,2),(11,2),(12,2),(13,2),(14,2),(15,2),(16,2),
(4,3),(5,3),(6,3),(7,3),(8,3),(9,3),(10,3),(11,3),(12,3),(13,3),(14,3),(15,3),(16,3), (4,3),(5,3),(6,3),(7,3),(8,3),(9,3),(10,3),(11,3),(12,3),(13,3),(14,3),(15,3),(16,3),
@@ -758,7 +778,7 @@ macro_repeated!(
(16,15) (16,15)
); );
macro_repeated!( macro_repeated!(
impl_fix_lhs_lt_rhs_not_const_generic,(), impl_widen_not_const_generic,(),
(1,2), (1,2),
(1,3),(2,3), (1,3),(2,3),
(1,4),(2,4),(3,4), (1,4),(2,4),(3,4),
@@ -773,11 +793,8 @@ macro_repeated!(
(1,13),(2,13),(3,13),(4,13),(5,13),(6,13),(7,13),(8,13),(9,13),(10,13),(11,13),(12,13), (1,13),(2,13),(3,13),(4,13),(5,13),(6,13),(7,13),(8,13),(9,13),(10,13),(11,13),(12,13),
(1,14),(2,14),(3,14),(4,14),(5,14),(6,14),(7,14),(8,14),(9,14),(10,14),(11,14),(12,14),(13,14), (1,14),(2,14),(3,14),(4,14),(5,14),(6,14),(7,14),(8,14),(9,14),(10,14),(11,14),(12,14),(13,14),
(1,15),(2,15),(3,15),(4,15),(5,15),(6,15),(7,15),(8,15),(9,15),(10,15),(11,15),(12,15),(13,15),(14,15), (1,15),(2,15),(3,15),(4,15),(5,15),(6,15),(7,15),(8,15),(9,15),(10,15),(11,15),(12,15),(13,15),(14,15),
(1,16),(2,16),(3,16),(4,16),(5,16),(6,16),(7,16),(8,16),(9,16),(10,16),(11,16),(12,16),(13,16),(14,16),(15,16) (1,16),(2,16),(3,16),(4,16),(5,16),(6,16),(7,16),(8,16),(9,16),(10,16),(11,16),(12,16),(13,16),(14,16),(15,16),
); (1,17)
macro_repeated!(
impl_fix_lhs_eq_rhs_not_const_generic,(),
(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9),(10,10),(11,11),(12,12),(13,13),(14,14),(15,15),(16,16)
); );
macro_rules! impl_not_const_generic{ macro_rules! impl_not_const_generic{
@@ -797,7 +814,7 @@ macro_rules! impl_not_const_generic{
let mut result=Self::ZERO; let mut result=Self::ZERO;
//resize self to match the wide mul output //resize self to match the wide mul output
let wide_self=self.[<fix_ $_2n>](); let wide_self=self.[<widen_ $_2n>]();
//descend down the bits and check if flipping each bit would push the square over the input value //descend down the bits and check if flipping each bit would push the square over the input value
for shift in (0..=max_shift).rev(){ for shift in (0..=max_shift).rev(){
result.as_bits_mut().as_bits_mut().set_bit(shift,true); result.as_bits_mut().as_bits_mut().set_bit(shift,true);

View File

@@ -61,7 +61,7 @@ fn from_f32(){
let b:Result<I32F32,_>=Into::<f32>::into(I32F32::MIN).try_into(); let b:Result<I32F32,_>=Into::<f32>::into(I32F32::MIN).try_into();
assert_eq!(b,Err(crate::fixed::FixedFromFloatError::Overflow)); assert_eq!(b,Err(crate::fixed::FixedFromFloatError::Overflow));
//16 is within the 24 bits of float precision //16 is within the 24 bits of float precision
let b:Result<I32F32,_>=Into::<f32>::into(-I32F32::MIN.fix_2()).try_into(); let b:Result<I32F32,_>=Into::<f32>::into(-I32F32::MIN.widen_2()).try_into();
assert_eq!(b,Err(crate::fixed::FixedFromFloatError::Overflow)); assert_eq!(b,Err(crate::fixed::FixedFromFloatError::Overflow));
let b:Result<I32F32,_>=f32::MIN_POSITIVE.try_into(); let b:Result<I32F32,_>=f32::MIN_POSITIVE.try_into();
assert_eq!(b,Err(crate::fixed::FixedFromFloatError::Underflow)); assert_eq!(b,Err(crate::fixed::FixedFromFloatError::Underflow));
@@ -136,11 +136,24 @@ fn test_bint(){
} }
#[test] #[test]
fn test_fix(){ fn test_wrap(){
assert_eq!(I32F32::ONE.fix_8(),I256F256::ONE); assert_eq!(I32F32::ONE,I256F256::ONE.wrap_1());
assert_eq!(I32F32::ONE,I256F256::ONE.fix_1()); assert_eq!(I32F32::NEG_ONE,I256F256::NEG_ONE.wrap_1());
assert_eq!(I32F32::NEG_ONE.fix_8(),I256F256::NEG_ONE); }
assert_eq!(I32F32::NEG_ONE,I256F256::NEG_ONE.fix_1()); #[test]
fn test_narrow(){
assert_eq!(Ok(I32F32::ONE),I256F256::ONE.narrow_1());
assert_eq!(Ok(I32F32::NEG_ONE),I256F256::NEG_ONE.narrow_1());
}
#[test]
fn test_widen(){
assert_eq!(I32F32::ONE.widen_8(),I256F256::ONE);
assert_eq!(I32F32::NEG_ONE.widen_8(),I256F256::NEG_ONE);
}
#[test]
fn test_clamp(){
assert_eq!(I32F32::ONE,I256F256::ONE.clamp_1());
assert_eq!(I32F32::NEG_ONE,I256F256::NEG_ONE.clamp_1());
} }
#[test] #[test]
fn test_sqrt(){ fn test_sqrt(){

View File

@@ -15,8 +15,10 @@ macro_rules! impl_zeroes{
let radicand=a1*a1-a2*a0*4; let radicand=a1*a1-a2*a0*4;
match radicand.cmp(&<Self as core::ops::Mul>::Output::ZERO){ match radicand.cmp(&<Self as core::ops::Mul>::Output::ZERO){
Ordering::Greater=>{ Ordering::Greater=>{
// using wrap because sqrt always halves the number of leading digits.
// clamp would be more defensive, but is slower.
paste::item!{ paste::item!{
let planar_radicand=radicand.sqrt().[<fix_ $n>](); let planar_radicand=radicand.sqrt().[<wrap_ $n>]();
} }
//sort roots ascending and avoid taking the difference of large numbers //sort roots ascending and avoid taking the difference of large numbers
let zeroes=match (a2pos,Self::ZERO<a1){ let zeroes=match (a2pos,Self::ZERO<a1){

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "linear_ops" name = "linear_ops"
version = "0.1.0" version = "0.1.1"
edition = "2024" edition = "2024"
repository = "https://git.itzana.me/StrafesNET/strafe-project" repository = "https://git.itzana.me/StrafesNET/strafe-project"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
@@ -15,7 +15,7 @@ deferred-division=["dep:ratio_ops"]
[dependencies] [dependencies]
ratio_ops = { version = "0.1.0", path = "../ratio_ops", registry = "strafesnet", optional = true } ratio_ops = { version = "0.1.0", path = "../ratio_ops", registry = "strafesnet", optional = true }
fixed_wide = { version = "0.1.2", path = "../fixed_wide", registry = "strafesnet", optional = true } fixed_wide = { version = "0.2.0", path = "../fixed_wide", registry = "strafesnet", optional = true }
paste = { version = "1.0.15", optional = true } paste = { version = "1.0.15", optional = true }
[dev-dependencies] [dev-dependencies]

View File

@@ -38,40 +38,95 @@ macro_rules! impl_fixed_wide_vector {
$crate::macro_4!(impl_fixed_wide_vector_not_const_generic,()); $crate::macro_4!(impl_fixed_wide_vector_not_const_generic,());
// I LOVE NOT BEING ABLE TO USE CONST GENERICS // I LOVE NOT BEING ABLE TO USE CONST GENERICS
$crate::macro_repeated!( $crate::macro_repeated!(
impl_fix_not_const_generic,(), impl_narrow_not_const_generic,(),
(1,1),(2,1),(3,1),(4,1),(5,1),(6,1),(7,1),(8,1),(9,1),(10,1),(11,1),(12,1),(13,1),(14,1),(15,1),(16,1), (2,1),(3,1),(4,1),(5,1),(6,1),(7,1),(8,1),(9,1),(10,1),(11,1),(12,1),(13,1),(14,1),(15,1),(16,1),(17,1),
(1,2),(2,2),(3,2),(4,2),(5,2),(6,2),(7,2),(8,2),(9,2),(10,2),(11,2),(12,2),(13,2),(14,2),(15,2),(16,2), (3,2),(4,2),(5,2),(6,2),(7,2),(8,2),(9,2),(10,2),(11,2),(12,2),(13,2),(14,2),(15,2),(16,2),
(1,3),(2,3),(3,3),(4,3),(5,3),(6,3),(7,3),(8,3),(9,3),(10,3),(11,3),(12,3),(13,3),(14,3),(15,3),(16,3), (4,3),(5,3),(6,3),(7,3),(8,3),(9,3),(10,3),(11,3),(12,3),(13,3),(14,3),(15,3),(16,3),
(1,4),(2,4),(3,4),(4,4),(5,4),(6,4),(7,4),(8,4),(9,4),(10,4),(11,4),(12,4),(13,4),(14,4),(15,4),(16,4), (5,4),(6,4),(7,4),(8,4),(9,4),(10,4),(11,4),(12,4),(13,4),(14,4),(15,4),(16,4),
(1,5),(2,5),(3,5),(4,5),(5,5),(6,5),(7,5),(8,5),(9,5),(10,5),(11,5),(12,5),(13,5),(14,5),(15,5),(16,5), (6,5),(7,5),(8,5),(9,5),(10,5),(11,5),(12,5),(13,5),(14,5),(15,5),(16,5),
(1,6),(2,6),(3,6),(4,6),(5,6),(6,6),(7,6),(8,6),(9,6),(10,6),(11,6),(12,6),(13,6),(14,6),(15,6),(16,6), (7,6),(8,6),(9,6),(10,6),(11,6),(12,6),(13,6),(14,6),(15,6),(16,6),
(1,7),(2,7),(3,7),(4,7),(5,7),(6,7),(7,7),(8,7),(9,7),(10,7),(11,7),(12,7),(13,7),(14,7),(15,7),(16,7), (8,7),(9,7),(10,7),(11,7),(12,7),(13,7),(14,7),(15,7),(16,7),
(1,8),(2,8),(3,8),(4,8),(5,8),(6,8),(7,8),(8,8),(9,8),(10,8),(11,8),(12,8),(13,8),(14,8),(15,8),(16,8), (9,8),(10,8),(11,8),(12,8),(13,8),(14,8),(15,8),(16,8),
(1,9),(2,9),(3,9),(4,9),(5,9),(6,9),(7,9),(8,9),(9,9),(10,9),(11,9),(12,9),(13,9),(14,9),(15,9),(16,9), (10,9),(11,9),(12,9),(13,9),(14,9),(15,9),(16,9),
(1,10),(2,10),(3,10),(4,10),(5,10),(6,10),(7,10),(8,10),(9,10),(10,10),(11,10),(12,10),(13,10),(14,10),(15,10),(16,10), (11,10),(12,10),(13,10),(14,10),(15,10),(16,10),
(1,11),(2,11),(3,11),(4,11),(5,11),(6,11),(7,11),(8,11),(9,11),(10,11),(11,11),(12,11),(13,11),(14,11),(15,11),(16,11), (12,11),(13,11),(14,11),(15,11),(16,11),
(1,12),(2,12),(3,12),(4,12),(5,12),(6,12),(7,12),(8,12),(9,12),(10,12),(11,12),(12,12),(13,12),(14,12),(15,12),(16,12), (13,12),(14,12),(15,12),(16,12),
(1,13),(2,13),(3,13),(4,13),(5,13),(6,13),(7,13),(8,13),(9,13),(10,13),(11,13),(12,13),(13,13),(14,13),(15,13),(16,13), (14,13),(15,13),(16,13),
(1,14),(2,14),(3,14),(4,14),(5,14),(6,14),(7,14),(8,14),(9,14),(10,14),(11,14),(12,14),(13,14),(14,14),(15,14),(16,14), (15,14),(16,14),
(1,15),(2,15),(3,15),(4,15),(5,15),(6,15),(7,15),(8,15),(9,15),(10,15),(11,15),(12,15),(13,15),(14,15),(15,15),(16,15), (16,15)
(1,16),(2,16),(3,16),(4,16),(5,16),(6,16),(7,16),(8,16),(9,16),(10,16),(11,16),(12,16),(13,16),(14,16),(15,16),(16,16)
); );
$crate::macro_repeated!(
impl_widen_not_const_generic,(),
(1,2),
(1,3),(2,3),
(1,4),(2,4),(3,4),
(1,5),(2,5),(3,5),(4,5),
(1,6),(2,6),(3,6),(4,6),(5,6),
(1,7),(2,7),(3,7),(4,7),(5,7),(6,7),
(1,8),(2,8),(3,8),(4,8),(5,8),(6,8),(7,8),
(1,9),(2,9),(3,9),(4,9),(5,9),(6,9),(7,9),(8,9),
(1,10),(2,10),(3,10),(4,10),(5,10),(6,10),(7,10),(8,10),(9,10),
(1,11),(2,11),(3,11),(4,11),(5,11),(6,11),(7,11),(8,11),(9,11),(10,11),
(1,12),(2,12),(3,12),(4,12),(5,12),(6,12),(7,12),(8,12),(9,12),(10,12),(11,12),
(1,13),(2,13),(3,13),(4,13),(5,13),(6,13),(7,13),(8,13),(9,13),(10,13),(11,13),(12,13),
(1,14),(2,14),(3,14),(4,14),(5,14),(6,14),(7,14),(8,14),(9,14),(10,14),(11,14),(12,14),(13,14),
(1,15),(2,15),(3,15),(4,15),(5,15),(6,15),(7,15),(8,15),(9,15),(10,15),(11,15),(12,15),(13,15),(14,15),
(1,16),(2,16),(3,16),(4,16),(5,16),(6,16),(7,16),(8,16),(9,16),(10,16),(11,16),(12,16),(13,16),(14,16),(15,16),
(1,17)
);
impl<const N:usize,T:fixed_wide::fixed::Wrap<U>,U> fixed_wide::fixed::Wrap<Vector<N,U>> for Vector<N,T>
{
#[inline]
fn wrap(self)->Vector<N,U>{
self.map(|t|t.wrap())
}
}
impl<const N:usize,T:fixed_wide::fixed::Clamp<U>,U> fixed_wide::fixed::Clamp<Vector<N,U>> for Vector<N,T>
{
#[inline]
fn clamp(self)->Vector<N,U>{
self.map(|t|t.clamp())
}
}
}; };
} }
#[doc(hidden)] #[doc(hidden)]
#[macro_export(local_inner_macros)] #[macro_export(local_inner_macros)]
macro_rules! impl_fix_not_const_generic{ macro_rules! impl_narrow_not_const_generic{
( (
(), (),
($lhs:expr,$rhs:expr) ($lhs:expr,$rhs:expr)
)=>{ )=>{
impl<const N:usize> Vector<N,fixed_wide::fixed::Fixed<$lhs,{$lhs*32}>> paste::item!{
{ impl<const N:usize> Vector<N,fixed_wide::fixed::Fixed<$lhs,{$lhs*32}>>{
paste::item!{
#[inline] #[inline]
pub fn [<fix_ $rhs>](self)->Vector<N,fixed_wide::fixed::Fixed<$rhs,{$rhs*32}>>{ pub fn [<wrap_ $rhs>](self)->Vector<N,fixed_wide::fixed::Fixed<$rhs,{$rhs*32}>>{
self.map(|t|t.[<fix_ $rhs>]()) self.map(|t|t.[<wrap_ $rhs>]())
}
#[inline]
pub fn [<narrow_ $rhs>](self)->Vector<N,Result<fixed_wide::fixed::Fixed<$rhs,{$rhs*32}>,fixed_wide::fixed::NarrowError>>{
self.map(|t|t.[<narrow_ $rhs>]())
}
#[inline]
pub fn [<clamp_ $rhs>](self)->Vector<N,fixed_wide::fixed::Fixed<$rhs,{$rhs*32}>>{
self.map(|t|t.[<clamp_ $rhs>]())
}
}
}
}
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_widen_not_const_generic{
(
(),
($lhs:expr,$rhs:expr)
)=>{
paste::item!{
impl<const N:usize> Vector<N,fixed_wide::fixed::Fixed<$lhs,{$lhs*32}>>{
#[inline]
pub fn [<widen_ $rhs>](self)->Vector<N,fixed_wide::fixed::Fixed<$rhs,{$rhs*32}>>{
self.map(|t|t.[<widen_ $rhs>]())
} }
} }
} }

View File

@@ -58,6 +58,15 @@ macro_rules! impl_vector {
} }
} }
impl<const N:usize,T,E:std::fmt::Debug> Vector<N,Result<T,E>>{
#[inline]
pub fn unwrap(self)->Vector<N,T>{
Vector{
array:self.array.map(Result::unwrap)
}
}
}
impl<const N:usize,T:Ord> Vector<N,T>{ impl<const N:usize,T:Ord> Vector<N,T>{
#[inline] #[inline]
pub fn min(self,rhs:Self)->Self{ pub fn min(self,rhs:Self)->Self{

View File

@@ -47,7 +47,7 @@ fn planar64_affine3_from_roblox(cf:&rbx_dom_weak::types::CFrame,size:&rbx_dom_we
*integer::try_from_f32(size.y/2.0).unwrap(), *integer::try_from_f32(size.y/2.0).unwrap(),
vec3::try_from_f32_array([cf.orientation.x.z,cf.orientation.y.z,cf.orientation.z.z]).unwrap() vec3::try_from_f32_array([cf.orientation.x.z,cf.orientation.y.z,cf.orientation.z.z]).unwrap()
*integer::try_from_f32(size.z/2.0).unwrap(), *integer::try_from_f32(size.z/2.0).unwrap(),
].map(|t|t.fix_1())), ].map(|t|t.narrow_1().unwrap())),
vec3::try_from_f32_array([cf.position.x,cf.position.y,cf.position.z]).unwrap() vec3::try_from_f32_array([cf.position.x,cf.position.y,cf.position.z]).unwrap()
) )
} }
@@ -837,9 +837,9 @@ impl PartialMap1<'_>{
color:deferred_model_deferred_attributes.model.color, color:deferred_model_deferred_attributes.model.color,
transform:Planar64Affine3::new( transform:Planar64Affine3::new(
Planar64Mat3::from_cols([ Planar64Mat3::from_cols([
(deferred_model_deferred_attributes.model.transform.matrix3.x_axis*2/size.x).divide().fix_1(), (deferred_model_deferred_attributes.model.transform.matrix3.x_axis*2/size.x).divide().narrow_1().unwrap(),
(deferred_model_deferred_attributes.model.transform.matrix3.y_axis*2/size.y).divide().fix_1(), (deferred_model_deferred_attributes.model.transform.matrix3.y_axis*2/size.y).divide().narrow_1().unwrap(),
(deferred_model_deferred_attributes.model.transform.matrix3.z_axis*2/size.z).divide().fix_1() (deferred_model_deferred_attributes.model.transform.matrix3.z_axis*2/size.z).divide().narrow_1().unwrap(),
]), ]),
deferred_model_deferred_attributes.model.transform.translation deferred_model_deferred_attributes.model.transform.translation
), ),
@@ -861,9 +861,9 @@ impl PartialMap1<'_>{
color:deferred_union_deferred_attributes.model.color, color:deferred_union_deferred_attributes.model.color,
transform:Planar64Affine3::new( transform:Planar64Affine3::new(
Planar64Mat3::from_cols([ Planar64Mat3::from_cols([
(deferred_union_deferred_attributes.model.transform.matrix3.x_axis*2/size.x).divide().fix_1(), (deferred_union_deferred_attributes.model.transform.matrix3.x_axis*2/size.x).divide().narrow_1().unwrap(),
(deferred_union_deferred_attributes.model.transform.matrix3.y_axis*2/size.y).divide().fix_1(), (deferred_union_deferred_attributes.model.transform.matrix3.y_axis*2/size.y).divide().narrow_1().unwrap(),
(deferred_union_deferred_attributes.model.transform.matrix3.z_axis*2/size.z).divide().fix_1() (deferred_union_deferred_attributes.model.transform.matrix3.z_axis*2/size.z).divide().narrow_1().unwrap(),
]), ]),
deferred_union_deferred_attributes.model.transform.translation deferred_union_deferred_attributes.model.transform.translation
), ),

View File

@@ -386,7 +386,7 @@ pub fn write_map<W:BinWriterExt>(mut writer:W,map:strafesnet_common::map::Comple
let mesh=map.meshes.get(model.mesh.get() as usize).ok_or(Error::InvalidMeshId(model.mesh))?; let mesh=map.meshes.get(model.mesh.get() as usize).ok_or(Error::InvalidMeshId(model.mesh))?;
let mut aabb=strafesnet_common::aabb::Aabb::default(); let mut aabb=strafesnet_common::aabb::Aabb::default();
for &pos in &mesh.unique_pos{ for &pos in &mesh.unique_pos{
aabb.grow(model.transform.transform_point3(pos).fix_1()); aabb.grow(model.transform.transform_point3(pos).narrow_1().unwrap());
} }
Ok(((model::ModelId::new(model_id as u32),model.into()),aabb)) Ok(((model::ModelId::new(model_id as u32),model.into()),aabb))
}).collect::<Result<Vec<_>,_>>()?; }).collect::<Result<Vec<_>,_>>()?;

1
tools/dev Executable file
View File

@@ -0,0 +1 @@
RUST_BACKTRACE=1 mangohud ../target/debug/strafe-client "$@"