diff --git a/Cargo.toml b/Cargo.toml index 49ec8ddcf8d3a1..576ee7cc17a1af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1148,6 +1148,17 @@ description = "Demonstrates how to process and load custom assets" category = "Assets" wasm = false +[[example]] +name = "texture_sampler" +path = "examples/asset/texture_sampler.rs" +doc-scrape-examples = true + +[package.metadata.example.texture_sampler] +name = "Texture sampler configuration" +description = "How configure image texture to repeat instead of default clamp" +category = "Assets" +wasm = true + # Async Tasks [[example]] name = "async_compute" diff --git a/assets/textures/house_tree_river.png b/assets/textures/house_tree_river.png new file mode 100644 index 00000000000000..10470a314584fc Binary files /dev/null and b/assets/textures/house_tree_river.png differ diff --git a/assets/textures/house_tree_river_copy.png b/assets/textures/house_tree_river_copy.png new file mode 100644 index 00000000000000..10470a314584fc Binary files /dev/null and b/assets/textures/house_tree_river_copy.png differ diff --git a/examples/README.md b/examples/README.md index db5ce7f96cfd97..a7dbf2e936b1b2 100644 --- a/examples/README.md +++ b/examples/README.md @@ -187,6 +187,7 @@ Example | Description [Custom Asset](../examples/asset/custom_asset.rs) | Implements a custom asset loader [Custom Asset IO](../examples/asset/custom_asset_reader.rs) | Implements a custom AssetReader [Hot Reloading of Assets](../examples/asset/hot_asset_reloading.rs) | Demonstrates automatic reloading of assets when modified on disk +[Texture Sampler](../examples/asset/texture_sampler.rs) | Demonstrates how to configure texture repeat instead of clamp ## Async Tasks diff --git a/examples/asset/texture_sampler.rs b/examples/asset/texture_sampler.rs new file mode 100644 index 00000000000000..c92caf7c3730e4 --- /dev/null +++ b/examples/asset/texture_sampler.rs @@ -0,0 +1,105 @@ +//! By default Bevy loads images to textures with sampler settings that clamp the image to the edges. +//! This example shows how to change the sampler settings to repeat the image instead. + +use bevy::app::App; +use bevy::app::Startup; +use bevy::asset::{AssetServer, Assets}; +use bevy::math::Vec3; +use bevy::prelude::DefaultPlugins; +use bevy::prelude::OrthographicProjection; +use bevy::prelude::{Camera2dBundle, ColorMaterial, Commands, Mesh, ResMut, Transform}; +use bevy::render::camera::ScalingMode; +use bevy::render::mesh::Indices; +use bevy::render::mesh::PrimitiveTopology; +use bevy::render::texture::{ + ImageAddressMode, ImageLoaderSettings, ImageSampler, ImageSamplerDescriptor, +}; +use bevy::sprite::MaterialMesh2dBundle; + +fn mesh() -> Mesh { + let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); + mesh.insert_attribute( + Mesh::ATTRIBUTE_POSITION, + vec![[0., 0., 0.], [1., 0., 0.], [1., 1., 0.], [0., 1., 0.]], + ); + mesh.insert_attribute( + Mesh::ATTRIBUTE_UV_0, + vec![[-1., 2.], [2., 2.], [2., -1.], [-1., -1.]], + ); + mesh.set_indices(Some(Indices::U16(vec![0, 1, 2, 2, 3, 0]))); + mesh +} + +fn setup( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, + mut asset_server: ResMut, +) { + commands.spawn(Camera2dBundle { + projection: OrthographicProjection { + scaling_mode: ScalingMode::AutoMin { + min_width: 2., + min_height: 1., + }, + far: 1000., + near: -1000., + ..OrthographicProjection::default() + }, + ..Camera2dBundle::default() + }); + + // By default Bevy loads images to textures with sampler settings that clamp the image to the edges. + let image = asset_server.load("textures/house_tree_river.png"); + + // Here we override the sampler settings to repeat the image instead. + let image_repeat = asset_server.load_with_settings( + // We are using another file name, because Bevy ignores different loader settings for the same file. + // This is probably a bug. + "textures/house_tree_river_copy.png", + |s: &mut ImageLoaderSettings| match &mut s.sampler { + ImageSampler::Default => { + s.sampler = ImageSampler::Descriptor(ImageSamplerDescriptor { + address_mode_u: ImageAddressMode::Repeat, + address_mode_v: ImageAddressMode::Repeat, + ..Default::default() + }) + } + ImageSampler::Descriptor(sampler) => { + sampler.address_mode_u = ImageAddressMode::Repeat; + sampler.address_mode_v = ImageAddressMode::Repeat; + } + }, + ); + + let mesh = meshes.add(mesh()); + + commands.spawn(MaterialMesh2dBundle { + mesh: mesh.clone().into(), + material: materials.add(ColorMaterial { + texture: Some(image), + ..ColorMaterial::default() + }), + transform: Transform::from_translation(Vec3::new(-0.95, -0.45, 0.)) + .with_scale(Vec3::new(0.9, 0.9, 0.9)), + ..MaterialMesh2dBundle::default() + }); + + commands.spawn(MaterialMesh2dBundle { + mesh: mesh.into(), + material: materials.add(ColorMaterial { + texture: Some(image_repeat), + ..ColorMaterial::default() + }), + transform: Transform::from_translation(Vec3::new(0.05, -0.45, 0.)) + .with_scale(Vec3::new(0.9, 0.9, 0.9)), + ..MaterialMesh2dBundle::default() + }); +} + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_systems(Startup, setup) + .run(); +}