bevyengine / bevy

A refreshingly simple data-driven game engine built in Rust
https://bevyengine.org
Apache License 2.0
35.04k stars 3.44k forks source link

AssetServer doesn't work in `FromWorld` #11473

Open ManevilleF opened 7 months ago

ManevilleF commented 7 months ago

Bevy version

0.12.1

Relevant system information

MacOs Intel

`AdapterInfo { name: "AMD Radeon Pro 5300M", vendor: 0, device: 0, device_type: DiscreteGpu, driver: "", driver_info: "", backend: Metal }`

What you did

I have a resource storing some materials called MapAssets:

#[derive(Resource)]
pub struct MapAssets {
    pub mat: Handle<StandardMaterial>
}

This material uses a base_color_texture loaded from the assets/ folder.

If I load that texture in my FromWorld impl

impl FromWorld for MapAssets {
    fn from_world(world: &mut World) -> Self {
       let server = world.resource::<AssetServer>();
       let texture = server.load("my_texture.png);
       let mut materials = world.resource_mut::<Assets<StandardMaterial>>;
       let mat = materials.add(StandardMaterial::from(texture));
       Self { mat }
    }
}

The object is invisible.

On the other hand if I use a Startup system using Res<AssetServer> and insert the resource through Commands it works as expected.

rparrett commented 7 months ago

Are you able to reproduce with this code based on the 3d_scene example? I had to modify your code slightly to make it compile.

use bevy::prelude::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .init_resource::<MapAssets>()
        .run();
}

#[derive(Resource)]
pub struct MapAssets {
    pub mat: Handle<StandardMaterial>,
}

impl FromWorld for MapAssets {
    fn from_world(world: &mut World) -> Self {
        let server = world.resource::<AssetServer>();
        let texture = server.load("branding/icon.png");
        let mut materials = world.resource_mut::<Assets<StandardMaterial>>();
        let mat = materials.add(StandardMaterial::from(texture));
        Self { mat }
    }
}

fn setup(mut commands: Commands, mut meshes: ResMut<Assets<Mesh>>, map_assets: Res<MapAssets>) {
    commands.spawn(PbrBundle {
        mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
        material: map_assets.mat.clone(),
        transform: Transform::from_xyz(0.0, 0.5, 0.0),
        ..default()
    });
    commands.spawn(PointLightBundle {
        point_light: PointLight {
            intensity: 1500.0,
            shadows_enabled: true,
            ..default()
        },
        transform: Transform::from_xyz(4.0, 8.0, 4.0),
        ..default()
    });
    commands.spawn(Camera3dBundle {
        transform: Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
        ..default()
    });
}

On my end (MacOS, M1), Bevy 0.12.1, I am seeing a cube (no problem).

edit: Oops, previous version of this comment had code compatible with Bevy main.

ManevilleF commented 7 months ago

I ran this and no cube is visible

juliohq commented 7 months ago

That's weird because something like this seems to work:

#[derive(Resource)]
pub struct UiAssets {
    pub font: Handle<Font>,
}

impl FromWorld for UiAssets {
    fn from_world(world: &mut World) -> Self {
        let asset_server: &AssetServer = world.resource();

        Self {
            font: asset_server.load("fonts/FiraSans-Bold.ttf"),
        }
    }
}

(source: https://github.com/TanTanDev/no_communication_0/blob/4d6983ea152bf140046cd20953ff4a6b22503679/src/ui_util.rs#L19)

ManevilleF commented 7 months ago

I suspect it has something to do with the nested handle: Handle<StandardMatierial> stores a Handle<Image>.

For example if I run the code of @rparrett with bevy_inspector_egui , the material exists but the texture field is blank, not even the default white square.

djeedai commented 1 month ago

I just hit the same bug (on 0.13), and I don't have any nesting, just a Handle<Image> inside a custom Resource. I need to upgrade to 0.14 see if that changes anything, but the symptoms are exactly the same, loading from a Startup system works but not from FromWorld::from_world().