Struct bevy::asset::AssetServer

pub struct AssetServer { /* private fields */ }
Expand description

Loads and tracks the state of Asset values from a configured AssetReader. This can be used to kick off new asset loads and retrieve their current load states.

The general process to load an asset is:

  1. Initialize a new Asset type with the AssetServer via AssetApp::init_asset, which will internally call AssetServer::register_asset and set up related ECS Assets storage and systems.
  2. Register one or more AssetLoaders for that asset with AssetApp::init_asset_loader
  3. Add the asset to your asset folder (defaults to assets).
  4. Call AssetServer::load with a path to your asset.

AssetServer can be cloned. It is backed by an Arc so clones will share state. Clones can be freely used in parallel.

Implementations§

§

impl AssetServer

pub fn new( sources: AssetSources, mode: AssetServerMode, watching_for_changes: bool ) -> AssetServer

Create a new instance of AssetServer. If watch_for_changes is true, the AssetReader storage will watch for changes to asset sources and hot-reload them.

pub fn new_with_meta_check( sources: AssetSources, mode: AssetServerMode, meta_check: AssetMetaCheck, watching_for_changes: bool ) -> AssetServer

Create a new instance of AssetServer. If watch_for_changes is true, the AssetReader storage will watch for changes to asset sources and hot-reload them.

pub fn get_source<'a>( &'a self, source: impl Into<AssetSourceId<'a>> ) -> Result<&'a AssetSource, MissingAssetSourceError>

Retrieves the AssetSource for the given source.

pub fn watching_for_changes(&self) -> bool

Returns true if the AssetServer watches for changes.

pub fn register_loader<L>(&self, loader: L)
where L: AssetLoader,

Registers a new AssetLoader. AssetLoaders must be registered before they can be used.

pub fn register_asset<A>(&self, assets: &Assets<A>)
where A: Asset,

Registers a new Asset type. Asset types must be registered before assets of that type can be loaded.

pub async fn get_asset_loader_with_extension( &self, extension: &str ) -> Result<Arc<dyn ErasedAssetLoader>, MissingAssetLoaderForExtensionError>

Returns the registered AssetLoader associated with the given extension, if it exists.

pub async fn get_asset_loader_with_type_name( &self, type_name: &str ) -> Result<Arc<dyn ErasedAssetLoader>, MissingAssetLoaderForTypeNameError>

Returns the registered AssetLoader associated with the given std::any::type_name, if it exists.

pub async fn get_path_asset_loader<'a>( &self, path: impl Into<AssetPath<'a>> ) -> Result<Arc<dyn ErasedAssetLoader>, MissingAssetLoaderForExtensionError>

Retrieves the default AssetLoader for the given path, if one can be found.

pub async fn get_asset_loader_with_asset_type_id<'a>( &self, type_id: TypeId ) -> Result<Arc<dyn ErasedAssetLoader>, MissingAssetLoaderForTypeIdError>

Retrieves the default AssetLoader for the given Asset TypeId, if one can be found.

pub async fn get_asset_loader_with_asset_type<'a, A>( &self ) -> Result<Arc<dyn ErasedAssetLoader>, MissingAssetLoaderForTypeIdError>
where A: Asset,

Retrieves the default AssetLoader for the given Asset type, if one can be found.

pub fn load<'a, A>(&self, path: impl Into<AssetPath<'a>>) -> Handle<A>
where A: Asset,

Begins loading an Asset of type A stored at path. This will not block on the asset load. Instead, it returns a “strong” Handle. When the Asset is loaded (and enters LoadState::Loaded), it will be added to the associated Assets resource.

You can check the asset’s load state by reading AssetEvent events, calling AssetServer::load_state, or checking the Assets storage to see if the Asset exists yet.

The asset load will fail and an error will be printed to the logs if the asset stored at path is not of type A.

Examples found in repository?
examples/state/state.rs (line 124)
122
123
124
125
126
127
fn setup_game(mut commands: Commands, asset_server: Res<AssetServer>) {
    commands.spawn(SpriteBundle {
        texture: asset_server.load("branding/icon.png"),
        ..default()
    });
}
More examples
Hide additional examples
examples/audio/audio.rs (line 15)
13
14
15
16
17
18
fn setup(asset_server: Res<AssetServer>, mut commands: Commands) {
    commands.spawn(AudioBundle {
        source: asset_server.load("sounds/Windless Slopes.ogg"),
        ..default()
    });
}
examples/3d/reflection_probes.rs (line 101)
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
fn spawn_scene(commands: &mut Commands, asset_server: &AssetServer) {
    commands.spawn(SceneBundle {
        scene: asset_server.load("models/cubes/Cubes.glb#Scene0"),
        ..SceneBundle::default()
    });
}

// Spawns the camera.
fn spawn_camera(commands: &mut Commands) {
    commands.spawn(Camera3dBundle {
        camera: Camera {
            hdr: true,
            ..default()
        },
        transform: Transform::from_xyz(-6.483, 0.325, 4.381).looking_at(Vec3::ZERO, Vec3::Y),
        ..default()
    });
}

// Creates the sphere mesh and spawns it.
fn spawn_sphere(
    commands: &mut Commands,
    meshes: &mut Assets<Mesh>,
    materials: &mut Assets<StandardMaterial>,
) {
    // Create a sphere mesh.
    let sphere_mesh = meshes.add(Sphere::new(1.0).mesh().ico(7).unwrap());

    // Create a sphere.
    commands.spawn(PbrBundle {
        mesh: sphere_mesh.clone(),
        material: materials.add(StandardMaterial {
            base_color: Srgba::hex("#ffd891").unwrap().into(),
            metallic: 1.0,
            perceptual_roughness: 0.0,
            ..StandardMaterial::default()
        }),
        transform: Transform::default(),
        ..PbrBundle::default()
    });
}

// Spawns the reflection probe.
fn spawn_reflection_probe(commands: &mut Commands, cubemaps: &Cubemaps) {
    commands.spawn(ReflectionProbeBundle {
        spatial: SpatialBundle {
            // 2.0 because the sphere's radius is 1.0 and we want to fully enclose it.
            transform: Transform::from_scale(Vec3::splat(2.0)),
            ..SpatialBundle::default()
        },
        light_probe: LightProbe,
        environment_map: EnvironmentMapLight {
            diffuse_map: cubemaps.diffuse.clone(),
            specular_map: cubemaps.specular_reflection_probe.clone(),
            intensity: 5000.0,
        },
    });
}

// Spawns the help text.
fn spawn_text(commands: &mut Commands, asset_server: &AssetServer, app_status: &AppStatus) {
    // Create the text.
    commands.spawn(
        TextBundle {
            text: app_status.create_text(asset_server),
            ..TextBundle::default()
        }
        .with_style(Style {
            position_type: PositionType::Absolute,
            bottom: Val::Px(10.0),
            left: Val::Px(10.0),
            ..default()
        }),
    );
}

// Adds a world environment map to the camera. This separate system is needed because the camera is
// managed by the scene spawner, as it's part of the glTF file with the cubes, so we have to add
// the environment map after the fact.
fn add_environment_map_to_camera(
    mut commands: Commands,
    query: Query<Entity, Added<Camera3d>>,
    cubemaps: Res<Cubemaps>,
) {
    for camera_entity in query.iter() {
        commands
            .entity(camera_entity)
            .insert(create_camera_environment_map_light(&cubemaps))
            .insert(Skybox {
                image: cubemaps.skybox.clone(),
                brightness: 5000.0,
            });
    }
}

