1
1
use bevy_math:: * ;
2
2
use bevy_reflect:: Reflect ;
3
- //use bevy_transform::components::GlobalTransform;
4
3
use std:: error:: Error ;
5
4
use std:: fmt;
6
5
7
6
pub trait Primitive3d {
8
- /*
9
- /// Returns true if this primitive is on the outside (normal direction) of the supplied
10
- fn outside_plane(
11
- &self,
12
- primitive_transform: GlobalTransform,
13
- plane: Plane,
14
- plane_transform: GlobalTransform,
15
- ) -> bool;*/
7
+ /// Returns true if this primitive is entirely on the outside (in the normal direction) of the
8
+ /// supplied plane.
9
+ fn outside_plane ( & self , plane : Plane ) -> bool ;
16
10
}
17
11
18
12
#[ derive( Debug , Clone ) ]
@@ -25,7 +19,10 @@ impl fmt::Display for PrimitiveError {
25
19
fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
26
20
match self {
27
21
PrimitiveError :: MinGreaterThanMax => {
28
- write ! ( f, "AxisAlignedBox minimums must be smaller than maximums" )
22
+ write ! (
23
+ f,
24
+ "AxisAlignedBox minimums must be smaller or equal to the maximums"
25
+ )
29
26
}
30
27
PrimitiveError :: NonPositiveExtents => {
31
28
write ! ( f, "AxisAlignedBox extents must be greater than zero" )
@@ -36,76 +33,214 @@ impl fmt::Display for PrimitiveError {
36
33
37
34
#[ derive( Copy , Clone , PartialEq , Debug , Reflect ) ]
38
35
pub struct Sphere {
39
- pub origin : Vec3 ,
40
- pub radius : f32 ,
36
+ origin : Vec3 ,
37
+ radius : f32 ,
38
+ }
39
+
40
+ impl Sphere {
41
+ /// Get a reference to the sphere's origin.
42
+ pub fn origin ( & self ) -> & Vec3 {
43
+ & self . origin
44
+ }
45
+
46
+ /// Get a reference to the sphere's radius.
47
+ pub fn radius ( & self ) -> & f32 {
48
+ & self . radius
49
+ }
50
+
51
+ /// Set the sphere's origin.
52
+ pub fn set_origin ( & mut self , origin : Vec3 ) {
53
+ self . origin = origin;
54
+ }
55
+
56
+ /// Set the sphere's radius.
57
+ pub fn set_radius ( & mut self , radius : f32 ) {
58
+ self . radius = radius;
59
+ }
60
+ }
61
+ impl Primitive3d for Sphere {
62
+ /// Use the sphere's position and radius to determin eif it is entirely on the outside of the
63
+ /// the supplied plane.
64
+ fn outside_plane ( & self , plane : Plane ) -> bool {
65
+ plane. distance_to_point ( & self . origin ) > self . radius
66
+ }
41
67
}
42
- impl Primitive3d for Sphere { }
43
68
44
69
/// An oriented box, unlike an axis aligned box, can be rotated and is not constrained to match the
45
70
/// orientation of the coordinate system it is defined in. Internally, this is represented as an
46
71
/// axis aligned box with some rotation ([Quat]) applied.
47
72
#[ derive( Copy , Clone , PartialEq , Debug , Reflect ) ]
48
73
pub struct OrientedBox {
49
- pub aab : AxisAlignedBox ,
50
- pub orientation : Quat ,
74
+ aab : AxisAlignedBox ,
75
+ transform : Mat4 ,
76
+ }
77
+ impl Primitive3d for OrientedBox {
78
+ fn outside_plane ( & self , plane : Plane ) -> bool {
79
+ for vertex in self . vertices ( ) . iter ( ) {
80
+ if plane. distance_to_point ( vertex) <= 0.0 {
81
+ return false ;
82
+ }
83
+ }
84
+ true
85
+ }
86
+ }
87
+ impl OrientedBox {
88
+ /// An ordered list of the vertices that form the 8 corners of the [AxisAlignedBox].
89
+ /// ```none
90
+ /// (5)------(1)
91
+ /// | \ | \
92
+ /// | (4)------(0)
93
+ /// | | | |
94
+ /// (7)--|---(3) |
95
+ /// \ | \ |
96
+ /// (6)------(2)
97
+ /// ```
98
+ pub fn vertices ( & self ) -> [ Vec3 ; 8 ] {
99
+ let mut vertices = [ Vec3 :: ZERO ; 8 ] ;
100
+ let aab_vertices = self . aab . vertices ( ) ;
101
+ for i in 0 ..vertices. len ( ) {
102
+ vertices[ i] = self . transform . project_point3 ( aab_vertices[ i] )
103
+ }
104
+ vertices
105
+ }
106
+
107
+ /// Set the oriented box's aab.
108
+ pub fn set_aab ( & mut self , aab : AxisAlignedBox ) {
109
+ self . aab = aab;
110
+ }
111
+
112
+ /// Set the oriented box's transform.
113
+ pub fn set_transform ( & mut self , transform : Mat4 ) {
114
+ self . transform = transform;
115
+ }
116
+ pub fn fast_aabb ( & self ) -> AxisAlignedBox {
117
+ let vertices = self . vertices ( ) ;
118
+ let mut max = Vec3 :: splat ( f32:: MIN ) ;
119
+ let mut min = Vec3 :: splat ( f32:: MAX ) ;
120
+ for vertex in vertices. iter ( ) {
121
+ max = vertex. max ( max) ;
122
+ min = vertex. min ( min) ;
123
+ }
124
+ // Unwrap is okay here because min < max
125
+ AxisAlignedBox :: from_min_max ( min, max) . unwrap ( )
126
+ }
51
127
}
52
- impl Primitive3d for OrientedBox { }
53
128
54
129
/// An axis aligned box is a box whose axes lie in the x/y/z directions of the coordinate system
55
130
/// the box is defined in.
56
131
#[ derive( Copy , Clone , PartialEq , Debug , Reflect ) ]
57
132
pub struct AxisAlignedBox {
58
- minimums : Vec3 ,
59
- maximums : Vec3 ,
133
+ min : Vec3 ,
134
+ max : Vec3 ,
135
+ }
136
+ impl Primitive3d for AxisAlignedBox {
137
+ fn outside_plane ( & self , plane : Plane ) -> bool {
138
+ for vertex in self . vertices ( ) . iter ( ) {
139
+ if plane. distance_to_point ( vertex) <= 0.0 {
140
+ return false ;
141
+ }
142
+ }
143
+ true
144
+ }
60
145
}
61
- impl Primitive3d for AxisAlignedBox { }
62
146
impl AxisAlignedBox {
63
- pub fn from_min_max ( minimums : Vec3 , maximums : Vec3 ) -> Result < AxisAlignedBox , PrimitiveError > {
64
- if ( maximums - minimums) . min_element ( ) > 0.0 {
65
- Ok ( AxisAlignedBox { minimums, maximums } )
147
+ /// An ordered list of the vertices that form the 8 corners of the [AxisAlignedBox].
148
+ /// ```none
149
+ /// (5)------(1) Y
150
+ /// | \ | \ |
151
+ /// | (4)------(0) MAX o---X
152
+ /// | | | | \
153
+ /// MIN (7)--|---(3) | Z
154
+ /// \ | \ |
155
+ /// (6)------(2)
156
+ /// ```
157
+ pub fn vertices ( & self ) -> [ Vec3 ; 8 ] {
158
+ let min = self . min ;
159
+ let max = self . max ;
160
+ [
161
+ Vec3 :: new ( max. x , max. y , max. z ) ,
162
+ Vec3 :: new ( max. x , max. y , min. z ) ,
163
+ Vec3 :: new ( max. x , min. y , max. z ) ,
164
+ Vec3 :: new ( max. x , min. y , min. z ) ,
165
+ Vec3 :: new ( min. x , max. y , max. z ) ,
166
+ Vec3 :: new ( min. x , max. y , min. z ) ,
167
+ Vec3 :: new ( min. x , min. y , max. z ) ,
168
+ Vec3 :: new ( min. x , min. y , min. z ) ,
169
+ ]
170
+ }
171
+ /// Construct an [AxisAlignedBox] given the coordinates of the minimum and maximum corners.
172
+ pub fn from_min_max ( min : Vec3 , max : Vec3 ) -> Result < AxisAlignedBox , PrimitiveError > {
173
+ if ( max - min) . min_element ( ) >= 0.0 {
174
+ Ok ( AxisAlignedBox { min, max } )
66
175
} else {
67
176
Err ( PrimitiveError :: MinGreaterThanMax )
68
177
}
69
178
}
179
+ /// Construct an [AxisALignedBox] from the origin at the minimum corner, and the extents - the
180
+ /// dimensions of the box in each axis.
70
181
pub fn from_extents_origin (
71
182
extents : Vec3 ,
72
183
origin : Vec3 ,
73
184
) -> Result < AxisAlignedBox , PrimitiveError > {
74
185
if extents. min_element ( ) > 0.0 {
75
186
Ok ( AxisAlignedBox {
76
- minimums : origin,
77
- maximums : extents + origin,
187
+ min : origin,
188
+ max : extents + origin,
78
189
} )
79
190
} else {
80
191
Err ( PrimitiveError :: NonPositiveExtents )
81
192
}
82
193
}
194
+ /// Computes the AAB that
195
+ pub fn from_points ( points : Vec < Vec3 > ) -> AxisAlignedBox {
196
+ let mut max = Vec3 :: splat ( f32:: MIN ) ;
197
+ let mut min = Vec3 :: splat ( f32:: MAX ) ;
198
+ for & point in points. iter ( ) {
199
+ max = point. max ( max) ;
200
+ min = point. min ( min) ;
201
+ }
202
+ // Unwrap is okay here because min < max
203
+ AxisAlignedBox :: from_min_max ( min, max) . unwrap ( )
204
+ }
83
205
}
84
206
85
- /// A frustum is a truncated pyramid that is used to represent the " volume" of world space that is
207
+ /// A frustum is a truncated pyramid that is used to represent the volume of world space that is
86
208
/// visible to the camera.
87
209
#[ derive( Copy , Clone , PartialEq , Debug , Reflect ) ]
88
210
#[ reflect_value( PartialEq ) ]
89
211
pub struct Frustum {
90
212
planes : [ Plane ; 6 ] ,
213
+ vertices : [ Vec3 ; 8 ] ,
214
+ }
215
+ impl Primitive3d for Frustum {
216
+ fn outside_plane ( & self , plane : Plane ) -> bool {
217
+ for vertex in self . vertices ( ) . iter ( ) {
218
+ if plane. distance_to_point ( vertex) <= 0.0 {
219
+ return false ;
220
+ }
221
+ }
222
+ true
223
+ }
91
224
}
92
- impl Primitive3d for Frustum { }
93
225
impl Frustum {
94
- pub fn from_camera_properties (
95
- & self ,
96
- camera_position : Mat4 ,
97
- projection_matrix : Mat4 ,
98
- ) -> Frustum {
226
+ fn compute_vertices ( camera_position : Mat4 , projection_matrix : Mat4 ) -> [ Vec3 ; 8 ] {
99
227
let ndc_to_world: Mat4 = camera_position * projection_matrix. inverse ( ) ;
100
- // Near/Far, Top/Bottom, Left/Right
101
- let nbl_world = ndc_to_world. project_point3 ( Vec3 :: new ( -1.0 , -1.0 , -1.0 ) ) ;
102
- let nbr_world = ndc_to_world. project_point3 ( Vec3 :: new ( 1.0 , -1.0 , -1.0 ) ) ;
103
- let ntl_world = ndc_to_world. project_point3 ( Vec3 :: new ( -1.0 , 1.0 , -1.0 ) ) ;
104
- let fbl_world = ndc_to_world. project_point3 ( Vec3 :: new ( -1.0 , -1.0 , 1.0 ) ) ;
105
- let ftr_world = ndc_to_world. project_point3 ( Vec3 :: new ( 1.0 , 1.0 , 1.0 ) ) ;
106
- let ftl_world = ndc_to_world. project_point3 ( Vec3 :: new ( -1.0 , 1.0 , 1.0 ) ) ;
107
- let fbr_world = ndc_to_world. project_point3 ( Vec3 :: new ( 1.0 , -1.0 , 1.0 ) ) ;
108
- let ntr_world = ndc_to_world. project_point3 ( Vec3 :: new ( 1.0 , 1.0 , -1.0 ) ) ;
228
+ [
229
+ ndc_to_world. project_point3 ( Vec3 :: new ( -1.0 , -1.0 , -1.0 ) ) ,
230
+ ndc_to_world. project_point3 ( Vec3 :: new ( 1.0 , -1.0 , -1.0 ) ) ,
231
+ ndc_to_world. project_point3 ( Vec3 :: new ( -1.0 , 1.0 , -1.0 ) ) ,
232
+ ndc_to_world. project_point3 ( Vec3 :: new ( 1.0 , 1.0 , -1.0 ) ) ,
233
+ ndc_to_world. project_point3 ( Vec3 :: new ( -1.0 , -1.0 , 1.0 ) ) ,
234
+ ndc_to_world. project_point3 ( Vec3 :: new ( 1.0 , -1.0 , 1.0 ) ) ,
235
+ ndc_to_world. project_point3 ( Vec3 :: new ( -1.0 , 1.0 , 1.0 ) ) ,
236
+ ndc_to_world. project_point3 ( Vec3 :: new ( 1.0 , 1.0 , 1.0 ) ) ,
237
+ ]
238
+ }
239
+
240
+ pub fn from_camera_properties ( camera_position : Mat4 , projection_matrix : Mat4 ) -> Frustum {
241
+ let vertices = Frustum :: compute_vertices ( camera_position, projection_matrix) ;
242
+ let [ nbl_world, nbr_world, ntl_world, ntr_world, fbl_world, fbr_world, ftl_world, ftr_world] =
243
+ vertices;
109
244
110
245
let near_normal = ( nbr_world - nbl_world)
111
246
. cross ( ntl_world - nbl_world)
@@ -150,9 +285,30 @@ impl Frustum {
150
285
point : ftr_world,
151
286
normal : far_normal,
152
287
} ;
153
- Frustum {
154
- planes : [ left, right, top, bottom, near, far] ,
155
- }
288
+
289
+ let planes = [ left, right, top, bottom, near, far] ;
290
+
291
+ Frustum { planes, vertices }
292
+ }
293
+
294
+ /// Get a reference to the frustum's vertices. These are given as an ordered list of vertices
295
+ /// that form the 8 corners of a [Frustum].
296
+ /// ```none
297
+ /// (6)--------------(7)
298
+ /// | \ TOP / |
299
+ /// | (2)------(3) |
300
+ /// | L | | R |
301
+ /// (4) | NEAR | (5)
302
+ /// \ | | /
303
+ /// (0)------(1)
304
+ /// ```
305
+ pub fn vertices ( & self ) -> & [ Vec3 ; 8 ] {
306
+ & self . vertices
307
+ }
308
+
309
+ /// Get a reference to the frustum's planes.
310
+ pub fn planes ( & self ) -> & [ Plane ; 6 ] {
311
+ & self . planes
156
312
}
157
313
}
158
314
@@ -162,7 +318,11 @@ pub struct Plane {
162
318
point : Vec3 ,
163
319
normal : Vec3 ,
164
320
}
165
- impl Primitive3d for Plane { }
321
+ impl Primitive3d for Plane {
322
+ fn outside_plane ( & self , plane : Plane ) -> bool {
323
+ self . normal == plane. normal && self . distance_to_point ( plane. point ( ) ) > 0.0
324
+ }
325
+ }
166
326
impl Plane {
167
327
/// Generate a plane from three points that lie on the plane.
168
328
pub fn from_points ( points : [ Vec3 ; 3 ] ) -> Plane {
@@ -180,4 +340,20 @@ impl Plane {
180
340
normal : normal. normalize ( ) ,
181
341
}
182
342
}
343
+ /// Returns the nearest distance from the supplied point to this plane. Positive values are in
344
+ /// the direction of the plane's normal (outside), negative values are opposite the direction
345
+ /// of the planes normal (inside).
346
+ pub fn distance_to_point ( & self , point : & Vec3 ) -> f32 {
347
+ self . normal . dot ( * point) + -self . normal . dot ( self . point )
348
+ }
349
+
350
+ /// Get a reference to the plane's point.
351
+ pub fn point ( & self ) -> & Vec3 {
352
+ & self . point
353
+ }
354
+
355
+ /// Get a reference to the plane's normal.
356
+ pub fn normal ( & self ) -> & Vec3 {
357
+ & self . normal
358
+ }
183
359
}
0 commit comments