Skip to main content

array_texture/
array_texture.rs

1//! This example illustrates how to create a texture for use with a
2//! `texture_2d_array<f32>` shader uniform variable and then how to sample from
3//! that texture in the shader by using a `MeshTag` component on the mesh
4//! entity.
5
6use bevy::{
7    image::{ImageArrayLayout, ImageLoaderSettings},
8    mesh::MeshTag,
9    prelude::*,
10    reflect::TypePath,
11    render::render_resource::AsBindGroup,
12    shader::ShaderRef,
13};
14
15/// This example uses a shader source file from the assets subdirectory.
16const SHADER_ASSET_PATH: &str = "shaders/array_texture.wgsl";
17
18/// Corresponds to the number of layers in the array texture.
19const TEXTURE_COUNT: u32 = 4;
20
21fn main() {
22    App::new()
23        .add_plugins((
24            DefaultPlugins,
25            MaterialPlugin::<ArrayTextureMaterial>::default(),
26        ))
27        .add_systems(Startup, setup)
28        .add_systems(Update, update_mesh_tags)
29        .run();
30}
31
32fn setup(
33    mut commands: Commands,
34    mut meshes: ResMut<Assets<Mesh>>,
35    mut materials: ResMut<Assets<ArrayTextureMaterial>>,
36    asset_server: Res<AssetServer>,
37) {
38    // Load the texture.
39    let array_texture = asset_server
40        .load_builder()
41        .with_settings(|settings: &mut ImageLoaderSettings| {
42            settings.array_layout = Some(ImageArrayLayout::RowCount {
43                rows: TEXTURE_COUNT,
44            });
45        })
46        .load("textures/array_texture.png");
47
48    // light
49    commands.spawn((
50        DirectionalLight::default(),
51        Transform::from_xyz(3.0, 2.0, 1.0).looking_at(Vec3::ZERO, Vec3::Y),
52    ));
53
54    // camera
55    commands.spawn((
56        Camera3d::default(),
57        Transform::from_xyz(5.0, 5.0, 5.0).looking_at(Vec3::new(1.5, 0.0, 0.0), Vec3::Y),
58    ));
59
60    // Spawn some cubes using the array texture.
61    let mesh_handle = meshes.add(Cuboid::default());
62    let material_handle = materials.add(ArrayTextureMaterial { array_texture });
63    for x in -5..=5 {
64        commands.spawn((
65            Mesh3d(mesh_handle.clone()),
66            MeshMaterial3d(material_handle.clone()),
67            // Pass a different mesh tag to allow selecting different layers of
68            // the array texture in the shader.
69            MeshTag(x as u32 % TEXTURE_COUNT),
70            Transform::from_xyz(x as f32 + 0.5, 0.0, 0.0),
71        ));
72    }
73}
74
75fn update_mesh_tags(time: Res<Time>, mut query: Query<&mut MeshTag>, mut timer: Local<Timer>) {
76    // Initialize the timer on the first run.
77    if timer.duration().is_zero() {
78        *timer = Timer::from_seconds(1.5, TimerMode::Repeating);
79    }
80
81    timer.tick(time.delta());
82    if timer.just_finished() {
83        for mut tag in query.iter_mut() {
84            // Cycle through the texture layers to demonstrate that we can
85            // select different layers of the array texture at runtime.
86            tag.0 = (tag.0 + 1) % TEXTURE_COUNT;
87        }
88    }
89}
90
91#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]
92struct ArrayTextureMaterial {
93    #[texture(0, dimension = "2d_array")]
94    #[sampler(1)]
95    array_texture: Handle<Image>,
96}
97
98impl Material for ArrayTextureMaterial {
99    fn fragment_shader() -> ShaderRef {
100        SHADER_ASSET_PATH.into()
101    }
102}