// A system that handles switching between different reflection modes.
fn change_reflection_type(
    mut commands: Commands,
    light_probe_query: Query<Entity, With<LightProbe>>,
    camera_query: Query<Entity, With<Camera3d>>,
    keyboard: Res<ButtonInput<KeyCode>>,
    mut app_status: ResMut<AppStatus>,
    cubemaps: Res<Cubemaps>,
) {
    // Only do anything if space was pressed.
    if !keyboard.just_pressed(KeyCode::Space) {
        return;
    }

    // Switch reflection mode.
    app_status.reflection_mode =
        ReflectionMode::try_from((app_status.reflection_mode as u32 + 1) % 3).unwrap();

    // Add or remove the light probe.
    for light_probe in light_probe_query.iter() {
        commands.entity(light_probe).despawn();
    }
    match app_status.reflection_mode {
        ReflectionMode::None | ReflectionMode::EnvironmentMap => {}
        ReflectionMode::ReflectionProbe => spawn_reflection_probe(&mut commands, &cubemaps),
    }

    // Add or remove the environment map from the camera.
    for camera in camera_query.iter() {
        match app_status.reflection_mode {
            ReflectionMode::None => {
                commands.entity(camera).remove::<EnvironmentMapLight>();
            }
            ReflectionMode::EnvironmentMap | ReflectionMode::ReflectionProbe => {
                commands
                    .entity(camera)
                    .insert(create_camera_environment_map_light(&cubemaps));
            }
        }
    }
}

// A system that handles enabling and disabling rotation.
fn toggle_rotation(keyboard: Res<ButtonInput<KeyCode>>, mut app_status: ResMut<AppStatus>) {
    if keyboard.just_pressed(KeyCode::Enter) {
        app_status.rotating = !app_status.rotating;
    }
}

// A system that updates the help text.
fn update_text(
    mut text_query: Query<&mut Text>,
    app_status: Res<AppStatus>,
    asset_server: Res<AssetServer>,
) {
    for mut text in text_query.iter_mut() {
        *text = app_status.create_text(&asset_server);
    }
}

impl TryFrom<u32> for ReflectionMode {
    type Error = ();

    fn try_from(value: u32) -> Result<Self, Self::Error> {
        match value {
            0 => Ok(ReflectionMode::None),
            1 => Ok(ReflectionMode::EnvironmentMap),
            2 => Ok(ReflectionMode::ReflectionProbe),
            _ => Err(()),
        }
    }
}

impl Display for ReflectionMode {
    fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
        let text = match *self {
            ReflectionMode::None => "No reflections",
            ReflectionMode::EnvironmentMap => "Environment map",
            ReflectionMode::ReflectionProbe => "Reflection probe",
        };
        formatter.write_str(text)
    }
}

impl AppStatus {
    // Constructs the help text at the bottom of the screen based on the
    // application status.
    fn create_text(&self, asset_server: &AssetServer) -> Text {
        let rotation_help_text = if self.rotating {
            STOP_ROTATION_HELP_TEXT
        } else {
            START_ROTATION_HELP_TEXT
        };

        Text::from_section(
            format!(
                "{}\n{}\n{}",
                self.reflection_mode, rotation_help_text, REFLECTION_MODE_HELP_TEXT
            ),
            TextStyle {
                font: asset_server.load("fonts/FiraMono-Medium.ttf"),
                font_size: 24.0,
                ..default()
            },
        )
    }
examples/state/sub_states.rs (line 231)
229
230
231
232
233
234
    pub fn setup_game(mut commands: Commands, asset_server: Res<AssetServer>) {
        commands.spawn(SpriteBundle {
            texture: asset_server.load("branding/icon.png"),
            ..default()
        });
    }
examples/asset/custom_asset_reader.rs (line 65)
62
63
64
65
66
67
68
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
    commands.spawn(Camera2dBundle::default());
    commands.spawn(SpriteBundle {
        texture: asset_server.load("branding/icon.png"),
        ..default()
    });
}
examples/window/transparent_window.rs (line 34)
31
32
33
34
35
36
37
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
    commands.spawn(Camera2dBundle::default());
    commands.spawn(SpriteBundle {
        texture: asset_server.load("branding/icon.png"),
        ..default()
    });
}

pub fn load_acquire<'a, A, G>( &self, path: impl Into<AssetPath<'a>>, guard: G ) -> Handle<A>
where A: Asset, G: Send + Sync + 'static,

Begins loading an Asset of type A stored at path while holding a guard item. The guard item is dropped when either the asset is loaded or loading has failed.

This function returns a “strong” Handle. When the Asset is loaded (and enters LoadState::Loaded), it will be added to the associated Assets resource.

The guard item should notify the caller in its Drop implementation. See example multi_asset_sync. Synchronously this can be a Arc<AtomicU32> that decrements its counter, asynchronously this can be a Barrier. This function only guarantees the asset referenced by the Handle is loaded. If your asset is separated into multiple files, sub-assets referenced by the main asset might still be loading, depend on the implementation of the AssetLoader.

Additionally, you can check the asset’s load state by reading AssetEvent events, calling AssetServer::load_state, or checking the Assets storage to see if the Asset exists yet.

The asset load will fail and an error will be printed to the logs if the asset stored at path is not of type A.

Examples found in repository?
examples/asset/multi_asset_sync.rs (line 145)
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
fn setup_assets(mut commands: Commands, asset_server: Res<AssetServer>) {
    let (barrier, guard) = AssetBarrier::new();
    commands.insert_resource(OneHundredThings(std::array::from_fn(|i| match i % 5 {
        0 => asset_server.load_acquire("models/GolfBall/GolfBall.glb", guard.clone()),
        1 => asset_server.load_acquire("models/AlienCake/alien.glb", guard.clone()),
        2 => asset_server.load_acquire("models/AlienCake/cakeBirthday.glb", guard.clone()),
        3 => asset_server.load_acquire("models/FlightHelmet/FlightHelmet.gltf", guard.clone()),
        4 => asset_server.load_acquire("models/torus/torus.gltf", guard.clone()),
        _ => unreachable!(),
    })));
    let future = barrier.wait_async();
    commands.insert_resource(barrier);

    let loading_state = Arc::new(AtomicBool::new(false));
    commands.insert_resource(AsyncLoadingState(loading_state.clone()));

    // await the `AssetBarrierFuture`.
    AsyncComputeTaskPool::get()
        .spawn(async move {
            future.await;
            // Notify via `AsyncLoadingState`
            loading_state.store(true, Ordering::Release);
        })
        .detach();
}

pub fn load_with_settings<'a, A, S>( &self, path: impl Into<AssetPath<'a>>, settings: impl Fn(&mut S) + Send + Sync + 'static ) -> Handle<A>
where A: Asset, S: Settings,

Begins loading an Asset of type A stored at path. The given settings function will override the asset’s AssetLoader settings. The type S must match the configured AssetLoader::Settings or settings changes will be ignored and an error will be printed to the log.

