bevy_assets_extensions 0.7.0

Extensions for bevy assets, with support of collection as assets and a loader manager.
Documentation
use bevy::{asset::AssetLoader, prelude::*};
use serde::{Deserialize, Serialize};

use crate::prelude::*;

#[derive(Debug, Deserialize, Serialize)]
pub struct UVector2 {
    pub x: u32,
    pub y: u32,
}

impl From<UVector2> for UVec2 {
    fn from(value: UVector2) -> Self {
        Self::new(value.x, value.y)
    }
}

/// Bundle of assets
#[derive(Debug, Deserialize, Serialize)]
pub enum AtlasLayoutDefinition {
    Grid {
        tile_size: UVector2,
        columns: u32,
        rows: u32,
        padding: Option<UVector2>,
        offset: Option<UVector2>,
    },
}

impl From<AtlasLayoutDefinition> for TextureAtlasLayout {
    fn from(value: AtlasLayoutDefinition) -> Self {
        match value {
            AtlasLayoutDefinition::Grid {
                tile_size,
                columns,
                rows,
                padding,
                offset,
            } => TextureAtlasLayout::from_grid(
                tile_size.into(),
                columns,
                rows,
                padding.map(|x| x.into()),
                offset.map(|x| x.into()),
            ),
        }
    }
}

#[derive(Default)]
pub(crate) struct TextureAtlasLayoutLoader;

impl AssetLoader for TextureAtlasLayoutLoader {
    type Asset = TextureAtlasLayout;
    type Settings = ();
    type Error = Error;
    async fn load(
        &self,
        reader: &mut dyn bevy::asset::io::Reader,
        _: &Self::Settings,
        _: &mut bevy::asset::LoadContext<'_>,
    ) -> std::result::Result<Self::Asset, Self::Error> {
        let mut bytes = Vec::new();
        reader.read_to_end(&mut bytes).await?;

        let def: AtlasLayoutDefinition = ron::de::from_bytes(&bytes)?;
        Ok(def.into())
    }
    fn extensions(&self) -> &[&str] {
        &["atlas_layout.ron"]
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use bevy::asset::LoadState;
    use std::time::Duration;

    fn is_loading(load_state: LoadState) -> bool {
        matches!(load_state, LoadState::Loading)
    }

    #[test]
    fn test_atlas_layout_asset_loading() {
        let mut app = App::new();
        app.add_plugins((MinimalPlugins, AssetPlugin::default()));
        app.init_asset::<TextureAtlasLayout>();
        app.init_asset_loader::<TextureAtlasLayoutLoader>();
        let handle: Handle<TextureAtlasLayout> = app
            .world()
            .resource::<AssetServer>()
            .load("atlas_layout.ron");
        app.update();
        while is_loading(
            app.world()
                .resource::<AssetServer>()
                .get_load_state(&handle)
                .unwrap(),
        ) {
            app.update();
            std::thread::sleep(Duration::from_secs(1));
        }
        let texture_atlas = app
            .world()
            .resource::<Assets<TextureAtlasLayout>>()
            .get(&handle)
            .unwrap();
        assert_eq!(texture_atlas.size, UVec2::new(192, 297));
        assert_eq!(
            texture_atlas.textures,
            vec![
                URect::new(0, 0, 96, 99),
                URect::new(96, 0, 192, 99),
                URect::new(0, 99, 96, 198),
                URect::new(96, 99, 192, 198),
                URect::new(0, 198, 96, 297),
                URect::new(96, 198, 192, 297),
            ]
        );
    }
}