Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update texture_atlas example with different padding and sampling #10073

Merged
merged 6 commits into from
Dec 14, 2023
182 changes: 152 additions & 30 deletions examples/2d/texture_atlas.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
//! In this example we generate a new texture atlas (sprite sheet) from a folder containing
//! In this example we generate 4 texture atlases (sprite sheets) from a folder containing
//! individual sprites.
alice-i-cecile marked this conversation as resolved.
Show resolved Hide resolved
//!
//! The texture atlases are generated with different padding and sampling to demonstrate the
//! effect of these settings, and how bleeding issues can be resolved by padding the sprites.
//!
//! Only one padded and one unpadded texture atlas are rendered to the screen.
//! An upscaled sprite from each of the 4 atlases are rendered to the screen.

alice-i-cecile marked this conversation as resolved.
Show resolved Hide resolved
use bevy::{asset::LoadedFolder, prelude::*};
use bevy_internal::render::texture::ImageSampler;

fn main() {
App::new()
.add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) // prevents blurry sprites
.add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) // fallback to nearest sampling
.add_state::<AppState>()
.add_systems(OnEnter(AppState::Setup), load_textures)
.add_systems(Update, check_textures.run_if(in_state(AppState::Setup)))
Expand Down Expand Up @@ -49,47 +56,162 @@ fn setup(
mut texture_atlases: ResMut<Assets<TextureAtlas>>,
mut textures: ResMut<Assets<Image>>,
) {
// Build a `TextureAtlas` using the individual sprites
let mut texture_atlas_builder = TextureAtlasBuilder::default();
let loaded_folder = loaded_folders.get(&rpg_sprite_handles.0).unwrap();
for handle in loaded_folder.handles.iter() {
let id = handle.id().typed_unchecked::<Image>();
let Some(texture) = textures.get(id) else {
warn!(
"{:?} did not resolve to an `Image` asset.",
handle.path().unwrap()
);
continue;
};

texture_atlas_builder.add_texture(id, texture);
}
// create texture atlases with different padding and sampling

let texture_atlas_linear = create_texture_atlas(
loaded_folder,
None,
Some(ImageSampler::linear()),
&mut textures,
);
let atlas_linear_handle = texture_atlases.add(texture_atlas_linear.clone());

let texture_atlas_nearest = create_texture_atlas(
loaded_folder,
None,
Some(ImageSampler::nearest()),
&mut textures,
);
let atlas_nearest_handle = texture_atlases.add(texture_atlas_nearest.clone());

let texture_atlas_linear_padded = create_texture_atlas(
loaded_folder,
Some(UVec2::new(6, 6)),
Some(ImageSampler::linear()),
&mut textures,
);
let atlas_linear_padded_handle = texture_atlases.add(texture_atlas_linear_padded.clone());

let texture_atlas_nearest_padded = create_texture_atlas(
loaded_folder,
Some(UVec2::new(6, 6)),
Some(ImageSampler::nearest()),
&mut textures,
);
let atlas_nearest_padded_handle = texture_atlases.add(texture_atlas_nearest_padded.clone());

// setup 2d scene
commands.spawn(Camera2dBundle::default());

let texture_atlas = texture_atlas_builder.finish(&mut textures).unwrap();
let texture_atlas_texture = texture_atlas.texture.clone();
let vendor_handle = asset_server
// padded textures are to the right, unpadded to the left

// draw unpadded texture atlas
commands.spawn(SpriteBundle {
texture: texture_atlas_linear.clone().texture,
transform: Transform::from_xyz(-500.0, -200.0, 0.0),
alice-i-cecile marked this conversation as resolved.
Show resolved Hide resolved
..default()
});

// draw padded texture atlas
commands.spawn(SpriteBundle {
texture: texture_atlas_linear_padded.clone().texture,
transform: Transform::from_xyz(500.0, -200.0, 0.0),
alice-i-cecile marked this conversation as resolved.
Show resolved Hide resolved
..default()
});

// draw sprites from texture atlases

// get handle to a sprite to render
let vendor_handle: Handle<Image> = asset_server
.get_handle("textures/rpg/chars/vendor/generic-rpg-vendor.png")
.unwrap();
let vendor_index = texture_atlas.get_texture_index(&vendor_handle).unwrap();
let atlas_handle = texture_atlases.add(texture_atlas);

// set up a scene to display our texture atlas
commands.spawn(Camera2dBundle::default());
// draw a sprite from the atlas
// linear, no padding
commands.spawn(SpriteSheetBundle {
transform: Transform {
translation: Vec3::new(150.0, 0.0, 0.0),
translation: Vec3::new(-650.0, 250.0, 0.0),
scale: Vec3::splat(4.0),
..default()
},
sprite: TextureAtlasSprite::new(vendor_index),
texture_atlas: atlas_handle,
sprite: TextureAtlasSprite::new(
texture_atlas_linear
.get_texture_index(&vendor_handle)
.unwrap(),
),
texture_atlas: atlas_linear_handle,
..default()
});
// draw the atlas itself
commands.spawn(SpriteBundle {
texture: texture_atlas_texture,
transform: Transform::from_xyz(-300.0, 0.0, 0.0),

// linear, padding
commands.spawn(SpriteSheetBundle {
transform: Transform {
translation: Vec3::new(250.0, 250.0, 0.0),
scale: Vec3::splat(4.0),
..default()
},
sprite: TextureAtlasSprite::new(
texture_atlas_linear_padded
.get_texture_index(&vendor_handle)
.unwrap(),
),
texture_atlas: atlas_linear_padded_handle,
..default()
});

// nearest, no padding
commands.spawn(SpriteSheetBundle {
transform: Transform {
translation: Vec3::new(-250.0, 250.0, 0.0),
scale: Vec3::splat(4.0),
..default()
},
sprite: TextureAtlasSprite::new(
texture_atlas_nearest
.get_texture_index(&vendor_handle)
.unwrap(),
),
texture_atlas: atlas_nearest_handle,
..default()
});

// nearest, padding
commands.spawn(SpriteSheetBundle {
transform: Transform {
translation: Vec3::new(650.0, 250.0, 0.0),
scale: Vec3::splat(4.0),
..default()
},
sprite: TextureAtlasSprite::new(
texture_atlas_nearest_padded
.get_texture_index(&vendor_handle)
.unwrap(),
),
texture_atlas: atlas_nearest_padded_handle,
..default()
});
}

/// Create a texture atlas with the given padding and sampling settings
/// from the individual sprites in the given folder.
fn create_texture_atlas(
folder: &LoadedFolder,
padding: Option<UVec2>,
sampling: Option<ImageSampler>,
textures: &mut ResMut<Assets<Image>>,
) -> TextureAtlas {
// Build a `TextureAtlas` using the individual sprites
let mut texture_atlas_builder =
TextureAtlasBuilder::default().padding(padding.unwrap_or_default());
for handle in folder.handles.iter() {
let id = handle.id().typed_unchecked::<Image>();
let Some(texture) = textures.get(id) else {
warn!(
"{:?} did not resolve to an `Image` asset.",
handle.path().unwrap()
);
continue;
};

texture_atlas_builder.add_texture(id, texture);
}

let texture_atlas = texture_atlas_builder.finish(textures).unwrap();

// Update the sampling settings of the texture atlas
let image = textures.get_mut(&texture_atlas.texture).unwrap();
image.sampler_descriptor = sampling.unwrap_or_default();

texture_atlas
}
Loading