Examples found in repository?
examples/3d/clearcoat.rs (lines 105-108)
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
fn spawn_car_paint_sphere(
    commands: &mut Commands,
    materials: &mut Assets<StandardMaterial>,
    asset_server: &AssetServer,
    sphere: &Handle<Mesh>,
) {
    commands
        .spawn(PbrBundle {
            mesh: sphere.clone(),
            material: materials.add(StandardMaterial {
                clearcoat: 1.0,
                clearcoat_perceptual_roughness: 0.1,
                normal_map_texture: Some(asset_server.load_with_settings(
                    "textures/BlueNoise-Normal.png",
                    |settings: &mut ImageLoaderSettings| settings.is_srgb = false,
                )),
                metallic: 0.9,
                perceptual_roughness: 0.5,
                base_color: BLUE.into(),
                ..default()
            }),
            transform: Transform::from_xyz(-1.0, 1.0, 0.0).with_scale(Vec3::splat(SPHERE_SCALE)),
            ..default()
        })
        .insert(ExampleSphere);
}

/// Spawn a semitransparent object with a clearcoat layer.
fn spawn_coated_glass_bubble_sphere(
    commands: &mut Commands,
    materials: &mut Assets<StandardMaterial>,
    sphere: &Handle<Mesh>,
) {
    commands
        .spawn(PbrBundle {
            mesh: sphere.clone(),
            material: materials.add(StandardMaterial {
                clearcoat: 1.0,
                clearcoat_perceptual_roughness: 0.1,
                metallic: 0.5,
                perceptual_roughness: 0.1,
                base_color: Color::srgba(0.9, 0.9, 0.9, 0.3),
                alpha_mode: AlphaMode::Blend,
                ..default()
            }),
            transform: Transform::from_xyz(-1.0, -1.0, 0.0).with_scale(Vec3::splat(SPHERE_SCALE)),
            ..default()
        })
        .insert(ExampleSphere);
}

/// Spawns an object with both a clearcoat normal map (a scratched varnish) and
/// a main layer normal map (the golf ball pattern).
///
/// This object is in glTF format, using the `KHR_materials_clearcoat`
/// extension.
fn spawn_golf_ball(commands: &mut Commands, asset_server: &AssetServer) {
    commands
        .spawn(SceneBundle {
            scene: asset_server.load("models/GolfBall/GolfBall.glb#Scene0"),
            transform: Transform::from_xyz(1.0, 1.0, 0.0).with_scale(Vec3::splat(SPHERE_SCALE)),
            ..default()
        })
        .insert(ExampleSphere);
}

