NiklasEi / bevy_asset_loader

Bevy plugin helping with asset loading and organization
Apache License 2.0
474 stars 52 forks source link

Support for more than files in StandardMaterial #52

Closed PastMoments closed 2 years ago

PastMoments commented 2 years ago

This plugin has been very helpful so far. However, I have a use case that is a little annoying to work around.

#[derive(AssetCollection)]
struct ImageAssets {
    #[asset(path = "attack_uv.png")]
    attack_uv: Handle<Image>,
}

#[derive(Default)]
struct MaterialAssets {
    attack: Handle<StandardMaterial>,
    blank: Handle<StandardMaterial>,
    empty: Handle<StandardMaterial>,
}

fn material_loader(
    mut materials: ResMut<Assets<StandardMaterial>>,
    mut material_assets: ResMut<MaterialAssets>,
    image_assets: Res<ImageAssets>,
) {
    material_assets.attack = materials.add(StandardMaterial {
        base_color_texture: Some(image_assets.attack_uv.clone()),
        ..default()
    });
    material_assets.blank = materials.add(StandardMaterial::default()); // use default
    material_assets.empty = materials.add(StandardMaterial {
        base_color: Color::rgba(1., 0., 0., 0.), // custom colour
        alpha_mode: AlphaMode::Blend, // Note: won't be necessary in 0.8
        ..default()
    });
}

From what I understand, currently with StandardMaterial it is possible to do

    #[asset(standard_material)]
    #[asset(path = "attack.png")]
    attack: Handle<StandardMaterial>,

However, I have a resource that has a combination of materials from an image, and some that are just a base colour, as well as with default. I know this plugin focuses mainly focuses on loading assets, but being able to manage assets like this without boilerplate would be immensely useful.

NiklasEi commented 2 years ago

Since the last plugin version, you can handle part of what you want with some const generics magic:

struct ColorStandardMaterial<const R: u8, const G: u8, const B: u8, const A: u8> {
    pub handle: Handle<StandardMaterial>,
}

impl<const R: u8, const G: u8, const B: u8, const A: u8> FromWorld
    for ColorStandardMaterial<R, G, B, A>
{
    fn from_world(world: &mut World) -> Self {
        let mut materials = world
            .get_resource_mut::<Assets<StandardMaterial>>()
            .unwrap();
        ColorStandardMaterial {
            handle: materials.add(StandardMaterial::from(Color::rgba_u8(R, G, B, A))),
        }
    }
}

Then just add that type with your color as fields on an asset collection:

#[derive(AssetCollection)]
struct MyAssets {
    red_standard_material: ColorStandardMaterial<{ u8::MAX }, 0, 0, 0>,
    blue_standard_material: ColorStandardMaterial<0, 0, { u8::MAX }, 0>,
}

I want to offer more possibilities to load custom assets, especially as dynamic assets. But I will probably not support cases like this with new derive attributes. That would get too much.

NiklasEi commented 2 years ago

I opened an issue for custom dynamic assets in general #55

With that I would close this issue here, considering that the const generic approach already works. Any more complex standard material could be loaded as a dynamic asset with #55.