@@ -2,6 +2,10 @@ use std::ops::Mul;
22
33use glam:: { IVec3 , Vec3 } ;
44
5+ const fn nan_as_inf ( value : f32 ) -> f32 {
6+ if value. is_nan ( ) { f32:: INFINITY } else { value }
7+ }
8+
59#[ derive( Debug , Clone , Copy ) ]
610pub struct Ray {
711 origin : Vec3 ,
@@ -34,8 +38,9 @@ impl Ray {
3438 }
3539
3640 #[ must_use]
41+ #[ inline]
3742 pub fn new ( origin : Vec3 , direction : Vec3 ) -> Self {
38- let inv_direction = Vec3 :: new ( 1.0 / direction . x , 1.0 / direction . y , 1.0 / direction . z ) ;
43+ let inv_direction = direction . map ( f32 :: recip ) . map ( nan_as_inf ) ;
3944
4045 Self {
4146 origin,
@@ -58,6 +63,7 @@ impl Ray {
5863
5964 /// Efficiently traverse through grid cells that the ray intersects using the Amanatides and Woo algorithm.
6065 /// Returns an iterator over the grid cells ([`IVec3`]) that the ray passes through.
66+ #[ inline]
6167 pub fn voxel_traversal ( & self , bounds_min : IVec3 , bounds_max : IVec3 ) -> VoxelTraversal {
6268 let current_pos = self . origin . as_ivec3 ( ) ;
6369
@@ -88,17 +94,8 @@ impl Ray {
8894 ) ;
8995
9096 // Calculate t_max and t_delta using precomputed inv_direction
91- let t_max = Vec3 :: new (
92- next_boundary. x * self . inv_direction . x . abs ( ) ,
93- next_boundary. y * self . inv_direction . y . abs ( ) ,
94- next_boundary. z * self . inv_direction . z . abs ( ) ,
95- ) ;
96-
97- let t_delta = Vec3 :: new (
98- self . inv_direction . x . abs ( ) ,
99- self . inv_direction . y . abs ( ) ,
100- self . inv_direction . z . abs ( ) ,
101- ) ;
97+ let t_max = ( next_boundary * self . inv_direction . abs ( ) ) . map ( nan_as_inf) ;
98+ let t_delta = self . inv_direction . abs ( ) ;
10299
103100 VoxelTraversal {
104101 current_pos,
@@ -159,3 +156,36 @@ impl Iterator for VoxelTraversal {
159156 Some ( current)
160157 }
161158}
159+
160+ #[ cfg( test) ]
161+ mod tests {
162+ use itertools:: Itertools ;
163+
164+ use super :: * ;
165+
166+ #[ test]
167+ fn test_traverse_axis_aligned_ray ( ) {
168+ static DIRECTIONS : [ IVec3 ; 6 ] = [
169+ IVec3 :: new ( -1 , 0 , 0 ) ,
170+ IVec3 :: new ( 1 , 0 , 0 ) ,
171+ IVec3 :: new ( 0 , -1 , 0 ) ,
172+ IVec3 :: new ( 0 , 1 , 0 ) ,
173+ IVec3 :: new ( 0 , 0 , -1 ) ,
174+ IVec3 :: new ( 0 , 0 , 1 ) ,
175+ ] ;
176+
177+ static ORIGIN : IVec3 = IVec3 :: new ( -1 , 0 , 1 ) ;
178+
179+ for direction in DIRECTIONS {
180+ let ray = Ray :: new ( ORIGIN . as_vec3 ( ) , direction. as_vec3 ( ) ) ;
181+ let voxels = ray
182+ . voxel_traversal ( IVec3 :: MIN , IVec3 :: MAX )
183+ . take ( 10 )
184+ . collect :: < Vec < _ > > ( ) ;
185+ assert_eq ! ( voxels[ 0 ] , ORIGIN ) ;
186+ for ( a, b) in voxels. iter ( ) . tuple_windows ( ) {
187+ assert_eq ! ( b - a, direction) ;
188+ }
189+ }
190+ }
191+ }
0 commit comments