/// Spawns an object with only a clearcoat normal map (a scratch pattern) and no
/// main layer normal map.
fn spawn_scratched_gold_ball(
    commands: &mut Commands,
    materials: &mut Assets<StandardMaterial>,
    asset_server: &AssetServer,
    sphere: &Handle<Mesh>,
) {
    commands
        .spawn(PbrBundle {
            mesh: sphere.clone(),
            material: materials.add(StandardMaterial {
                clearcoat: 1.0,
                clearcoat_perceptual_roughness: 0.3,
                clearcoat_normal_texture: Some(asset_server.load_with_settings(
                    "textures/ScratchedGold-Normal.png",
                    |settings: &mut ImageLoaderSettings| settings.is_srgb = false,
                )),
                metallic: 0.9,
                perceptual_roughness: 0.1,
                base_color: GOLD.into(),
                ..default()
            }),
            transform: Transform::from_xyz(1.0, -1.0, 0.0).with_scale(Vec3::splat(SPHERE_SCALE)),
            ..default()
        })
        .insert(ExampleSphere);
}
More examples
Hide additional examples
examples/3d/deferred_rendering.rs (lines 248-253)
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
fn setup_parallax(
    mut commands: Commands,
    mut materials: ResMut<Assets<StandardMaterial>>,
    mut meshes: ResMut<Assets<Mesh>>,
    asset_server: Res<AssetServer>,
) {
    // The normal map. Note that to generate it in the GIMP image editor, you should
    // open the depth map, and do Filters → Generic → Normal Map
    // You should enable the "flip X" checkbox.
    let normal_handle = asset_server.load_with_settings(
        "textures/parallax_example/cube_normal.png",
        // The normal map texture is in linear color space. Lighting won't look correct
        // if `is_srgb` is `true`, which is the default.
        |settings: &mut ImageLoaderSettings| settings.is_srgb = false,
    );

    let mut cube = Mesh::from(Cuboid::new(0.15, 0.15, 0.15));

    // NOTE: for normal maps and depth maps to work, the mesh
    // needs tangents generated.
    cube.generate_tangents().unwrap();

    let parallax_material = materials.add(StandardMaterial {
        perceptual_roughness: 0.4,
        base_color_texture: Some(asset_server.load("textures/parallax_example/cube_color.png")),
        normal_map_texture: Some(normal_handle),
        // The depth map is a greyscale texture where black is the highest level and
        // white the lowest.
        depth_map: Some(asset_server.load("textures/parallax_example/cube_depth.png")),
        parallax_depth_scale: 0.09,
        parallax_mapping_method: ParallaxMappingMethod::Relief { max_steps: 4 },
        max_parallax_layer_count: 5.0f32.exp2(),
        ..default()
    });
    commands.spawn((
        PbrBundle {
            mesh: meshes.add(cube),
            material: parallax_material,
            transform: Transform::from_xyz(0.4, 0.2, -0.8),
            ..default()
        },
        Spin { speed: 0.3 },
    ));
}
examples/asset/repeated_texture.rs (lines 43-56)
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
fn setup(
    mut commands: Commands,
    asset_server: Res<AssetServer>,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    let image_with_default_sampler =
        asset_server.load("textures/fantasy_ui_borders/panel-border-010.png");

    // central cube with not repeated texture
    commands.spawn(PbrBundle {
        mesh: meshes.add(Cuboid::new(1.0, 1.0, 1.0)),
        material: materials.add(StandardMaterial {
            base_color_texture: Some(image_with_default_sampler.clone()),
            ..default()
        }),
        transform: Transform::from_translation(Vec3::ZERO),
        ..default()
    });

    // left cube with repeated texture
    commands.spawn(PbrBundle {
        mesh: meshes.add(Cuboid::new(1.0, 1.0, 1.0)),
        material: materials.add(StandardMaterial {
            base_color_texture: Some(asset_server.load_with_settings(
                "textures/fantasy_ui_borders/panel-border-010-repeated.png",
                |s: &mut _| {
                    *s = ImageLoaderSettings {
                        sampler: ImageSampler::Descriptor(ImageSamplerDescriptor {
                            // rewriting mode to repeat image,
                            address_mode_u: ImageAddressMode::Repeat,
                            address_mode_v: ImageAddressMode::Repeat,
                            ..default()
                        }),
                        ..default()
                    }
                },
            )),

            // uv_transform used here for proportions only, but it is full Affine2
            // that's why you can use rotation and shift also
            uv_transform: Affine2::from_scale(Vec2::new(2., 3.)),
            ..default()
        }),
        transform: Transform::from_xyz(-1.5, 0.0, 0.0),
        ..default()
    });

    // right cube with scaled texture, because with default sampler
    commands.spawn(PbrBundle {
        mesh: meshes.add(Cuboid::new(1.0, 1.0, 1.0)),
        material: materials.add(StandardMaterial {
            // there is no sampler set, that's why
            // by default you see only one small image in a row/column
            // and other space is filled by image edge
            base_color_texture: Some(image_with_default_sampler),

            // uv_transform used here for proportions only, but it is full Affine2
            // that's why you can use rotation and shift also
            uv_transform: Affine2::from_scale(Vec2::new(2., 3.)),
            ..default()
        }),
        transform: Transform::from_xyz(1.5, 0.0, 0.0),
        ..default()
    });

    // light
    commands.spawn(PointLightBundle {
        point_light: PointLight {
            shadows_enabled: true,
            ..default()
        },
        transform: Transform::from_xyz(4.0, 8.0, 4.0),
        ..default()
    });
    // camera
    commands.spawn(Camera3dBundle {
        transform: Transform::from_xyz(0.0, 1.5, 4.0).looking_at(Vec3::ZERO, Vec3::Y),
        ..default()
    });
}
examples/asset/asset_settings.rs (lines 66-71)
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
    // Without any .meta file specifying settings, the default sampler [ImagePlugin::default()] is used for loading images.
    // If you are using a very small image and rendering it larger like seen here, the default linear filtering will result in a blurry image.
    // Useful note: The default sampler specified by the ImagePlugin is *not* the same as the default implementation of sampler. This is why
    // everything uses linear by default but if you look at the default of sampler, it uses nearest.
    commands.spawn(SpriteBundle {
        texture: asset_server.load("bevy_pixel_dark.png"),
        sprite: Sprite {
            custom_size: Some(Vec2 { x: 160.0, y: 120.0 }),
            ..Default::default()
        },
        transform: Transform::from_xyz(-100.0, 0.0, 0.0),
        ..Default::default()
    });

    // When a .meta file is added with the same name as the asset and a '.meta' extension
    // you can (and must) specify all fields of the asset loader's settings for that
    // particular asset, in this case [ImageLoaderSettings]. Take a look at
    // examples/asset/files/bevy_pixel_dark_with_meta.png.meta
    // for the format and you'll notice, the only non-default option is setting Nearest
    // filtering. This tends to work much better for pixel art assets.
    // A good reference when filling this out is to check out [ImageLoaderSettings::default()]
    // and follow to the default implementation of each fields type.
    // https://docs.rs/bevy/latest/bevy/render/texture/struct.ImageLoaderSettings.html#
    commands.spawn(SpriteBundle {
        texture: asset_server.load("bevy_pixel_dark_with_meta.png"),
        sprite: Sprite {
            custom_size: Some(Vec2 { x: 160.0, y: 120.0 }),
            ..Default::default()
        },
        transform: Transform::from_xyz(100.0, 0.0, 0.0),
        ..Default::default()
    });

    // Another option is to use the AssetServers load_with_settings function.
    // With this you can specify the same settings upon loading your asset with a
    // couple of differences. A big one is that you aren't required to set *every*
    // setting, just modify the ones that you need. It works by passing in a function
    // (in this case an anonymous closure) that takes a reference to the settings type
    // that is then modified in the function.
    // Do note that if you want to load the same asset with different settings, the
    // settings changes from any loads after the first of the same asset will be ignored.
    // This is why this one loads a differently named copy of the asset instead of using
    // same one as without a .meta file.
    commands.spawn(SpriteBundle {
        texture: asset_server.load_with_settings(
            "bevy_pixel_dark_with_settings.png",
            |settings: &mut ImageLoaderSettings| {
                settings.sampler = ImageSampler::nearest();
            },
        ),
        sprite: Sprite {
            custom_size: Some(Vec2 { x: 160.0, y: 120.0 }),
            ..Default::default()
        },
        transform: Transform::from_xyz(0.0, 150.0, 0.0),
        ..Default::default()
    });

    commands.spawn(Camera2dBundle::default());
}
examples/3d/parallax_mapping.rs (lines 206-211)
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
fn setup(
    mut commands: Commands,
    mut materials: ResMut<Assets<StandardMaterial>>,
    mut meshes: ResMut<Assets<Mesh>>,
    asset_server: Res<AssetServer>,
) {
    // The normal map. Note that to generate it in the GIMP image editor, you should
    // open the depth map, and do Filters → Generic → Normal Map
    // You should enable the "flip X" checkbox.
    let normal_handle = asset_server.load_with_settings(
        "textures/parallax_example/cube_normal.png",
        // The normal map texture is in linear color space. Lighting won't look correct
        // if `is_srgb` is `true`, which is the default.
        |settings: &mut ImageLoaderSettings| settings.is_srgb = false,
    );

    // Camera
    commands.spawn((
        Camera3dBundle {
            transform: Transform::from_xyz(1.5, 1.5, 1.5).looking_at(Vec3::ZERO, Vec3::Y),
            ..default()
        },
        CameraController,
    ));

    // light
    commands
        .spawn(PointLightBundle {
            transform: Transform::from_xyz(2.0, 1.0, -1.1),
            point_light: PointLight {
                shadows_enabled: true,
                ..default()
            },
            ..default()
        })
        .with_children(|commands| {
            // represent the light source as a sphere
            let mesh = meshes.add(Sphere::new(0.05).mesh().ico(3).unwrap());
            commands.spawn(PbrBundle { mesh, ..default() });
        });

    // Plane
    commands.spawn(PbrBundle {
        mesh: meshes.add(Plane3d::default().mesh().size(10.0, 10.0)),
        material: materials.add(StandardMaterial {
            // standard material derived from dark green, but
            // with roughness and reflectance set.
            perceptual_roughness: 0.45,
            reflectance: 0.18,
            ..Color::srgb_u8(0, 80, 0).into()
        }),
        transform: Transform::from_xyz(0.0, -1.0, 0.0),
        ..default()
    });

    let parallax_depth_scale = TargetDepth::default().0;
    let max_parallax_layer_count = TargetLayers::default().0.exp2();
    let parallax_mapping_method = CurrentMethod::default();
    let parallax_material = materials.add(StandardMaterial {
        perceptual_roughness: 0.4,
        base_color_texture: Some(asset_server.load("textures/parallax_example/cube_color.png")),
        normal_map_texture: Some(normal_handle),
        // The depth map is a greyscale texture where black is the highest level and
        // white the lowest.
        depth_map: Some(asset_server.load("textures/parallax_example/cube_depth.png")),
        parallax_depth_scale,
        parallax_mapping_method: parallax_mapping_method.0,
        max_parallax_layer_count,
        ..default()
    });
    commands.spawn((
        PbrBundle {
            mesh: meshes.add(
                // NOTE: for normal maps and depth maps to work, the mesh
                // needs tangents generated.
                Mesh::from(Cuboid::default())
                    .with_generated_tangents()
                    .unwrap(),
            ),
            material: parallax_material.clone_weak(),
            ..default()
        },
        Spin { speed: 0.3 },
    ));

    let background_cube = meshes.add(
        Mesh::from(Cuboid::new(40.0, 40.0, 40.0))
            .with_generated_tangents()
            .unwrap(),
    );

    let background_cube_bundle = |translation| {
        (
            PbrBundle {
                transform: Transform::from_translation(translation),
                mesh: background_cube.clone(),
                material: parallax_material.clone(),
                ..default()
            },
            Spin { speed: -0.1 },
        )
    };
    commands.spawn(background_cube_bundle(Vec3::new(45., 0., 0.)));
    commands.spawn(background_cube_bundle(Vec3::new(-45., 0., 0.)));
    commands.spawn(background_cube_bundle(Vec3::new(0., 0., 45.)));
    commands.spawn(background_cube_bundle(Vec3::new(0., 0., -45.)));

    let style = TextStyle {
        font_size: 20.0,
        ..default()
    };

    // example instructions
    commands.spawn(
        TextBundle::from_sections(vec![
            TextSection::new(
                format!("Parallax depth scale: {parallax_depth_scale:.5}\n"),
                style.clone(),
            ),
            TextSection::new(
                format!("Layers: {max_parallax_layer_count:.0}\n"),
                style.clone(),
            ),
            TextSection::new(format!("{parallax_mapping_method}\n"), style.clone()),
            TextSection::new("\n\n", style.clone()),
            TextSection::new("Controls:\n", style.clone()),
            TextSection::new("Left click - Change view angle\n", style.clone()),
            TextSection::new(
                "1/2 - Decrease/Increase parallax depth scale\n",
                style.clone(),
            ),
            TextSection::new("3/4 - Decrease/Increase layer count\n", style.clone()),
            TextSection::new("Space - Switch parallaxing algorithm\n", style),
        ])
        .with_style(Style {
            position_type: PositionType::Absolute,
            top: Val::Px(12.0),
            left: Val::Px(12.0),
            ..default()
        }),
    );
}

