bevy icon indicating copy to clipboard operation
bevy copied to clipboard

Implement images with layout for TextureAtlasBuilder

Open s-puig opened this issue 9 months ago • 0 comments

Objective

  • Closes #10027
  • Partially #9986
    • Doesn't add spacing but allows a way to solve the issue.

Solution

  • Allow TextureAtlasBuilder to accept an atlas layout. Given image is sliced into sub-images to generate the final atlas.

Testing

Example code

use bevy::{asset::LoadedFolder, prelude::*};

fn main() {
    App::new()
        .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) // fallback to nearest sampling
        .init_state::<AppState>()
        .add_systems(OnEnter(AppState::Setup), load_textures)
        .add_systems(Update, check_textures.run_if(in_state(AppState::Setup)))
        .add_systems(OnEnter(AppState::Finished), setup)
        .run();
}

#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, States)]
enum AppState {
    #[default]
    Setup,
    Finished,
}

#[derive(Resource, Default)]
struct RpgSpriteFolder(Handle<LoadedFolder>);

fn load_textures(mut commands: Commands, asset_server: Res<AssetServer>) {
    // load multiple, individual sprites from a folder
    commands.insert_resource(RpgSpriteFolder(asset_server.load_folder("textures/rpg")));
}

fn check_textures(
    mut next_state: ResMut<NextState<AppState>>,
    rpg_sprite_folder: Res<RpgSpriteFolder>,
    mut events: EventReader<AssetEvent<LoadedFolder>>,
) {
    // Advance the `AppState` once all sprite handles have been loaded by the `AssetServer`
    for event in events.read() {
        if event.is_loaded_with_dependencies(&rpg_sprite_folder.0) {
            next_state.set(AppState::Finished);
        }
    }
}

fn setup(
    mut commands: Commands,
    //rpg_sprite_handles: Res<RpgSpriteFolder>,
    asset_server: Res<AssetServer>,
    mut texture_atlases: ResMut<Assets<TextureAtlasLayout>>,
    //loaded_folders: Res<Assets<LoadedFolder>>,
    mut textures: ResMut<Assets<Image>>,
) {
    let gabe = asset_server.load("textures/rpg/chars/gabe/gabe-idle-run.png");
    let mani = asset_server.load("textures/rpg/chars/mani/mani-idle-run.png");

    commands.spawn(Camera2dBundle::default());

    let mut atlas_builder = TextureAtlasBuilder::default().padding(UVec2::splat(0));
    atlas_builder.add_texture_with_layout(
        Some(gabe.id()),
        textures.get(gabe.id()).unwrap(),
        TextureAtlasLayout::from_grid(UVec2::splat(24), 7, 1, None, None),
    );
    atlas_builder.add_texture_with_layout(
        Some(mani.id()),
        textures.get(mani.id()).unwrap(),
        TextureAtlasLayout::from_grid(UVec2::splat(24), 7, 1, None, None),
    );
    let (layout, image) = atlas_builder.finish().unwrap();
    let texture = textures.add(image);
    let gabe_layout = texture_atlases.add(layout.sub_layout(gabe.id()).unwrap());
    //let mani_indexes = layout.get_texture_index(mani.id()).unwrap();
    //let layout_handle = texture_atlases.add(layout.clone());
    commands
        .spawn(SpriteBundle {
            texture: texture.clone(),
            transform: Transform::from_xyz(0.0, 48.0, 0.0),
            ..default()
        })
        .insert(TextureAtlas {
            layout: gabe_layout,
            index: 6,
        });

    commands.spawn(SpriteBundle {
        texture,
        ..default()
    });
}

Changelog

Added

  • Image::try_sub_image

Generates a subimage based on the texture and the given rectangular region.

  • TextureAtlasBuilder::add_texture_with_layout

Iterates over each rectangle defined in the layout and attempts to extract sub-images from the provided image based on the layout. The sub-images are then added to the textures to place in the texture atlas builder.

  • TextureAtlasLayout::sub_layout

Generates the TextureAtlasLayout from the given Asset<Image> in the method above.

Changed

- get_texture_index(&self, texture: impl Into<AssetId<Image>>) -> Option<usize>
+ get_texture_index(&self, texture: impl Into<AssetId<Image>>) -> Option<&[usize]>

Migration Guide

let image_id: AssetId<Image>
let layout: TextureAtlasLayout
- let layout_index = layout.get_texture_index(image_id);
+ let layout_index = layout.get_texture_index(image_id)[0];

s-puig avatar May 09 '24 15:05 s-puig