diff --git a/src/model_physics.rs b/src/model_physics.rs
index e99d44ea..b045aaed 100644
--- a/src/model_physics.rs
+++ b/src/model_physics.rs
@@ -1,7 +1,8 @@
 use std::borrow::{Borrow,Cow};
 use std::collections::{HashSet,HashMap};
+use strafesnet_common::integer::vec3::Vector3;
 use strafesnet_common::model::{self,MeshId,PolygonIter};
-use strafesnet_common::integer::{self,vec3,Fixed,Planar64,Planar64Vec3};
+use strafesnet_common::integer::{self,vec3,Fixed,Planar64,Planar64Vec3,Ratio};
 
 pub trait UndirectedEdge{
 	type DirectedEdge:Copy+DirectedEdge;
@@ -63,6 +64,9 @@ struct Face{
 }
 struct Vert(Planar64Vec3);
 pub trait MeshQuery<FACE:Clone,EDGE:Clone+DirectedEdge,VERT:Clone>{
+	// Vertex must be Planar64Vec3 because it represents an actual position
+	type Normal;
+	type Offset;
 	fn edge_n(&self,edge_id:EDGE::UndirectedEdge)->Planar64Vec3{
 		let verts=self.edge_verts(edge_id);
 		self.vert(verts[1].clone())-self.vert(verts[0].clone())
@@ -72,7 +76,7 @@ pub trait MeshQuery<FACE:Clone,EDGE:Clone+DirectedEdge,VERT:Clone>{
 		(self.vert(verts[1].clone())-self.vert(verts[0].clone()))*((directed_edge_id.parity() as i64)*2-1)
 	}
 	fn vert(&self,vert_id:VERT)->Planar64Vec3;
-	fn face_nd(&self,face_id:FACE)->(Planar64Vec3,Planar64);
+	fn face_nd(&self,face_id:FACE)->(Self::Normal,Self::Offset);
 	fn face_edges(&self,face_id:FACE)->Cow<Vec<EDGE>>;
 	fn edge_faces(&self,edge_id:EDGE::UndirectedEdge)->Cow<[FACE;2]>;
 	fn edge_verts(&self,edge_id:EDGE::UndirectedEdge)->Cow<[VERT;2]>;
@@ -329,7 +333,7 @@ impl TryFrom<&model::Mesh> for PhysicsMesh{
 				for poly_vertices in polygon_group.polys(){
 					let submesh_face_id=SubmeshFaceId::new(submesh_faces.len() as u32);
 					//one face per poly
-					let mut normal=vec3::ZERO;
+					let mut normal=Vector3::new([Fixed::ZERO,Fixed::ZERO,Fixed::ZERO]);
 					let len=poly_vertices.len();
 					let face_edges=poly_vertices.into_iter().enumerate().map(|(i,vert_id)|{
 						let vert0_id=MeshVertId::new(mesh.unique_vertices[vert_id.get() as usize].pos.get() as u32);
@@ -340,7 +344,7 @@ impl TryFrom<&model::Mesh> for PhysicsMesh{
 						//https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal (Newell's Method)
 						let v0=mesh.unique_pos[vert0_id.get() as usize];
 						let v1=mesh.unique_pos[vert1_id.get() as usize];
-						normal+=Planar64Vec3::new([
+						normal+=Vector3::new([
 							(v0.y-v1.y)*(v0.z+v1.z),
 							(v0.z-v1.z)*(v0.x+v1.x),
 							(v0.x-v1.x)*(v0.y+v1.y),
@@ -361,14 +365,16 @@ impl TryFrom<&model::Mesh> for PhysicsMesh{
 						//return directed_edge_id
 						edge_id.as_directed(is_sorted)
 					}).collect();
-					//choose precision loss randomly idk
-					normal=normal/len as i64;
-					let mut dot=Planar64::ZERO;
+					let mut dot=Fixed::ZERO;
+					// find the average dot
 					for &v in poly_vertices{
 						dot+=normal.dot(mesh.unique_pos[mesh.unique_vertices[v.get() as usize].pos.get() as usize]);
 					}
 					//assume face hash is stable, and there are no flush faces...
-					let face=Face{normal,dot:dot/len as i64};
+					let face=Face{
+						normal:(normal/len as i64).divide().fix_1(),
+						dot:(dot/(len*len) as i64).fix_1(),
+					};
 					let face_id=match face_id_from_face.get(&face){
 						Some(&face_id)=>face_id,
 						None=>{
@@ -415,6 +421,8 @@ pub struct PhysicsMeshView<'a>{
 	topology:&'a PhysicsMeshTopology,
 }
 impl MeshQuery<SubmeshFaceId,SubmeshDirectedEdgeId,SubmeshVertId> for PhysicsMeshView<'_>{
+	type Normal=Planar64Vec3;
+	type Offset=Planar64;
 	fn face_nd(&self,face_id:SubmeshFaceId)->(Planar64Vec3,Planar64){
 		let face_idx=self.topology.faces[face_id.get() as usize].get() as usize;
 		(self.data.faces[face_idx].normal,self.data.faces[face_idx].dot)
@@ -487,14 +495,16 @@ impl TransformedMesh<'_>{
 	}
 }
 impl MeshQuery<SubmeshFaceId,SubmeshDirectedEdgeId,SubmeshVertId> for TransformedMesh<'_>{
-	fn face_nd(&self,face_id:SubmeshFaceId)->(Planar64Vec3,Planar64){
+	type Normal=Vector3<Fixed<3,96>>;
+	type Offset=Fixed<4,128>;
+	fn face_nd(&self,face_id:SubmeshFaceId)->(Self::Normal,Self::Offset){
 		let (n,d)=self.view.face_nd(face_id);
 		let transformed_n=self.transform.normal*n;
-		let transformed_d=d+transformed_n.dot(self.transform.vertex.translation);
+		let transformed_d=d.fix_4()+transformed_n.dot(self.transform.vertex.translation);
 		(transformed_n,transformed_d)
 	}
 	fn vert(&self,vert_id:SubmeshVertId)->Planar64Vec3{
-		self.transform.vertex.transform_point3(self.view.vert(vert_id))
+		self.transform.vertex.transform_point3(self.view.vert(vert_id)).fix_1()
 	}
 	#[inline]
 	fn face_edges(&self,face_id:SubmeshFaceId)->Cow<Vec<SubmeshDirectedEdgeId>>{
@@ -737,9 +747,9 @@ impl MinkowskiMesh<'_>{
 			let verts=self.edge_verts(directed_edge_id.as_undirected());
 			let d=n.dot(self.vert(verts[0])+self.vert(verts[1]));
 			//WARNING! d outside of *2
-			for t in Fixed::<3,96>::zeroes2((n.dot(relative_body.position))*2-d,n.dot(relative_body.velocity)*2,n.dot(relative_body.acceleration)){
-				let t=relative_body.time+integer::Time::from(t);
-				if relative_body.time<t&&t<best_time&&n.dot(relative_body.extrapolated_velocity(t)).is_negative(){
+			for t in Fixed::<5,160>::zeroes2((n.dot(relative_body.position))*2-d,n.dot(relative_body.velocity)*2,n.dot(relative_body.acceleration)){
+				let t=relative_body.time.to_ratio().add_ratio(t);
+				if relative_body.time.to_ratio().lt_ratio(t)&&t.lt_ratio(best_time)&&n.dot(relative_body.extrapolated_velocity(t)).is_negative(){
 					best_time=t;
 					best_edge=Some(directed_edge_id);
 					break;
@@ -767,7 +777,9 @@ impl MinkowskiMesh<'_>{
 	}
 }
 impl MeshQuery<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert> for MinkowskiMesh<'_>{
-	fn face_nd(&self,face_id:MinkowskiFace)->(Planar64Vec3,Planar64){
+	type Normal=Vector3<Fixed<3,96>>;
+	type Offset=Fixed<4,128>;
+	fn face_nd(&self,face_id:MinkowskiFace)->(Self::Normal,Self::Offset){
 		match face_id{
 			MinkowskiFace::VertFace(v0,f1)=>{
 				let (n,d)=self.mesh1.face_nd(f1);
@@ -781,7 +793,7 @@ impl MeshQuery<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert> for MinkowskiM
 				let n=edge0_n.cross(edge1_n);
 				let e0d=n.dot(self.mesh0.vert(e0v0)+self.mesh0.vert(e0v1));
 				let e1d=n.dot(self.mesh1.vert(e1v0)+self.mesh1.vert(e1v1));
-				(n*(parity as i64*4-2),(e0d-e1d)*(parity as i64*2-1))
+				((n*(parity as i64*4-2)).fix_3(),((e0d-e1d)*(parity as i64*2-1)).fix_4())
 			},
 			MinkowskiFace::FaceVert(f0,v1)=>{
 				let (n,d)=self.mesh0.face_nd(f0);
@@ -830,17 +842,18 @@ impl MeshQuery<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert> for MinkowskiM
 				let &[e1f0,e1f1]=self.mesh1.edge_faces(e1).borrow();
 				Cow::Owned([(e1f1,false),(e1f0,true)].map(|(edge_face_id1,face_parity)|{
 					let mut best_edge=None;
-					let mut best_d=Planar64::ZERO;
+					let mut best_d:Ratio<Fixed<8,256>,Fixed<8,256>>=Ratio::new(Fixed::ZERO,Fixed::ONE);
 					let edge_face1_n=self.mesh1.face_nd(edge_face_id1).0;
 					let edge_face1_nn=edge_face1_n.dot(edge_face1_n);
 					for &directed_edge_id0 in v0e.iter(){
 						let edge0_n=self.mesh0.directed_edge_n(directed_edge_id0);
 						//must be behind other face.
 						let d=edge_face1_n.dot(edge0_n);
-						if d<Planar64::ZERO{
+						if d.is_negative(){
 							let edge0_nn=edge0_n.dot(edge0_n);
-							//divide by zero???
-							let dd=d*d/(edge_face1_nn*edge0_nn);
+							// Assume not every number is huge
+							// TODO: revisit this
+							let dd=(d*d).fix_8()/(edge_face1_nn*edge0_nn).fix_8();
 							if best_d<dd{
 								best_d=dd;
 								best_edge=Some(directed_edge_id0);
@@ -859,15 +872,15 @@ impl MeshQuery<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert> for MinkowskiM
 				let &[e0f0,e0f1]=self.mesh0.edge_faces(e0).borrow();
 				Cow::Owned([(e0f0,true),(e0f1,false)].map(|(edge_face_id0,face_parity)|{
 					let mut best_edge=None;
-					let mut best_d=Planar64::ZERO;
+					let mut best_d:Ratio<Fixed<8,256>,Fixed<8,256>>=Ratio::new(Fixed::ZERO,Fixed::ONE);
 					let edge_face0_n=self.mesh0.face_nd(edge_face_id0).0;
 					let edge_face0_nn=edge_face0_n.dot(edge_face0_n);
 					for &directed_edge_id1 in v1e.iter(){
 						let edge1_n=self.mesh1.directed_edge_n(directed_edge_id1);
 						let d=edge_face0_n.dot(edge1_n);
-						if d<Planar64::ZERO{
+						if d.is_negative(){
 							let edge1_nn=edge1_n.dot(edge1_n);
-							let dd=d*d/(edge_face0_nn*edge1_nn);
+							let dd=(d*d).fix_8()/(edge_face0_nn*edge1_nn).fix_8();
 							if best_d<dd{
 								best_d=dd;
 								best_edge=Some(directed_edge_id1);
@@ -903,19 +916,20 @@ impl MeshQuery<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert> for MinkowskiM
 				//detect shared volume when the other mesh is mirrored along a test edge dir
 				let v0f=self.mesh0.vert_faces(v0);
 				let v1f=self.mesh1.vert_faces(v1);
-				let v0f_n:Vec<Planar64Vec3>=v0f.iter().map(|&face_id|self.mesh0.face_nd(face_id).0).collect();
-				let v1f_n:Vec<Planar64Vec3>=v1f.iter().map(|&face_id|self.mesh1.face_nd(face_id).0).collect();
+				let v0f_n:Vec<_>=v0f.iter().map(|&face_id|self.mesh0.face_nd(face_id).0).collect();
+				let v1f_n:Vec<_>=v1f.iter().map(|&face_id|self.mesh1.face_nd(face_id).0).collect();
 				let the_len=v0f.len()+v1f.len();
 				for &directed_edge_id in self.mesh0.vert_edges(v0).iter(){
 					let n=self.mesh0.directed_edge_n(directed_edge_id);
 					let nn=n.dot(n);
+					// TODO: there's gotta be a better way to do this
 					//make a set of faces
 					let mut face_normals=Vec::with_capacity(the_len);
 					//add mesh0 faces as-is
 					face_normals.clone_from(&v0f_n);
 					for face_n in &v1f_n{
 						//add reflected mesh1 faces
-						face_normals.push(*face_n-n*(face_n.dot(n)*2/nn));
+						face_normals.push(*face_n-(n*face_n.dot(n)*2/nn).divide().fix_3());
 					}
 					if is_empty_volume(face_normals){
 						edges.push(MinkowskiDirectedEdge::EdgeVert(directed_edge_id,v1));
@@ -927,7 +941,7 @@ impl MeshQuery<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert> for MinkowskiM
 					let mut face_normals=Vec::with_capacity(the_len);
 					face_normals.clone_from(&v1f_n);
 					for face_n in &v0f_n{
-						face_normals.push(*face_n-n*(face_n.dot(n)*2/nn));
+						face_normals.push(*face_n-(n*face_n.dot(n)*2/nn).divide().fix_3());
 					}
 					if is_empty_volume(face_normals){
 						edges.push(MinkowskiDirectedEdge::VertEdge(v0,directed_edge_id));
@@ -942,12 +956,12 @@ impl MeshQuery<MinkowskiFace,MinkowskiDirectedEdge,MinkowskiVert> for MinkowskiM
 	}
 }
 
-fn is_empty_volume(normals:Vec<Planar64Vec3>)->bool{
+fn is_empty_volume(normals:Vec<Vector3<Fixed<3,96>>>)->bool{
 	let len=normals.len();
 	for i in 0..len-1{
 		for j in i+1..len{
 			let n=normals[i].cross(normals[j]);
-			let mut d_comp:Option<Fixed<3,96>>=None;
+			let mut d_comp:Option<Fixed<9,{96*3}>>=None;
 			for k in 0..len{
 				if k!=i&&k!=j{
 					let d=n.dot(normals[k]);
@@ -967,8 +981,8 @@ fn is_empty_volume(normals:Vec<Planar64Vec3>)->bool{
 
 #[test]
 fn test_is_empty_volume(){
-	assert!(!is_empty_volume([vec3::X,vec3::Y,vec3::Z].to_vec()));
-	assert!(is_empty_volume([vec3::X,vec3::Y,vec3::Z,vec3::NEG_X].to_vec()));
+	assert!(!is_empty_volume([vec3::X.fix_3(),vec3::Y.fix_3(),vec3::Z.fix_3()].to_vec()));
+	assert!(is_empty_volume([vec3::X.fix_3(),vec3::Y.fix_3(),vec3::Z.fix_3(),vec3::NEG_X.fix_3()].to_vec()));
 }
 
 #[test]