pub fn load_acquire_with_settings<'a, A, S, G>( &self, path: impl Into<AssetPath<'a>>, settings: impl Fn(&mut S) + Send + Sync + 'static, guard: G ) -> Handle<A>
where A: Asset, S: Settings, G: Send + Sync + 'static,

Begins loading an Asset of type A stored at path while holding a guard item. The guard item is dropped when either the asset is loaded or loading has failed.

This function only guarantees the asset referenced by the Handle is loaded. If your asset is separated into multiple files, sub-assets referenced by the main asset might still be loading, depend on the implementation of the AssetLoader.

The given settings function will override the asset’s AssetLoader settings. The type S must match the configured AssetLoader::Settings or settings changes will be ignored and an error will be printed to the log.

pub async fn load_untyped_async<'a>( &self, path: impl Into<AssetPath<'a>> ) -> Result<UntypedHandle, AssetLoadError>

Asynchronously load an asset that you do not know the type of statically. If you do know the type of the asset, you should use AssetServer::load. If you don’t know the type of the asset, but you can’t use an async method, consider using AssetServer::load_untyped.

pub fn load_untyped<'a>( &self, path: impl Into<AssetPath<'a>> ) -> Handle<LoadedUntypedAsset>

Load an asset without knowing its type. The method returns a handle to a LoadedUntypedAsset.

Once the LoadedUntypedAsset is loaded, an untyped handle for the requested path can be retrieved from it.

use bevy_asset::{Assets, Handle, LoadedUntypedAsset};
use bevy_ecs::system::{Res, Resource};

#[derive(Resource)]
struct LoadingUntypedHandle(Handle<LoadedUntypedAsset>);

fn resolve_loaded_untyped_handle(loading_handle: Res<LoadingUntypedHandle>, loaded_untyped_assets: Res<Assets<LoadedUntypedAsset>>) {
    if let Some(loaded_untyped_asset) = loaded_untyped_assets.get(&loading_handle.0) {
        let handle = loaded_untyped_asset.handle.clone();
        // continue working with `handle` which points to the asset at the originally requested path
    }
}

This indirection enables a non blocking load of an untyped asset, since I/O is required to figure out the asset type before a handle can be created.

pub fn reload<'a>(&self, path: impl Into<AssetPath<'a>>)

Kicks off a reload of the asset stored at the given path. This will only reload the asset if it currently loaded.

pub fn add<A>(&self, asset: A) -> Handle<A>
where A: Asset,

Queues a new asset to be tracked by the AssetServer and returns a Handle to it. This can be used to track dependencies of assets created at runtime.

After the asset has been fully loaded by the AssetServer, it will show up in the relevant Assets storage.

Examples found in repository?
examples/asset/asset_decompression.rs (line 136)
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
fn decompress<A: Asset>(
    mut commands: Commands,
    asset_server: Res<AssetServer>,
    mut compressed_assets: ResMut<Assets<GzAsset>>,
    query: Query<(Entity, &Compressed<A>)>,
) {
    for (entity, Compressed { compressed, .. }) in query.iter() {
        let Some(GzAsset { uncompressed }) = compressed_assets.remove(compressed) else {
            continue;
        };

        let uncompressed = uncompressed.take::<A>().unwrap();

        commands
            .entity(entity)
            .remove::<Compressed<A>>()
            .insert(asset_server.add(uncompressed));
    }
}

pub fn load_folder<'a>( &self, path: impl Into<AssetPath<'a>> ) -> Handle<LoadedFolder>

Loads all assets from the specified folder recursively. The LoadedFolder asset (when it loads) will contain handles to all assets in the folder. You can wait for all assets to load by checking the LoadedFolder’s RecursiveDependencyLoadState.

Loading the same folder multiple times will return the same handle. If the file_watcher feature is enabled, LoadedFolder handles will reload when a file in the folder is removed, added or moved. This includes files in subdirectories and moving, adding, or removing complete subdirectories.

Examples found in repository?
examples/2d/texture_atlas.rs (line 34)
32
33
34
35
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")));
}
More examples
Hide additional examples
examples/asset/asset_loading.rs (line 46)
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
fn setup(
    mut commands: Commands,
    asset_server: Res<AssetServer>,
    meshes: Res<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    // By default AssetServer will load assets from inside the "assets" folder.
    // For example, the next line will load "ROOT/assets/models/cube/cube.gltf#Mesh0/Primitive0",
    // where "ROOT" is the directory of the Application.
    //
    // This can be overridden by setting the "CARGO_MANIFEST_DIR" environment variable (see
    // https://doc.rust-lang.org/cargo/reference/environment-variables.html)
    // to another directory. When the Application is run through Cargo, "CARGO_MANIFEST_DIR" is
    // automatically set to your crate (workspace) root directory.
    let cube_handle = asset_server.load("models/cube/cube.gltf#Mesh0/Primitive0");
    let sphere_handle = asset_server.load("models/sphere/sphere.gltf#Mesh0/Primitive0");

    // All assets end up in their Assets<T> collection once they are done loading:
    if let Some(sphere) = meshes.get(&sphere_handle) {
        // You might notice that this doesn't run! This is because assets load in parallel without
        // blocking. When an asset has loaded, it will appear in relevant Assets<T>
        // collection.
        info!("{:?}", sphere.primitive_topology());
    } else {
        info!("sphere hasn't loaded yet");
    }

    // You can load all assets in a folder like this. They will be loaded in parallel without
    // blocking. The LoadedFolder asset holds handles to each asset in the folder. These are all
    // dependencies of the LoadedFolder asset, meaning you can wait for the LoadedFolder asset to
    // fire AssetEvent::LoadedWithDependencies if you want to wait for all assets in the folder
    // to load.
    // If you want to keep the assets in the folder alive, make sure you store the returned handle
    // somewhere.
    let _loaded_folder: Handle<LoadedFolder> = asset_server.load_folder("models/torus");

    // If you want a handle to a specific asset in a loaded folder, the easiest way to get one is to call load.
    // It will _not_ be loaded a second time.
    // The LoadedFolder asset will ultimately also hold handles to the assets, but waiting for it to load
    // and finding the right handle is more work!
    let torus_handle = asset_server.load("models/torus/torus.gltf#Mesh0/Primitive0");

    // You can also add assets directly to their Assets<T> storage:
    let material_handle = materials.add(StandardMaterial {
        base_color: Color::srgb(0.8, 0.7, 0.6),
        ..default()
    });

    // torus
    commands.spawn(PbrBundle {
        mesh: torus_handle,
        material: material_handle.clone(),
        transform: Transform::from_xyz(-3.0, 0.0, 0.0),
        ..default()
    });
    // cube
    commands.spawn(PbrBundle {
        mesh: cube_handle,
        material: material_handle.clone(),
        transform: Transform::from_xyz(0.0, 0.0, 0.0),
        ..default()
    });
    // sphere
    commands.spawn(PbrBundle {
        mesh: sphere_handle,
        material: material_handle,
        transform: Transform::from_xyz(3.0, 0.0, 0.0),
        ..default()
    });
    // light
    commands.spawn(PointLightBundle {
        transform: Transform::from_xyz(4.0, 5.0, 4.0),
        ..default()
    });
    // camera
    commands.spawn(Camera3dBundle {
        transform: Transform::from_xyz(0.0, 3.0, 10.0).looking_at(Vec3::ZERO, Vec3::Y),
        ..default()
    });
}

