Skip to main content

many_materials/
many_materials.rs

1//! Benchmark to test rendering many animated materials
2use argh::FromArgs;
3use bevy::{
4    diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
5    prelude::*,
6    window::{PresentMode, WindowResolution},
7    winit::WinitSettings,
8};
9use std::f32::consts::PI;
10
11#[derive(FromArgs, Resource)]
12/// Command-line arguments for the `many_materials` stress test.
13struct Args {
14    /// the size of the grid of materials to render (n x n)
15    #[argh(option, short = 'n', default = "10")]
16    grid_size: usize,
17}
18
19fn main() {
20    // `from_env` panics on the web
21    #[cfg(not(target_arch = "wasm32"))]
22    let args: Args = argh::from_env();
23    #[cfg(target_arch = "wasm32")]
24    let args = Args::from_args(&[], &[]).unwrap();
25
26    App::new()
27        .add_plugins((
28            DefaultPlugins.set(WindowPlugin {
29                primary_window: Some(Window {
30                    resolution: WindowResolution::new(1920, 1080).with_scale_factor_override(1.0),
31                    title: "many_materials".into(),
32                    present_mode: PresentMode::AutoNoVsync,
33                    ..default()
34                }),
35                ..default()
36            }),
37            FrameTimeDiagnosticsPlugin::default(),
38            LogDiagnosticsPlugin::default(),
39        ))
40        .insert_resource(WinitSettings::continuous())
41        .insert_resource(args)
42        .add_systems(Startup, setup)
43        .add_systems(Update, animate_materials)
44        .run();
45}
46
47fn setup(
48    mut commands: Commands,
49    args: Res<Args>,
50    mesh_assets: ResMut<Assets<Mesh>>,
51    material_assets: ResMut<Assets<StandardMaterial>>,
52) {
53    let args = args.into_inner();
54    let material_assets = material_assets.into_inner();
55    let mesh_assets = mesh_assets.into_inner();
56    let n = args.grid_size;
57
58    // Camera
59    let w = n as f32;
60    commands.spawn((
61        Camera3d::default(),
62        Transform::from_xyz(w * 1.25, w + 1.0, w * 1.25)
63            .looking_at(Vec3::new(0.0, (w * -1.1) + 1.0, 0.0), Vec3::Y),
64    ));
65
66    // Light
67    commands.spawn((
68        Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -PI / 4.)),
69        DirectionalLight {
70            illuminance: 3000.0,
71            shadow_maps_enabled: true,
72            ..default()
73        },
74    ));
75
76    // Cubes
77    let mesh_handle = mesh_assets.add(Cuboid::from_size(Vec3::ONE));
78    for x in 0..n {
79        for z in 0..n {
80            commands.spawn((
81                Mesh3d(mesh_handle.clone()),
82                MeshMaterial3d(material_assets.add(Color::WHITE)),
83                Transform::from_translation(Vec3::new(x as f32, 0.0, z as f32)),
84            ));
85        }
86    }
87}
88
89fn animate_materials(
90    material_handles: Query<&MeshMaterial3d<StandardMaterial>>,
91    time: Res<Time>,
92    mut materials: ResMut<Assets<StandardMaterial>>,
93) {
94    for (i, material_handle) in material_handles.iter().enumerate() {
95        if let Some(mut material) = materials.get_mut(material_handle) {
96            let color = Color::hsl(
97                ((i as f32 * 2.345 + time.elapsed_secs()) * 100.0) % 360.0,
98                1.0,
99                0.5,
100            );
101            material.base_color = color;
102        }
103    }
104}