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,41 @@ 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:: { seq:: SliceRandom , Rng , RngCore , SeedableRng } ;
22
+ use rand_xoshiro:: Xoshiro256StarStar ;
23
+
24
+ #[ derive( Parser , Resource ) ]
25
+ #[ command( author, version, about, long_about = None ) ]
26
+ struct Args {
27
+ /// How the cube instances should be positioned.
28
+ #[ arg( short, long, value_enum, default_value_t=Layout :: Sphere ) ]
29
+ layout : Layout ,
30
+
31
+ /// Whether to step the camera animation by a fixed amount such that each frame is the same across runs.
32
+ #[ arg( short, long, default_value_t = false ) ]
33
+ benchmark : bool ,
34
+
35
+ /// Whether to vary the material data in each instance.
36
+ #[ arg( short, long, default_value_t = false ) ]
37
+ vary_material_data : bool ,
38
+
39
+ /// The number of different textures from which to randomly select the material base color. 0 means no textures.
40
+ #[ arg( short, long, default_value_t = 0 ) ]
41
+ material_texture_count : usize ,
42
+ }
43
+
44
+ #[ derive( Default , Clone , ValueEnum ) ]
45
+ enum Layout {
46
+ Cube ,
47
+ #[ default]
48
+ Sphere ,
49
+ }
21
50
22
51
fn main ( ) {
52
+ let args = Args :: parse ( ) ;
53
+
23
54
App :: new ( )
24
55
. add_plugins ( (
25
56
DefaultPlugins . set ( WindowPlugin {
@@ -32,28 +63,47 @@ fn main() {
32
63
FrameTimeDiagnosticsPlugin ,
33
64
LogDiagnosticsPlugin :: default ( ) ,
34
65
) )
66
+ . insert_resource ( args)
35
67
. add_systems ( Startup , setup)
36
68
. add_systems ( Update , ( move_camera, print_mesh_count) )
37
69
. run ( ) ;
38
70
}
39
71
40
72
fn setup (
41
73
mut commands : Commands ,
74
+ args : Res < Args > ,
42
75
mut meshes : ResMut < Assets < Mesh > > ,
43
- mut materials : ResMut < Assets < StandardMaterial > > ,
76
+ materials : ResMut < Assets < StandardMaterial > > ,
77
+ images : ResMut < Assets < Image > > ,
44
78
) {
45
79
warn ! ( include_str!( "warning_string.txt" ) ) ;
46
80
81
+ let args = args. into_inner ( ) ;
82
+ let images = images. into_inner ( ) ;
83
+ let materials = materials. into_inner ( ) ;
84
+
47
85
const WIDTH : usize = 200 ;
48
86
const HEIGHT : usize = 200 ;
87
+
49
88
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
89
55
- match std:: env:: args ( ) . nth ( 1 ) . as_deref ( ) {
56
- Some ( "sphere" ) => {
90
+ let material_textures = init_textures ( args, images) ;
91
+
92
+ let material = materials. add ( Color :: PINK . into ( ) ) ;
93
+ let mut texture_rng = Xoshiro256StarStar :: seed_from_u64 ( 42 ) ;
94
+ let texture_material = if args. material_texture_count == 1 {
95
+ Some ( materials. add ( StandardMaterial {
96
+ base_color : Color :: WHITE ,
97
+ base_color_texture : get_texture ( args, & mut texture_rng, & material_textures) ,
98
+ ..default ( )
99
+ } ) )
100
+ } else {
101
+ None
102
+ } ;
103
+
104
+ let mut color_rng = Xoshiro256StarStar :: seed_from_u64 ( 42 ) ;
105
+ match args. layout {
106
+ Layout :: Sphere => {
57
107
// NOTE: This pattern is good for testing performance of culling as it provides roughly
58
108
// the same number of visible meshes regardless of the viewing angle.
59
109
const N_POINTS : usize = WIDTH * HEIGHT * 4 ;
@@ -66,7 +116,15 @@ fn setup(
66
116
let unit_sphere_p = spherical_polar_to_cartesian ( spherical_polar_theta_phi) ;
67
117
commands. spawn ( PbrBundle {
68
118
mesh : mesh. clone_weak ( ) ,
69
- material : material. clone_weak ( ) ,
119
+ material : get_material (
120
+ args,
121
+ & material,
122
+ texture_material. as_ref ( ) ,
123
+ materials,
124
+ & mut color_rng,
125
+ & mut texture_rng,
126
+ & material_textures,
127
+ ) ,
70
128
transform : Transform :: from_translation ( ( radius * unit_sphere_p) . as_vec3 ( ) ) ,
71
129
..default ( )
72
130
} ) ;
@@ -87,13 +145,29 @@ fn setup(
87
145
// cube
88
146
commands. spawn ( PbrBundle {
89
147
mesh : mesh. clone_weak ( ) ,
90
- material : material. clone_weak ( ) ,
148
+ material : get_material (
149
+ args,
150
+ & material,
151
+ texture_material. as_ref ( ) ,
152
+ materials,
153
+ & mut color_rng,
154
+ & mut texture_rng,
155
+ & material_textures,
156
+ ) ,
91
157
transform : Transform :: from_xyz ( ( x as f32 ) * 2.5 , ( y as f32 ) * 2.5 , 0.0 ) ,
92
158
..default ( )
93
159
} ) ;
94
160
commands. spawn ( PbrBundle {
95
161
mesh : mesh. clone_weak ( ) ,
96
- material : material. clone_weak ( ) ,
162
+ material : get_material (
163
+ args,
164
+ & material,
165
+ texture_material. as_ref ( ) ,
166
+ materials,
167
+ & mut color_rng,
168
+ & mut texture_rng,
169
+ & material_textures,
170
+ ) ,
97
171
transform : Transform :: from_xyz (
98
172
( x as f32 ) * 2.5 ,
99
173
HEIGHT as f32 * 2.5 ,
@@ -103,13 +177,29 @@ fn setup(
103
177
} ) ;
104
178
commands. spawn ( PbrBundle {
105
179
mesh : mesh. clone_weak ( ) ,
106
- material : material. clone_weak ( ) ,
180
+ material : get_material (
181
+ args,
182
+ & material,
183
+ texture_material. as_ref ( ) ,
184
+ materials,
185
+ & mut color_rng,
186
+ & mut texture_rng,
187
+ & material_textures,
188
+ ) ,
107
189
transform : Transform :: from_xyz ( ( x as f32 ) * 2.5 , 0.0 , ( y as f32 ) * 2.5 ) ,
108
190
..default ( )
109
191
} ) ;
110
192
commands. spawn ( PbrBundle {
111
193
mesh : mesh. clone_weak ( ) ,
112
- material : material. clone_weak ( ) ,
194
+ material : get_material (
195
+ args,
196
+ & material,
197
+ texture_material. as_ref ( ) ,
198
+ materials,
199
+ & mut color_rng,
200
+ & mut texture_rng,
201
+ & material_textures,
202
+ ) ,
113
203
transform : Transform :: from_xyz ( 0.0 , ( x as f32 ) * 2.5 , ( y as f32 ) * 2.5 ) ,
114
204
..default ( )
115
205
} ) ;
@@ -139,6 +229,64 @@ fn setup(
139
229
commands. spawn ( DirectionalLightBundle { ..default ( ) } ) ;
140
230
}
141
231
232
+ fn init_textures ( args : & Args , images : & mut Assets < Image > ) -> Vec < Handle < Image > > {
233
+ let mut color_bytes: Vec < u8 > = vec ! [ 0u8 ; args. material_texture_count * 4 ] ;
234
+ Xoshiro256StarStar :: seed_from_u64 ( 42 ) . fill_bytes ( & mut color_bytes) ;
235
+ color_bytes
236
+ . chunks_mut ( 4 )
237
+ . map ( |p| {
238
+ p[ 3 ] = 255 ;
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
+ p,
247
+ TextureFormat :: Rgba8UnormSrgb ,
248
+ ) )
249
+ } )
250
+ . collect ( )
251
+ }
252
+
253
+ fn get_texture < R : Rng + ?Sized > (
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 + ?Sized > (
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