pub fn get_load_states( &self, id: impl Into<UntypedAssetId> ) -> Option<(LoadState, DependencyLoadState, RecursiveDependencyLoadState)>

Retrieves all loads states for the given asset id.

Examples found in repository?
examples/games/loading_screen.rs (line 237)
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
fn update_loading_data(
    mut loading_data: ResMut<LoadingData>,
    mut loading_state: ResMut<LoadingState>,
    asset_server: Res<AssetServer>,
    pipelines_ready: Res<PipelinesReady>,
) {
    if !loading_data.loading_assets.is_empty() || !pipelines_ready.0 {
        // If we are still loading assets / pipelines are not fully compiled,
        // we reset the confirmation frame count.
        loading_data.confirmation_frames_count = 0;

        // Go through each asset and verify their load states.
        // Any assets that are loaded are then added to the pop list for later removal.
        let mut pop_list: Vec<usize> = Vec::new();
        for (index, asset) in loading_data.loading_assets.iter().enumerate() {
            if let Some(state) = asset_server.get_load_states(asset) {
                if let bevy::asset::RecursiveDependencyLoadState::Loaded = state.2 {
                    pop_list.push(index);
                }
            }
        }

        // Remove all loaded assets from the loading_assets list.
        for i in pop_list.iter() {
            loading_data.loading_assets.remove(*i);
        }

        // If there are no more assets being monitored, and pipelines
        // are compiled, then start counting confirmation frames.
        // Once enough confirmations have passed, everything will be
        // considered to be fully loaded.
    } else {
        loading_data.confirmation_frames_count += 1;
        if loading_data.confirmation_frames_count == loading_data.confirmation_frames_target {
            *loading_state = LoadingState::LevelReady;
        }
    }
}

pub fn get_load_state(&self, id: impl Into<UntypedAssetId>) -> Option<LoadState>

Retrieves the main LoadState of a given asset id.

Note that this is “just” the root asset load state. To check if an asset and its recursive dependencies have loaded, see AssetServer::is_loaded_with_dependencies.

pub fn get_recursive_dependency_load_state( &self, id: impl Into<UntypedAssetId> ) -> Option<RecursiveDependencyLoadState>

Retrieves the RecursiveDependencyLoadState of a given asset id.

pub fn load_state(&self, id: impl Into<UntypedAssetId>) -> LoadState

Retrieves the main LoadState of a given asset id.

Examples found in repository?
examples/3d/pbr.rs (line 145)
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
fn environment_map_load_finish(
    mut commands: Commands,
    asset_server: Res<AssetServer>,
    environment_maps: Query<&EnvironmentMapLight>,
    label_query: Query<Entity, With<EnvironmentMapLabel>>,
) {
    if let Ok(environment_map) = environment_maps.get_single() {
        if asset_server.load_state(&environment_map.diffuse_map) == LoadState::Loaded
            && asset_server.load_state(&environment_map.specular_map) == LoadState::Loaded
        {
            if let Ok(label_entity) = label_query.get_single() {
                commands.entity(label_entity).despawn();
            }
        }
    }
}
More examples
Hide additional examples
examples/3d/skybox.rs (line 149)
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
fn asset_loaded(
    asset_server: Res<AssetServer>,
    mut images: ResMut<Assets<Image>>,
    mut cubemap: ResMut<Cubemap>,
    mut skyboxes: Query<&mut Skybox>,
) {
    if !cubemap.is_loaded && asset_server.load_state(&cubemap.image_handle) == LoadState::Loaded {
        info!("Swapping to {}...", CUBEMAPS[cubemap.index].0);
        let image = images.get_mut(&cubemap.image_handle).unwrap();
        // NOTE: PNGs do not have any metadata that could indicate they contain a cubemap texture,
        // so they appear as one texture. The following code reconfigures the texture as necessary.
        if image.texture_descriptor.array_layer_count() == 1 {
            image.reinterpret_stacked_2d_as_array(image.height() / image.width());
            image.texture_view_descriptor = Some(TextureViewDescriptor {
                dimension: Some(TextureViewDimension::Cube),
                ..default()
            });
        }

        for mut skybox in &mut skyboxes {
            skybox.image = cubemap.image_handle.clone();
        }

        cubemap.is_loaded = true;
    }
}
examples/shader/array_texture.rs (line 57)
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
fn create_array_texture(
    mut commands: Commands,
    asset_server: Res<AssetServer>,
    mut loading_texture: ResMut<LoadingTexture>,
    mut images: ResMut<Assets<Image>>,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<ArrayTextureMaterial>>,
) {
    if loading_texture.is_loaded
        || asset_server.load_state(loading_texture.handle.id()) != LoadState::Loaded
    {
        return;
    }
    loading_texture.is_loaded = true;
    let image = images.get_mut(&loading_texture.handle).unwrap();

    // Create a new array texture asset from the loaded texture.
    let array_layers = 4;
    image.reinterpret_stacked_2d_as_array(array_layers);

    // Spawn some cubes using the array texture
    let mesh_handle = meshes.add(Cuboid::default());
    let material_handle = materials.add(ArrayTextureMaterial {
        array_texture: loading_texture.handle.clone(),
    });
    for x in -5..=5 {
        commands.spawn(MaterialMeshBundle {
            mesh: mesh_handle.clone(),
            material: material_handle.clone(),
            transform: Transform::from_xyz(x as f32 + 0.5, 0.0, 0.0),
            ..Default::default()
        });
    }
}
examples/tools/scene_viewer/scene_viewer_plugin.rs (line 95)
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
fn scene_load_check(
    asset_server: Res<AssetServer>,
    mut scenes: ResMut<Assets<Scene>>,
    gltf_assets: Res<Assets<Gltf>>,
    mut scene_handle: ResMut<SceneHandle>,
    mut scene_spawner: ResMut<SceneSpawner>,
) {
    match scene_handle.instance_id {
        None => {
            if asset_server.load_state(&scene_handle.gltf_handle) == LoadState::Loaded {
                let gltf = gltf_assets.get(&scene_handle.gltf_handle).unwrap();
                if gltf.scenes.len() > 1 {
                    info!(
                        "Displaying scene {} out of {}",
                        scene_handle.scene_index,
                        gltf.scenes.len()
                    );
                    info!("You can select the scene by adding '#Scene' followed by a number to the end of the file path (e.g '#Scene1' to load the second scene).");
                }

                let gltf_scene_handle =
                    gltf.scenes
                        .get(scene_handle.scene_index)
                        .unwrap_or_else(|| {
                            panic!(
                                "glTF file doesn't contain scene {}!",
                                scene_handle.scene_index
                            )
                        });
                let scene = scenes.get_mut(gltf_scene_handle).unwrap();

                let mut query = scene
                    .world
                    .query::<(Option<&DirectionalLight>, Option<&PointLight>)>();
                scene_handle.has_light =
                    query
                        .iter(&scene.world)
                        .any(|(maybe_directional_light, maybe_point_light)| {
                            maybe_directional_light.is_some() || maybe_point_light.is_some()
                        });

                scene_handle.instance_id =
                    Some(scene_spawner.spawn(gltf_scene_handle.clone_weak()));

                info!("Spawning scene...");
            }
        }
        Some(instance_id) if !scene_handle.is_loaded => {
            if scene_spawner.instance_is_ready(instance_id) {
                info!("...done!");
                scene_handle.is_loaded = true;
            }
        }
        Some(_) => {}
    }
}

