3
3
//! To measure performance realistically, be sure to run this in release mode.
4
4
//! `cargo run --example many_cubes --release`
5
5
//!
6
- //! By default, this arranges the meshes in a cubical pattern, where the number of visible meshes
7
- //! varies with the viewing angle. You can choose to run the demo with a spherical pattern that
6
+ //! By default, this arranges the meshes in a spherical pattern that
8
7
//! distributes the meshes evenly.
9
8
//!
10
- //! To start the demo using the spherical layout run
11
- //! `cargo run --example many_cubes --release sphere`
9
+ //! See `cargo run --example many_cubes --release -- --help` for more options.
12
10
13
11
use std:: f64:: consts:: PI ;
14
12
@@ -18,8 +16,40 @@ use bevy::{
18
16
prelude:: * ,
19
17
window:: { PresentMode , WindowPlugin } ,
20
18
} ;
19
+ use bevy_internal:: render:: render_resource:: { Extent3d , TextureDimension , TextureFormat } ;
20
+ use clap:: { Parser , ValueEnum } ;
21
+ use rand:: { rngs:: StdRng , seq:: SliceRandom , Rng , SeedableRng } ;
22
+
23
+ #[ derive( Parser , Resource ) ]
24
+ #[ command( author, version, about, long_about = None ) ]
25
+ struct Args {
26
+ /// How the cube instances should be positioned.
27
+ #[ arg( short, long, value_enum, default_value_t=Layout :: Sphere ) ]
28
+ layout : Layout ,
29
+
30
+ /// Whether to step the camera animation by a fixed amount such that each frame is the same across runs.
31
+ #[ arg( short, long, default_value_t = false ) ]
32
+ benchmark : bool ,
33
+
34
+ /// Whether to vary the material data in each instance.
35
+ #[ arg( short, long, default_value_t = false ) ]
36
+ vary_material_data : bool ,
37
+
38
+ /// The number of different textures from which to randomly select the material base color. 0 means no textures.
39
+ #[ arg( short, long, default_value_t = 0 ) ]
40
+ material_texture_count : usize ,
41
+ }
42
+
43
+ #[ derive( Default , Clone , ValueEnum ) ]
44
+ enum Layout {
45
+ Cube ,
46
+ #[ default]
47
+ Sphere ,
48
+ }
21
49
22
50
fn main ( ) {
51
+ let args = Args :: parse ( ) ;
52
+
23
53
App :: new ( )
24
54
. add_plugins ( (
25
55
DefaultPlugins . set ( WindowPlugin {
@@ -32,28 +62,47 @@ fn main() {
32
62
FrameTimeDiagnosticsPlugin ,
33
63
LogDiagnosticsPlugin :: default ( ) ,
34
64
) )
65
+ . insert_resource ( args)
35
66
. add_systems ( Startup , setup)
36
67
. add_systems ( Update , ( move_camera, print_mesh_count) )
37
68
. run ( ) ;
38
69
}
39
70
40
71
fn setup (
41
72
mut commands : Commands ,
73
+ args : Res < Args > ,
42
74
mut meshes : ResMut < Assets < Mesh > > ,
43
- mut materials : ResMut < Assets < StandardMaterial > > ,
75
+ materials : ResMut < Assets < StandardMaterial > > ,
76
+ images : ResMut < Assets < Image > > ,
44
77
) {
45
78
warn ! ( include_str!( "warning_string.txt" ) ) ;
46
79
80
+ let args = args. into_inner ( ) ;
81
+ let images = images. into_inner ( ) ;
82
+ let materials = materials. into_inner ( ) ;
83
+
47
84
const WIDTH : usize = 200 ;
48
85
const HEIGHT : usize = 200 ;
86
+
49
87
let mesh = meshes. add ( Mesh :: from ( shape:: Cube { size : 1.0 } ) ) ;
50
- let material = materials. add ( StandardMaterial {
51
- base_color : Color :: PINK ,
52
- ..default ( )
53
- } ) ;
54
88
55
- match std:: env:: args ( ) . nth ( 1 ) . as_deref ( ) {
56
- Some ( "sphere" ) => {
89
+ let material_textures = init_textures ( args, images) ;
90
+
91
+ let material = materials. add ( Color :: PINK . into ( ) ) ;
92
+ let mut texture_rng = StdRng :: seed_from_u64 ( 42 ) ;
93
+ let texture_material = if args. material_texture_count == 1 {
94
+ Some ( materials. add ( StandardMaterial {
95
+ base_color : Color :: WHITE ,
96
+ base_color_texture : get_texture ( args, & mut texture_rng, & material_textures) ,
97
+ ..default ( )
98
+ } ) )
99
+ } else {
100
+ None
101
+ } ;
102
+
103
+ let mut color_rng = StdRng :: seed_from_u64 ( 42 ) ;
104
+ match args. layout {
105
+ Layout :: Sphere => {
57
106
// NOTE: This pattern is good for testing performance of culling as it provides roughly
58
107
// the same number of visible meshes regardless of the viewing angle.
59
108
const N_POINTS : usize = WIDTH * HEIGHT * 4 ;
@@ -66,7 +115,15 @@ fn setup(
66
115
let unit_sphere_p = spherical_polar_to_cartesian ( spherical_polar_theta_phi) ;
67
116
commands. spawn ( PbrBundle {
68
117
mesh : mesh. clone_weak ( ) ,
69
- material : material. clone_weak ( ) ,
118
+ material : get_material (
119
+ args,
120
+ & material,
121
+ texture_material. as_ref ( ) ,
122
+ materials,
123
+ & mut color_rng,
124
+ & mut texture_rng,
125
+ & material_textures,
126
+ ) ,
70
127
transform : Transform :: from_translation ( ( radius * unit_sphere_p) . as_vec3 ( ) ) ,
71
128
..default ( )
72
129
} ) ;
@@ -87,13 +144,29 @@ fn setup(
87
144
// cube
88
145
commands. spawn ( PbrBundle {
89
146
mesh : mesh. clone_weak ( ) ,
90
- material : material. clone_weak ( ) ,
147
+ material : get_material (
148
+ args,
149
+ & material,
150
+ texture_material. as_ref ( ) ,
151
+ materials,
152
+ & mut color_rng,
153
+ & mut texture_rng,
154
+ & material_textures,
155
+ ) ,
91
156
transform : Transform :: from_xyz ( ( x as f32 ) * 2.5 , ( y as f32 ) * 2.5 , 0.0 ) ,
92
157
..default ( )
93
158
} ) ;
94
159
commands. spawn ( PbrBundle {
95
160
mesh : mesh. clone_weak ( ) ,
96
- material : material. clone_weak ( ) ,
161
+ material : get_material (
162
+ args,
163
+ & material,
164
+ texture_material. as_ref ( ) ,
165
+ materials,
166
+ & mut color_rng,
167
+ & mut texture_rng,
168
+ & material_textures,
169
+ ) ,
97
170
transform : Transform :: from_xyz (
98
171
( x as f32 ) * 2.5 ,
99
172
HEIGHT as f32 * 2.5 ,
@@ -103,13 +176,29 @@ fn setup(
103
176
} ) ;
104
177
commands. spawn ( PbrBundle {
105
178
mesh : mesh. clone_weak ( ) ,
106
- material : material. clone_weak ( ) ,
179
+ material : get_material (
180
+ args,
181
+ & material,
182
+ texture_material. as_ref ( ) ,
183
+ materials,
184
+ & mut color_rng,
185
+ & mut texture_rng,
186
+ & material_textures,
187
+ ) ,
107
188
transform : Transform :: from_xyz ( ( x as f32 ) * 2.5 , 0.0 , ( y as f32 ) * 2.5 ) ,
108
189
..default ( )
109
190
} ) ;
110
191
commands. spawn ( PbrBundle {
111
192
mesh : mesh. clone_weak ( ) ,
112
- material : material. clone_weak ( ) ,
193
+ material : get_material (
194
+ args,
195
+ & material,
196
+ texture_material. as_ref ( ) ,
197
+ materials,
198
+ & mut color_rng,
199
+ & mut texture_rng,
200
+ & material_textures,
201
+ ) ,
113
202
transform : Transform :: from_xyz ( 0.0 , ( x as f32 ) * 2.5 , ( y as f32 ) * 2.5 ) ,
114
203
..default ( )
115
204
} ) ;
@@ -139,6 +228,65 @@ fn setup(
139
228
commands. spawn ( DirectionalLightBundle { ..default ( ) } ) ;
140
229
}
141
230
231
+ fn init_textures ( args : & Args , images : & mut Assets < Image > ) -> Vec < Handle < Image > > {
232
+ let mut color_rng = StdRng :: seed_from_u64 ( 42 ) ;
233
+ let color_bytes: Vec < u8 > = ( 0 ..( args. material_texture_count * 4 ) )
234
+ . map ( |i| if ( i % 4 ) == 3 { 255 } else { color_rng. gen ( ) } )
235
+ . collect ( ) ;
236
+ color_bytes
237
+ . chunks ( 4 )
238
+ . map ( |pixel| {
239
+ images. add ( Image :: new_fill (
240
+ Extent3d {
241
+ width : 1 ,
242
+ height : 1 ,
243
+ depth_or_array_layers : 1 ,
244
+ } ,
245
+ TextureDimension :: D2 ,
246
+ pixel,
247
+ TextureFormat :: Rgba8UnormSrgb ,
248
+ ) )
249
+ } )
250
+ . collect ( )
251
+ }
252
+
253
+ fn get_texture < R : Rng > (
254
+ args : & Args ,
255
+ texture_rng : & mut R ,
256
+ material_textures : & [ Handle < Image > ] ,
257
+ ) -> Option < Handle < Image > > {
258
+ match args. material_texture_count {
259
+ c if c == 0 => None ,
260
+ c if c == 1 => Some ( material_textures[ 0 ] . clone_weak ( ) ) ,
261
+ _ => Some ( material_textures. choose ( texture_rng) . unwrap ( ) . clone_weak ( ) ) ,
262
+ }
263
+ }
264
+
265
+ fn get_material < R : Rng > (
266
+ args : & Args ,
267
+ material : & Handle < StandardMaterial > ,
268
+ texture_material : Option < & Handle < StandardMaterial > > ,
269
+ materials : & mut Assets < StandardMaterial > ,
270
+ color_rng : & mut R ,
271
+ texture_rng : & mut R ,
272
+ material_textures : & [ Handle < Image > ] ,
273
+ ) -> Handle < StandardMaterial > {
274
+ match ( args. vary_material_data , args. material_texture_count ) {
275
+ ( false , 0 ) => material. clone_weak ( ) ,
276
+ ( false , 1 ) => texture_material. unwrap ( ) . clone_weak ( ) ,
277
+ ( false , _) => materials. add ( StandardMaterial {
278
+ base_color : Color :: WHITE ,
279
+ base_color_texture : get_texture ( args, texture_rng, material_textures) ,
280
+ ..default ( )
281
+ } ) ,
282
+ ( true , _) => materials. add ( StandardMaterial {
283
+ base_color : Color :: rgb_u8 ( color_rng. gen ( ) , color_rng. gen ( ) , color_rng. gen ( ) ) ,
284
+ base_color_texture : get_texture ( args, texture_rng, material_textures) ,
285
+ ..default ( )
286
+ } ) ,
287
+ }
288
+ }
289
+
142
290
// NOTE: This epsilon value is apparently optimal for optimizing for the average
143
291
// nearest-neighbor distance. See:
144
292
// http://extremelearning.com.au/how-to-evenly-distribute-points-on-a-sphere-more-effectively-than-the-canonical-fibonacci-lattice/
@@ -159,9 +307,18 @@ fn spherical_polar_to_cartesian(p: DVec2) -> DVec3 {
159
307
}
160
308
161
309
// System for rotating the camera
162
- fn move_camera ( time : Res < Time > , mut camera_query : Query < & mut Transform , With < Camera > > ) {
310
+ fn move_camera (
311
+ time : Res < Time > ,
312
+ args : Res < Args > ,
313
+ mut camera_query : Query < & mut Transform , With < Camera > > ,
314
+ ) {
163
315
let mut camera_transform = camera_query. single_mut ( ) ;
164
- let delta = time. delta_seconds ( ) * 0.15 ;
316
+ let delta = 0.15
317
+ * if args. benchmark {
318
+ 1.0 / 60.0
319
+ } else {
320
+ time. delta_seconds ( )
321
+ } ;
165
322
camera_transform. rotate_z ( delta) ;
166
323
camera_transform. rotate_x ( delta) ;
167
324
}
0 commit comments