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

Implement images with layout for TextureAtlasBuilder #13302

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

s-puig
Copy link
Contributor

@s-puig s-puig commented May 9, 2024

Objective

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 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 s-puig changed the title Implement images with layout to TextureAtlasBuilder Implement images with layout for TextureAtlasBuilder May 9, 2024
@alice-i-cecile alice-i-cecile added C-Enhancement A new feature A-Rendering Drawing game state to the screen X-Uncontroversial This work is generally agreed upon D-Straightforward Simple bug fixes and API improvements, docs, test and examples labels May 9, 2024
Co-authored-by: IceSentry <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Rendering Drawing game state to the screen C-Enhancement A new feature D-Straightforward Simple bug fixes and API improvements, docs, test and examples X-Uncontroversial This work is generally agreed upon
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Combine multiple sprites into a single texture atlas
3 participants