Skip to content

Commit 3851679

Browse files
authored
Allow TextureAtlasBuilder in AssetLoader (#11548)
# Objective Allow TextureAtlasBuilder in AssetLoader. Fixes #2987 ## Solution - TextureAtlasBuilder no longer hold just AssetIds that are used to retrieve the actual image data in `finish`, but &Image instead. - TextureAtlasBuilder now required AssetId only optionally (and it is only used to retrieve the index from the AssetId in TextureAtlasLayout), ## Issues - The issue mentioned here #11474 (comment) now also extends to the actual atlas texture. In short: Calling add_texture multiple times for the same texture will lead to duplicate image data in the atlas texture and additional indices. If you provide an AssetId we can probably do something to de-duplicate the entries while keeping insertion order (suggestions welcome on how exactly). But if you don't then we are out of luck (unless we can and want to hash the image, which I do not think we want). --- ## Changelog ### Changed - TextureAtlasBuilder `add_texture` can be called without providing an AssetId - TextureAtlasBuilder `finish` no longer takes Assets<Image> and no longer returns a Handle<Image> ## Migration Guide - For `add_texture` you need to wrap your AssetId in Some - `finish` now returns the atlas texture image directly instead of a handle. Provide the atlas texture to `add` on Assets<Texture> to get a Handle<Image>
1 parent b35b9e5 commit 3851679

File tree

2 files changed

+31
-30
lines changed

2 files changed

+31
-30
lines changed

crates/bevy_sprite/src/texture_atlas_builder.rs

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
use bevy_asset::Handle;
2-
use bevy_asset::{AssetId, Assets};
1+
use bevy_asset::AssetId;
32
use bevy_log::{debug, error, warn};
43
use bevy_math::{Rect, UVec2, Vec2};
54
use bevy_render::{
@@ -28,9 +27,9 @@ pub enum TextureAtlasBuilderError {
2827
#[must_use]
2928
/// A builder which is used to create a texture atlas from many individual
3029
/// sprites.
31-
pub struct TextureAtlasBuilder {
32-
/// Collection of textures and their size to be packed into an atlas
33-
textures_to_place: Vec<(AssetId<Image>, Extent3d)>,
30+
pub struct TextureAtlasBuilder<'a> {
31+
/// Collection of texture's asset id (optional) and image data to be packed into an atlas
32+
textures_to_place: Vec<(Option<AssetId<Image>>, &'a Image)>,
3433
/// The initial atlas size in pixels.
3534
initial_size: Vec2,
3635
/// The absolute maximum size of the texture atlas in pixels.
@@ -43,7 +42,7 @@ pub struct TextureAtlasBuilder {
4342
padding: UVec2,
4443
}
4544

46-
impl Default for TextureAtlasBuilder {
45+
impl Default for TextureAtlasBuilder<'_> {
4746
fn default() -> Self {
4847
Self {
4948
textures_to_place: Vec::new(),
@@ -58,7 +57,7 @@ impl Default for TextureAtlasBuilder {
5857

5958
pub type TextureAtlasBuilderResult<T> = Result<T, TextureAtlasBuilderError>;
6059

61-
impl TextureAtlasBuilder {
60+
impl<'a> TextureAtlasBuilder<'a> {
6261
/// Sets the initial size of the atlas in pixels.
6362
pub fn initial_size(mut self, size: Vec2) -> Self {
6463
self.initial_size = size;
@@ -85,10 +84,10 @@ impl TextureAtlasBuilder {
8584

8685
/// Adds a texture to be copied to the texture atlas.
8786
///
87+
/// Optionally an asset id can be passed that can later be used with the texture layout to retrieve the index of this texture.
8888
/// The insertion order will reflect the index of the added texture in the finished texture atlas.
89-
pub fn add_texture(&mut self, image_id: AssetId<Image>, texture: &Image) {
90-
self.textures_to_place
91-
.push((image_id, texture.texture_descriptor.size));
89+
pub fn add_texture(&mut self, image_id: Option<AssetId<Image>>, texture: &'a Image) {
90+
self.textures_to_place.push((image_id, texture));
9291
}
9392

9493
/// Sets the amount of padding in pixels to add between the textures in the texture atlas.
@@ -149,14 +148,12 @@ impl TextureAtlasBuilder {
149148
}
150149
}
151150

152-
/// Consumes the builder, and returns the newly created texture handle and
153-
/// the assciated atlas layout.
151+
/// Consumes the builder, and returns the newly created texture atlas and
152+
/// the associated atlas layout.
154153
///
155154
/// Assigns indices to the textures based on the insertion order.
156155
/// Internally it copies all rectangles from the textures and copies them
157156
/// into a new texture.
158-
/// It is not useful to hold a strong handle to the texture afterwards else
159-
/// it will exist twice in memory.
160157
///
161158
/// # Usage
162159
///
@@ -172,7 +169,8 @@ impl TextureAtlasBuilder {
172169
/// // Customize it
173170
/// // ...
174171
/// // Build your texture and the atlas layout
175-
/// let (atlas_layout, texture) = builder.finish(&mut textures).unwrap();
172+
/// let (atlas_layout, texture) = builder.finish().unwrap();
173+
/// let texture = textures.add(texture);
176174
/// let layout = layouts.add(atlas_layout);
177175
/// // Spawn your sprite
178176
/// commands.spawn(SpriteSheetBundle {
@@ -190,10 +188,7 @@ impl TextureAtlasBuilder {
190188
///
191189
/// If there is not enough space in the atlas texture, an error will
192190
/// be returned. It is then recommended to make a larger sprite sheet.
193-
pub fn finish(
194-
self,
195-
textures: &mut Assets<Image>,
196-
) -> Result<(TextureAtlasLayout, Handle<Image>), TextureAtlasBuilderError> {
191+
pub fn finish(self) -> Result<(TextureAtlasLayout, Image), TextureAtlasBuilderError> {
197192
let initial_width = self.initial_size.x as u32;
198193
let initial_height = self.initial_size.y as u32;
199194
let max_width = self.max_size.x as u32;
@@ -203,14 +198,18 @@ impl TextureAtlasBuilder {
203198
let mut current_height = initial_height;
204199
let mut rect_placements = None;
205200
let mut atlas_texture = Image::default();
206-
let mut rects_to_place = GroupedRectsToPlace::<AssetId<Image>>::new();
201+
let mut rects_to_place = GroupedRectsToPlace::<usize>::new();
207202

208203
// Adds textures to rectangle group packer
209-
for (image_id, size) in &self.textures_to_place {
204+
for (index, (_, texture)) in self.textures_to_place.iter().enumerate() {
210205
rects_to_place.push_rect(
211-
*image_id,
206+
index,
212207
None,
213-
RectToInsert::new(size.width + self.padding.x, size.height + self.padding.y, 1),
208+
RectToInsert::new(
209+
texture.width() + self.padding.x,
210+
texture.height() + self.padding.y,
211+
1,
212+
),
214213
);
215214
}
216215

@@ -263,17 +262,18 @@ impl TextureAtlasBuilder {
263262
let mut texture_rects = Vec::with_capacity(rect_placements.packed_locations().len());
264263
let mut texture_ids = HashMap::default();
265264
// We iterate through the textures to place to respect the insertion order for the texture indices
266-
for (image_id, _) in &self.textures_to_place {
267-
let (_, packed_location) = rect_placements.packed_locations().get(image_id).unwrap();
265+
for (index, (image_id, texture)) in self.textures_to_place.iter().enumerate() {
266+
let (_, packed_location) = rect_placements.packed_locations().get(&index).unwrap();
268267

269-
let texture = textures.get(*image_id).unwrap();
270268
let min = Vec2::new(packed_location.x() as f32, packed_location.y() as f32);
271269
let max = min
272270
+ Vec2::new(
273271
(packed_location.width() - self.padding.x) as f32,
274272
(packed_location.height() - self.padding.y) as f32,
275273
);
276-
texture_ids.insert(*image_id, texture_rects.len());
274+
if let Some(image_id) = image_id {
275+
texture_ids.insert(*image_id, index);
276+
}
277277
texture_rects.push(Rect { min, max });
278278
if texture.texture_descriptor.format != self.format && !self.auto_format_conversion {
279279
warn!(
@@ -291,7 +291,7 @@ impl TextureAtlasBuilder {
291291
textures: texture_rects,
292292
texture_handles: Some(texture_ids),
293293
},
294-
textures.add(atlas_texture),
294+
atlas_texture,
295295
))
296296
}
297297
}

examples/2d/texture_atlas.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,10 +220,11 @@ fn create_texture_atlas(
220220
continue;
221221
};
222222

223-
texture_atlas_builder.add_texture(id, texture);
223+
texture_atlas_builder.add_texture(Some(id), texture);
224224
}
225225

226-
let (texture_atlas, texture) = texture_atlas_builder.finish(textures).unwrap();
226+
let (texture_atlas, texture) = texture_atlas_builder.finish().unwrap();
227+
let texture = textures.add(texture);
227228

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

0 commit comments

Comments
 (0)