1
1
use bevy:: {
2
2
diagnostic:: { FrameTimeDiagnosticsPlugin , LogDiagnosticsPlugin } ,
3
+ math:: { DVec2 , DVec3 } ,
3
4
prelude:: * ,
4
5
} ;
5
-
6
6
fn main ( ) {
7
7
App :: new ( )
8
8
. add_plugins ( DefaultPlugins )
@@ -26,41 +26,75 @@ fn setup(
26
26
base_color : Color :: PINK ,
27
27
..default ( )
28
28
} ) ;
29
- for x in 0 ..WIDTH {
30
- for y in 0 ..HEIGHT {
31
- // introduce spaces to break any kind of moiré pattern
32
- if x % 10 == 0 || y % 10 == 0 {
33
- continue ;
29
+
30
+ match std:: env:: args ( ) . nth ( 1 ) . as_deref ( ) {
31
+ Some ( "sphere" ) => {
32
+ // NOTE: This pattern is good for testing performance of culling as it provides roughly
33
+ // the same number of visible meshes regardless of the viewing angle.
34
+ const N_POINTS : usize = WIDTH * HEIGHT * 4 ;
35
+ // NOTE: f64 is used to avoid precision issues that produce visual artifacts in the distribution
36
+ let radius = WIDTH as f64 * 2.5 ;
37
+ let golden_ratio = 0.5f64 * ( 1.0f64 + 5.0f64 . sqrt ( ) ) ;
38
+ for i in 0 ..N_POINTS {
39
+ let spherical_polar_theta_phi =
40
+ fibonacci_spiral_on_sphere ( golden_ratio, i, N_POINTS ) ;
41
+ let unit_sphere_p = spherical_polar_to_cartesian ( spherical_polar_theta_phi) ;
42
+ commands. spawn_bundle ( PbrBundle {
43
+ mesh : mesh. clone_weak ( ) ,
44
+ material : material. clone_weak ( ) ,
45
+ transform : Transform :: from_translation ( ( radius * unit_sphere_p) . as_vec3 ( ) ) ,
46
+ ..default ( )
47
+ } ) ;
48
+ }
49
+
50
+ // camera
51
+ commands. spawn_bundle ( PerspectiveCameraBundle :: default ( ) ) ;
52
+ }
53
+ _ => {
54
+ // NOTE: This pattern is good for demonstrating that frustum culling is working correctly
55
+ // as the number of visible meshes rises and falls depending on the viewing angle.
56
+ for x in 0 ..WIDTH {
57
+ for y in 0 ..HEIGHT {
58
+ // introduce spaces to break any kind of moiré pattern
59
+ if x % 10 == 0 || y % 10 == 0 {
60
+ continue ;
61
+ }
62
+ // cube
63
+ commands. spawn_bundle ( PbrBundle {
64
+ mesh : mesh. clone_weak ( ) ,
65
+ material : material. clone_weak ( ) ,
66
+ transform : Transform :: from_xyz ( ( x as f32 ) * 2.5 , ( y as f32 ) * 2.5 , 0.0 ) ,
67
+ ..default ( )
68
+ } ) ;
69
+ commands. spawn_bundle ( PbrBundle {
70
+ mesh : mesh. clone_weak ( ) ,
71
+ material : material. clone_weak ( ) ,
72
+ transform : Transform :: from_xyz (
73
+ ( x as f32 ) * 2.5 ,
74
+ HEIGHT as f32 * 2.5 ,
75
+ ( y as f32 ) * 2.5 ,
76
+ ) ,
77
+ ..default ( )
78
+ } ) ;
79
+ commands. spawn_bundle ( PbrBundle {
80
+ mesh : mesh. clone_weak ( ) ,
81
+ material : material. clone_weak ( ) ,
82
+ transform : Transform :: from_xyz ( ( x as f32 ) * 2.5 , 0.0 , ( y as f32 ) * 2.5 ) ,
83
+ ..default ( )
84
+ } ) ;
85
+ commands. spawn_bundle ( PbrBundle {
86
+ mesh : mesh. clone_weak ( ) ,
87
+ material : material. clone_weak ( ) ,
88
+ transform : Transform :: from_xyz ( 0.0 , ( x as f32 ) * 2.5 , ( y as f32 ) * 2.5 ) ,
89
+ ..default ( )
90
+ } ) ;
91
+ }
34
92
}
35
- // cube
36
- commands. spawn_bundle ( PbrBundle {
37
- mesh : mesh. clone_weak ( ) ,
38
- material : material. clone_weak ( ) ,
39
- transform : Transform :: from_xyz ( ( x as f32 ) * 2.5 , ( y as f32 ) * 2.5 , 0.0 ) ,
93
+ // camera
94
+ commands. spawn_bundle ( PerspectiveCameraBundle {
95
+ transform : Transform :: from_xyz ( WIDTH as f32 , HEIGHT as f32 , WIDTH as f32 ) ,
40
96
..default ( )
41
97
} ) ;
42
- commands. spawn_bundle ( PbrBundle {
43
- mesh : mesh. clone_weak ( ) ,
44
- material : material. clone_weak ( ) ,
45
- transform : Transform :: from_xyz (
46
- ( x as f32 ) * 2.5 ,
47
- HEIGHT as f32 * 2.5 ,
48
- ( y as f32 ) * 2.5 ,
49
- ) ,
50
- ..Default :: default ( )
51
- } ) ;
52
- commands. spawn_bundle ( PbrBundle {
53
- mesh : mesh. clone_weak ( ) ,
54
- material : material. clone_weak ( ) ,
55
- transform : Transform :: from_xyz ( ( x as f32 ) * 2.5 , 0.0 , ( y as f32 ) * 2.5 ) ,
56
- ..Default :: default ( )
57
- } ) ;
58
- commands. spawn_bundle ( PbrBundle {
59
- mesh : mesh. clone_weak ( ) ,
60
- material : material. clone_weak ( ) ,
61
- transform : Transform :: from_xyz ( 0.0 , ( x as f32 ) * 2.5 , ( y as f32 ) * 2.5 ) ,
62
- ..Default :: default ( )
63
- } ) ;
64
98
}
65
99
}
66
100
@@ -72,20 +106,30 @@ fn setup(
72
106
transform : Transform {
73
107
translation : Vec3 :: new ( 0.0 , HEIGHT as f32 * 2.5 , 0.0 ) ,
74
108
scale : Vec3 :: splat ( 5.0 ) ,
75
- ..Default :: default ( )
109
+ ..default ( )
76
110
} ,
77
- ..Default :: default ( )
78
- } ) ;
79
-
80
- // camera
81
- commands. spawn_bundle ( PerspectiveCameraBundle {
82
- transform : Transform :: from_xyz ( WIDTH as f32 , HEIGHT as f32 , WIDTH as f32 ) ,
83
111
..default ( )
84
112
} ) ;
85
113
86
- commands. spawn_bundle ( DirectionalLightBundle {
87
- ..Default :: default ( )
88
- } ) ;
114
+ commands. spawn_bundle ( DirectionalLightBundle { ..default ( ) } ) ;
115
+ }
116
+
117
+ // NOTE: This epsilon value is apparently optimal for optimizing for the average
118
+ // nearest-neighbor distance. See:
119
+ // http://extremelearning.com.au/how-to-evenly-distribute-points-on-a-sphere-more-effectively-than-the-canonical-fibonacci-lattice/
120
+ // for details.
121
+ const EPSILON : f64 = 0.36 ;
122
+ fn fibonacci_spiral_on_sphere ( golden_ratio : f64 , i : usize , n : usize ) -> DVec2 {
123
+ DVec2 :: new (
124
+ 2.0 * std:: f64:: consts:: PI * ( i as f64 / golden_ratio) ,
125
+ ( 1.0 - 2.0 * ( i as f64 + EPSILON ) / ( n as f64 - 1.0 + 2.0 * EPSILON ) ) . acos ( ) ,
126
+ )
127
+ }
128
+
129
+ fn spherical_polar_to_cartesian ( p : DVec2 ) -> DVec3 {
130
+ let ( sin_theta, cos_theta) = p. x . sin_cos ( ) ;
131
+ let ( sin_phi, cos_phi) = p. y . sin_cos ( ) ;
132
+ DVec3 :: new ( cos_theta * sin_phi, sin_theta * sin_phi, cos_phi)
89
133
}
90
134
91
135
// System for rotating the camera
0 commit comments