1
1
mod primitive_impls;
2
2
3
+ use glam:: Mat3 ;
4
+
3
5
use super :: { BoundingVolume , IntersectsVolume } ;
4
6
use crate :: prelude:: { Quat , Vec3 } ;
5
7
@@ -87,11 +89,12 @@ impl Aabb3d {
87
89
}
88
90
89
91
impl BoundingVolume for Aabb3d {
90
- type Position = Vec3 ;
92
+ type Translation = Vec3 ;
93
+ type Rotation = Quat ;
91
94
type HalfSize = Vec3 ;
92
95
93
96
#[ inline( always) ]
94
- fn center ( & self ) -> Self :: Position {
97
+ fn center ( & self ) -> Self :: Translation {
95
98
( self . min + self . max ) / 2.
96
99
}
97
100
@@ -143,6 +146,54 @@ impl BoundingVolume for Aabb3d {
143
146
debug_assert ! ( b. min. x <= b. max. x && b. min. y <= b. max. y && b. min. z <= b. max. z) ;
144
147
b
145
148
}
149
+
150
+ /// Transforms the bounding volume by first rotating it around the origin and then applying a translation.
151
+ ///
152
+ /// The result is an Axis-Aligned Bounding Box that encompasses the rotated shape.
153
+ #[ inline( always) ]
154
+ fn transformed_by ( mut self , translation : Self :: Translation , rotation : Self :: Rotation ) -> Self {
155
+ self . transform_by ( translation, rotation) ;
156
+ self
157
+ }
158
+
159
+ /// Transforms the bounding volume by first rotating it around the origin and then applying a translation.
160
+ ///
161
+ /// The result is an Axis-Aligned Bounding Box that encompasses the rotated shape.
162
+ #[ inline( always) ]
163
+ fn transform_by ( & mut self , translation : Self :: Translation , rotation : Self :: Rotation ) {
164
+ self . rotate_by ( rotation) ;
165
+ self . translate_by ( translation) ;
166
+ }
167
+
168
+ #[ inline( always) ]
169
+ fn translate_by ( & mut self , translation : Self :: Translation ) {
170
+ self . min += translation;
171
+ self . max += translation;
172
+ }
173
+
174
+ /// Rotates the bounding volume around the origin by the given rotation.
175
+ ///
176
+ /// The result is an Axis-Aligned Bounding Box that encompasses the rotated shape.
177
+ #[ inline( always) ]
178
+ fn rotated_by ( mut self , rotation : Self :: Rotation ) -> Self {
179
+ self . rotate_by ( rotation) ;
180
+ self
181
+ }
182
+
183
+ /// Rotates the bounding volume around the origin by the given rotation.
184
+ ///
185
+ /// The result is an Axis-Aligned Bounding Box that encompasses the rotated shape.
186
+ #[ inline( always) ]
187
+ fn rotate_by ( & mut self , rotation : Self :: Rotation ) {
188
+ let rot_mat = Mat3 :: from_quat ( rotation) ;
189
+ let abs_rot_mat = Mat3 :: from_cols (
190
+ rot_mat. x_axis . abs ( ) ,
191
+ rot_mat. y_axis . abs ( ) ,
192
+ rot_mat. z_axis . abs ( ) ,
193
+ ) ;
194
+ let half_size = abs_rot_mat * self . half_size ( ) ;
195
+ * self = Self :: new ( rot_mat * self . center ( ) , half_size) ;
196
+ }
146
197
}
147
198
148
199
impl IntersectsVolume < Self > for Aabb3d {
@@ -170,7 +221,7 @@ mod aabb3d_tests {
170
221
use super :: Aabb3d ;
171
222
use crate :: {
172
223
bounding:: { BoundingSphere , BoundingVolume , IntersectsVolume } ,
173
- Vec3 ,
224
+ Quat , Vec3 ,
174
225
} ;
175
226
176
227
#[ test]
@@ -273,6 +324,27 @@ mod aabb3d_tests {
273
324
assert ! ( !shrunk. contains( & a) ) ;
274
325
}
275
326
327
+ #[ test]
328
+ fn transform ( ) {
329
+ let a = Aabb3d {
330
+ min : Vec3 :: new ( -2.0 , -2.0 , -2.0 ) ,
331
+ max : Vec3 :: new ( 2.0 , 2.0 , 2.0 ) ,
332
+ } ;
333
+ let transformed = a. transformed_by (
334
+ Vec3 :: new ( 2.0 , -2.0 , 4.0 ) ,
335
+ Quat :: from_rotation_z ( std:: f32:: consts:: FRAC_PI_4 ) ,
336
+ ) ;
337
+ let half_length = 2_f32 . hypot ( 2.0 ) ;
338
+ assert_eq ! (
339
+ transformed. min,
340
+ Vec3 :: new( 2.0 - half_length, -half_length - 2.0 , 2.0 )
341
+ ) ;
342
+ assert_eq ! (
343
+ transformed. max,
344
+ Vec3 :: new( 2.0 + half_length, half_length - 2.0 , 6.0 )
345
+ ) ;
346
+ }
347
+
276
348
#[ test]
277
349
fn closest_point ( ) {
278
350
let aabb = Aabb3d {
@@ -388,11 +460,12 @@ impl BoundingSphere {
388
460
}
389
461
390
462
impl BoundingVolume for BoundingSphere {
391
- type Position = Vec3 ;
463
+ type Translation = Vec3 ;
464
+ type Rotation = Quat ;
392
465
type HalfSize = f32 ;
393
466
394
467
#[ inline( always) ]
395
- fn center ( & self ) -> Self :: Position {
468
+ fn center ( & self ) -> Self :: Translation {
396
469
self . center
397
470
}
398
471
@@ -451,6 +524,16 @@ impl BoundingVolume for BoundingSphere {
451
524
} ,
452
525
}
453
526
}
527
+
528
+ #[ inline( always) ]
529
+ fn translate_by ( & mut self , translation : Vec3 ) {
530
+ self . center += translation;
531
+ }
532
+
533
+ #[ inline( always) ]
534
+ fn rotate_by ( & mut self , rotation : Quat ) {
535
+ self . center = rotation * self . center ;
536
+ }
454
537
}
455
538
456
539
impl IntersectsVolume < Self > for BoundingSphere {
@@ -471,10 +554,12 @@ impl IntersectsVolume<Aabb3d> for BoundingSphere {
471
554
472
555
#[ cfg( test) ]
473
556
mod bounding_sphere_tests {
557
+ use approx:: assert_relative_eq;
558
+
474
559
use super :: BoundingSphere ;
475
560
use crate :: {
476
561
bounding:: { BoundingVolume , IntersectsVolume } ,
477
- Vec3 ,
562
+ Quat , Vec3 ,
478
563
} ;
479
564
480
565
#[ test]
@@ -553,6 +638,20 @@ mod bounding_sphere_tests {
553
638
assert ! ( !shrunk. contains( & a) ) ;
554
639
}
555
640
641
+ #[ test]
642
+ fn transform ( ) {
643
+ let a = BoundingSphere :: new ( Vec3 :: ONE , 5.0 ) ;
644
+ let transformed = a. transformed_by (
645
+ Vec3 :: new ( 2.0 , -2.0 , 4.0 ) ,
646
+ Quat :: from_rotation_z ( std:: f32:: consts:: FRAC_PI_4 ) ,
647
+ ) ;
648
+ assert_relative_eq ! (
649
+ transformed. center,
650
+ Vec3 :: new( 2.0 , std:: f32 :: consts:: SQRT_2 - 2.0 , 5.0 )
651
+ ) ;
652
+ assert_eq ! ( transformed. radius( ) , 5.0 ) ;
653
+ }
654
+
556
655
#[ test]
557
656
fn closest_point ( ) {
558
657
let sphere = BoundingSphere :: new ( Vec3 :: ZERO , 1.0 ) ;
0 commit comments