Skip to content

Commit

Permalink
Split TextureAtlasSources out of TextureAtlasLayout and make TextureA…
Browse files Browse the repository at this point in the history
…tlasLayout serializable
  • Loading branch information
clarfonthey committed Sep 21, 2024
1 parent 417e6cc commit f272475
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 69 deletions.
13 changes: 7 additions & 6 deletions crates/bevy_internal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,17 +71,18 @@ shader_format_glsl = [
shader_format_spirv = ["bevy_render/shader_format_spirv"]

serialize = [
"bevy_color?/serialize",
"bevy_core/serialize",
"bevy_input/serialize",
"bevy_ecs/serialize",
"bevy_time/serialize",
"bevy_window/serialize",
"bevy_winit?/serialize",
"bevy_transform/serialize",
"bevy_input/serialize",
"bevy_math/serialize",
"bevy_scene?/serialize",
"bevy_sprite/serialize",
"bevy_time/serialize",
"bevy_transform/serialize",
"bevy_ui?/serialize",
"bevy_color?/serialize",
"bevy_window/serialize",
"bevy_winit?/serialize",
]
multi_threaded = [
"bevy_asset?/multi_threaded",
Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_sprite/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ keywords = ["bevy"]

[features]
bevy_picking = ["dep:bevy_picking", "dep:bevy_window"]
serialize = ["dep:serde"]
webgl = []
webgpu = []

Expand Down Expand Up @@ -40,6 +41,7 @@ rectangle-pack = "0.4"
bitflags = "2.3"
radsort = "0.1"
nonmax = "0.5"
serde = { version = "1", features = ["derive"], optional = true }

[lints]
workspace = true
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_sprite/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pub mod prelude {
pub use crate::{
bundle::SpriteBundle,
sprite::{ImageScaleMode, Sprite},
texture_atlas::{TextureAtlas, TextureAtlasLayout},
texture_atlas::{TextureAtlas, TextureAtlasLayout, TextureAtlasSources},
texture_slice::{BorderRect, SliceScaleMode, TextureSlice, TextureSlicer},
ColorMaterial, ColorMesh2dBundle, TextureAtlasBuilder,
};
Expand Down
61 changes: 41 additions & 20 deletions crates/bevy_sprite/src/texture_atlas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,45 @@ use bevy_reflect::Reflect;
use bevy_render::texture::Image;
use bevy_utils::HashMap;

/// Stores a mapping from sub texture handles to the related area index.
///
/// Generated by [`TextureAtlasBuilder`].
///
/// [`TextureAtlasBuilder`]: crate::TextureAtlasBuilder
#[derive(Debug)]
pub struct TextureAtlasSources {
/// Maps from a specific image handle to the index in `textures` where they can be found.
pub texture_ids: HashMap<AssetId<Image>, usize>,
}
impl TextureAtlasSources {
/// Retrieves the texture *section* index of the given `texture` handle.
pub fn texture_index(&self, texture: impl Into<AssetId<Image>>) -> Option<usize> {
let id = texture.into();
self.texture_ids.get(&id).cloned()
}

/// Creates a [`TextureAtlas`] handle for the given `texture` handle.
pub fn handle(
&self,
layout: Handle<TextureAtlasLayout>,
texture: impl Into<AssetId<Image>>,
) -> Option<TextureAtlas> {
Some(TextureAtlas {
layout,
index: self.texture_index(texture)?,
})
}

/// Retrieves the texture *section* rectangle of the given `texture` handle.
pub fn texture_rect(
&self,
layout: &TextureAtlasLayout,
texture: impl Into<AssetId<Image>>,
) -> Option<URect> {
layout.textures.get(self.texture_index(texture)?).cloned()
}
}

/// Stores a map used to lookup the position of a texture in a [`TextureAtlas`].
/// This can be used to either use and look up a specific section of a texture, or animate frame-by-frame as a sprite sheet.
///
Expand All @@ -18,26 +57,21 @@ use bevy_utils::HashMap;
/// [Example usage loading sprite sheet.](https://github.com/bevyengine/bevy/blob/latest/examples/2d/texture_atlas.rs)
///
/// [`TextureAtlasBuilder`]: crate::TextureAtlasBuilder
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[derive(Asset, Reflect, Debug, Clone)]
#[reflect(Debug)]
pub struct TextureAtlasLayout {
/// Total size of texture atlas.
pub size: UVec2,
/// The specific areas of the atlas where each texture can be found
pub textures: Vec<URect>,
/// Maps from a specific image handle to the index in `textures` where they can be found.
///
/// This field is set by [`TextureAtlasBuilder`].
///
/// [`TextureAtlasBuilder`]: crate::TextureAtlasBuilder
pub(crate) texture_handles: Option<HashMap<AssetId<Image>, usize>>,
}

impl TextureAtlasLayout {
/// Create a new empty layout with custom `dimensions`
pub fn new_empty(dimensions: UVec2) -> Self {
Self {
size: dimensions,
texture_handles: None,
textures: Vec::new(),
}
}
Expand Down Expand Up @@ -91,7 +125,6 @@ impl TextureAtlasLayout {
Self {
size: ((tile_size + current_padding) * grid_size) - current_padding,
textures: sprites,
texture_handles: None,
}
}

Expand All @@ -116,18 +149,6 @@ impl TextureAtlasLayout {
pub fn is_empty(&self) -> bool {
self.textures.is_empty()
}

/// Retrieves the texture *section* index of the given `texture` handle.
///
/// This requires the layout to have been built using a [`TextureAtlasBuilder`]
///
/// [`TextureAtlasBuilder`]: crate::TextureAtlasBuilder
pub fn get_texture_index(&self, texture: impl Into<AssetId<Image>>) -> Option<usize> {
let id = texture.into();
self.texture_handles
.as_ref()
.and_then(|texture_handles| texture_handles.get(&id).cloned())
}
}

/// Component used to draw a specific section of a texture.
Expand Down
14 changes: 9 additions & 5 deletions crates/bevy_sprite/src/texture_atlas_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use rectangle_pack::{
};
use thiserror::Error;

use crate::TextureAtlasLayout;
use crate::{TextureAtlasLayout, TextureAtlasSources};

#[derive(Debug, Error)]
pub enum TextureAtlasBuilderError {
Expand Down Expand Up @@ -157,7 +157,9 @@ impl<'a> TextureAtlasBuilder<'a> {
since = "0.14.0",
note = "TextureAtlasBuilder::finish() was not idiomatic. Use TextureAtlasBuilder::build() instead."
)]
pub fn finish(&mut self) -> Result<(TextureAtlasLayout, Image), TextureAtlasBuilderError> {
pub fn finish(
&mut self,
) -> Result<(TextureAtlasLayout, TextureAtlasSources, Image), TextureAtlasBuilderError> {
self.build()
}

Expand All @@ -182,7 +184,7 @@ impl<'a> TextureAtlasBuilder<'a> {
/// // Customize it
/// // ...
/// // Build your texture and the atlas layout
/// let (atlas_layout, texture) = builder.build().unwrap();
/// let (atlas_layout, atlas_sources, texture) = builder.build().unwrap();
/// let texture = textures.add(texture);
/// let layout = layouts.add(atlas_layout);
/// // Spawn your sprite
Expand All @@ -197,7 +199,9 @@ impl<'a> TextureAtlasBuilder<'a> {
///
/// If there is not enough space in the atlas texture, an error will
/// be returned. It is then recommended to make a larger sprite sheet.
pub fn build(&mut self) -> Result<(TextureAtlasLayout, Image), TextureAtlasBuilderError> {
pub fn build(
&mut self,
) -> Result<(TextureAtlasLayout, TextureAtlasSources, Image), TextureAtlasBuilderError> {
let max_width = self.max_size.x;
let max_height = self.max_size.y;

Expand Down Expand Up @@ -293,8 +297,8 @@ impl<'a> TextureAtlasBuilder<'a> {
TextureAtlasLayout {
size: atlas_texture.size(),
textures: texture_rects,
texture_handles: Some(texture_ids),
},
TextureAtlasSources { texture_ids },
atlas_texture,
))
}
Expand Down
90 changes: 53 additions & 37 deletions examples/2d/texture_atlas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,36 +59,38 @@ fn setup(

// create texture atlases with different padding and sampling

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

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

let (texture_atlas_linear_padded, linear_padded_texture) = create_texture_atlas(
loaded_folder,
Some(UVec2::new(6, 6)),
Some(ImageSampler::linear()),
&mut textures,
);
let (texture_atlas_linear_padded, linear_padded_sources, linear_padded_texture) =
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, nearest_padded_texture) = create_texture_atlas(
loaded_folder,
Some(UVec2::new(6, 6)),
Some(ImageSampler::nearest()),
&mut textures,
);
let (texture_atlas_nearest_padded, nearest_padded_sources, nearest_padded_texture) =
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);

// setup 2d scene
Expand Down Expand Up @@ -145,25 +147,39 @@ fn setup(
.get_handle("textures/rpg/chars/vendor/generic-rpg-vendor.png")
.unwrap();

// get index of the sprite in the texture atlas, this is used to render the sprite
// the index is the same for all the texture atlases, since they are created from the same folder
let vendor_index = texture_atlas_linear
.get_texture_index(&vendor_handle)
.unwrap();

// configuration array to render sprites through iteration
let configurations: [(&str, Handle<TextureAtlasLayout>, Handle<Image>, f32); 4] = [
("Linear", atlas_linear_handle, linear_texture, -350.0),
("Nearest", atlas_nearest_handle, nearest_texture, -150.0),
let configurations: [(
&str,
Handle<TextureAtlasLayout>,
TextureAtlasSources,
Handle<Image>,
f32,
); 4] = [
(
"Linear",
atlas_linear_handle,
linear_sources,
linear_texture,
-350.0,
),
(
"Nearest",
atlas_nearest_handle,
nearest_sources,
nearest_texture,
-150.0,
),
(
"Linear",
atlas_linear_padded_handle,
linear_padded_sources,
linear_padded_texture,
150.0,
),
(
"Nearest",
atlas_nearest_padded_handle,
nearest_padded_sources,
nearest_padded_texture,
350.0,
),
Expand All @@ -178,14 +194,15 @@ fn setup(

let base_y = 170.0; // y position of the sprites

for (sampling, atlas_handle, image_handle, x) in configurations {
for (sampling, atlas_handle, atlas_sources, atlas_texture, x) in configurations {
// render a sprite from the texture_atlas
create_sprite_from_atlas(
&mut commands,
(x, base_y, 0.0),
vendor_index,
atlas_texture,
atlas_sources,
atlas_handle,
image_handle,
&vendor_handle,
);

// render a label to indicate the sampling setting
Expand All @@ -205,7 +222,7 @@ fn create_texture_atlas(
padding: Option<UVec2>,
sampling: Option<ImageSampler>,
textures: &mut ResMut<Assets<Image>>,
) -> (TextureAtlasLayout, Handle<Image>) {
) -> (TextureAtlasLayout, TextureAtlasSources, Handle<Image>) {
// Build a texture atlas using the individual sprites
let mut texture_atlas_builder = TextureAtlasBuilder::default();
texture_atlas_builder.padding(padding.unwrap_or_default());
Expand All @@ -222,23 +239,25 @@ fn create_texture_atlas(
texture_atlas_builder.add_texture(Some(id), texture);
}

let (texture_atlas_layout, texture) = texture_atlas_builder.build().unwrap();
let (texture_atlas_layout, texture_atlas_sources, texture) =
texture_atlas_builder.build().unwrap();
let texture = textures.add(texture);

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

(texture_atlas_layout, texture)
(texture_atlas_layout, texture_atlas_sources, texture)
}

/// Create and spawn a sprite from a texture atlas
fn create_sprite_from_atlas(
commands: &mut Commands,
translation: (f32, f32, f32),
sprite_index: usize,
atlas_texture: Handle<Image>,
atlas_sources: TextureAtlasSources,
atlas_handle: Handle<TextureAtlasLayout>,
texture: Handle<Image>,
vendor_handle: &Handle<Image>,
) {
commands.spawn((
SpriteBundle {
Expand All @@ -247,13 +266,10 @@ fn create_sprite_from_atlas(
scale: Vec3::splat(3.0),
..default()
},
texture,
texture: atlas_texture,
..default()
},
TextureAtlas {
layout: atlas_handle,
index: sprite_index,
},
atlas_sources.handle(atlas_handle, vendor_handle).unwrap(),
));
}

Expand Down

0 comments on commit f272475

Please sign in to comment.