NiklasEi / bevy_asset_loader

Bevy plugin helping with asset loading and organization
Apache License 2.0
449 stars 53 forks source link

gltf scenes randomly get swapped #182

Closed GitGhillie closed 4 months ago

GitGhillie commented 5 months ago

Since the 0.12 update it seems to be possible to spawn gltf scenes without the #Scene0 at the end of the path, using bevy_asset_loader. However, doing this I ran into a strange issue where sometimes when I open the app the scenes get swapped. Here A and B are two different files:

AB And on another run: BA

Reproducer can be found here: https://github.com/GitGhillie/asset_loading_mre

NiklasEi commented 5 months ago

Does it still happen when you use the label #Scene0?

Have you tried to reproduce this without bevy_asset_loader?

GitGhillie commented 5 months ago

It does not, but it's quite the footgun imo

NiklasEi commented 5 months ago

It does not, but it's quite the footgun imo

Yeah, definitely a bug. Just trying to understand where the issue might be.

GitGhillie commented 5 months ago

Have you tried to reproduce this without bevy_asset_loader?

Haven't tried to reproduce without yet (I did try some things but I've never looked into assets before so it will take me while to come up with something that's equivalent I guess). I did try with bevy_asset_loader on bevy main (2951ddf), same issue

GitGhillie commented 5 months ago

For example, if I try scene_handles.handle_a = asset_server.load("A.glb"); and then try to spawn it:

#[derive(Resource, Default)]
struct SceneHandles {
    handle_a: Handle<Scene>,
    handle_b: Handle<Scene>,
}

...
commands.spawn((
        Name::from("Scene A"),
        SceneBundle {
            scene: scene_handles.handle_a.clone(),
            transform: Transform::from_xyz(-1.0, 0.0, 0.0),
            ..default()
        },
    ));

I get:

ERROR bevy_asset::server: Requested handle of type TypeId { t: 20984825428628115851185960703696596255 } for asset 'B.glb' does not match actual asset type 'bevy_gltf::Gltf', which used loader 'bevy_gltf::loader::GltfLoader'

So I'm guessing bevy_asset_loader is doing something more complicated under the hood than I expect

NiklasEi commented 5 months ago

I can reproduce the issue with your repository. This is the generated AssetCollection impl:

impl AssetCollection for SceneAssets {
    fn create(world: &mut ::bevy::ecs::world::World) -> Self {
        let from_world_fields = ();
        world
            .resource_scope(|
                world,
                asset_keys: ::bevy::prelude::Mut<
                    ::bevy_asset_loader::dynamic_asset::DynamicAssets,
                >|
                {
                    SceneAssets {
                        scene_a: {
                            let asset_server = world
                                .get_resource::<::bevy::asset::AssetServer>()
                                .expect("Cannot get AssetServer");
                            asset_server.load("A.glb")
                        },
                        scene_b: {
                            let asset_server = world
                                .get_resource::<::bevy::asset::AssetServer>()
                                .expect("Cannot get AssetServer");
                            asset_server.load("B.glb")
                        },
                    }
                })
    }

    fn load(
        world: &mut ::bevy::ecs::world::World,
    ) -> Vec<::bevy::prelude::UntypedHandle> {
        let cell = world.cell();
        let asset_server = cell
            .get_resource::<::bevy::prelude::AssetServer>()
            .expect("Cannot get AssetServer");
        let asset_keys = cell
            .get_resource::<bevy_asset_loader::prelude::DynamicAssets>()
            .expect("Cannot get bevy_asset_loader::prelude::DynamicAssets");
        let mut handles = ::alloc::vec::Vec::new();
        handles.push(asset_server.load_untyped("A.glb").untyped());
        handles.push(asset_server.load_untyped("B.glb").untyped());
        handles
    }
}

The only difference to what you tried to do in Bevy directly seems to be the use of load_untyped. Maybe there is some issue with handle reusage :thinking:

GitGhillie commented 5 months ago

Interesting (TIL about cargo expand). So you are loading it twice (once untyped in load and once normally in create)? I would've expected it to get the handle in create from the LoadingAssetHandles or something like that.

GitGhillie commented 5 months ago

I was able to repro without bevy_asset_loader: https://github.com/bevyengine/bevy/issues/11509 In the meantime I think we should be able to come up with a workaround

NiklasEi commented 5 months ago

So you are loading it twice (once untyped in load and once normally in create)? I would've expected it to get the handle in create from the LoadingAssetHandles or something like that.

The actual loading should only happen once since Bevy keeps assets in memory as long as we keep a Handle. LoadingAssetHandles contains only untyped handles and no easy mapping from paths, while load will directly use the right handle type based on the collection resource.

In the meantime I think we should be able to come up with a workaround

The "workaround" would be to use the #Scene0 label in my opinion. Is there a reason you do not use it?

GitGhillie commented 5 months ago

Thanks, that makes sense.

The "workaround" would be to use the #Scene0 label in my opinion. Is there a reason you do not use it?

I used to but since the 0.12 update it seems not possible anymore to use #Scene0 with bevy_gltf_components (for context https://github.com/kaosat-dev/Blender_bevy_components_workflow/issues/102#issuecomment-1902772089).

GitGhillie commented 4 months ago

Closing as it's not a bevy_asset_loader issue