Compare commits
22 Commits
entity
...
minkowksi-
| Author | SHA1 | Date | |
|---|---|---|---|
| 05999f73f4 | |||
| dcdf57b389 | |||
| b8a0175686 | |||
| 2a5f610650 | |||
| 02615af1fd | |||
| 6594ffa87e | |||
| 6295dc68ef | |||
| 8fb0d30e1d | |||
| 0fb0230cb1 | |||
| bdb1090664 | |||
| 3ce7746489 | |||
| 7978050f8a | |||
| 77ee36fa72 | |||
| 0fc1ec3086 | |||
| f60c03ac56 | |||
| b570d8809d | |||
| 3f268ec034 | |||
| ba161ef6d1 | |||
| 0755d4413c | |||
| 459cb13957 | |||
| a5ad7d4d6e | |||
| 3d5e76f078 |
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -911,7 +911,7 @@ checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da"
|
||||
|
||||
[[package]]
|
||||
name = "fixed_wide"
|
||||
version = "0.1.2"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bnum",
|
||||
@@ -1860,7 +1860,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "linear_ops"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
dependencies = [
|
||||
"fixed_wide",
|
||||
"paste",
|
||||
|
||||
@@ -24,3 +24,7 @@ resolver = "2"
|
||||
#lto = true
|
||||
strip = true
|
||||
codegen-units = 1
|
||||
|
||||
[profile.dev]
|
||||
strip = false
|
||||
opt-level = 3
|
||||
|
||||
@@ -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{
|
||||
pipelines:GraphicsPipelines,
|
||||
bind_groups:GraphicsBindGroups,
|
||||
@@ -967,22 +987,3 @@ impl GraphicsState{
|
||||
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
|
||||
}
|
||||
|
||||
@@ -42,12 +42,12 @@ impl<T> Body<T>
|
||||
pub fn extrapolated_position(&self,time:Time<T>)->Planar64Vec3{
|
||||
let dt=time-self.time;
|
||||
self.position
|
||||
+(self.velocity*dt).map(|elem|elem.divide().fix_1())
|
||||
+self.acceleration.map(|elem|(dt*dt*elem/2).divide().fix_1())
|
||||
+(self.velocity*dt).map(|elem|elem.divide().clamp_1())
|
||||
+self.acceleration.map(|elem|(dt*dt*elem/2).divide().clamp_1())
|
||||
}
|
||||
pub fn extrapolated_velocity(&self,time:Time<T>)->Planar64Vec3{
|
||||
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>){
|
||||
self.position=self.extrapolated_position(time);
|
||||
@@ -71,12 +71,12 @@ impl<T> Body<T>
|
||||
D2:Copy,
|
||||
Planar64:core::ops::Mul<D2,Output=N4>,
|
||||
N4:integer::Divide<D2,Output=T1>,
|
||||
T1:integer::Fix<Planar64>,
|
||||
T1:integer::Clamp<Planar64>,
|
||||
{
|
||||
// a*dt^2/2 + v*dt + p
|
||||
// (a*dt/2+v)*dt+p
|
||||
(self.acceleration.map(|elem|dt*elem/2)+self.velocity).map(|elem|dt.mul_ratio(elem))
|
||||
.map(|elem|elem.divide().fix())+self.position
|
||||
.map(|elem|elem.divide().clamp())+self.position
|
||||
}
|
||||
pub fn extrapolated_velocity_ratio_dt<Num,Den,N1,T1>(&self,dt:integer::Ratio<Num,Den>)->Planar64Vec3
|
||||
where
|
||||
@@ -85,10 +85,10 @@ impl<T> Body<T>
|
||||
Num:core::ops::Mul<Planar64,Output=N1>,
|
||||
Planar64:core::ops::Mul<Den,Output=N1>,
|
||||
N1:integer::Divide<Den,Output=T1>,
|
||||
T1:integer::Fix<Planar64>,
|
||||
T1:integer::Clamp<Planar64>,
|
||||
{
|
||||
// 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){
|
||||
self.position=self.extrapolated_position_ratio_dt(dt);
|
||||
|
||||
@@ -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
|
||||
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 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: 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(){
|
||||
best_time=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
|
||||
let edge_n=mesh.edge_n(edge_id);
|
||||
let edge_verts=mesh.edge_verts(edge_id);
|
||||
let delta_pos=body.position*2-(mesh.vert(edge_verts[0])+mesh.vert(edge_verts[1]));
|
||||
for (i,&edge_face_id) in mesh.edge_faces(edge_id).iter().enumerate(){
|
||||
let &[ev0,ev1]=edge_verts.as_ref();
|
||||
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;
|
||||
//edge_n gets parity from the order of edge_faces
|
||||
let n=face_n.cross(edge_n)*((i as i64)*2-1);
|
||||
//WARNING yada yada d *2
|
||||
for 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(){
|
||||
best_time=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
|
||||
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
|
||||
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)){
|
||||
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_transition=Transition::Next(FEV::Vert(vert_id),dt);
|
||||
break;
|
||||
@@ -108,12 +111,12 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
|
||||
},
|
||||
&FEV::Vert(vert_id)=>{
|
||||
//test each edge collision time, ignoring roots with zero or conflicting derivative
|
||||
for &directed_edge_id in mesh.vert_edges(vert_id).iter(){
|
||||
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
|
||||
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)){
|
||||
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_transition=Transition::Next(FEV::Edge(directed_edge_id.as_undirected()),dt);
|
||||
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>{
|
||||
let mut body_time={
|
||||
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 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{
|
||||
match self.next_transition(body_time,mesh,relative_body,time_limit){
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use std::borrow::{Borrow,Cow};
|
||||
use std::collections::{HashSet,HashMap};
|
||||
use core::ops::Range;
|
||||
use strafesnet_common::integer::vec3::Vector3;
|
||||
@@ -8,6 +7,13 @@ use strafesnet_common::physics::Time;
|
||||
|
||||
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{
|
||||
type DirectedEdge:Copy+DirectedEdge;
|
||||
fn as_directed(&self,parity:bool)->Self::DirectedEdge;
|
||||
@@ -69,27 +75,27 @@ struct Face{
|
||||
}
|
||||
struct Vert(Planar64Vec3);
|
||||
pub trait MeshQuery{
|
||||
type Face:Clone;
|
||||
type Edge:Clone+DirectedEdge;
|
||||
type Vert:Clone;
|
||||
type Face:Copy;
|
||||
type Edge:Copy+DirectedEdge;
|
||||
type Vert:Copy;
|
||||
// Vertex must be Planar64Vec3 because it represents an actual position
|
||||
type Normal;
|
||||
type Offset;
|
||||
fn edge_n(&self,edge_id:<Self::Edge as DirectedEdge>::UndirectedEdge)->Planar64Vec3{
|
||||
let verts=self.edge_verts(edge_id);
|
||||
self.vert(verts[1].clone())-self.vert(verts[0].clone())
|
||||
let &[v0,v1]=self.edge_verts(edge_id).as_ref();
|
||||
self.vert(v1)-self.vert(v0)
|
||||
}
|
||||
fn directed_edge_n(&self,directed_edge_id:Self::Edge)->Planar64Vec3{
|
||||
let verts=self.edge_verts(directed_edge_id.as_undirected());
|
||||
(self.vert(verts[1].clone())-self.vert(verts[0].clone()))*((directed_edge_id.parity() as i64)*2-1)
|
||||
let &[v0,v1]=self.edge_verts(directed_edge_id.as_undirected()).as_ref();
|
||||
(self.vert(v1)-self.vert(v0))*((directed_edge_id.parity() as i64)*2-1)
|
||||
}
|
||||
fn vert(&self,vert_id:Self::Vert)->Planar64Vec3;
|
||||
fn face_nd(&self,face_id:Self::Face)->(Self::Normal,Self::Offset);
|
||||
fn face_edges(&self,face_id:Self::Face)->Cow<[Self::Edge]>;
|
||||
fn edge_faces(&self,edge_id:<Self::Edge as DirectedEdge>::UndirectedEdge)->Cow<[Self::Face;2]>;
|
||||
fn edge_verts(&self,edge_id:<Self::Edge as DirectedEdge>::UndirectedEdge)->Cow<[Self::Vert;2]>;
|
||||
fn vert_edges(&self,vert_id:Self::Vert)->Cow<[Self::Edge]>;
|
||||
fn vert_faces(&self,vert_id:Self::Vert)->Cow<[Self::Face]>;
|
||||
fn face_edges(&self,face_id:Self::Face)->impl AsRef<[Self::Edge]>;
|
||||
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)->impl AsRef<[Self::Vert;2]>;
|
||||
fn vert_edges(&self,vert_id:Self::Vert)->impl AsRef<[Self::Edge]>;
|
||||
fn vert_faces(&self,vert_id:Self::Vert)->impl AsRef<[Self::Face]>;
|
||||
}
|
||||
struct FaceRefs{
|
||||
edges:Vec<SubmeshDirectedEdgeId>,
|
||||
@@ -372,8 +378,8 @@ impl TryFrom<&model::Mesh> for PhysicsMesh{
|
||||
}
|
||||
//assume face hash is stable, and there are no flush faces...
|
||||
let face=Face{
|
||||
normal:(normal/len as i64).divide().fix_1(),
|
||||
dot:(dot/(len*len) as i64).fix_1(),
|
||||
normal:(normal/len as i64).divide().narrow_1().unwrap(),
|
||||
dot:(dot/(len*len) as i64).narrow_1().unwrap(),
|
||||
};
|
||||
let face_id=match face_id_from_face.get(&face){
|
||||
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;
|
||||
self.data.verts[vert_idx].0
|
||||
}
|
||||
fn face_edges(&self,face_id:SubmeshFaceId)->Cow<[SubmeshDirectedEdgeId]>{
|
||||
Cow::Borrowed(&self.topology.face_topology[face_id.get() as usize].edges)
|
||||
fn face_edges(&self,face_id:SubmeshFaceId)->impl AsRef<[SubmeshDirectedEdgeId]>{
|
||||
self.topology.face_topology[face_id.get() as usize].edges.as_slice()
|
||||
}
|
||||
fn edge_faces(&self,edge_id:SubmeshEdgeId)->Cow<[SubmeshFaceId;2]>{
|
||||
Cow::Borrowed(&self.topology.edge_topology[edge_id.get() as usize].faces)
|
||||
fn edge_faces(&self,edge_id:SubmeshEdgeId)->impl AsRef<[SubmeshFaceId;2]>{
|
||||
AsRefHelper(self.topology.edge_topology[edge_id.get() as usize].faces)
|
||||
}
|
||||
fn edge_verts(&self,edge_id:SubmeshEdgeId)->Cow<[SubmeshVertId;2]>{
|
||||
Cow::Borrowed(&self.topology.edge_topology[edge_id.get() as usize].verts)
|
||||
fn edge_verts(&self,edge_id:SubmeshEdgeId)->impl AsRef<[SubmeshVertId;2]>{
|
||||
AsRefHelper(self.topology.edge_topology[edge_id.get() as usize].verts)
|
||||
}
|
||||
fn vert_edges(&self,vert_id:SubmeshVertId)->Cow<[SubmeshDirectedEdgeId]>{
|
||||
Cow::Borrowed(&self.topology.vert_topology[vert_id.get() as usize].edges)
|
||||
fn vert_edges(&self,vert_id:SubmeshVertId)->impl AsRef<[SubmeshDirectedEdgeId]>{
|
||||
self.topology.vert_topology[vert_id.get() as usize].edges.as_slice()
|
||||
}
|
||||
fn vert_faces(&self,vert_id:SubmeshVertId)->Cow<[SubmeshFaceId]>{
|
||||
Cow::Borrowed(&self.topology.vert_topology[vert_id.get() as usize].faces)
|
||||
fn vert_faces(&self,vert_id:SubmeshVertId)->impl AsRef<[SubmeshFaceId]>{
|
||||
self.topology.vert_topology[vert_id.get() as usize].faces.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -513,26 +519,27 @@ impl MeshQuery for TransformedMesh<'_>{
|
||||
(transformed_n,transformed_d)
|
||||
}
|
||||
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]
|
||||
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)
|
||||
}
|
||||
#[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)
|
||||
}
|
||||
#[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)
|
||||
}
|
||||
#[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)
|
||||
}
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
@@ -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{
|
||||
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);
|
||||
//is boundary uncrossable by a crawl from infinity
|
||||
let edge_verts=self.edge_verts(directed_edge_id.as_undirected());
|
||||
//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
|
||||
let diff=point-self.vert(test_vert_id);
|
||||
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{
|
||||
let mut best_transition=EV::Vert(vert_id);
|
||||
let diff=point-self.vert(vert_id);
|
||||
for &directed_edge_id in self.vert_edges(vert_id).iter(){
|
||||
for &directed_edge_id in self.vert_edges(vert_id).as_ref(){
|
||||
let edge_n=self.directed_edge_n(directed_edge_id);
|
||||
//is boundary uncrossable by a crawl from infinity
|
||||
//check if time of collision is outside Time::MIN..Time::MAX
|
||||
@@ -653,7 +660,8 @@ impl MinkowskiMesh<'_>{
|
||||
if !d.is_negative()&&d<=edge_nn{
|
||||
let distance_squared={
|
||||
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{
|
||||
best_transition=EV::Edge(directed_edge_id.as_undirected());
|
||||
@@ -689,10 +697,10 @@ impl MinkowskiMesh<'_>{
|
||||
let edge_n=self.edge_n(edge_id);
|
||||
// point is multiplied by two because vert_sum sums two vertices.
|
||||
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)
|
||||
};
|
||||
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;
|
||||
//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);
|
||||
@@ -749,7 +757,7 @@ impl MinkowskiMesh<'_>{
|
||||
.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)
|
||||
//determine when it passes an edge ("sliding off" case)
|
||||
let start_time={
|
||||
@@ -758,19 +766,20 @@ impl MinkowskiMesh<'_>{
|
||||
};
|
||||
let mut best_time={
|
||||
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 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);
|
||||
//f x e points in
|
||||
let n=face_n.cross(edge_n);
|
||||
let verts=self.edge_verts(directed_edge_id.as_undirected());
|
||||
let d=n.dot(self.vert(verts[0])+self.vert(verts[1]));
|
||||
let &[v0,v1]=self.edge_verts(directed_edge_id.as_undirected()).as_ref();
|
||||
let d=n.dot(self.vert(v0)+self.vert(v1));
|
||||
//WARNING! d outside of *2
|
||||
//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(){
|
||||
best_time=dt;
|
||||
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)>{
|
||||
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)=>{
|
||||
let edge0_n=self.mesh0.edge_n(e0);
|
||||
let edge1_n=self.mesh1.edge_n(e1);
|
||||
let &[e0v0,e0v1]=self.mesh0.edge_verts(e0).borrow();
|
||||
let &[e1v0,e1v1]=self.mesh1.edge_verts(e1).borrow();
|
||||
let &[e0v0,e0v1]=self.mesh0.edge_verts(e0).as_ref();
|
||||
let &[e1v0,e1v1]=self.mesh1.edge_verts(e1).as_ref();
|
||||
let n=edge0_n.cross(edge1_n);
|
||||
let e0d=n.dot(self.mesh0.vert(e0v0)+self.mesh0.vert(e0v1));
|
||||
let e1d=n.dot(self.mesh1.vert(e1v0)+self.mesh1.vert(e1v1));
|
||||
((n*(parity as i64*4-2)).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)=>{
|
||||
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{
|
||||
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())
|
||||
}).collect())
|
||||
).collect()
|
||||
},
|
||||
MinkowskiFace::EdgeEdge(e0,e1,parity)=>{
|
||||
let e0v=self.mesh0.edge_verts(e0);
|
||||
let e1v=self.mesh1.edge_verts(e1);
|
||||
let &[e0v0,e0v1]=self.mesh0.edge_verts(e0).as_ref();
|
||||
let &[e1v0,e1v1]=self.mesh1.edge_verts(e1).as_ref();
|
||||
//could sort this if ordered edges are needed
|
||||
//probably just need to reverse this list according to parity
|
||||
Cow::Owned(vec![
|
||||
MinkowskiDirectedEdge::VertEdge(e0v[0],e1.as_directed(parity)),
|
||||
MinkowskiDirectedEdge::EdgeVert(e0.as_directed(!parity),e1v[0]),
|
||||
MinkowskiDirectedEdge::VertEdge(e0v[1],e1.as_directed(!parity)),
|
||||
MinkowskiDirectedEdge::EdgeVert(e0.as_directed(parity),e1v[1]),
|
||||
])
|
||||
vec![
|
||||
MinkowskiDirectedEdge::VertEdge(e0v0,e1.as_directed(parity)),
|
||||
MinkowskiDirectedEdge::EdgeVert(e0.as_directed(!parity),e1v0),
|
||||
MinkowskiDirectedEdge::VertEdge(e0v1,e1.as_directed(!parity)),
|
||||
MinkowskiDirectedEdge::EdgeVert(e0.as_directed(parity),e1v1),
|
||||
]
|
||||
},
|
||||
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)
|
||||
}).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{
|
||||
MinkowskiEdge::VertEdge(v0,e1)=>{
|
||||
//faces are listed backwards from the minkowski mesh
|
||||
let v0e=self.mesh0.vert_edges(v0);
|
||||
let &[e1f0,e1f1]=self.mesh1.edge_faces(e1).borrow();
|
||||
Cow::Owned([(e1f1,false),(e1f0,true)].map(|(edge_face_id1,face_parity)|{
|
||||
let &[e1f0,e1f1]=self.mesh1.edge_faces(e1).as_ref();
|
||||
AsRefHelper([(e1f1,false),(e1f0,true)].map(|(edge_face_id1,face_parity)|{
|
||||
let mut best_edge=None;
|
||||
let mut best_d:Ratio<Fixed<8,256>,Fixed<8,256>>=Ratio::new(Fixed::ZERO,Fixed::ONE);
|
||||
let edge_face1_n=self.mesh1.face_nd(edge_face_id1).0;
|
||||
let edge_face1_nn=edge_face1_n.dot(edge_face1_n);
|
||||
for &directed_edge_id0 in v0e.iter(){
|
||||
for &directed_edge_id0 in v0e.as_ref(){
|
||||
let edge0_n=self.mesh0.directed_edge_n(directed_edge_id0);
|
||||
//must be behind other face.
|
||||
let d=edge_face1_n.dot(edge0_n);
|
||||
@@ -892,13 +901,13 @@ impl MeshQuery for MinkowskiMesh<'_>{
|
||||
MinkowskiEdge::EdgeVert(e0,v1)=>{
|
||||
//tracking index with an external variable because .enumerate() is not available
|
||||
let v1e=self.mesh1.vert_edges(v1);
|
||||
let &[e0f0,e0f1]=self.mesh0.edge_faces(e0).borrow();
|
||||
Cow::Owned([(e0f0,true),(e0f1,false)].map(|(edge_face_id0,face_parity)|{
|
||||
let &[e0f0,e0f1]=self.mesh0.edge_faces(e0).as_ref();
|
||||
AsRefHelper([(e0f0,true),(e0f1,false)].map(|(edge_face_id0,face_parity)|{
|
||||
let mut best_edge=None;
|
||||
let mut best_d:Ratio<Fixed<8,256>,Fixed<8,256>>=Ratio::new(Fixed::ZERO,Fixed::ONE);
|
||||
let edge_face0_n=self.mesh0.face_nd(edge_face_id0).0;
|
||||
let edge_face0_nn=edge_face0_n.dot(edge_face0_n);
|
||||
for &directed_edge_id1 in v1e.iter(){
|
||||
for &directed_edge_id1 in v1e.as_ref(){
|
||||
let edge1_n=self.mesh1.directed_edge_n(directed_edge_id1);
|
||||
let d=edge_face0_n.dot(edge1_n);
|
||||
if d.is_negative(){
|
||||
@@ -918,31 +927,27 @@ impl MeshQuery for MinkowskiMesh<'_>{
|
||||
},
|
||||
}
|
||||
}
|
||||
fn edge_verts(&self,edge_id:MinkowskiEdge)->Cow<[MinkowskiVert;2]>{
|
||||
match edge_id{
|
||||
MinkowskiEdge::VertEdge(v0,e1)=>{
|
||||
Cow::Owned(self.mesh1.edge_verts(e1).map(|vert_id1|{
|
||||
MinkowskiVert::VertVert(v0,vert_id1)
|
||||
}))
|
||||
},
|
||||
MinkowskiEdge::EdgeVert(e0,v1)=>{
|
||||
Cow::Owned(self.mesh0.edge_verts(e0).map(|vert_id0|{
|
||||
MinkowskiVert::VertVert(vert_id0,v1)
|
||||
}))
|
||||
},
|
||||
}
|
||||
fn edge_verts(&self,edge_id:MinkowskiEdge)->impl AsRef<[MinkowskiVert;2]>{
|
||||
AsRefHelper(match edge_id{
|
||||
MinkowskiEdge::VertEdge(v0,e1)=>(*self.mesh1.edge_verts(e1).as_ref()).map(|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)
|
||||
),
|
||||
})
|
||||
}
|
||||
fn vert_edges(&self,vert_id:MinkowskiVert)->Cow<[MinkowskiDirectedEdge]>{
|
||||
fn vert_edges(&self,vert_id:MinkowskiVert)->impl AsRef<[MinkowskiDirectedEdge]>{
|
||||
match vert_id{
|
||||
MinkowskiVert::VertVert(v0,v1)=>{
|
||||
let mut edges=Vec::new();
|
||||
//detect shared volume when the other mesh is mirrored along a test edge dir
|
||||
let v0f=self.mesh0.vert_faces(v0);
|
||||
let v1f=self.mesh1.vert_faces(v1);
|
||||
let v0f_n:Vec<_>=v0f.iter().map(|&face_id|self.mesh0.face_nd(face_id).0).collect();
|
||||
let v1f_n:Vec<_>=v1f.iter().map(|&face_id|self.mesh1.face_nd(face_id).0).collect();
|
||||
let the_len=v0f.len()+v1f.len();
|
||||
for &directed_edge_id in self.mesh0.vert_edges(v0).iter(){
|
||||
let v0f_n:Vec<_>=v0f.as_ref().iter().map(|&face_id|self.mesh0.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.as_ref().len()+v1f.as_ref().len();
|
||||
for &directed_edge_id in self.mesh0.vert_edges(v0).as_ref(){
|
||||
let n=self.mesh0.directed_edge_n(directed_edge_id);
|
||||
let nn=n.dot(n);
|
||||
// TODO: there's gotta be a better way to do this
|
||||
@@ -952,30 +957,33 @@ impl MeshQuery for MinkowskiMesh<'_>{
|
||||
face_normals.clone_from(&v0f_n);
|
||||
for face_n in &v1f_n{
|
||||
//add reflected mesh1 faces
|
||||
face_normals.push(*face_n-(n*face_n.dot(n)*2/nn).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){
|
||||
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 nn=n.dot(n);
|
||||
let mut face_normals=Vec::with_capacity(the_len);
|
||||
face_normals.clone_from(&v1f_n);
|
||||
for face_n in &v0f_n{
|
||||
face_normals.push(*face_n-(n*face_n.dot(n)*2/nn).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){
|
||||
edges.push(MinkowskiDirectedEdge::VertEdge(v0,directed_edge_id));
|
||||
}
|
||||
}
|
||||
Cow::Owned(edges)
|
||||
edges
|
||||
},
|
||||
}
|
||||
}
|
||||
fn vert_faces(&self,_vert_id:MinkowskiVert)->Cow<[MinkowskiFace]>{
|
||||
unimplemented!()
|
||||
fn vert_faces(&self,_vert_id:MinkowskiVert)->impl AsRef<[MinkowskiFace]>{
|
||||
unimplemented!();
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1005,6 +1013,6 @@ fn is_empty_volume(normals:Vec<Vector3<Fixed<3,96>>>)->bool{
|
||||
|
||||
#[test]
|
||||
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.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()].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()));
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ use strafesnet_common::physics::{Instruction,MouseInstruction,ModeInstruction,Mi
|
||||
#[derive(Debug)]
|
||||
pub enum InternalInstruction{
|
||||
CollisionStart(Collision,model_physics::GigaTime),
|
||||
// transfer to a flush minkowski face
|
||||
CollisionTransfer(ContactCollision,model_physics::MinkowskiFace),
|
||||
CollisionEnd(Collision,model_physics::GigaTime),
|
||||
StrafeTick,
|
||||
ReachWalkTargetVelocity,
|
||||
@@ -117,8 +119,8 @@ impl TransientAcceleration{
|
||||
}else{
|
||||
//normal friction acceleration is clippedAcceleration.dot(normal)*friction
|
||||
TransientAcceleration::Reachable{
|
||||
acceleration:target_diff.with_length(accel).divide().fix_1(),
|
||||
time:time+Time::from((target_diff.length()/accel).divide().fix_1())
|
||||
acceleration:target_diff.with_length(accel).divide().wrap_1(),
|
||||
time:time+Time::from((target_diff.length()/accel).divide().clamp_1())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -407,7 +409,7 @@ impl HitboxMesh{
|
||||
let transform=PhysicsMeshTransform::new(transform);
|
||||
let transformed_mesh=TransformedMesh::new(mesh.complete_mesh_view(),&transform);
|
||||
for vert in transformed_mesh.verts(){
|
||||
aabb.grow(vert.fix_1());
|
||||
aabb.grow(vert.narrow_1().unwrap());
|
||||
}
|
||||
Self{
|
||||
halfsize:aabb.size()>>1,
|
||||
@@ -460,12 +462,12 @@ impl StyleHelper for StyleModifiers{
|
||||
}
|
||||
|
||||
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{
|
||||
//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{
|
||||
let mesh=match self.hitbox.mesh{
|
||||
@@ -785,19 +787,39 @@ impl TouchingState{
|
||||
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){
|
||||
use model_physics::DirectedEdge;
|
||||
// let relative_body=body.relative_to(&Body::ZERO);
|
||||
let relative_body=body;
|
||||
for contact in &self.contacts{
|
||||
//detect face slide off
|
||||
let model_mesh=models.contact_mesh(contact);
|
||||
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh());
|
||||
collector.collect(minkowski.predict_collision_face_out(&relative_body,start_time..collector.time(),contact.face_id).map(|(_face,time)|{
|
||||
TimedInstruction{
|
||||
time:relative_body.time+time.into(),
|
||||
instruction:InternalInstruction::CollisionEnd(
|
||||
Collision::Contact(*contact),
|
||||
time
|
||||
),
|
||||
collector.collect(minkowski.predict_collision_face_out(&relative_body,start_time..collector.time(),contact.face_id).map(|(out_edge,time)|{
|
||||
// TODO: determine if this code should go inside predict_collision_face_out
|
||||
// the face across may be left or right depending on the directed edge parity
|
||||
let &[f0,f1]=minkowski.edge_faces(out_edge.as_undirected()).as_ref();
|
||||
let (f0n,_f0d)=minkowski.face_nd(f0);
|
||||
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{
|
||||
/// use with caution, this is the only non-instruction way to mess with physics
|
||||
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_intersect_attributes=Vec::new();
|
||||
|
||||
@@ -1084,7 +1106,7 @@ impl PhysicsData{
|
||||
let mut aabb=aabb::Aabb::default();
|
||||
let transformed_mesh=TransformedMesh::new(view,transform);
|
||||
for v in transformed_mesh.verts(){
|
||||
aabb.grow(v.fix_1());
|
||||
aabb.grow(v.narrow_1().unwrap());
|
||||
}
|
||||
(ConvexMeshId{
|
||||
model_id,
|
||||
@@ -1162,7 +1184,8 @@ fn contact_normal(models:&PhysicsModels,hitbox_mesh:&HitboxMesh,contact:&Contact
|
||||
let model_mesh=models.contact_mesh(contact);
|
||||
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh());
|
||||
// TODO: normalize to i64::MAX>>1
|
||||
minkowski.face_nd(contact.face_id).0.fix_1()
|
||||
// wrap for speed
|
||||
minkowski.face_nd(contact.face_id).0.wrap_1()
|
||||
}
|
||||
|
||||
fn recalculate_touching(
|
||||
@@ -1321,7 +1344,7 @@ fn teleport_to_spawn(
|
||||
const EPSILON:Planar64=Planar64::raw((1<<32)/16);
|
||||
let transform=models.get_model_transform(spawn_model_id).ok_or(TeleportToSpawnError::NoModel)?;
|
||||
//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);
|
||||
Ok(())
|
||||
}
|
||||
@@ -1485,7 +1508,7 @@ fn collision_start_contact(
|
||||
Some(gameplay_attributes::ContactingBehaviour::Surf)=>(),
|
||||
Some(gameplay_attributes::ContactingBehaviour::Cling)=>println!("Unimplemented!"),
|
||||
&Some(gameplay_attributes::ContactingBehaviour::Elastic(elasticity))=>{
|
||||
let reflected_velocity=body.velocity+((body.velocity-incident_velocity)*Planar64::raw(elasticity as i64+1)).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);
|
||||
},
|
||||
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::StrafeTick
|
||||
|InternalInstruction::ReachWalkTargetVelocity=>(true,None),
|
||||
InternalInstruction::CollisionTransfer(..)=>(false,None),
|
||||
};
|
||||
if should_advance_body{
|
||||
match goober_time{
|
||||
@@ -1699,6 +1723,15 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
|
||||
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=>{
|
||||
//TODO make this less huge
|
||||
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);
|
||||
if control_dir!=vec3::ZERO{
|
||||
let camera_mat=state.camera.simulate_move_rotation_y(state.input_state.lerp_delta(state.time).x);
|
||||
if let Some(ticked_velocity)=strafe_settings.tick_velocity(state.body.velocity,(camera_mat*control_dir).with_length(Planar64::ONE).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
|
||||
//need to note which push planes activate in push solve and keep those
|
||||
state.cull_velocity(data,ticked_velocity);
|
||||
|
||||
@@ -152,18 +152,19 @@ const fn get_push_ray_0(point:Planar64Vec3)->Ray{
|
||||
Ray{origin:point,direction:vec3::ZERO}
|
||||
}
|
||||
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)?;
|
||||
if s0.lt_ratio(RATIO_ZERO){
|
||||
return None;
|
||||
}
|
||||
let origin=point+solve1(
|
||||
&c0.relative_to(point),
|
||||
)?.divide().fix_1();
|
||||
)?.divide().wrap_1();
|
||||
Some(Ray{origin,direction})
|
||||
}
|
||||
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)?;
|
||||
if s0.lt_ratio(RATIO_ZERO)||s1.lt_ratio(RATIO_ZERO){
|
||||
return None;
|
||||
@@ -171,11 +172,11 @@ fn get_push_ray_2(point:Planar64Vec3,c0:&Contact,c1:&Contact)->Option<Ray>{
|
||||
let origin=point+solve2(
|
||||
&c0.relative_to(point),
|
||||
&c1.relative_to(point),
|
||||
)?.divide().fix_1();
|
||||
)?.divide().wrap_1();
|
||||
Some(Ray{origin,direction})
|
||||
}
|
||||
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)?;
|
||||
if s0.lt_ratio(RATIO_ZERO)||s1.lt_ratio(RATIO_ZERO)||s2.lt_ratio(RATIO_ZERO){
|
||||
return None;
|
||||
@@ -184,7 +185,7 @@ fn get_push_ray_3(point:Planar64Vec3,c0:&Contact,c1:&Contact,c2:&Contact)->Optio
|
||||
&c0.relative_to(point),
|
||||
&c1.relative_to(point),
|
||||
&c2.relative_to(point),
|
||||
)?.divide().fix_1();
|
||||
)?.divide().wrap_1();
|
||||
Some(Ray{origin,direction})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
use std::io::Cursor;
|
||||
use std::path::Path;
|
||||
use std::time::Instant;
|
||||
@@ -6,7 +5,12 @@ use std::time::Instant;
|
||||
use strafesnet_physics::physics::{PhysicsData,PhysicsState,PhysicsContext};
|
||||
|
||||
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)]
|
||||
|
||||
@@ -34,6 +34,7 @@ pub enum PlanesToFacesError{
|
||||
InitFace2,
|
||||
InitIntersection,
|
||||
FindNewIntersection,
|
||||
// Narrow(strafesnet_common::integer::NarrowError),
|
||||
EmptyFaces,
|
||||
InfiniteLoop1,
|
||||
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
|
||||
for new_face in &face_list{
|
||||
// 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
|
||||
// dont' try to replace face0 because we are exploring that face in particular
|
||||
if let Some(new_intersection)=solve3(face0,new_face,face2){
|
||||
// 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;
|
||||
intersection=new_intersection;
|
||||
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){
|
||||
// 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;
|
||||
intersection=new_intersection;
|
||||
continue 'find;
|
||||
@@ -119,7 +120,7 @@ fn planes_to_faces(face_list:std::collections::HashSet<Face>)->Result<Faces,Plan
|
||||
continue;
|
||||
}
|
||||
// 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
|
||||
continue 'face;
|
||||
}
|
||||
@@ -137,7 +138,7 @@ fn planes_to_faces(face_list:std::collections::HashSet<Face>)->Result<Faces,Plan
|
||||
loop{
|
||||
// push point onto vertices
|
||||
// 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!
|
||||
if core::ptr::eq(face1,face2){
|
||||
|
||||
@@ -53,9 +53,9 @@ fn add_brush<'a>(
|
||||
rendercolor.b as f32
|
||||
])/255.0).extend(1.0);
|
||||
|
||||
match model.split_at(1){
|
||||
match model.chars().next(){
|
||||
// The first character of brush.model is '*'
|
||||
("*",id_str)=>match id_str.parse(){
|
||||
Some('*')=>match model[1..].parse(){
|
||||
Ok(mesh_id)=>{
|
||||
let mesh=model::MeshId::new(mesh_id);
|
||||
world_models.push(
|
||||
@@ -209,204 +209,52 @@ pub fn convert<'a>(
|
||||
color:glam::Vec4::W,
|
||||
});
|
||||
|
||||
// THE CUBE OF DESTINY
|
||||
let destination_mesh_id=model::MeshId::new(world_meshes.len() as u32);
|
||||
world_meshes.push(crate::brush::unit_cube());
|
||||
|
||||
const WHITE:vbsp::Color=vbsp::Color{r:255,g:255,b:255};
|
||||
const ENTITY_ATTRIBUTE:attr::CollisionAttributesId=ATTRIBUTE_DECORATION;
|
||||
const ENTITY_TRIGGER_ATTRIBUTE:attr::CollisionAttributesId=ATTRIBUTE_INTERSECT_DEFAULT;
|
||||
for raw_ent in &bsp.entities{
|
||||
macro_rules! ent_brush_default{
|
||||
($entity:ident)=>{
|
||||
add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,$entity.model,$entity.origin,$entity.rendercolor,ENTITY_ATTRIBUTE)
|
||||
};
|
||||
} macro_rules! ent_brush_prop{
|
||||
($entity:ident)=>{
|
||||
add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,$entity.model,$entity.origin,WHITE,ENTITY_ATTRIBUTE)
|
||||
};
|
||||
}
|
||||
macro_rules! ent_brush_trigger{
|
||||
($entity:ident)=>{
|
||||
add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,$entity.model,$entity.origin,WHITE,ENTITY_TRIGGER_ATTRIBUTE)
|
||||
};
|
||||
}
|
||||
match raw_ent.parse(){
|
||||
Ok(Entity::AmbientGeneric(_ambient_generic))=>(),
|
||||
Ok(Entity::Cycler(brush))=>ent_brush_default!(brush),
|
||||
Ok(Entity::EnvBeam(_env_beam))=>(),
|
||||
Ok(Entity::EnvBubbles(_env_bubbles))=>(),
|
||||
Ok(Entity::EnvDetailController(_env_detail_controller))=>(),
|
||||
Ok(Entity::EnvEmbers(_env_embers))=>(),
|
||||
Ok(Entity::EnvEntityMaker(_env_entity_maker))=>(),
|
||||
Ok(Entity::EnvExplosion(_env_explosion))=>(),
|
||||
Ok(Entity::EnvFade(_env_fade))=>(),
|
||||
Ok(Entity::EnvFire(_env_fire))=>(),
|
||||
Ok(Entity::EnvFireTrail(_env_fire_trail))=>(),
|
||||
Ok(Entity::EnvFiresource(_env_firesource))=>(),
|
||||
Ok(Entity::EnvFogController(_env_fog_controller))=>(),
|
||||
Ok(Entity::EnvHudhint(_env_hudhint))=>(),
|
||||
Ok(Entity::EnvLaser(_env_laser))=>(),
|
||||
Ok(Entity::EnvLightglow(_env_lightglow))=>(),
|
||||
Ok(Entity::EnvPhysexplosion(_env_physexplosion))=>(),
|
||||
Ok(Entity::EnvProjectedtexture(_env_projectedtexture))=>(),
|
||||
Ok(Entity::EnvScreenoverlay(_env_screenoverlay))=>(),
|
||||
Ok(Entity::EnvShake(_env_shake))=>(),
|
||||
Ok(Entity::EnvShooter(_env_shooter))=>(),
|
||||
Ok(Entity::EnvSmokestack(_env_smokestack))=>(),
|
||||
Ok(Entity::EnvSoundscape(_env_soundscape))=>(),
|
||||
Ok(Entity::EnvSoundscapeProxy(_env_soundscape_proxy))=>(),
|
||||
Ok(Entity::EnvSoundscapeTriggerable(_env_soundscape_triggerable))=>(),
|
||||
Ok(Entity::EnvSpark(_env_spark))=>(),
|
||||
Ok(Entity::EnvSprite(brush))=>ent_brush_default!(brush),
|
||||
Ok(Entity::EnvSpritetrail(_env_spritetrail))=>(),
|
||||
Ok(Entity::EnvSteam(_env_steam))=>(),
|
||||
Ok(Entity::EnvSun(_env_sun))=>(),
|
||||
Ok(Entity::EnvTonemapController(_env_tonemap_controller))=>(),
|
||||
Ok(Entity::EnvWind(_env_wind))=>(),
|
||||
// trigger_teleport.filtername probably has to do with one of these
|
||||
Ok(Entity::FilterActivatorClass(_filter_activator_class))=>(),
|
||||
Ok(Entity::FilterActivatorName(_filter_activator_name))=>(),
|
||||
Ok(Entity::FilterDamageType(_filter_damage_type))=>(),
|
||||
Ok(Entity::FilterMulti(_filter_multi))=>(),
|
||||
Ok(Entity::FuncAreaportal(_func_areaportal))=>(),
|
||||
Ok(Entity::FuncAreaportalwindow(_func_areaportalwindow))=>(),
|
||||
Ok(Entity::FuncBombTarget(_func_bomb_target))=>(),
|
||||
Ok(Entity::FuncBreakable(brush))=>ent_brush_default!(brush),
|
||||
Ok(Entity::FuncBreakableSurf(_func_breakable_surf))=>(),
|
||||
Ok(Entity::FuncBrush(brush))=>ent_brush_default!(brush),
|
||||
Ok(Entity::FuncButton(brush))=>ent_brush_default!(brush),
|
||||
Ok(Entity::FuncBuyzone(_func_buyzone))=>(),
|
||||
Ok(Entity::FuncClipVphysics(_func_clip_vphysics))=>(),
|
||||
Ok(Entity::FuncConveyor(_func_conveyor))=>(),
|
||||
// FuncDoor is Platform
|
||||
Ok(Entity::FuncDoor(brush))=>ent_brush_default!(brush),
|
||||
Ok(Entity::FuncDoorRotating(brush))=>ent_brush_default!(brush),
|
||||
Ok(Entity::FuncDustcloud(_func_dustcloud))=>(),
|
||||
Ok(Entity::FuncDustmotes(_func_dustmotes))=>(),
|
||||
Ok(Entity::FuncFishPool(_func_fish_pool))=>(),
|
||||
Ok(Entity::FuncFootstepControl(_func_footstep_control))=>(),
|
||||
Ok(Entity::FuncHostageRescue(_func_hostage_rescue))=>(),
|
||||
Ok(Entity::Cycler(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::EnvSprite(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::FuncBreakable(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::FuncBrush(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::FuncButton(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::FuncDoor(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::FuncDoorRotating(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::FuncIllusionary(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::FuncLod(_func_lod))=>(),
|
||||
Ok(Entity::FuncMonitor(brush))=>ent_brush_default!(brush),
|
||||
Ok(Entity::FuncMovelinear(brush))=>ent_brush_default!(brush),
|
||||
Ok(Entity::FuncOccluder(_func_occluder))=>(),
|
||||
Ok(Entity::FuncPhysbox(brush))=>ent_brush_default!(brush),
|
||||
Ok(Entity::FuncPhysboxMultiplayer(brush))=>ent_brush_default!(brush),
|
||||
Ok(Entity::FuncPrecipitation(_func_precipitation))=>(),
|
||||
Ok(Entity::FuncRotButton(brush))=>ent_brush_prop!(brush),
|
||||
Ok(Entity::FuncRotating(brush))=>ent_brush_default!(brush),
|
||||
Ok(Entity::FuncSmokevolume(_func_smokevolume))=>(),
|
||||
Ok(Entity::FuncTracktrain(brush))=>ent_brush_default!(brush),
|
||||
Ok(Entity::FuncTrain(brush))=>ent_brush_default!(brush),
|
||||
Ok(Entity::FuncWall(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin.unwrap_or_default(),brush.rendercolor,ENTITY_ATTRIBUTE),
|
||||
Ok(Entity::FuncWallToggle(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin.unwrap_or_default(),brush.rendercolor,ENTITY_ATTRIBUTE),
|
||||
Ok(Entity::FuncWaterAnalog(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor.unwrap_or(WHITE),ENTITY_ATTRIBUTE),
|
||||
Ok(Entity::GamePlayerEquip(_game_player_equip))=>(),
|
||||
Ok(Entity::GameText(_game_text))=>(),
|
||||
Ok(Entity::GameUi(_game_ui))=>(),
|
||||
Ok(Entity::GameWeaponManager(_game_weapon_manager))=>(),
|
||||
Ok(Entity::HostageEntity(_hostage_entity))=>(),
|
||||
Ok(Entity::InfoCameraLink(_info_camera_link))=>(),
|
||||
Ok(Entity::InfoLadder(_info_ladder))=>(),
|
||||
Ok(Entity::InfoLightingRelative(_info_lighting_relative))=>(),
|
||||
Ok(Entity::InfoMapParameters(_info_map_parameters))=>(),
|
||||
Ok(Entity::InfoNode(_info_node))=>(),
|
||||
Ok(Entity::InfoNodeHint(_info_node_hint))=>(),
|
||||
Ok(Entity::InfoParticleSystem(_info_particle_system))=>(),
|
||||
Ok(Entity::InfoPlayerCounterterrorist(spawn))=>found_spawn=Some(spawn.origin),
|
||||
Ok(Entity::InfoPlayerLogo(_info_player_logo))=>(),
|
||||
Ok(Entity::InfoPlayerStart(_info_player_start))=>(),
|
||||
Ok(Entity::InfoPlayerTerrorist(spawn))=>found_spawn=Some(spawn.origin),
|
||||
Ok(Entity::InfoTarget(_info_target))=>(),
|
||||
// InfoTeleportDestination is Spawn#
|
||||
Ok(Entity::InfoTeleportDestination(_info_teleport_destination))=>(),
|
||||
Ok(Entity::Infodecal(_infodecal))=>(),
|
||||
Ok(Entity::KeyframeRope(_keyframe_rope))=>(),
|
||||
Ok(Entity::Light(_light))=>(),
|
||||
Ok(Entity::LightEnvironment(_light_environment))=>(),
|
||||
Ok(Entity::LightSpot(_light_spot))=>(),
|
||||
Ok(Entity::LogicAuto(_logic_auto))=>(),
|
||||
Ok(Entity::LogicBranch(_logic_branch))=>(),
|
||||
Ok(Entity::LogicCase(_logic_case))=>(),
|
||||
Ok(Entity::LogicCompare(_logic_compare))=>(),
|
||||
Ok(Entity::LogicMeasureMovement(_logic_measure_movement))=>(),
|
||||
Ok(Entity::LogicRelay(_logic_relay))=>(),
|
||||
Ok(Entity::LogicTimer(_logic_timer))=>(),
|
||||
Ok(Entity::MathCounter(_math_counter))=>(),
|
||||
Ok(Entity::MoveRope(_move_rope))=>(),
|
||||
Ok(Entity::PathTrack(_path_track))=>(),
|
||||
Ok(Entity::PhysBallsocket(_phys_ballsocket))=>(),
|
||||
Ok(Entity::PhysConstraint(_phys_constraint))=>(),
|
||||
Ok(Entity::PhysConstraintsystem(_phys_constraintsystem))=>(),
|
||||
Ok(Entity::PhysHinge(_phys_hinge))=>(),
|
||||
Ok(Entity::PhysKeepupright(_phys_keepupright))=>(),
|
||||
Ok(Entity::PhysLengthconstraint(_phys_lengthconstraint))=>(),
|
||||
Ok(Entity::PhysPulleyconstraint(_phys_pulleyconstraint))=>(),
|
||||
Ok(Entity::PhysRagdollconstraint(_phys_ragdollconstraint))=>(),
|
||||
Ok(Entity::PhysRagdollmagnet(_phys_ragdollmagnet))=>(),
|
||||
Ok(Entity::PhysThruster(_phys_thruster))=>(),
|
||||
Ok(Entity::PhysTorque(_phys_torque))=>(),
|
||||
Ok(Entity::PlayerSpeedmod(_player_speedmod))=>(),
|
||||
Ok(Entity::PlayerWeaponstrip(_player_weaponstrip))=>(),
|
||||
Ok(Entity::PointCamera(_point_camera))=>(),
|
||||
Ok(Entity::PointClientcommand(_point_clientcommand))=>(),
|
||||
Ok(Entity::PointDevshotCamera(_point_devshot_camera))=>(),
|
||||
Ok(Entity::PointServercommand(_point_servercommand))=>(),
|
||||
Ok(Entity::PointSpotlight(_point_spotlight))=>(),
|
||||
Ok(Entity::PointSurroundtest(_point_surroundtest))=>(),
|
||||
Ok(Entity::PointTemplate(_point_template))=>(),
|
||||
Ok(Entity::PointTesla(_point_tesla))=>(),
|
||||
Ok(Entity::PointViewcontrol(_point_viewcontrol))=>(),
|
||||
Ok(Entity::PropDoorRotating(brush))=>ent_brush_prop!(brush),
|
||||
Ok(Entity::PropDynamic(brush))=>ent_brush_prop!(brush),
|
||||
Ok(Entity::PropDynamicOverride(brush))=>ent_brush_prop!(brush),
|
||||
Ok(Entity::PropPhysics(brush))=>ent_brush_prop!(brush),
|
||||
Ok(Entity::PropPhysicsMultiplayer(brush))=>ent_brush_prop!(brush),
|
||||
Ok(Entity::PropPhysicsOverride(brush))=>ent_brush_prop!(brush),
|
||||
Ok(Entity::PropRagdoll(brush))=>ent_brush_prop!(brush),
|
||||
Ok(Entity::ShadowControl(_shadow_control))=>(),
|
||||
Ok(Entity::SkyCamera(_sky_camera))=>(),
|
||||
Ok(Entity::TriggerGravity(brush))=>ent_brush_trigger!(brush),
|
||||
Ok(Entity::TriggerHurt(brush))=>ent_brush_trigger!(brush),
|
||||
Ok(Entity::TriggerLook(brush))=>ent_brush_trigger!(brush),
|
||||
Ok(Entity::TriggerMultiple(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model.unwrap_or_default(),brush.origin,WHITE,ENTITY_TRIGGER_ATTRIBUTE),
|
||||
Ok(Entity::TriggerOnce(brush))=>ent_brush_trigger!(brush),
|
||||
Ok(Entity::TriggerProximity(brush))=>ent_brush_trigger!(brush),
|
||||
// TriggerPush is booster
|
||||
Ok(Entity::TriggerPush(brush))=>ent_brush_trigger!(brush),
|
||||
Ok(Entity::TriggerSoundscape(brush))=>ent_brush_trigger!(brush),
|
||||
// TriggerTeleport is Trigger#
|
||||
Ok(Entity::TriggerTeleport(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model.unwrap_or_default(),brush.origin,WHITE,ENTITY_TRIGGER_ATTRIBUTE),
|
||||
Ok(Entity::TriggerVphysicsMotion(brush))=>ent_brush_trigger!(brush),
|
||||
Ok(Entity::TriggerWind(brush))=>ent_brush_trigger!(brush),
|
||||
Ok(Entity::WaterLodControl(_water_lod_control))=>(),
|
||||
Ok(Entity::WeaponAk47(_weapon_ak47))=>(),
|
||||
Ok(Entity::WeaponAwp(_weapon_awp))=>(),
|
||||
Ok(Entity::WeaponDeagle(_weapon_deagle))=>(),
|
||||
Ok(Entity::WeaponElite(_weapon_elite))=>(),
|
||||
Ok(Entity::WeaponFamas(_weapon_famas))=>(),
|
||||
Ok(Entity::WeaponFiveseven(_weapon_fiveseven))=>(),
|
||||
Ok(Entity::WeaponFlashbang(_weapon_flashbang))=>(),
|
||||
Ok(Entity::WeaponG3sg1(_weapon_g3sg1))=>(),
|
||||
Ok(Entity::WeaponGlock(_weapon_glock))=>(),
|
||||
Ok(Entity::WeaponHegrenade(_weapon_hegrenade))=>(),
|
||||
Ok(Entity::WeaponKnife(_weapon_knife))=>(),
|
||||
Ok(Entity::WeaponM249(_weapon_m249))=>(),
|
||||
Ok(Entity::WeaponM3(_weapon_m3))=>(),
|
||||
Ok(Entity::WeaponM4a1(_weapon_m4a1))=>(),
|
||||
Ok(Entity::WeaponMac10(_weapon_mac10))=>(),
|
||||
Ok(Entity::WeaponP228(_weapon_p228))=>(),
|
||||
Ok(Entity::WeaponP90(_weapon_p90))=>(),
|
||||
Ok(Entity::WeaponScout(_weapon_scout))=>(),
|
||||
Ok(Entity::WeaponSg550(_weapon_sg550))=>(),
|
||||
Ok(Entity::WeaponSmokegrenade(_weapon_smokegrenade))=>(),
|
||||
Ok(Entity::WeaponTmp(_weapon_tmp))=>(),
|
||||
Ok(Entity::WeaponUmp45(_weapon_ump45))=>(),
|
||||
Ok(Entity::WeaponUsp(_weapon_usp))=>(),
|
||||
Ok(Entity::WeaponXm1014(_weapon_xm1014))=>(),
|
||||
Ok(Entity::Worldspawn(_worldspawn))=>(),
|
||||
Ok(Entity::FuncMonitor(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::FuncMovelinear(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::FuncPhysbox(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::FuncPhysboxMultiplayer(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::FuncRotButton(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::FuncRotating(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::FuncTracktrain(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::FuncTrain(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::FuncWall(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin.unwrap_or_default(),brush.rendercolor,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::FuncWallToggle(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin.unwrap_or_default(),brush.rendercolor,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::FuncWaterAnalog(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor.unwrap_or(WHITE),ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::PropDoorRotating(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::PropDynamic(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::PropDynamicOverride(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::PropPhysics(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::PropPhysicsMultiplayer(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::PropPhysicsOverride(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::PropRagdoll(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::TriggerGravity(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::TriggerHurt(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::TriggerLook(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::TriggerMultiple(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model.unwrap_or_default(),brush.origin,WHITE,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::TriggerOnce(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::TriggerProximity(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::TriggerPush(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::TriggerSoundscape(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::TriggerTeleport(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model.unwrap_or_default(),brush.origin,WHITE,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::TriggerVphysicsMotion(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::TriggerWind(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
|
||||
Ok(Entity::InfoPlayerCounterterrorist(spawn))=>{
|
||||
found_spawn=Some(valve_transform(spawn.origin.into()));
|
||||
},
|
||||
Ok(Entity::InfoPlayerTerrorist(spawn))=>{
|
||||
found_spawn=Some(valve_transform(spawn.origin.into()));
|
||||
},
|
||||
Err(e)=>{
|
||||
println!("Bsp Entity parse error: {e}");
|
||||
},
|
||||
@@ -458,12 +306,15 @@ pub fn convert<'a>(
|
||||
|
||||
let mut modes_list=Vec::new();
|
||||
if let Some(spawn_point)=found_spawn{
|
||||
// create a new mesh
|
||||
let mesh_id=model::MeshId::new(world_meshes.len() as u32);
|
||||
world_meshes.push(crate::brush::unit_cube());
|
||||
// create a new model
|
||||
let model_id=model::ModelId::new(world_models.len() as u32);
|
||||
world_models.push(model::Model{
|
||||
mesh:destination_mesh_id,
|
||||
mesh:mesh_id,
|
||||
attributes:ATTRIBUTE_INTERSECT_DEFAULT,
|
||||
transform:integer::Planar64Affine3::from_translation(valve_transform(spawn_point.into())),
|
||||
transform:integer::Planar64Affine3::from_translation(spawn_point),
|
||||
color:glam::Vec4::W,
|
||||
});
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@ authors = ["Rhys Lloyd <krakow20@gmail.com>"]
|
||||
[dependencies]
|
||||
arrayvec = "0.7.4"
|
||||
bitflags = "2.6.0"
|
||||
fixed_wide = { version = "0.1.2", 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"] }
|
||||
fixed_wide = { version = "0.2.0", path = "../fixed_wide", registry = "strafesnet", features = ["deferred-division","zeroes","wide-mul"] }
|
||||
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" }
|
||||
glam = "0.30.0"
|
||||
id = { version = "0.1.0", registry = "strafesnet" }
|
||||
|
||||
@@ -66,18 +66,18 @@ impl JumpImpulse{
|
||||
_mass:Planar64,
|
||||
)->Planar64Vec3{
|
||||
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)=>{
|
||||
//height==-v.y*v.y/(2*g.y);
|
||||
//use energy to determine max height
|
||||
let gg=gravity.length_squared();
|
||||
let g=gg.sqrt().fix_1();
|
||||
let g=gg.sqrt();
|
||||
let v_g=gravity.dot(velocity);
|
||||
//do it backwards
|
||||
let radicand=v_g*v_g+(g*height*2).fix_4();
|
||||
velocity-(*gravity*(radicand.sqrt().fix_2()+v_g)/gg).divide().fix_1()
|
||||
let radicand=v_g*v_g+(g*height*2).widen_4();
|
||||
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)=>{
|
||||
//calculate energy
|
||||
//let e=gravity.dot(velocity);
|
||||
@@ -91,10 +91,10 @@ impl JumpImpulse{
|
||||
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
|
||||
match self{
|
||||
&JumpImpulse::Time(time)=>(gravity.length().fix_1()*time/2).divide().fix_1(),
|
||||
&JumpImpulse::Height(height)=>(gravity.length()*height*2).sqrt().fix_1(),
|
||||
&JumpImpulse::Time(time)=>(gravity.length().wrap_1()*time/2).divide().clamp_1(),
|
||||
&JumpImpulse::Height(height)=>(gravity.length()*height*2).sqrt().wrap_1(),
|
||||
&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,
|
||||
};
|
||||
let j=boost_vel.dot(jump_dir);
|
||||
let js=jump_speed.fix_2();
|
||||
let js=jump_speed.widen_2();
|
||||
if j<js{
|
||||
//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{
|
||||
//activate booster normally, jump does nothing
|
||||
boost_vel
|
||||
@@ -142,13 +142,13 @@ impl JumpSettings{
|
||||
None=>rel_velocity,
|
||||
};
|
||||
let j=boost_vel.dot(jump_dir);
|
||||
let js=jump_speed.fix_2();
|
||||
let js=jump_speed.widen_2();
|
||||
if j<js{
|
||||
//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{
|
||||
//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)=>{
|
||||
@@ -159,10 +159,10 @@ impl JumpSettings{
|
||||
None=>rel_velocity,
|
||||
};
|
||||
let boost_dot=boost_vel.dot(jump_dir);
|
||||
let js=jump_speed.fix_2();
|
||||
let js=jump_speed.widen_2();
|
||||
if boost_dot<js{
|
||||
//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{
|
||||
//activate booster normally, jump does nothing
|
||||
boost_vel
|
||||
@@ -174,7 +174,7 @@ impl JumpSettings{
|
||||
Some(booster)=>booster.boost(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{
|
||||
pub fn tick_velocity(&self,velocity:Planar64Vec3,control_dir:Planar64Vec3)->Option<Planar64Vec3>{
|
||||
let d=velocity.dot(control_dir);
|
||||
let mv=self.mv.fix_2();
|
||||
let mv=self.mv.widen_2();
|
||||
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,
|
||||
}
|
||||
}
|
||||
@@ -290,7 +290,7 @@ pub struct PropulsionSettings{
|
||||
}
|
||||
impl PropulsionSettings{
|
||||
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{
|
||||
pub fn accel(&self,target_diff:Planar64Vec3,gravity:Planar64Vec3)->Planar64{
|
||||
//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{
|
||||
self.static_friction
|
||||
}else{
|
||||
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{
|
||||
if control_dir==crate::integer::vec3::ZERO{
|
||||
@@ -332,7 +332,7 @@ impl WalkSettings{
|
||||
if cr==crate::integer::vec3::ZERO_2{
|
||||
crate::integer::vec3::ZERO
|
||||
}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{
|
||||
crate::integer::vec3::ZERO
|
||||
@@ -341,7 +341,7 @@ impl WalkSettings{
|
||||
pub fn is_slope_walkable(&self,normal:Planar64Vec3,up:Planar64Vec3)->bool{
|
||||
//normal is not guaranteed to be unit length
|
||||
let ny=normal.dot(up);
|
||||
let h=normal.length().fix_1();
|
||||
let h=normal.length().wrap_1();
|
||||
//remember this is a normal vector
|
||||
ny.is_positive()&&h*self.surf_dot<ny
|
||||
}
|
||||
@@ -368,13 +368,13 @@ impl LadderSettings{
|
||||
let nnmm=nn*mm;
|
||||
let d=normal.dot(control_dir);
|
||||
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(){
|
||||
control_dir=Planar64Vec3::new([Planar64::ZERO,mm.fix_1(),Planar64::ZERO]);
|
||||
control_dir=Planar64Vec3::new([Planar64::ZERO,mm.clamp_1(),Planar64::ZERO]);
|
||||
}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.
|
||||
//two fixes:
|
||||
@@ -385,7 +385,7 @@ impl LadderSettings{
|
||||
if cr==crate::integer::vec3::ZERO_2{
|
||||
crate::integer::vec3::ZERO
|
||||
}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{
|
||||
crate::integer::vec3::ZERO
|
||||
@@ -417,7 +417,7 @@ impl Hitbox{
|
||||
}
|
||||
pub fn source()->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,
|
||||
}
|
||||
}
|
||||
@@ -538,11 +538,11 @@ impl StyleModifiers{
|
||||
tick_rate:Ratio64::new(100,AbsoluteTime::ONE_SECOND.get() as u64).unwrap(),
|
||||
}),
|
||||
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,
|
||||
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),
|
||||
rocket:None,
|
||||
walk:Some(WalkSettings{
|
||||
@@ -565,7 +565,7 @@ impl StyleModifiers{
|
||||
magnitude:int(12),//?
|
||||
}),
|
||||
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{
|
||||
@@ -574,16 +574,16 @@ impl StyleModifiers{
|
||||
controls_mask_state:Controls::all(),
|
||||
strafe:Some(StrafeSettings{
|
||||
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),
|
||||
tick_rate:Ratio64::new(66,AbsoluteTime::ONE_SECOND.get() as u64).unwrap(),
|
||||
}),
|
||||
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,
|
||||
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),
|
||||
rocket:None,
|
||||
walk:Some(WalkSettings{
|
||||
@@ -606,7 +606,7 @@ impl StyleModifiers{
|
||||
magnitude:int(12),//?
|
||||
}),
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
pub use fixed_wide::fixed::{Fixed,Fix};
|
||||
pub use fixed_wide::fixed::*;
|
||||
pub use ratio_ops::ratio::{Ratio,Divide};
|
||||
|
||||
//integer units
|
||||
@@ -60,7 +60,7 @@ impl<T> Time<T>{
|
||||
impl<T> From<Planar64> for Time<T>{
|
||||
#[inline]
|
||||
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>{
|
||||
@@ -73,11 +73,11 @@ impl<T,Num,Den,N1,T1> From<Ratio<Num,Den>> for Time<T>
|
||||
where
|
||||
Num:core::ops::Mul<Planar64,Output=N1>,
|
||||
N1:Divide<Den,Output=T1>,
|
||||
T1:Fix<Planar64>,
|
||||
T1:Clamp<Planar64>,
|
||||
{
|
||||
#[inline]
|
||||
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>{
|
||||
@@ -401,6 +401,10 @@ impl Angle32{
|
||||
pub const NEG_FRAC_PI_2:Self=Self(-1<<30);
|
||||
pub const PI:Self=Self(-1<<31);
|
||||
#[inline]
|
||||
pub const fn raw(num:i32)->Self{
|
||||
Self(num)
|
||||
}
|
||||
#[inline]
|
||||
pub const fn wrap_from_i64(theta:i64)->Self{
|
||||
//take lower bits
|
||||
//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());
|
||||
let (fs,fc)=f.sin_cos();
|
||||
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((s/h).divide().fix_1(),Planar64::raw((fs*((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().wrap_1(),Planar64::raw((fs*((1u64<<32) as f64)) as i64)));
|
||||
}
|
||||
test_angle(1.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 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_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 Y:Planar64Vec3=Planar64Vec3::new([Planar64::ZERO,Planar64::ONE,Planar64::ZERO]);
|
||||
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();
|
||||
Planar64Mat3::from_cols([
|
||||
Planar64Vec3::new([xc,Planar64::ZERO,-xs]),
|
||||
Planar64Vec3::new([(xs*ys).fix_1(),yc,(xc*ys).fix_1()]),
|
||||
Planar64Vec3::new([(xs*yc).fix_1(),-ys,(xc*yc).fix_1()]),
|
||||
Planar64Vec3::new([(xs*ys).wrap_1(),yc,(xc*ys).wrap_1()]),
|
||||
Planar64Vec3::new([(xs*yc).wrap_1(),-ys,(xc*yc).wrap_1()]),
|
||||
])
|
||||
}
|
||||
#[inline]
|
||||
@@ -668,7 +676,7 @@ impl Planar64Affine3{
|
||||
}
|
||||
#[inline]
|
||||
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{
|
||||
|
||||
@@ -13,8 +13,8 @@ impl Ray{
|
||||
Num:core::ops::Mul<Planar64,Output=N1>,
|
||||
Planar64:core::ops::Mul<Den,Output=N1>,
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "fixed_wide"
|
||||
version = "0.1.2"
|
||||
version = "0.2.0"
|
||||
edition = "2024"
|
||||
repository = "https://git.itzana.me/StrafesNET/strafe-project"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
@@ -663,74 +663,94 @@ macro_repeated!(
|
||||
1,2,3,4,5,6,7,8
|
||||
);
|
||||
|
||||
pub trait Fix<Out>{
|
||||
fn fix(self)->Out;
|
||||
#[derive(Debug,Eq,PartialEq)]
|
||||
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)
|
||||
)=>{
|
||||
impl Fixed<$lhs,{$lhs*32}>
|
||||
{
|
||||
paste::item!{
|
||||
paste::item!{
|
||||
impl Fixed<$lhs,{$lhs*32}>
|
||||
{
|
||||
#[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)))
|
||||
}
|
||||
#[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 Fix<Fixed<$rhs,{$rhs*32}>> for Fixed<$lhs,{$lhs*32}>{
|
||||
fn fix(self)->Fixed<$rhs,{$rhs*32}>{
|
||||
paste::item!{
|
||||
self.[<fix_ $rhs>]()
|
||||
impl Wrap<Fixed<$rhs,{$rhs*32}>> for Fixed<$lhs,{$lhs*32}>{
|
||||
#[inline]
|
||||
fn wrap(self)->Fixed<$rhs,{$rhs*32}>{
|
||||
self.[<wrap_ $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)
|
||||
)=>{
|
||||
impl Fixed<$lhs,{$lhs*32}>
|
||||
{
|
||||
paste::item!{
|
||||
paste::item!{
|
||||
impl Fixed<$lhs,{$lhs*32}>
|
||||
{
|
||||
#[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))
|
||||
}
|
||||
}
|
||||
}
|
||||
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!{
|
||||
impl Into<Fixed<$rhs,{$rhs*32}>> for Fixed<$lhs,{$lhs*32}>{
|
||||
#[inline]
|
||||
pub fn [<fix_ $rhs>](self)->Fixed<$rhs,{$rhs*32}>{
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Fix<Fixed<$rhs,{$rhs*32}>> for Fixed<$lhs,{$lhs*32}>{
|
||||
fn fix(self)->Fixed<$rhs,{$rhs*32}>{
|
||||
paste::item!{
|
||||
self.[<fix_ $rhs>]()
|
||||
fn into(self)->Fixed<$rhs,{$rhs*32}>{
|
||||
self.[<widen_ $rhs>]()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -740,7 +760,7 @@ macro_rules! impl_fix_lhs_eq_rhs_not_const_generic{
|
||||
// I LOVE NOT BEING ABLE TO USE CONST GENERICS
|
||||
|
||||
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),
|
||||
(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),
|
||||
@@ -758,7 +778,7 @@ macro_repeated!(
|
||||
(16,15)
|
||||
);
|
||||
macro_repeated!(
|
||||
impl_fix_lhs_lt_rhs_not_const_generic,(),
|
||||
impl_widen_not_const_generic,(),
|
||||
(1,2),
|
||||
(1,3),(2,3),
|
||||
(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,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)
|
||||
);
|
||||
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)
|
||||
(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_rules! impl_not_const_generic{
|
||||
@@ -797,7 +814,7 @@ macro_rules! impl_not_const_generic{
|
||||
let mut result=Self::ZERO;
|
||||
|
||||
//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
|
||||
for shift in (0..=max_shift).rev(){
|
||||
result.as_bits_mut().as_bits_mut().set_bit(shift,true);
|
||||
|
||||
@@ -61,7 +61,7 @@ fn from_f32(){
|
||||
let b:Result<I32F32,_>=Into::<f32>::into(I32F32::MIN).try_into();
|
||||
assert_eq!(b,Err(crate::fixed::FixedFromFloatError::Overflow));
|
||||
//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));
|
||||
let b:Result<I32F32,_>=f32::MIN_POSITIVE.try_into();
|
||||
assert_eq!(b,Err(crate::fixed::FixedFromFloatError::Underflow));
|
||||
@@ -136,11 +136,24 @@ fn test_bint(){
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fix(){
|
||||
assert_eq!(I32F32::ONE.fix_8(),I256F256::ONE);
|
||||
assert_eq!(I32F32::ONE,I256F256::ONE.fix_1());
|
||||
assert_eq!(I32F32::NEG_ONE.fix_8(),I256F256::NEG_ONE);
|
||||
assert_eq!(I32F32::NEG_ONE,I256F256::NEG_ONE.fix_1());
|
||||
fn test_wrap(){
|
||||
assert_eq!(I32F32::ONE,I256F256::ONE.wrap_1());
|
||||
assert_eq!(I32F32::NEG_ONE,I256F256::NEG_ONE.wrap_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]
|
||||
fn test_sqrt(){
|
||||
|
||||
@@ -15,8 +15,10 @@ macro_rules! impl_zeroes{
|
||||
let radicand=a1*a1-a2*a0*4;
|
||||
match radicand.cmp(&<Self as core::ops::Mul>::Output::ZERO){
|
||||
Ordering::Greater=>{
|
||||
// using wrap because sqrt always halves the number of leading digits.
|
||||
// clamp would be more defensive, but is slower.
|
||||
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
|
||||
let zeroes=match (a2pos,Self::ZERO<a1){
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "linear_ops"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
edition = "2024"
|
||||
repository = "https://git.itzana.me/StrafesNET/strafe-project"
|
||||
license = "MIT OR Apache-2.0"
|
||||
@@ -15,7 +15,7 @@ deferred-division=["dep:ratio_ops"]
|
||||
|
||||
[dependencies]
|
||||
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 }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -38,40 +38,95 @@ macro_rules! impl_fixed_wide_vector {
|
||||
$crate::macro_4!(impl_fixed_wide_vector_not_const_generic,());
|
||||
// I LOVE NOT BEING ABLE TO USE CONST GENERICS
|
||||
$crate::macro_repeated!(
|
||||
impl_fix_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),
|
||||
(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),
|
||||
(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),
|
||||
(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),
|
||||
(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),
|
||||
(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),
|
||||
(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),
|
||||
(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),
|
||||
(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),
|
||||
(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),
|
||||
(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),
|
||||
(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),
|
||||
(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),
|
||||
(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),
|
||||
(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),
|
||||
(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)
|
||||
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),
|
||||
(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),
|
||||
(5,4),(6,4),(7,4),(8,4),(9,4),(10,4),(11,4),(12,4),(13,4),(14,4),(15,4),(16,4),
|
||||
(6,5),(7,5),(8,5),(9,5),(10,5),(11,5),(12,5),(13,5),(14,5),(15,5),(16,5),
|
||||
(7,6),(8,6),(9,6),(10,6),(11,6),(12,6),(13,6),(14,6),(15,6),(16,6),
|
||||
(8,7),(9,7),(10,7),(11,7),(12,7),(13,7),(14,7),(15,7),(16,7),
|
||||
(9,8),(10,8),(11,8),(12,8),(13,8),(14,8),(15,8),(16,8),
|
||||
(10,9),(11,9),(12,9),(13,9),(14,9),(15,9),(16,9),
|
||||
(11,10),(12,10),(13,10),(14,10),(15,10),(16,10),
|
||||
(12,11),(13,11),(14,11),(15,11),(16,11),
|
||||
(13,12),(14,12),(15,12),(16,12),
|
||||
(14,13),(15,13),(16,13),
|
||||
(15,14),(16,14),
|
||||
(16,15)
|
||||
);
|
||||
$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)]
|
||||
#[macro_export(local_inner_macros)]
|
||||
macro_rules! impl_fix_not_const_generic{
|
||||
macro_rules! impl_narrow_not_const_generic{
|
||||
(
|
||||
(),
|
||||
($lhs:expr,$rhs:expr)
|
||||
)=>{
|
||||
impl<const N:usize> Vector<N,fixed_wide::fixed::Fixed<$lhs,{$lhs*32}>>
|
||||
{
|
||||
paste::item!{
|
||||
paste::item!{
|
||||
impl<const N:usize> Vector<N,fixed_wide::fixed::Fixed<$lhs,{$lhs*32}>>{
|
||||
#[inline]
|
||||
pub fn [<fix_ $rhs>](self)->Vector<N,fixed_wide::fixed::Fixed<$rhs,{$rhs*32}>>{
|
||||
self.map(|t|t.[<fix_ $rhs>]())
|
||||
pub fn [<wrap_ $rhs>](self)->Vector<N,fixed_wide::fixed::Fixed<$rhs,{$rhs*32}>>{
|
||||
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>]())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>{
|
||||
#[inline]
|
||||
pub fn min(self,rhs:Self)->Self{
|
||||
|
||||
@@ -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(),
|
||||
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(),
|
||||
].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()
|
||||
)
|
||||
}
|
||||
@@ -837,9 +837,9 @@ impl PartialMap1<'_>{
|
||||
color:deferred_model_deferred_attributes.model.color,
|
||||
transform:Planar64Affine3::new(
|
||||
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.y_axis*2/size.y).divide().fix_1(),
|
||||
(deferred_model_deferred_attributes.model.transform.matrix3.z_axis*2/size.z).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().narrow_1().unwrap(),
|
||||
(deferred_model_deferred_attributes.model.transform.matrix3.z_axis*2/size.z).divide().narrow_1().unwrap(),
|
||||
]),
|
||||
deferred_model_deferred_attributes.model.transform.translation
|
||||
),
|
||||
@@ -861,9 +861,9 @@ impl PartialMap1<'_>{
|
||||
color:deferred_union_deferred_attributes.model.color,
|
||||
transform:Planar64Affine3::new(
|
||||
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.y_axis*2/size.y).divide().fix_1(),
|
||||
(deferred_union_deferred_attributes.model.transform.matrix3.z_axis*2/size.z).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().narrow_1().unwrap(),
|
||||
(deferred_union_deferred_attributes.model.transform.matrix3.z_axis*2/size.z).divide().narrow_1().unwrap(),
|
||||
]),
|
||||
deferred_union_deferred_attributes.model.transform.translation
|
||||
),
|
||||
|
||||
@@ -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 mut aabb=strafesnet_common::aabb::Aabb::default();
|
||||
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))
|
||||
}).collect::<Result<Vec<_>,_>>()?;
|
||||
|
||||
Reference in New Issue
Block a user