use bevy::{ asset::LoadState, prelude::*, reflect::TypeUuid, render::{ render_asset::RenderAssets, render_resource::{ AsBindGroup, AsBindGroupError, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingResource, BindingType, OwnedBindingResource, PreparedBindGroup, SamplerBindingType, ShaderRef, ShaderStages, TextureSampleType, TextureViewDimension, }, renderer::RenderDevice, texture::FallbackImage, }, }; /// This example illustrates how to create a texture for use with a `texture_2d_array` shader /// uniform variable. fn main() { App::new() .add_plugins(DefaultPlugins) .add_plugin(MaterialPlugin::::default()) .add_startup_system(setup) .add_system(create_array_texture) .run(); } struct LoadingTexture { is_loaded: bool, handle: Handle, } fn setup(mut commands: Commands, asset_server: Res) { // Start loading the texture. commands.insert_resource(LoadingTexture { is_loaded: false, handle: asset_server.load("textures/array_texture.png"), }); // light commands.spawn_bundle(PointLightBundle { point_light: PointLight { intensity: 3000.0, ..Default::default() }, transform: Transform::from_xyz(-3.0, 2.0, -1.0), ..Default::default() }); commands.spawn_bundle(PointLightBundle { point_light: PointLight { intensity: 3000.0, ..Default::default() }, transform: Transform::from_xyz(3.0, 2.0, 1.0), ..Default::default() }); // camera commands.spawn_bundle(Camera3dBundle { transform: Transform::from_xyz(5.0, 5.0, 5.0).looking_at(Vec3::new(1.5, 0.0, 0.0), Vec3::Y), ..Default::default() }); } fn create_array_texture( mut commands: Commands, asset_server: Res, mut loading_texture: ResMut, mut images: ResMut>, mut meshes: ResMut>, mut materials: ResMut>, ) { if loading_texture.is_loaded || asset_server.get_load_state(loading_texture.handle.clone()) != LoadState::Loaded { return; } loading_texture.is_loaded = true; let image = images.get_mut(&loading_texture.handle).unwrap(); // Create a new array texture asset from the loaded texture. let array_layers = 4; image.reinterpret_stacked_2d_as_array(array_layers); // Spawn some cubes using the array texture let mesh_handle = meshes.add(Mesh::from(shape::Cube { size: 1.0 })); let material_handle = materials.add(ArrayTextureMaterial { array_texture: loading_texture.handle.clone(), }); for x in -5..=5 { commands.spawn_bundle(MaterialMeshBundle { mesh: mesh_handle.clone(), material: material_handle.clone(), transform: Transform::from_xyz(x as f32 + 0.5, 0.0, 0.0), ..Default::default() }); } } #[derive(Debug, Clone, TypeUuid)] #[uuid = "9c5a0ddf-1eaf-41b4-9832-ed736fd26af3"] struct ArrayTextureMaterial { array_texture: Handle, } impl Material for ArrayTextureMaterial { fn fragment_shader() -> ShaderRef { "shaders/array_texture.wgsl".into() } } impl AsBindGroup for ArrayTextureMaterial { type Data = (); fn as_bind_group( &self, layout: &BindGroupLayout, render_device: &RenderDevice, images: &RenderAssets, _fallback_image: &FallbackImage, ) -> Result, AsBindGroupError> { let image = images .get(&self.array_texture) .ok_or(AsBindGroupError::RetryNextUpdate)?; let bind_group = render_device.create_bind_group(&BindGroupDescriptor { entries: &[ BindGroupEntry { binding: 0, resource: BindingResource::TextureView(&image.texture_view), }, BindGroupEntry { binding: 1, resource: BindingResource::Sampler(&image.sampler), }, ], label: Some("array_texture_material_bind_group"), layout, }); Ok(PreparedBindGroup { bind_group, bindings: vec![ OwnedBindingResource::TextureView(image.texture_view.clone()), OwnedBindingResource::Sampler(image.sampler.clone()), ], data: (), }) } fn bind_group_layout(render_device: &RenderDevice) -> BindGroupLayout { render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { entries: &[ // Array Texture BindGroupLayoutEntry { binding: 0, visibility: ShaderStages::FRAGMENT, ty: BindingType::Texture { multisampled: false, sample_type: TextureSampleType::Float { filterable: true }, view_dimension: TextureViewDimension::D2Array, }, count: None, }, // Array Texture Sampler BindGroupLayoutEntry { binding: 1, visibility: ShaderStages::FRAGMENT, ty: BindingType::Sampler(SamplerBindingType::Filtering), count: None, }, ], label: None, }) } }