From e5420564f0b19325f40703d276e8affa4edc4ac4 Mon Sep 17 00:00:00 2001 From: = <=> Date: Tue, 16 Aug 2022 10:17:00 -0700 Subject: [PATCH 1/5] Change boudning box shape and first attempt at refactor --- common/src/collision.rs | 76 +++++++++++++++++++++++------------------ common/src/dodeca.rs | 12 +++++++ 2 files changed, 54 insertions(+), 34 deletions(-) diff --git a/common/src/collision.rs b/common/src/collision.rs index da88548c..cf2f75b0 100644 --- a/common/src/collision.rs +++ b/common/src/collision.rs @@ -1,5 +1,5 @@ use crate::node::DualGraph; -use crate::{dodeca::Vertex, graph::NodeId}; +use crate::{dodeca::{Vertex, Side}, graph::NodeId, math}; use std::fmt; /* @@ -157,6 +157,7 @@ impl BoundingBox { // translated_position should be the object position in the node coordinates of the chunk. // node can be easily factored out if it stops being convienent. impl ChunkBoundingBox { + #[rustfmt::skip] pub fn get_chunk_bounding_box( node: NodeId, chunk: Vertex, @@ -164,47 +165,54 @@ impl ChunkBoundingBox { radius: f64, dimension: u8, ) -> Option { - let euclidean_position = { - let temp = chunk.node_to_chunk() * translated_position; - temp.xyz() / temp[3] - }; - - let mut min_xyz = na::Vector3::::new(0_u32, 0_u32, 0_u32); - let mut max_xyz = na::Vector3::::new(0_u32, 0_u32, 0_u32); - - // It's important to note that euclidean_position is measured as chunk lengths, and radius is measured in absolute units. - // By coincidence, an absolute unit is approximately a chunk's diameter, and only because of that there is no unit conversion here. - - // verify at least one box corner is within the chunk - if euclidean_position - .iter() - .all(|n| n + radius > 0_f64 && n - radius < 1_f64) - { - min_xyz.x = - ((euclidean_position.x - radius).max(0_f64) * dimension as f64).floor() as u32; - max_xyz.x = - ((euclidean_position.x + radius).min(1_f64) * dimension as f64).ceil() as u32; - - min_xyz.y = - ((euclidean_position.y - radius).max(0_f64) * dimension as f64).floor() as u32; - max_xyz.y = - ((euclidean_position.y + radius).min(1_f64) * dimension as f64).ceil() as u32; - - min_xyz.z = - ((euclidean_position.z - radius).max(0_f64) * dimension as f64).floor() as u32; - max_xyz.z = - ((euclidean_position.z + radius).min(1_f64) * dimension as f64).ceil() as u32; + let chunk_positioning = chunk.chunk_to_node_unscaled(); + + let cube_to_klein = ((-math::mip(&(Side::A.reflection() * Side::B.reflection() * Side::C.reflection() * math::origin()), &math::origin())).acosh() / 2.0).tanh() / 3.0f64.sqrt(); + + let sinh_radius = radius.sinh(); + + // position of entity relative to chunk corner. + let chunk_relative_position = chunk_positioning * translated_position; + + // positions of the theoretical outer corners in the klein metric + let divisor = chunk_relative_position[3].powi(2) + sinh_radius.powi(2); + + let klein_x_min = (chunk_relative_position[0] * chunk_relative_position[3] - sinh_radius * (chunk_relative_position[3].powi(2) + sinh_radius.powi(2) - chunk_relative_position[0].powi(2)).sqrt()) / divisor; + let klein_x_max = (chunk_relative_position[0] * chunk_relative_position[3] + sinh_radius * (chunk_relative_position[3].powi(2) + sinh_radius.powi(2) - chunk_relative_position[0].powi(2)).sqrt()) / divisor; + let klein_y_min = (chunk_relative_position[1] * chunk_relative_position[3] - sinh_radius * (chunk_relative_position[3].powi(2) + sinh_radius.powi(2) - chunk_relative_position[1].powi(2)).sqrt()) / divisor; + let klein_y_max = (chunk_relative_position[1] * chunk_relative_position[3] + sinh_radius * (chunk_relative_position[3].powi(2) + sinh_radius.powi(2) - chunk_relative_position[1].powi(2)).sqrt()) / divisor; + let klein_z_min = (chunk_relative_position[2] * chunk_relative_position[3] - sinh_radius * (chunk_relative_position[3].powi(2) + sinh_radius.powi(2) - chunk_relative_position[2].powi(2)).sqrt()) / divisor; + let klein_z_max = (chunk_relative_position[2] * chunk_relative_position[3] + sinh_radius * (chunk_relative_position[3].powi(2) + sinh_radius.powi(2) - chunk_relative_position[2].powi(2)).sqrt()) / divisor; + + // using the fact that the klien metric scales linearly with the cubic chunk space, we can tranlate the klein coordinates + // of the bounding box to chunk coordinates. + let cube_x_min = 1.0 - klein_x_max / cube_to_klein; + let cube_x_max = 1.0 - klein_x_min / cube_to_klein; + let cube_y_min = 1.0 - klein_y_max / cube_to_klein; + let cube_y_max = 1.0 - klein_y_min / cube_to_klein; + let cube_z_min = 1.0 - klein_z_max / cube_to_klein; + let cube_z_max = 1.0 - klein_z_min / cube_to_klein; + + if cube_x_min < 1.0 && cube_x_max > 0.0 && cube_y_min < 1.0 && cube_y_max > 0.0 && cube_z_min < 1.0 && cube_z_max > 0.0 { Some(ChunkBoundingBox { node, chunk, - min_xyz, - max_xyz, + min_xyz: na::Vector3::::new( + (cube_x_min.max(0.0) * dimension as f64).floor() as u32, + (cube_y_min.max(0.0) * dimension as f64).floor() as u32, + (cube_z_min.max(0.0) * dimension as f64).floor() as u32, + ), + max_xyz: na::Vector3::::new( + (cube_x_max.min(1.0) * dimension as f64).ceil() as u32, + (cube_y_max.min(1.0) * dimension as f64).ceil() as u32, + (cube_z_max.min(1.0) * dimension as f64).ceil() as u32, + ), dimension, }) } else { None } - } +} pub fn every_voxel(&self) -> impl Iterator + '_ { let lwm = (self.dimension as u32) + 2; diff --git a/common/src/dodeca.rs b/common/src/dodeca.rs index f0160ec8..7009e102 100644 --- a/common/src/dodeca.rs +++ b/common/src/dodeca.rs @@ -149,6 +149,18 @@ impl Vertex { ]) * na::Matrix4::new_scaling(0.5) } + // not sure if this should be powered with a static reference. + /// Generates a matrix representing the vector of the vertex's x, y, and z coordinates from the refernce frame of the chunk corner + pub fn chunk_to_node_unscaled(self) -> na::Matrix4 { + let [a, b, c] = self.canonical_sides(); + na::Matrix4::from_columns(&[ + { let temp = a.reflection() * math::origin() - math::origin(); temp / math::mip(&temp, &temp).sqrt() }, + { let temp = b.reflection() * math::origin() - math::origin(); temp / math::mip(&temp, &temp).sqrt() }, + { let temp = c.reflection() * math::origin() - math::origin(); temp / math::mip(&temp, &temp).sqrt() }, + { let temp = self.chunk_to_node() * na::Vector4::new(1.0, 1.0, 1.0, 1.0); temp / (-math::mip(&temp, &temp)).sqrt() }, + ]) + } + /// Transform from hyperbolic node space to euclidean chunk coordinates pub fn node_to_chunk(self) -> na::Matrix4 { self.chunk_to_node().try_inverse().unwrap() From 2a11d5ce471bc781098d2b2d630c6a0ded1fc7d8 Mon Sep 17 00:00:00 2001 From: Patrick Owen Date: Wed, 17 Aug 2022 00:47:57 -0400 Subject: [PATCH 2/5] Take advantage of side normals to simplify coordinate conversion --- common/src/collision.rs | 32 +++++++++++--------- common/src/dodeca.rs | 66 ++++++++++++++++++++++++----------------- 2 files changed, 57 insertions(+), 41 deletions(-) diff --git a/common/src/collision.rs b/common/src/collision.rs index cf2f75b0..cbf77d00 100644 --- a/common/src/collision.rs +++ b/common/src/collision.rs @@ -1,5 +1,9 @@ use crate::node::DualGraph; -use crate::{dodeca::{Vertex, Side}, graph::NodeId, math}; +use crate::{ + dodeca::{Side, Vertex}, + graph::NodeId, + math, +}; use std::fmt; /* @@ -165,7 +169,7 @@ impl ChunkBoundingBox { radius: f64, dimension: u8, ) -> Option { - let chunk_positioning = chunk.chunk_to_node_unscaled(); + let chunk_positioning = chunk.dual_to_node().try_inverse().unwrap(); let cube_to_klein = ((-math::mip(&(Side::A.reflection() * Side::B.reflection() * Side::C.reflection() * math::origin()), &math::origin())).acosh() / 2.0).tanh() / 3.0f64.sqrt(); @@ -173,16 +177,16 @@ impl ChunkBoundingBox { // position of entity relative to chunk corner. let chunk_relative_position = chunk_positioning * translated_position; - + // positions of the theoretical outer corners in the klein metric let divisor = chunk_relative_position[3].powi(2) + sinh_radius.powi(2); - - let klein_x_min = (chunk_relative_position[0] * chunk_relative_position[3] - sinh_radius * (chunk_relative_position[3].powi(2) + sinh_radius.powi(2) - chunk_relative_position[0].powi(2)).sqrt()) / divisor; - let klein_x_max = (chunk_relative_position[0] * chunk_relative_position[3] + sinh_radius * (chunk_relative_position[3].powi(2) + sinh_radius.powi(2) - chunk_relative_position[0].powi(2)).sqrt()) / divisor; - let klein_y_min = (chunk_relative_position[1] * chunk_relative_position[3] - sinh_radius * (chunk_relative_position[3].powi(2) + sinh_radius.powi(2) - chunk_relative_position[1].powi(2)).sqrt()) / divisor; - let klein_y_max = (chunk_relative_position[1] * chunk_relative_position[3] + sinh_radius * (chunk_relative_position[3].powi(2) + sinh_radius.powi(2) - chunk_relative_position[1].powi(2)).sqrt()) / divisor; - let klein_z_min = (chunk_relative_position[2] * chunk_relative_position[3] - sinh_radius * (chunk_relative_position[3].powi(2) + sinh_radius.powi(2) - chunk_relative_position[2].powi(2)).sqrt()) / divisor; - let klein_z_max = (chunk_relative_position[2] * chunk_relative_position[3] + sinh_radius * (chunk_relative_position[3].powi(2) + sinh_radius.powi(2) - chunk_relative_position[2].powi(2)).sqrt()) / divisor; + + let klein_x_min = (-chunk_relative_position[0] * chunk_relative_position[3] - sinh_radius * (chunk_relative_position[3].powi(2) + sinh_radius.powi(2) - chunk_relative_position[0].powi(2)).sqrt()) / divisor; + let klein_x_max = (-chunk_relative_position[0] * chunk_relative_position[3] + sinh_radius * (chunk_relative_position[3].powi(2) + sinh_radius.powi(2) - chunk_relative_position[0].powi(2)).sqrt()) / divisor; + let klein_y_min = (-chunk_relative_position[1] * chunk_relative_position[3] - sinh_radius * (chunk_relative_position[3].powi(2) + sinh_radius.powi(2) - chunk_relative_position[1].powi(2)).sqrt()) / divisor; + let klein_y_max = (-chunk_relative_position[1] * chunk_relative_position[3] + sinh_radius * (chunk_relative_position[3].powi(2) + sinh_radius.powi(2) - chunk_relative_position[1].powi(2)).sqrt()) / divisor; + let klein_z_min = (-chunk_relative_position[2] * chunk_relative_position[3] - sinh_radius * (chunk_relative_position[3].powi(2) + sinh_radius.powi(2) - chunk_relative_position[2].powi(2)).sqrt()) / divisor; + let klein_z_max = (-chunk_relative_position[2] * chunk_relative_position[3] + sinh_radius * (chunk_relative_position[3].powi(2) + sinh_radius.powi(2) - chunk_relative_position[2].powi(2)).sqrt()) / divisor; // using the fact that the klien metric scales linearly with the cubic chunk space, we can tranlate the klein coordinates // of the bounding box to chunk coordinates. @@ -360,11 +364,11 @@ mod tests { // Getting the correct estimation for the number of voxels can be tricky let expected_voxel_count = (radius * 2.0 * CHUNK_SIZE_F).powf(3.0); // value to display let minimum_expected_voxel_count = - ((((radius * CHUNK_SIZE_F) - 1_f64).powf(3.0) / margin_of_error).floor() * 8_f64 ) - as i32; + ((((radius * CHUNK_SIZE_F) - 1_f64).powf(3.0) / margin_of_error).floor() + * 8_f64) as i32; let maximum_expected_voxel_count = - ((((radius * CHUNK_SIZE_F) + 1_f64).powf(3.0) * margin_of_error).ceil() * 20_f64) - as i32; + ((((radius * CHUNK_SIZE_F) + 1_f64).powf(3.0) * margin_of_error).ceil() + * 20_f64) as i32; let position = central_chunk.chunk_to_node() * na::Vector4::new( diff --git a/common/src/dodeca.rs b/common/src/dodeca.rs index 7009e102..dc3b0600 100644 --- a/common/src/dodeca.rs +++ b/common/src/dodeca.rs @@ -49,6 +49,12 @@ impl Side { SIDE_VERTICES[self as usize] } + /// Outward normal vector of this side + #[inline] + pub fn normal(self) -> &'static na::Vector4 { + &SIDE_NORMALS[self as usize] + } + /// Reflection across this side #[inline] pub fn reflection(self) -> &'static na::Matrix4 { @@ -139,26 +145,18 @@ impl Vertex { /// Transform from euclidean chunk coordinates to hyperbolic node space pub fn chunk_to_node(self) -> na::Matrix4 { - let origin = na::Vector4::new(0.0, 0.0, 0.0, 1.0); let [a, b, c] = self.canonical_sides(); na::Matrix4::from_columns(&[ - a.reflection().column(3) - origin, - b.reflection().column(3) - origin, - c.reflection().column(3) - origin, - origin, - ]) * na::Matrix4::new_scaling(0.5) + a.normal() * a.normal()[3], + b.normal() * b.normal()[3], + c.normal() * c.normal()[3], + math::origin(), + ]) } - // not sure if this should be powered with a static reference. - /// Generates a matrix representing the vector of the vertex's x, y, and z coordinates from the refernce frame of the chunk corner - pub fn chunk_to_node_unscaled(self) -> na::Matrix4 { - let [a, b, c] = self.canonical_sides(); - na::Matrix4::from_columns(&[ - { let temp = a.reflection() * math::origin() - math::origin(); temp / math::mip(&temp, &temp).sqrt() }, - { let temp = b.reflection() * math::origin() - math::origin(); temp / math::mip(&temp, &temp).sqrt() }, - { let temp = c.reflection() * math::origin() - math::origin(); temp / math::mip(&temp, &temp).sqrt() }, - { let temp = self.chunk_to_node() * na::Vector4::new(1.0, 1.0, 1.0, 1.0); temp / (-math::mip(&temp, &temp)).sqrt() }, - ]) + /// Generates a matrix representing the vector of the vertex's x, y, and z coordinates from the reference frame of the chunk corner + pub fn dual_to_node(self) -> &'static na::Matrix4 { + &DUAL_TO_NODE[self as usize] } /// Transform from hyperbolic node space to euclidean chunk coordinates @@ -192,13 +190,12 @@ lazy_static! { result }; - /// Transform that moves from a neighbor to a reference node, for each side - static ref REFLECTIONS: [na::Matrix4; SIDE_COUNT] = { + /// Vector corresponding to the outer normal of each side + static ref SIDE_NORMALS: [na::Vector4; SIDE_COUNT] = { let phi = 1.25f64.sqrt() + 0.5; // golden ratio - let root_phi = phi.sqrt(); - let f = math::lorentz_normalize(&na::Vector4::new(root_phi, phi * root_phi, 0.0, phi + 2.0)); + let f = math::lorentz_normalize(&na::Vector4::new(1.0, phi, 0.0, phi.sqrt())); - let mut result = [na::zero(); SIDE_COUNT]; + let mut result: [na::Vector4; SIDE_COUNT] = [na::zero(); SIDE_COUNT]; let mut i = 0; for (x, y, z, w) in [ (f.x, f.y, f.z, f.w), @@ -206,19 +203,20 @@ lazy_static! { (f.x, -f.y, -f.z, f.w), (-f.x, -f.y, f.z, f.w), ] - .iter() - .cloned() { - for (x, y, z, w) in [(x, y, z, w), (y, z, x, w), (z, x, y, w)].iter().cloned() { - result[i] = math::translate(&math::origin(), &na::Vector4::new(x, y, z, w)) - * math::euclidean_reflect(&na::Vector4::new(x, y, z, 0.0)) - * math::translate(&math::origin(), &na::Vector4::new(-x, -y, -z, w)); + for (x, y, z, w) in [(x, y, z, w), (y, z, x, w), (z, x, y, w)] { + result[i] = na::Vector4::new(x, y, z, w); i += 1; } } result }; + /// 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)) + }; + /// Sides incident to a vertex, in canonical order static ref VERTEX_SIDES: [[Side; 3]; VERTEX_COUNT] = { let mut result = [[Side::A; 3]; VERTEX_COUNT]; @@ -239,6 +237,20 @@ lazy_static! { result }; + /// 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 + 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), + ); + result[i] = na::Matrix4::from_columns(&[*a.normal(), *b.normal(), *c.normal(), vertex_position]); + } + result + }; + /// Vertex shared by 3 sides static ref SIDES_TO_VERTEX: [[[Option; SIDE_COUNT]; SIDE_COUNT]; SIDE_COUNT] = { let mut result = [[[None; SIDE_COUNT]; SIDE_COUNT]; SIDE_COUNT]; From c895b0feed146fd2af73c7ddc15c7082ca3c408a Mon Sep 17 00:00:00 2001 From: Patrick Owen Date: Wed, 17 Aug 2022 01:54:29 -0400 Subject: [PATCH 3/5] Work out cube_to_klein solution by hand --- common/src/collision.rs | 13 +++---------- common/src/dodeca.rs | 18 ++++++++++++++---- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/common/src/collision.rs b/common/src/collision.rs index cbf77d00..ca5bbfb4 100644 --- a/common/src/collision.rs +++ b/common/src/collision.rs @@ -1,9 +1,5 @@ use crate::node::DualGraph; -use crate::{ - dodeca::{Side, Vertex}, - graph::NodeId, - math, -}; +use crate::{dodeca::Vertex, graph::NodeId}; use std::fmt; /* @@ -169,14 +165,11 @@ impl ChunkBoundingBox { radius: f64, dimension: u8, ) -> Option { - let chunk_positioning = chunk.dual_to_node().try_inverse().unwrap(); - - let cube_to_klein = ((-math::mip(&(Side::A.reflection() * Side::B.reflection() * Side::C.reflection() * math::origin()), &math::origin())).acosh() / 2.0).tanh() / 3.0f64.sqrt(); - + let cube_to_klein = (5.0f64.sqrt() - 2.0).sqrt(); let sinh_radius = radius.sinh(); // position of entity relative to chunk corner. - let chunk_relative_position = chunk_positioning * translated_position; + let chunk_relative_position = chunk.node_to_dual() * translated_position; // positions of the theoretical outer corners in the klein metric let divisor = chunk_relative_position[3].powi(2) + sinh_radius.powi(2); diff --git a/common/src/dodeca.rs b/common/src/dodeca.rs index dc3b0600..b583d69a 100644 --- a/common/src/dodeca.rs +++ b/common/src/dodeca.rs @@ -154,14 +154,19 @@ impl Vertex { ]) } - /// Generates a matrix representing the vector of the vertex's x, y, and z coordinates from the reference frame of the chunk corner + /// Transform from hyperbolic node space to euclidean chunk coordinates + pub fn node_to_chunk(self) -> na::Matrix4 { + self.chunk_to_node().try_inverse().unwrap() + } + + /// Transform from cube-centric coordinates to dodeca-centric coordinates pub fn dual_to_node(self) -> &'static na::Matrix4 { &DUAL_TO_NODE[self as usize] } - /// Transform from hyperbolic node space to euclidean chunk coordinates - pub fn node_to_chunk(self) -> na::Matrix4 { - self.chunk_to_node().try_inverse().unwrap() + /// Transform from dodeca-centric coordinates to cube-centric coordinates + pub fn node_to_dual(self) -> &'static na::Matrix4 { + &NODE_TO_DUAL[self as usize] } /// Convenience method for `self.chunk_to_node().determinant() < 0`. @@ -251,6 +256,11 @@ lazy_static! { 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| m.try_inverse().unwrap()) + }; + /// Vertex shared by 3 sides static ref SIDES_TO_VERTEX: [[[Option; SIDE_COUNT]; SIDE_COUNT]; SIDE_COUNT] = { let mut result = [[[None; SIDE_COUNT]; SIDE_COUNT]; SIDE_COUNT]; From d12c6aa0c9b284a1dc54b66b2260a7dc124a77e9 Mon Sep 17 00:00:00 2001 From: Patrick Owen Date: Fri, 19 Aug 2022 21:53:12 -0400 Subject: [PATCH 4/5] Use loops for voxel collision math coordinates --- common/src/collision.rs | 68 +++++++++++++++++++---------------------- common/src/dodeca.rs | 2 +- 2 files changed, 32 insertions(+), 38 deletions(-) diff --git a/common/src/collision.rs b/common/src/collision.rs index ca5bbfb4..786d6d76 100644 --- a/common/src/collision.rs +++ b/common/src/collision.rs @@ -157,7 +157,6 @@ impl BoundingBox { // translated_position should be the object position in the node coordinates of the chunk. // node can be easily factored out if it stops being convienent. impl ChunkBoundingBox { - #[rustfmt::skip] pub fn get_chunk_bounding_box( node: NodeId, chunk: Vertex, @@ -174,42 +173,37 @@ impl ChunkBoundingBox { // positions of the theoretical outer corners in the klein metric let divisor = chunk_relative_position[3].powi(2) + sinh_radius.powi(2); - let klein_x_min = (-chunk_relative_position[0] * chunk_relative_position[3] - sinh_radius * (chunk_relative_position[3].powi(2) + sinh_radius.powi(2) - chunk_relative_position[0].powi(2)).sqrt()) / divisor; - let klein_x_max = (-chunk_relative_position[0] * chunk_relative_position[3] + sinh_radius * (chunk_relative_position[3].powi(2) + sinh_radius.powi(2) - chunk_relative_position[0].powi(2)).sqrt()) / divisor; - let klein_y_min = (-chunk_relative_position[1] * chunk_relative_position[3] - sinh_radius * (chunk_relative_position[3].powi(2) + sinh_radius.powi(2) - chunk_relative_position[1].powi(2)).sqrt()) / divisor; - let klein_y_max = (-chunk_relative_position[1] * chunk_relative_position[3] + sinh_radius * (chunk_relative_position[3].powi(2) + sinh_radius.powi(2) - chunk_relative_position[1].powi(2)).sqrt()) / divisor; - let klein_z_min = (-chunk_relative_position[2] * chunk_relative_position[3] - sinh_radius * (chunk_relative_position[3].powi(2) + sinh_radius.powi(2) - chunk_relative_position[2].powi(2)).sqrt()) / divisor; - let klein_z_max = (-chunk_relative_position[2] * chunk_relative_position[3] + sinh_radius * (chunk_relative_position[3].powi(2) + sinh_radius.powi(2) - chunk_relative_position[2].powi(2)).sqrt()) / divisor; - - // using the fact that the klien metric scales linearly with the cubic chunk space, we can tranlate the klein coordinates - // of the bounding box to chunk coordinates. - let cube_x_min = 1.0 - klein_x_max / cube_to_klein; - let cube_x_max = 1.0 - klein_x_min / cube_to_klein; - let cube_y_min = 1.0 - klein_y_max / cube_to_klein; - let cube_y_max = 1.0 - klein_y_min / cube_to_klein; - let cube_z_min = 1.0 - klein_z_max / cube_to_klein; - let cube_z_max = 1.0 - klein_z_min / cube_to_klein; - - if cube_x_min < 1.0 && cube_x_max > 0.0 && cube_y_min < 1.0 && cube_y_max > 0.0 && cube_z_min < 1.0 && cube_z_max > 0.0 { - Some(ChunkBoundingBox { - node, - chunk, - min_xyz: na::Vector3::::new( - (cube_x_min.max(0.0) * dimension as f64).floor() as u32, - (cube_y_min.max(0.0) * dimension as f64).floor() as u32, - (cube_z_min.max(0.0) * dimension as f64).floor() as u32, - ), - max_xyz: na::Vector3::::new( - (cube_x_max.min(1.0) * dimension as f64).ceil() as u32, - (cube_y_max.min(1.0) * dimension as f64).ceil() as u32, - (cube_z_max.min(1.0) * dimension as f64).ceil() as u32, - ), - dimension, - }) - } else { - None - } -} + let mut voxel_min = [0.0; 3]; + let mut voxel_max = [0.0; 3]; + let chunk_to_voxel = dimension as f64; + #[rustfmt::skip] + for i in 0..3 { + let center = chunk_relative_position[i] * chunk_relative_position[3]; + let displacement = sinh_radius * (chunk_relative_position[3].powi(2) + sinh_radius.powi(2) - chunk_relative_position[i].powi(2)).sqrt(); + voxel_min[i] = ((1.0 - (center + displacement) / divisor / cube_to_klein) * chunk_to_voxel).max(0.0).floor(); + voxel_max[i] = ((1.0 - (center - displacement) / divisor / cube_to_klein) * chunk_to_voxel).min(chunk_to_voxel).ceil(); + + if voxel_min[i] >= chunk_to_voxel || voxel_max[i] <= 0.0 { + return None; + } + }; + + Some(ChunkBoundingBox { + node, + chunk, + min_xyz: na::Vector3::::new( + voxel_min[0] as u32, + voxel_min[1] as u32, + voxel_min[2] as u32, + ), + max_xyz: na::Vector3::::new( + voxel_max[0] as u32, + voxel_max[1] as u32, + voxel_max[2] as u32, + ), + dimension, + }) + } pub fn every_voxel(&self) -> impl Iterator + '_ { let lwm = (self.dimension as u32) + 2; diff --git a/common/src/dodeca.rs b/common/src/dodeca.rs index b583d69a..0f8a9fe1 100644 --- a/common/src/dodeca.rs +++ b/common/src/dodeca.rs @@ -251,7 +251,7 @@ lazy_static! { let vertex_position = math::lorentz_normalize( &(math::origin() - (a.normal() + b.normal() + c.normal()) * 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(), -b.normal(), -c.normal(), vertex_position]); } result }; From a2fc35e72a22433b73c1e4edd10000ffa7558c56 Mon Sep 17 00:00:00 2001 From: Patrick Owen Date: Fri, 19 Aug 2022 23:12:36 -0400 Subject: [PATCH 5/5] Finalize get_chunk_bounding_box for now --- common/src/collision.rs | 38 ++++++++++++++++++++++++-------------- common/src/dodeca.rs | 8 ++++++++ 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/common/src/collision.rs b/common/src/collision.rs index 786d6d76..bc1a8df4 100644 --- a/common/src/collision.rs +++ b/common/src/collision.rs @@ -1,3 +1,4 @@ +use crate::math; use crate::node::DualGraph; use crate::{dodeca::Vertex, graph::NodeId}; use std::fmt; @@ -164,29 +165,38 @@ impl ChunkBoundingBox { radius: f64, dimension: u8, ) -> Option { - let cube_to_klein = (5.0f64.sqrt() - 2.0).sqrt(); - let sinh_radius = radius.sinh(); + // The following code computes the bounds of the minimum-size bounding box that contains a sphere of the given + // radius centered at translated_position. Computing this requires solving a quadratic equation, whose solution + // was used to write this code. Not all intermediate values used in the computation have any intrinsic meaning. - // position of entity relative to chunk corner. - let chunk_relative_position = chunk.node_to_dual() * translated_position; + // Position of entity relative to chunk corner + let chunk_position = chunk.node_to_dual() * translated_position; - // positions of the theoretical outer corners in the klein metric - let divisor = chunk_relative_position[3].powi(2) + sinh_radius.powi(2); + let sinh_radius = radius.sinh(); + let common_factor = 1.0 / (chunk_position[3].powi(2) + sinh_radius.powi(2)) + * Vertex::dual_to_chunk_factor(); let mut voxel_min = [0.0; 3]; let mut voxel_max = [0.0; 3]; - let chunk_to_voxel = dimension as f64; - #[rustfmt::skip] + let dimension_float = dimension as f64; for i in 0..3 { - let center = chunk_relative_position[i] * chunk_relative_position[3]; - let displacement = sinh_radius * (chunk_relative_position[3].powi(2) + sinh_radius.powi(2) - chunk_relative_position[i].powi(2)).sqrt(); - voxel_min[i] = ((1.0 - (center + displacement) / divisor / cube_to_klein) * chunk_to_voxel).max(0.0).floor(); - voxel_max[i] = ((1.0 - (center - displacement) / divisor / cube_to_klein) * chunk_to_voxel).min(chunk_to_voxel).ceil(); + let aabb_center = chunk_position[i] * chunk_position[3]; + let aabb_width = sinh_radius + * (chunk_position[3].powi(2) + sinh_radius.powi(2) - chunk_position[i].powi(2)) + .sqrt(); + + voxel_min[i] = ((1.0 - (aabb_center + aabb_width) * common_factor).max(0.0) + * dimension_float) + .floor(); - if voxel_min[i] >= chunk_to_voxel || voxel_max[i] <= 0.0 { + voxel_max[i] = ((1.0 - (aabb_center - aabb_width) * common_factor).min(1.0) + * dimension_float) + .ceil(); + + if voxel_min[i] >= dimension_float || voxel_max[i] <= 0.0 { return None; } - }; + } Some(ChunkBoundingBox { node, diff --git a/common/src/dodeca.rs b/common/src/dodeca.rs index 0f8a9fe1..c9fae02c 100644 --- a/common/src/dodeca.rs +++ b/common/src/dodeca.rs @@ -169,6 +169,14 @@ impl Vertex { &NODE_TO_DUAL[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 chunk coordinates, but reflected such that 0 + /// and 1 are swapped for each coordinate. + pub fn dual_to_chunk_factor() -> f64 { + (2.0 + 5.0f64.sqrt()).sqrt() + } + /// Convenience method for `self.chunk_to_node().determinant() < 0`. pub fn parity(self) -> bool { CHUNK_TO_NODE_PARITY[self as usize]