pub fn recursive_dependency_load_state( &self, id: impl Into<UntypedAssetId> ) -> RecursiveDependencyLoadState

Retrieves the RecursiveDependencyLoadState of a given asset id.

pub fn is_loaded_with_dependencies(&self, id: impl Into<UntypedAssetId>) -> bool

Returns true if the asset and all of its dependencies (recursive) have been loaded.

pub fn get_handle<'a, A>( &self, path: impl Into<AssetPath<'a>> ) -> Option<Handle<A>>
where A: Asset,

Returns an active handle for the given path, if the asset at the given path has already started loading, or is still “alive”.

Examples found in repository?
examples/2d/texture_atlas.rs (line 145)
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
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 loaded_folder = loaded_folders.get(&rpg_sprite_handles.0).unwrap();

    // create texture atlases with different padding and sampling

    let (texture_atlas_linear, linear_texture) = create_texture_atlas(
        loaded_folder,
        None,
        Some(ImageSampler::linear()),
        &mut textures,
    );
    let atlas_linear_handle = texture_atlases.add(texture_atlas_linear.clone());

    let (texture_atlas_nearest, nearest_texture) = create_texture_atlas(
        loaded_folder,
        None,
        Some(ImageSampler::nearest()),
        &mut textures,
    );
    let atlas_nearest_handle = texture_atlases.add(texture_atlas_nearest);

    let (texture_atlas_linear_padded, linear_padded_texture) = create_texture_atlas(
        loaded_folder,
        Some(UVec2::new(6, 6)),
        Some(ImageSampler::linear()),
        &mut textures,
    );
    let atlas_linear_padded_handle = texture_atlases.add(texture_atlas_linear_padded.clone());

    let (texture_atlas_nearest_padded, nearest_padded_texture) = create_texture_atlas(
        loaded_folder,
        Some(UVec2::new(6, 6)),
        Some(ImageSampler::nearest()),
        &mut textures,
    );
    let atlas_nearest_padded_handle = texture_atlases.add(texture_atlas_nearest_padded);

    // setup 2d scene
    commands.spawn(Camera2dBundle::default());

    // padded textures are to the right, unpadded to the left

    // draw unpadded texture atlas
    commands.spawn(SpriteBundle {
        texture: linear_texture.clone(),
        transform: Transform {
            translation: Vec3::new(-250.0, -130.0, 0.0),
            scale: Vec3::splat(0.8),
            ..default()
        },
        ..default()
    });

    // draw padded texture atlas
    commands.spawn(SpriteBundle {
        texture: linear_padded_texture.clone(),
        transform: Transform {
            translation: Vec3::new(250.0, -130.0, 0.0),
            scale: Vec3::splat(0.8),
            ..default()
        },
        ..default()
    });

    let font = asset_server.load("fonts/FiraSans-Bold.ttf");

    // padding label text style
    let text_style: TextStyle = TextStyle {
        font: font.clone(),
        font_size: 50.0,
        color: Color::WHITE,
    };

    // labels to indicate padding

    // No padding
    create_label(
        &mut commands,
        (-250.0, 330.0, 0.0),
        "No padding",
        text_style.clone(),
    );

    // Padding
    create_label(&mut commands, (250.0, 330.0, 0.0), "Padding", text_style);

    // get handle to a sprite to render
    let vendor_handle: Handle<Image> = asset_server
        .get_handle("textures/rpg/chars/vendor/generic-rpg-vendor.png")
        .unwrap();

    // get index of the sprite in the texture atlas, this is used to render the sprite
    // the index is the same for all the texture atlases, since they are created from the same folder
    let vendor_index = texture_atlas_linear
        .get_texture_index(&vendor_handle)
        .unwrap();

    // configuration array to render sprites through iteration
    let configurations: [(&str, Handle<TextureAtlasLayout>, Handle<Image>, f32); 4] = [
        ("Linear", atlas_linear_handle, linear_texture, -350.0),
        ("Nearest", atlas_nearest_handle, nearest_texture, -150.0),
        (
            "Linear",
            atlas_linear_padded_handle,
            linear_padded_texture,
            150.0,
        ),
        (
            "Nearest",
            atlas_nearest_padded_handle,
            nearest_padded_texture,
            350.0,
        ),
    ];

    // label text style
    let sampling_label_style = TextStyle {
        font,
        font_size: 30.0,
        color: Color::WHITE,
    };

    let base_y = 170.0; // y position of the sprites

    for (sampling, atlas_handle, image_handle, x) in configurations {
        // render a sprite from the texture_atlas
        create_sprite_from_atlas(
            &mut commands,
            (x, base_y, 0.0),
            vendor_index,
            atlas_handle,
            image_handle,
        );

        // render a label to indicate the sampling setting
        create_label(
            &mut commands,
            (x, base_y + 110.0, 0.0), // offset to y position of the sprite
            sampling,
            sampling_label_style.clone(),
        );
    }
}

pub fn get_id_handle<A>(&self, id: AssetId<A>) -> Option<Handle<A>>
where A: Asset,

Get a Handle from an AssetId.

This only returns Some if id is derived from a Handle that was loaded through an AssetServer, otherwise it returns None.

Consider using Assets::get_strong_handle in the case the Handle comes from Assets::add.

