From 410372dd43d87686b0f1e454e3afe1e32cb92de6 Mon Sep 17 00:00:00 2001 From: Patrick Owen Date: Sun, 25 Feb 2024 02:02:55 -0500 Subject: [PATCH 1/3] Use breadth-first graph traversals to improve numerical stability --- common/src/traversal.rs | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/common/src/traversal.rs b/common/src/traversal.rs index a86608ce..72d536cd 100644 --- a/common/src/traversal.rs +++ b/common/src/traversal.rs @@ -13,14 +13,17 @@ use crate::{ /// Ensure all nodes within `distance` of `start` exist pub fn ensure_nearby(graph: &mut Graph, start: &Position, distance: f64) { - let mut pending = Vec::<(NodeId, na::Matrix4)>::new(); + // We do a breadth-first instead of a depth-first traversal here to ensure that we take the + // minimal path to each node. This greatly helps prevent error from accumulating due to + // hundreds of transformations being composed. + let mut pending = VecDeque::<(NodeId, na::Matrix4)>::new(); let mut visited = FxHashSet::::default(); - pending.push((start.node, na::Matrix4::identity())); + pending.push_back((start.node, na::Matrix4::identity())); visited.insert(start.node); let start_p = start.local.map(|x| x as f64) * math::origin(); - while let Some((node, current_transform)) = pending.pop() { + while let Some((node, current_transform)) = pending.pop_front() { for side in Side::iter() { let neighbor = graph.ensure_neighbor(node, side); if visited.contains(&neighbor) { @@ -32,7 +35,7 @@ pub fn ensure_nearby(graph: &mut Graph, start: &Position, distance: f64) { if math::distance(&start_p, &neighbor_p) > distance { continue; } - pending.push((neighbor, neighbor_transform)); + pending.push_back((neighbor, neighbor_transform)); } } } @@ -50,17 +53,21 @@ pub fn nearby_nodes( } let mut result = Vec::new(); - let mut pending = Vec::::new(); + + // We do a breadth-first instead of a depth-first traversal here to ensure that we take the + // minimal path to each node. This greatly helps prevent error from accumulating due to + // hundreds of transformations being composed. + let mut pending = VecDeque::::new(); let mut visited = FxHashSet::::default(); let start_p = start.local.map(|x| x as f64) * math::origin(); - pending.push(PendingNode { + pending.push_back(PendingNode { id: start.node, transform: na::Matrix4::identity(), }); visited.insert(start.node); - while let Some(current) = pending.pop() { + while let Some(current) = pending.pop_front() { let current_p = current.transform * math::origin(); if math::distance(&start_p, ¤t_p) > distance { continue; @@ -75,7 +82,7 @@ pub fn nearby_nodes( if visited.contains(&neighbor) { continue; } - pending.push(PendingNode { + pending.push_back(PendingNode { id: neighbor, transform: current.transform * side.reflection(), }); From 163aad768895409bf9f842e3903451fbf0e0947a Mon Sep 17 00:00:00 2001 From: Patrick Owen Date: Sun, 14 Apr 2024 17:21:38 -0400 Subject: [PATCH 2/3] Default to f32 instead of f64, removing unnecessary casts --- client/src/graphics/draw.rs | 8 +- client/src/graphics/voxels/mod.rs | 6 +- common/src/dodeca.rs | 124 +++++++++++++++++++++--------- common/src/graph.rs | 2 +- common/src/graph_collision.rs | 8 +- common/src/node.rs | 2 +- common/src/plane.rs | 6 +- common/src/sim_config.rs | 4 +- common/src/traversal.rs | 27 +++---- server/src/sim.rs | 10 +-- 10 files changed, 123 insertions(+), 74 deletions(-) diff --git a/client/src/graphics/draw.rs b/client/src/graphics/draw.rs index e282770a..d116cd8f 100644 --- a/client/src/graphics/draw.rs +++ b/client/src/graphics/draw.rs @@ -431,11 +431,9 @@ impl Draw { } if let Some(sim) = sim.as_deref() { - for (node, transform) in nearby_nodes( - &sim.graph, - &view, - f64::from(self.cfg.local_simulation.view_distance), - ) { + for (node, transform) in + nearby_nodes(&sim.graph, &view, self.cfg.local_simulation.view_distance) + { for &entity in sim.graph_entities.get(node) { if sim.local_character == Some(entity) { // Don't draw ourself diff --git a/client/src/graphics/voxels/mod.rs b/client/src/graphics/voxels/mod.rs index 3e9987b2..a76a45fb 100644 --- a/client/src/graphics/voxels/mod.rs +++ b/client/src/graphics/voxels/mod.rs @@ -124,7 +124,7 @@ impl Voxels { let mut nodes = nearby_nodes( &sim.graph, &view, - f64::from(self.config.local_simulation.view_distance), + self.config.local_simulation.view_distance, ); histogram!("frame.cpu.voxels.graph_traversal").record(graph_traversal_started.elapsed()); // Sort nodes by distance to the view to prioritize loading closer data and improve early Z @@ -142,7 +142,7 @@ impl Voxels { for &(node, ref node_transform) in &nodes { let node_to_view = local_to_view * node_transform; let origin = node_to_view * math::origin(); - if !frustum_planes.contain(&origin, dodeca::BOUNDING_SPHERE_RADIUS as f32) { + if !frustum_planes.contain(&origin, dodeca::BOUNDING_SPHERE_RADIUS) { // Don't bother generating or drawing chunks from nodes that are wholly outside the // frustum. continue; @@ -183,7 +183,7 @@ impl Voxels { frame.drawn.push(slot); // Transfer transform frame.surface.transforms_mut()[slot.0 as usize] = - node_transform * vertex.chunk_to_node().map(|x| x as f32); + node_transform * vertex.chunk_to_node(); } if let (None, &VoxelData::Dense(ref data)) = (&surface, voxels) { // Extract a surface so it can be drawn in future frames diff --git a/common/src/dodeca.rs b/common/src/dodeca.rs index fc7276d1..efc4c234 100644 --- a/common/src/dodeca.rs +++ b/common/src/dodeca.rs @@ -45,20 +45,32 @@ impl Side { /// Outward normal vector of this side #[inline] - pub fn normal(self) -> &'static na::Vector4 { - &SIDE_NORMALS[self as usize] + pub fn normal(self) -> &'static na::Vector4 { + &SIDE_NORMALS_F32[self as usize] + } + + /// Outward normal vector of this side + #[inline] + pub fn normal_f64(self) -> &'static na::Vector4 { + &SIDE_NORMALS_F64[self as usize] + } + + /// Reflection across this side + #[inline] + pub fn reflection(self) -> &'static na::Matrix4 { + &REFLECTIONS_F32[self as usize] } /// Reflection across this side #[inline] - pub fn reflection(self) -> &'static na::Matrix4 { - &REFLECTIONS[self as usize] + pub fn reflection_f64(self) -> &'static na::Matrix4 { + &REFLECTIONS_F64[self as usize] } /// Whether `p` is opposite the dodecahedron across the plane containing `self` #[inline] pub fn is_facing(self, p: &na::Vector4) -> bool { - let r = na::convert::<_, na::RowVector4>(self.reflection().row(3).clone_owned()); + let r = na::convert::<_, na::RowVector4>(self.reflection_f64().row(3).clone_owned()); (r * p).x < p.w } } @@ -138,37 +150,71 @@ impl Vertex { } /// Transform from euclidean chunk coordinates to hyperbolic node space - pub fn chunk_to_node(self) -> na::Matrix4 { + pub fn chunk_to_node(self) -> na::Matrix4 { self.dual_to_node() * na::Matrix4::new_scaling(1.0 / Self::dual_to_chunk_factor()) } + /// Transform from euclidean chunk coordinates to hyperbolic node space + pub fn chunk_to_node_f64(self) -> na::Matrix4 { + self.dual_to_node_f64() * na::Matrix4::new_scaling(1.0 / Self::dual_to_chunk_factor_f64()) + } + /// Transform from hyperbolic node space to euclidean chunk coordinates - pub fn node_to_chunk(self) -> na::Matrix4 { + pub fn node_to_chunk(self) -> na::Matrix4 { na::Matrix4::new_scaling(Self::dual_to_chunk_factor()) * self.node_to_dual() } + /// Transform from hyperbolic node space to euclidean chunk coordinates + pub fn node_to_chunk_f64(self) -> na::Matrix4 { + na::Matrix4::new_scaling(Self::dual_to_chunk_factor_f64()) * self.node_to_dual_f64() + } + /// Transform from cube-centric coordinates to dodeca-centric coordinates - pub fn dual_to_node(self) -> &'static na::Matrix4 { - &DUAL_TO_NODE[self as usize] + pub fn dual_to_node(self) -> &'static na::Matrix4 { + &DUAL_TO_NODE_F32[self as usize] + } + + /// Transform from cube-centric coordinates to dodeca-centric coordinates + pub fn dual_to_node_f64(self) -> &'static na::Matrix4 { + &DUAL_TO_NODE_F64[self as usize] + } + + /// Transform from dodeca-centric coordinates to cube-centric coordinates + pub fn node_to_dual(self) -> &'static na::Matrix4 { + &NODE_TO_DUAL_F32[self as usize] } /// Transform from dodeca-centric coordinates to cube-centric coordinates - pub fn node_to_dual(self) -> &'static na::Matrix4 { - &NODE_TO_DUAL[self as usize] + pub fn node_to_dual_f64(self) -> &'static na::Matrix4 { + &NODE_TO_DUAL_F64[self as usize] } /// Scale factor used in conversion from cube-centric coordinates to euclidean chunk coordinates. /// Scaling the x, y, and z components of a vector in cube-centric coordinates by this value /// and dividing them by the w coordinate will yield euclidean chunk coordinates. - pub fn dual_to_chunk_factor() -> f64 { - *DUAL_TO_CHUNK_FACTOR + pub fn dual_to_chunk_factor() -> f32 { + *DUAL_TO_CHUNK_FACTOR_F32 + } + + /// Scale factor used in conversion from cube-centric coordinates to euclidean chunk coordinates. + /// Scaling the x, y, and z components of a vector in cube-centric coordinates by this value + /// and dividing them by the w coordinate will yield euclidean chunk coordinates. + pub fn dual_to_chunk_factor_f64() -> f64 { + *DUAL_TO_CHUNK_FACTOR_F64 } /// Scale factor used in conversion from euclidean chunk coordinates to cube-centric coordinates. /// Scaling the x, y, and z components of a vector in homogeneous euclidean chunk coordinates by this value /// and lorentz-normalizing the result will yield cube-centric coordinates. - pub fn chunk_to_dual_factor() -> f64 { - *CHUNK_TO_DUAL_FACTOR + pub fn chunk_to_dual_factor() -> f32 { + *CHUNK_TO_DUAL_FACTOR_F32 + } + + /// Scale factor used in conversion from euclidean chunk coordinates to cube-centric coordinates. + /// Scaling the x, y, and z components of a vector in homogeneous euclidean chunk coordinates by this value + /// and lorentz-normalizing the result will yield cube-centric coordinates. + pub fn chunk_to_dual_factor_f64() -> f64 { + *CHUNK_TO_DUAL_FACTOR_F64 } /// Convenience method for `self.chunk_to_node().determinant() < 0`. @@ -179,7 +225,8 @@ impl Vertex { pub const VERTEX_COUNT: usize = 20; pub const SIDE_COUNT: usize = 12; -pub const BOUNDING_SPHERE_RADIUS: f64 = 1.2264568712514068; +pub const BOUNDING_SPHERE_RADIUS_F64: f64 = 1.2264568712514068; +pub const BOUNDING_SPHERE_RADIUS: f32 = BOUNDING_SPHERE_RADIUS_F64 as f32; lazy_static! { /// Whether two sides share an edge @@ -187,7 +234,7 @@ lazy_static! { let mut result = [[false; SIDE_COUNT]; SIDE_COUNT]; for i in 0..SIDE_COUNT { for j in 0..SIDE_COUNT { - let cosh_distance = (REFLECTIONS[i] * REFLECTIONS[j])[(3, 3)]; + let cosh_distance = (REFLECTIONS_F64[i] * REFLECTIONS_F64[j])[(3, 3)]; // Possile cosh_distances: 1, 4.23606 = 2+sqrt(5), 9.47213 = 5+2*sqrt(5), 12.70820 = 6+3*sqrt(5); // < 2.0 indicates identical faces; < 5.0 indicates adjacent faces; > 5.0 indicates non-adjacent faces result[i][j] = (2.0..5.0).contains(&cosh_distance); @@ -197,7 +244,7 @@ lazy_static! { }; /// Vector corresponding to the outer normal of each side - static ref SIDE_NORMALS: [na::Vector4; SIDE_COUNT] = { + static ref SIDE_NORMALS_F64: [na::Vector4; SIDE_COUNT] = { let phi = libm::sqrt(1.25) + 0.5; // golden ratio let f = math::lorentz_normalize(&na::Vector4::new(1.0, phi, 0.0, libm::sqrt(phi))); @@ -219,8 +266,8 @@ lazy_static! { }; /// Transform that moves from a neighbor to a reference node, for each side - static ref REFLECTIONS: [na::Matrix4; SIDE_COUNT] = { - SIDE_NORMALS.map(|r| math::reflect(&r)) + static ref REFLECTIONS_F64: [na::Matrix4; SIDE_COUNT] = { + SIDE_NORMALS_F64.map(|r| math::reflect(&r)) }; /// Sides incident to a vertex, in canonical order @@ -267,26 +314,26 @@ lazy_static! { }; /// Transform that converts from cube-centric coordinates to dodeca-centric coordinates - static ref DUAL_TO_NODE: [na::Matrix4; VERTEX_COUNT] = { - let mip_origin_normal = math::mip(&math::origin(), &SIDE_NORMALS[0]); // This value is the same for every side + static ref DUAL_TO_NODE_F64: [na::Matrix4; VERTEX_COUNT] = { + let mip_origin_normal = math::mip(&math::origin(), &SIDE_NORMALS_F64[0]); // This value is the same for every side let mut result = [na::zero(); VERTEX_COUNT]; for i in 0..VERTEX_COUNT { let [a, b, c] = VERTEX_SIDES[i]; let vertex_position = math::lorentz_normalize( - &(math::origin() - (a.normal() + b.normal() + c.normal()) * mip_origin_normal), + &(math::origin() - (a.normal_f64() + b.normal_f64() + c.normal_f64()) * mip_origin_normal), ); - result[i] = na::Matrix4::from_columns(&[-a.normal(), -b.normal(), -c.normal(), vertex_position]); + result[i] = na::Matrix4::from_columns(&[-a.normal_f64(), -b.normal_f64(), -c.normal_f64(), vertex_position]); } result }; /// Transform that converts from dodeca-centric coordinates to cube-centric coordinates - static ref NODE_TO_DUAL: [na::Matrix4; VERTEX_COUNT] = { - DUAL_TO_NODE.map(|m| math::mtranspose(&m)) + static ref NODE_TO_DUAL_F64: [na::Matrix4; VERTEX_COUNT] = { + DUAL_TO_NODE_F64.map(|m| math::mtranspose(&m)) }; - static ref DUAL_TO_CHUNK_FACTOR: f64 = (2.0 + 5.0f64.sqrt()).sqrt(); - static ref CHUNK_TO_DUAL_FACTOR: f64 = 1.0 / *DUAL_TO_CHUNK_FACTOR; + static ref DUAL_TO_CHUNK_FACTOR_F64: f64 = (2.0 + 5.0f64.sqrt()).sqrt(); + static ref CHUNK_TO_DUAL_FACTOR_F64: f64 = 1.0 / *DUAL_TO_CHUNK_FACTOR_F64; /// Vertex shared by 3 sides static ref SIDES_TO_VERTEX: [[[Option; SIDE_COUNT]; SIDE_COUNT]; SIDE_COUNT] = { @@ -321,11 +368,18 @@ lazy_static! { let mut result = [false; VERTEX_COUNT]; for v in Vertex::iter() { - result[v as usize] = math::parity(&v.chunk_to_node()); + result[v as usize] = math::parity(&v.chunk_to_node_f64()); } result }; + + static ref SIDE_NORMALS_F32: [na::Vector4; SIDE_COUNT] = SIDE_NORMALS_F64.map(|n| n.cast()); + static ref REFLECTIONS_F32: [na::Matrix4; SIDE_COUNT] = REFLECTIONS_F64.map(|n| n.cast()); + static ref DUAL_TO_NODE_F32: [na::Matrix4; VERTEX_COUNT] = DUAL_TO_NODE_F64.map(|n| n.cast()); + static ref NODE_TO_DUAL_F32: [na::Matrix4; VERTEX_COUNT] = NODE_TO_DUAL_F64.map(|n| n.cast()); + static ref DUAL_TO_CHUNK_FACTOR_F32: f32 = *DUAL_TO_CHUNK_FACTOR_F64 as f32; + static ref CHUNK_TO_DUAL_FACTOR_F32: f32 = *CHUNK_TO_DUAL_FACTOR_F64 as f32; } #[cfg(test)] @@ -371,15 +425,15 @@ mod tests { #[test] fn radius() { - let corner = Vertex::A.chunk_to_node() * math::origin(); + let corner = Vertex::A.chunk_to_node_f64() * math::origin(); assert_abs_diff_eq!( - BOUNDING_SPHERE_RADIUS, + BOUNDING_SPHERE_RADIUS_F64, math::distance(&corner, &math::origin()), epsilon = 1e-10 ); let phi = (1.0 + 5.0f64.sqrt()) / 2.0; // Golden ratio assert_abs_diff_eq!( - BOUNDING_SPHERE_RADIUS, + BOUNDING_SPHERE_RADIUS_F64, (1.5 * phi).sqrt().asinh(), epsilon = 1e-10 ); @@ -389,7 +443,7 @@ mod tests { fn chunk_to_node() { // Chunk coordinates of (1, 1, 1) should be at the center of a dodecahedron. let mut chunk_corner_in_node_coordinates = - Vertex::A.chunk_to_node() * na::Vector4::new(1.0, 1.0, 1.0, 1.0); + Vertex::A.chunk_to_node_f64() * na::Vector4::new(1.0, 1.0, 1.0, 1.0); chunk_corner_in_node_coordinates /= chunk_corner_in_node_coordinates.w; assert_abs_diff_eq!( chunk_corner_in_node_coordinates, @@ -401,8 +455,8 @@ mod tests { #[test] fn node_to_chunk() { assert_abs_diff_eq!( - Vertex::A.chunk_to_node().try_inverse().unwrap(), - Vertex::A.node_to_chunk(), + Vertex::A.chunk_to_node_f64().try_inverse().unwrap(), + Vertex::A.node_to_chunk_f64(), epsilon = 1e-10 ); } diff --git a/common/src/graph.rs b/common/src/graph.rs index ab2922ae..df64e70f 100644 --- a/common/src/graph.rs +++ b/common/src/graph.rs @@ -136,7 +136,7 @@ impl Graph { None => continue, Some(x) => x, }; - let mat = na::convert::<_, na::Matrix4>(*side.reflection()); + let mat = na::convert::<_, na::Matrix4>(*side.reflection_f64()); location = mat * location; transform = mat * transform; continue 'outer; diff --git a/common/src/graph_collision.rs b/common/src/graph_collision.rs index 4c388f43..cf0c8a1a 100644 --- a/common/src/graph_collision.rs +++ b/common/src/graph_collision.rs @@ -172,8 +172,8 @@ mod tests { let chosen_chunk_transform: na::Matrix4 = self.chosen_voxel.node_path.iter().fold( na::Matrix4::identity(), - |transform: na::Matrix4, side| transform * side.reflection().cast::(), - ) * self.chosen_voxel.vertex.dual_to_node().cast(); + |transform: na::Matrix4, side| transform * side.reflection(), + ) * self.chosen_voxel.vertex.dual_to_node(); let dual_to_grid_factor = graph.layout().dual_to_grid_factor(); let ray_target = chosen_chunk_transform @@ -184,7 +184,7 @@ mod tests { 1.0, )); - let ray_position = Vertex::A.dual_to_node().cast() + let ray_position = Vertex::A.dual_to_node() * math::lorentz_normalize(&na::Vector4::new( self.start_chunk_relative_grid_ray_start[0] / dual_to_grid_factor, self.start_chunk_relative_grid_ray_start[1] / dual_to_grid_factor, @@ -435,7 +435,7 @@ mod tests { } // The node coordinates of the corner of the missing node - let vertex_pos = Vertex::A.dual_to_node().cast::() * math::origin(); + let vertex_pos = Vertex::A.dual_to_node() * math::origin(); // Use a ray starting from the origin. The direction vector is vertex_pos with the w coordinate // set to 0 and normalized diff --git a/common/src/node.rs b/common/src/node.rs index a840bc5d..9a716665 100644 --- a/common/src/node.rs +++ b/common/src/node.rs @@ -383,7 +383,7 @@ impl ChunkLayout { pub fn new(dimension: u8) -> Self { ChunkLayout { dimension, - dual_to_grid_factor: Vertex::dual_to_chunk_factor() as f32 * dimension as f32, + dual_to_grid_factor: Vertex::dual_to_chunk_factor() * dimension as f32, } } diff --git a/common/src/plane.rs b/common/src/plane.rs index d38d1028..556d9a44 100644 --- a/common/src/plane.rs +++ b/common/src/plane.rs @@ -15,7 +15,7 @@ impl From for Plane { /// A surface overlapping with a particular dodecahedron side fn from(side: Side) -> Self { Self { - normal: *side.normal(), + normal: *side.normal_f64(), } } } @@ -42,7 +42,7 @@ impl Mul> for Side { type Output = Plane; /// Reflect a plane across the side fn mul(self, rhs: Plane) -> Plane { - self.reflection() * rhs + self.reflection_f64() * rhs } } @@ -72,7 +72,7 @@ impl Plane { impl Plane { /// Like `distance_to`, but using chunk coordinates for a chunk in the same node space pub fn distance_to_chunk(&self, chunk: Vertex, coord: &na::Vector3) -> f64 { - let pos = lorentz_normalize(&(chunk.chunk_to_node() * coord.push(1.0))); + let pos = lorentz_normalize(&(chunk.chunk_to_node_f64() * coord.push(1.0))); self.distance_to(&pos) } } diff --git a/common/src/sim_config.rs b/common/src/sim_config.rs index 353cff05..b1d98f40 100644 --- a/common/src/sim_config.rs +++ b/common/src/sim_config.rs @@ -64,8 +64,8 @@ fn meters_to_absolute(chunk_size: u8, voxel_size: f32) -> f32 { let a = dodeca::Vertex::A.chunk_to_node() * na::Vector4::new(1.0, 0.5, 0.5, 1.0); let b = dodeca::Vertex::A.chunk_to_node() * na::Vector4::new(0.0, 0.5, 0.5, 1.0); let minimum_chunk_face_separation = math::distance(&a, &b); - let absolute_voxel_size = minimum_chunk_face_separation / f64::from(chunk_size); - absolute_voxel_size as f32 / voxel_size + let absolute_voxel_size = minimum_chunk_face_separation / f32::from(chunk_size); + absolute_voxel_size / voxel_size } /// Static configuration information relevant to character physics as provided in configuration files diff --git a/common/src/traversal.rs b/common/src/traversal.rs index 72d536cd..cbd26da9 100644 --- a/common/src/traversal.rs +++ b/common/src/traversal.rs @@ -12,16 +12,16 @@ use crate::{ }; /// Ensure all nodes within `distance` of `start` exist -pub fn ensure_nearby(graph: &mut Graph, start: &Position, distance: f64) { +pub fn ensure_nearby(graph: &mut Graph, start: &Position, distance: f32) { // We do a breadth-first instead of a depth-first traversal here to ensure that we take the // minimal path to each node. This greatly helps prevent error from accumulating due to // hundreds of transformations being composed. - let mut pending = VecDeque::<(NodeId, na::Matrix4)>::new(); + let mut pending = VecDeque::<(NodeId, na::Matrix4)>::new(); let mut visited = FxHashSet::::default(); pending.push_back((start.node, na::Matrix4::identity())); visited.insert(start.node); - let start_p = start.local.map(|x| x as f64) * math::origin(); + let start_p = start.local * math::origin(); while let Some((node, current_transform)) = pending.pop_front() { for side in Side::iter() { @@ -45,11 +45,11 @@ pub fn ensure_nearby(graph: &mut Graph, start: &Position, distance: f64) { pub fn nearby_nodes( graph: &Graph, start: &Position, - distance: f64, + distance: f32, ) -> Vec<(NodeId, na::Matrix4)> { struct PendingNode { id: NodeId, - transform: na::Matrix4, + transform: na::Matrix4, } let mut result = Vec::new(); @@ -59,7 +59,7 @@ pub fn nearby_nodes( // hundreds of transformations being composed. let mut pending = VecDeque::::new(); let mut visited = FxHashSet::::default(); - let start_p = start.local.map(|x| x as f64) * math::origin(); + let start_p = start.local * math::origin(); pending.push_back(PendingNode { id: start.node, @@ -113,8 +113,7 @@ impl<'a> RayTraverser<'a> { let mut closest_vertex = Vertex::A; let mut closest_vertex_cosh_distance = f32::INFINITY; for vertex in Vertex::iter() { - let vertex_cosh_distance = - (vertex.node_to_dual().cast::() * position.local * math::origin()).w; + let vertex_cosh_distance = (vertex.node_to_dual() * position.local * math::origin()).w; if vertex_cosh_distance < closest_vertex_cosh_distance { closest_vertex = vertex; closest_vertex_cosh_distance = vertex_cosh_distance; @@ -130,8 +129,7 @@ impl<'a> RayTraverser<'a> { // Precalculate the chunk boundaries for collision purposes. If the collider goes outside these bounds, // the corresponding neighboring chunk will also be used for collision checking. let klein_lower_boundary = radius.tanh(); - let klein_upper_boundary = - ((Vertex::chunk_to_dual_factor() as f32).atanh() - radius).tanh(); + let klein_upper_boundary = (Vertex::chunk_to_dual_factor().atanh() - radius).tanh(); Self { graph, @@ -153,7 +151,7 @@ impl<'a> RayTraverser<'a> { // Combine node and vertex, and convert node transform to chunk transform return Some(( node.map(|node| ChunkId::new(node, vertex)), - vertex.node_to_dual().cast::() * node_transform, + vertex.node_to_dual() * node_transform, )); } @@ -164,7 +162,7 @@ impl<'a> RayTraverser<'a> { continue; }; - let local_ray = vertex.node_to_dual().cast::() * node_transform * self.ray; + let local_ray = vertex.node_to_dual() * node_transform * self.ray; // Compute the Klein-Beltrami coordinates of the ray segment's endpoints. To check whether neighboring chunks // are needed, we need to check whether the endpoints of the line segments lie outside the boundaries of the square @@ -180,15 +178,14 @@ impl<'a> RayTraverser<'a> { || klein_ray_end[axis] <= self.klein_lower_boundary { let side = vertex.canonical_sides()[axis]; - let next_node_transform = side.reflection().cast::() * node_transform; + let next_node_transform = side.reflection() * node_transform; // Crude check to ensure that the neighboring chunk's node can be in the path of the ray. For simplicity, this // check treats each node as a sphere and assumes the ray is pointed directly towards its center. The check is // needed because chunk generation uses this approximation, and this check is not guaranteed to pass near corners // because the AABB check can have false positives. let ray_node_distance = (next_node_transform * self.ray.position).w.acosh(); let ray_length = tanh_distance.atanh(); - if ray_node_distance - ray_length - self.radius - > dodeca::BOUNDING_SPHERE_RADIUS as f32 + if ray_node_distance - ray_length - self.radius > dodeca::BOUNDING_SPHERE_RADIUS { // Ray cannot intersect node continue; diff --git a/server/src/sim.rs b/server/src/sim.rs index e7206989..13ef1d03 100644 --- a/server/src/sim.rs +++ b/server/src/sim.rs @@ -276,7 +276,7 @@ impl Sim { // Extend graph structure for (_, (position, _)) in self.world.query::<(&mut Position, &mut Character)>().iter() { - ensure_nearby(&mut self.graph, position, f64::from(self.cfg.view_distance)); + ensure_nearby(&mut self.graph, position, self.cfg.view_distance); } let fresh_nodes = self.graph.fresh().to_vec(); @@ -297,10 +297,10 @@ impl Sim { // We want to load all chunks that a player can interact with in a single step, so chunk_generation_distance // is set up to cover that distance. let chunk_generation_distance = dodeca::BOUNDING_SPHERE_RADIUS - + self.cfg.character.character_radius as f64 - + self.cfg.character.speed_cap as f64 * self.cfg.step_interval.as_secs_f64() - + self.cfg.character.ground_distance_tolerance as f64 - + self.cfg.character.block_reach as f64 + + self.cfg.character.character_radius + + self.cfg.character.speed_cap * self.cfg.step_interval.as_secs_f32() + + self.cfg.character.ground_distance_tolerance + + self.cfg.character.block_reach + 0.001; // Load all chunks around entities corresponding to clients, which correspond to entities From 1bfa365a6be48ac1314565b49a42ad17087991c8 Mon Sep 17 00:00:00 2001 From: Patrick Owen Date: Sun, 14 Apr 2024 17:26:48 -0400 Subject: [PATCH 3/3] Regenerate protos.rs --- save/src/protos.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/save/src/protos.rs b/save/src/protos.rs index 15246e2a..2f4af608 100644 --- a/save/src/protos.rs +++ b/save/src/protos.rs @@ -1,3 +1,4 @@ +// This file is @generated by prost-build. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Meta {