Repository: bevyengine/bevy
File: examples/shader/array_texture.rs (Rust)

Code before:

    // Spawn some cubes using the array texture.
    let mesh_handle = meshes.add(Cuboid::default());
    let material_handle = materials.add(ArrayTextureMaterial { array_texture });
    for x in -5..=5 {
        commands.spawn((
            Mesh3d(mesh_handle.clone()),
            MeshMaterial3d(material_handle.clone()),
            // Pass a different mesh tag to allow selecting different layers of
            // the array texture in the shader.
            MeshTag(x as u32 % TEXTURE_COUNT),
            Transform::from_xyz(x as f32 + 0.5, 0.0, 0.0),
        ));
    }
}

fn update_mesh_tags(
    time: Res<Time>,
    mut query: Query<&mut MeshTag>,
    mut timer: Local<Timer>,
    mut rng: Local<Option<ChaCha8Rng>>,
) {
    // We're seeding the PRNG here to make this example deterministic for
    // testing purposes. This isn't strictly required in practical use unless
    // you need your app to be deterministic.
    let rng = rng.get_or_insert_with(|| ChaCha8Rng::seed_from_u64(19878367467713));

    // Initialize the timer on the first run.
    if timer.duration().is_zero() {
        *timer = Timer::from_seconds(0.5, TimerMode::Repeating);
    }

    timer.tick(time.delta());
    if timer.just_finished() {
        for mut tag in query.iter_mut() {
            // Randomly change the mesh tag to demonstrate that we can select
            // different layers of the array texture at runtime.
            *tag = MeshTag(rng.random::<u32>() % TEXTURE_COUNT);
        }
    }
}

#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]
struct ArrayTextureMaterial {
    #[texture(0, dimension = "2d_array")]
    #[sampler(1)]
    array_texture: Handle<Image>,
}

impl Material for ArrayTextureMaterial {
    fn fragment_shader() -> ShaderRef {

Review comment (suggestion):
I personally prefer avoiding rng if possible in examples. I don't like needing a third party dependency when just copying an example. Instead of using rng I'd suggest simply using the time and every 1 second offsetting every layer by 1. Showing that you can change the tags at runtime is good but I don't think the rng is necessary to show that.

Code after:
    let mesh_handle = meshes.add(Cuboid::default());
    let material_handle = materials.add(ArrayTextureMaterial { array_texture });
    for x in -5..=5 {
        commands.spawn((
            Mesh3d(mesh_handle.clone()),
            MeshMaterial3d(material_handle.clone()),
            // Pass a different mesh tag to allow selecting different layers of
            // the array texture in the shader.
            MeshTag(x as u32 % TEXTURE_COUNT),
            Transform::from_xyz(x as f32 + 0.5, 0.0, 0.0),
        ));
    }
}

fn update_mesh_tags(time: Res<Time>, mut query: Query<&mut MeshTag>, mut timer: Local<Timer>) {
    // Initialize the timer on the first run.
    if timer.duration().is_zero() {
        *timer = Timer::from_seconds(1.5, TimerMode::Repeating);
    }

    timer.tick(time.delta());
    if timer.just_finished() {
        for mut tag in query.iter_mut() {
            // Cycle through the texture layers to demonstrate that we can
            // select different layers of the array texture at runtime.
            tag.0 = (tag.0 + 1) % TEXTURE_COUNT;
        }
    }
}

#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]
struct ArrayTextureMaterial {
    #[texture(0, dimension = "2d_array")]
    #[sampler(1)]
    array_texture: Handle<Image>,
}

impl Material for ArrayTextureMaterial {
    fn fragment_shader() -> ShaderRef {
        SHADER_ASSET_PATH.into()
    }
}