Examples found in repository?
examples/tools/scene_viewer/animation_plugin.rs (line 123)
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
fn assign_clips(
    mut players: Query<&mut AnimationPlayer>,
    targets: Query<(Entity, &AnimationTarget)>,
    parents: Query<&Parent>,
    scene_handle: Res<SceneHandle>,
    clips: Res<Assets<AnimationClip>>,
    gltf_assets: Res<Assets<Gltf>>,
    assets: Res<AssetServer>,
    mut graphs: ResMut<Assets<AnimationGraph>>,
    mut commands: Commands,
    mut setup: Local<bool>,
) {
    if scene_handle.is_loaded && !*setup {
        *setup = true;
    } else {
        return;
    }

    let gltf = gltf_assets.get(&scene_handle.gltf_handle).unwrap();
    let animations = &gltf.animations;
    if animations.is_empty() {
        return;
    }

    let count = animations.len();
    let plural = if count == 1 { "" } else { "s" };
    info!("Found {} animation{plural}", animations.len());
    let names: Vec<_> = gltf.named_animations.keys().collect();
    info!("Animation names: {names:?}");

    // Map animation target IDs to entities.
    let animation_target_id_to_entity: HashMap<_, _> = targets
        .iter()
        .map(|(entity, target)| (target.id, entity))
        .collect();

    // Build up a list of all animation clips that belong to each player. A clip
    // is considered to belong to an animation player if all targets of the clip
    // refer to entities whose nearest ancestor player is that animation player.

    let mut player_to_graph: EntityHashMap<(AnimationGraph, Vec<AnimationNodeIndex>)> =
        EntityHashMap::default();

    for (clip_id, clip) in clips.iter() {
        let mut ancestor_player = None;
        for target_id in clip.curves().keys() {
            // If the animation clip refers to entities that aren't present in
            // the scene, bail.
            let Some(&target) = animation_target_id_to_entity.get(target_id) else {
                continue;
            };

            // Find the nearest ancestor animation player.
            let mut current = Some(target);
            while let Some(entity) = current {
                if players.contains(entity) {
                    match ancestor_player {
                        None => {
                            // If we haven't found a player yet, record the one
                            // we found.
                            ancestor_player = Some(entity);
                        }
                        Some(ancestor) => {
                            // If we have found a player, then make sure it's
                            // the same player we located before.
                            if ancestor != entity {
                                // It's a different player. Bail.
                                ancestor_player = None;
                                break;
                            }
                        }
                    }
                }

                // Go to the next parent.
                current = parents.get(entity).ok().map(|parent| parent.get());
            }
        }

        let Some(ancestor_player) = ancestor_player else {
            warn!(
                "Unexpected animation hierarchy for animation clip {:?}; ignoring.",
                clip_id
            );
            continue;
        };

        let Some(clip_handle) = assets.get_id_handle(clip_id) else {
            warn!("Clip {:?} wasn't loaded.", clip_id);
            continue;
        };

        let &mut (ref mut graph, ref mut clip_indices) =
            player_to_graph.entry(ancestor_player).or_default();
        let node_index = graph.add_clip(clip_handle, 1.0, graph.root);
        clip_indices.push(node_index);
    }

    // Now that we've built up a list of all clips that belong to each player,
    // package them up into a `Clips` component, play the first such animation,
    // and add that component to the player.
    for (player_entity, (graph, clips)) in player_to_graph {
        let Ok(mut player) = players.get_mut(player_entity) else {
            warn!("Animation targets referenced a nonexistent player. This shouldn't happen.");
            continue;
        };
        let graph = graphs.add(graph);
        let animations = Clips::new(clips);
        player.play(animations.current()).repeat();
        commands
            .entity(player_entity)
            .insert(animations)
            .insert(graph);
    }
}

pub fn get_id_handle_untyped(&self, id: UntypedAssetId) -> Option<UntypedHandle>

Get an UntypedHandle from an UntypedAssetId. See AssetServer::get_id_handle for details.

pub fn is_managed(&self, id: impl Into<UntypedAssetId>) -> bool

Returns true if the given id corresponds to an asset that is managed by this AssetServer. Otherwise, returns false.

pub fn get_path_id<'a>( &self, path: impl Into<AssetPath<'a>> ) -> Option<UntypedAssetId>

Returns an active untyped asset id for the given path, if the asset at the given path has already started loading, or is still “alive”. Returns the first ID in the event of multiple assets being registered against a single path.

§See also

get_path_ids for all handles.

pub fn get_path_ids<'a>( &self, path: impl Into<AssetPath<'a>> ) -> Vec<UntypedAssetId>

Returns all active untyped asset IDs for the given path, if the assets at the given path have already started loading, or are still “alive”. Multiple IDs will be returned in the event that a single path is used by multiple AssetLoader’s.

pub fn get_handle_untyped<'a>( &self, path: impl Into<AssetPath<'a>> ) -> Option<UntypedHandle>

Returns an active untyped handle for the given path, if the asset at the given path has already started loading, or is still “alive”. Returns the first handle in the event of multiple assets being registered against a single path.

§See also

get_handles_untyped for all handles.

pub fn get_handles_untyped<'a>( &self, path: impl Into<AssetPath<'a>> ) -> Vec<UntypedHandle>

Returns all active untyped handles for the given path, if the assets at the given path have already started loading, or are still “alive”. Multiple handles will be returned in the event that a single path is used by multiple AssetLoader’s.

pub fn get_path_and_type_id_handle( &self, path: &AssetPath<'_>, type_id: TypeId ) -> Option<UntypedHandle>

Returns an active untyped handle for the given path and TypeId, if the asset at the given path has already started loading, or is still “alive”.

pub fn get_path(&self, id: impl Into<UntypedAssetId>) -> Option<AssetPath<'_>>

Returns the path for the given id, if it has one.

pub fn mode(&self) -> AssetServerMode

Returns the AssetServerMode this server is currently in.

pub fn preregister_loader<L>(&self, extensions: &[&str])
where L: AssetLoader,

Pre-register a loader that will later be added.

Assets loaded with matching extensions will be blocked until the real loader is added.

Trait Implementations§

§

impl Clone for AssetServer

§

fn clone(&self) -> AssetServer

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
§

impl Debug for AssetServer

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
§

impl Resource for AssetServer
where AssetServer: Send + Sync + 'static,

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
§

impl<T, U> AsBindGroupShaderType<U> for T
where U: ShaderType, &'a T: for<'a> Into<U>,

§

fn as_bind_group_shader_type(&self, _images: &RenderAssets<GpuImage>) -> U

Return the T ShaderType for self. When used in AsBindGroup derives, it is safe to assume that all images in self exist.
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> Downcast<T> for T

§

fn downcast(&self) -> &T

§

impl<T> Downcast for T
where T: Any,

§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.
§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Convert Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
§

fn as_any(&self) -> &(dyn Any + 'static)

Convert &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Send + Sync>

Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<S> FromSample<S> for S

§

fn from_sample_(s: S) -> S

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
§

impl<F, T> IntoSample<T> for F
where T: FromSample<F>,

§

fn into_sample(self) -> T

§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToOwned for T
where T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
§

impl<T, U> ToSample<U> for T
where U: FromSample<T>,

§

fn to_sample_(self) -> U

source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> TypeData for T
where T: 'static + Send + Sync + Clone,

§

fn clone_type_data(&self) -> Box<dyn TypeData>

§

impl<T> Upcast<T> for T

§

fn upcast(&self) -> Option<&T>

§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
§

impl<T> ConditionalSend for T
where T: Send,

§

impl<S, T> Duplex<S> for T
where T: FromSample<S> + ToSample<S>,

§

impl<T> Settings for T
where T: 'static + Send + Sync,

§

impl<T> WasmNotSend for T
where T: Send,

§

impl<T> WasmNotSendSync for T
where T: WasmNotSend + WasmNotSync,

§

impl<T> WasmNotSync for T
where T: Sync,