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:
- Initialize a new
Assettype with theAssetServerviaAssetApp::init_asset, which will internally callAssetServer::register_assetand set up related ECSAssetsstorage and systems. - Register one or more
AssetLoaders for that asset withAssetApp::init_asset_loader - Add the asset to your asset folder (defaults to
assets). - Call
AssetServer::loadwith 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§
Source§impl AssetServer
impl AssetServer
Sourcepub fn new(
sources: AssetSources,
mode: AssetServerMode,
watching_for_changes: bool,
unapproved_path_mode: UnapprovedPathMode,
) -> AssetServer
pub fn new( sources: AssetSources, mode: AssetServerMode, watching_for_changes: bool, unapproved_path_mode: UnapprovedPathMode, ) -> 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.
Sourcepub fn new_with_meta_check(
sources: AssetSources,
mode: AssetServerMode,
meta_check: AssetMetaCheck,
watching_for_changes: bool,
unapproved_path_mode: UnapprovedPathMode,
) -> AssetServer
pub fn new_with_meta_check( sources: AssetSources, mode: AssetServerMode, meta_check: AssetMetaCheck, watching_for_changes: bool, unapproved_path_mode: UnapprovedPathMode, ) -> 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.
Sourcepub fn get_source<'a>(
&self,
source: impl Into<AssetSourceId<'a>>,
) -> Result<&AssetSource, MissingAssetSourceError>
pub fn get_source<'a>( &self, source: impl Into<AssetSourceId<'a>>, ) -> Result<&AssetSource, MissingAssetSourceError>
Retrieves the AssetSource for the given source.
Sourcepub fn watching_for_changes(&self) -> bool
pub fn watching_for_changes(&self) -> bool
Returns true if the AssetServer watches for changes.
Sourcepub fn register_loader<L>(&self, loader: L)where
L: AssetLoader,
pub fn register_loader<L>(&self, loader: L)where
L: AssetLoader,
Registers a new AssetLoader. AssetLoaders must be registered before they can be used.
Sourcepub fn register_asset<A>(&self, assets: &Assets<A>)where
A: Asset,
pub fn register_asset<A>(&self, assets: &Assets<A>)where
A: Asset,
Sourcepub async fn get_asset_loader_with_extension(
&self,
extension: &str,
) -> Result<Arc<dyn ErasedAssetLoader>, MissingAssetLoaderForExtensionError>
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.
Sourcepub async fn get_asset_loader_with_type_name(
&self,
type_name: &str,
) -> Result<Arc<dyn ErasedAssetLoader>, MissingAssetLoaderForTypeNameError>
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 core::any::type_name, if it exists.
Sourcepub async fn get_path_asset_loader<'a>(
&self,
path: impl Into<AssetPath<'a>>,
) -> Result<Arc<dyn ErasedAssetLoader>, MissingAssetLoaderForExtensionError>
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.
Sourcepub async fn get_asset_loader_with_asset_type_id(
&self,
type_id: TypeId,
) -> Result<Arc<dyn ErasedAssetLoader>, MissingAssetLoaderForTypeIdError>
pub async fn get_asset_loader_with_asset_type_id( &self, type_id: TypeId, ) -> Result<Arc<dyn ErasedAssetLoader>, MissingAssetLoaderForTypeIdError>
Retrieves the default AssetLoader for the given Asset TypeId, if one can be found.
Sourcepub async fn get_asset_loader_with_asset_type<A>(
&self,
) -> Result<Arc<dyn ErasedAssetLoader>, MissingAssetLoaderForTypeIdError>where
A: Asset,
pub async fn get_asset_loader_with_asset_type<A>(
&self,
) -> Result<Arc<dyn ErasedAssetLoader>, MissingAssetLoaderForTypeIdError>where
A: Asset,
Retrieves the default AssetLoader for the given Asset type, if one can be found.
Sourcepub fn load<'a, A>(&self, path: impl Into<AssetPath<'a>>) -> Handle<A>where
A: Asset,
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.
Note that if the asset at this path is already loaded, this function will return the existing handle, and will not waste work spawning a new load task.
In case the file path contains a hashtag (#), the path must be specified using Path
or AssetPath because otherwise the hashtag would be interpreted as separator between
the file path and the label. For example:
// `#path` is a label.
asset_server.load("some/file#path");
// `#path` is part of the file name.
asset_server.load(Path::new("some/file#path"));Furthermore, if you need to load a file with a hashtag in its name and a label, you can
manually construct an AssetPath.
asset_server.load(AssetPath::from_path(Path::new("some/file#path")).with_label("subasset"));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?
More examples
- examples/window/transparent_window.rs
- examples/2d/sprite.rs
- examples/3d/specular_tint.rs
- examples/3d/reflection_probes.rs
- examples/state/computed_states.rs
- examples/shader/shader_material_wesl.rs
- examples/asset/asset_decompression.rs
- examples/ecs/removal_detection.rs
- examples/2d/move_sprite.rs
- examples/shader_advanced/custom_shader_instancing.rs
- examples/shader_advanced/custom_render_phase.rs
- examples/testbed/ui.rs
- examples/3d/clearcoat.rs
- examples/asset/web_asset.rs
- examples/shader_advanced/specialized_mesh_pipeline.rs
- examples/2d/sprite_flipping.rs
- examples/animation/gltf_skinned_mesh.rs
- examples/asset/custom_asset.rs
- examples/asset/processing/asset_processing.rs
- examples/3d/query_gltf_primitives.rs
- examples/2d/sprite_tile.rs
- examples/3d/lightmaps.rs
- examples/shader/shader_material_2d.rs
- examples/ui/overflow_debug.rs
- examples/shader/array_texture.rs
- examples/ecs/fallible_params.rs
- examples/3d/color_grading.rs
- examples/window/window_settings.rs
- examples/3d/ssr.rs
- examples/3d/light_textures.rs
- examples/audio/soundtrack.rs
- examples/2d/custom_gltf_vertex_attribute.rs
- examples/3d/occlusion_culling.rs
- examples/2d/pixel_grid_snap.rs
- examples/3d/mixed_lighting.rs
- examples/shader/shader_material.rs
- examples/shader/shader_material_glsl.rs
- examples/3d/clustered_decals.rs
- examples/asset/extra_source.rs
- examples/shader_advanced/texture_binding_array.rs
- examples/ui/window_fallthrough.rs
- examples/2d/sprite_slice.rs
- examples/asset/hot_asset_reloading.rs
- examples/ecs/parallel_query.rs
- examples/audio/audio_control.rs
- examples/2d/transparency_2d.rs
- examples/ui/standard_widgets.rs
- examples/ui/standard_widgets_observers.rs
- examples/animation/morph_targets.rs
- examples/3d/post_processing.rs
- examples/3d/rotate_environment_map.rs
- examples/3d/load_gltf_extras.rs
- examples/shader/gpu_readback.rs
- examples/asset/embedded_asset.rs
- examples/2d/sprite_sheet.rs
- examples/games/loading_screen.rs
- examples/3d/tonemapping.rs
- examples/games/game_menu.rs
- examples/shader/shader_material_screenspace_texture.rs
- examples/3d/pcss.rs
- examples/3d/depth_of_field.rs
- examples/testbed/3d.rs
- examples/3d/edit_material_on_gltf.rs
- examples/shader/shader_material_bindless.rs
- examples/3d/anisotropy.rs
- examples/ui/font_atlas_debug.rs
- examples/animation/animation_masks.rs
- examples/2d/tilemap_chunk.rs
- examples/3d/animated_material.rs
- examples/3d/load_gltf.rs
- examples/input/text_input.rs
- examples/ui/button.rs
- examples/animation/animated_mesh.rs
- examples/shader/extended_material_bindless.rs
- examples/3d/skybox.rs
- examples/stress_tests/text_pipeline.rs
- examples/3d/update_gltf_scene.rs
- examples/3d/atmospheric_fog.rs
- examples/3d/fog_volumes.rs
- examples/shader/compute_shader_game_of_life.rs
- examples/window/custom_cursor_image.rs
- examples/2d/mesh2d_vertex_color_texture.rs
- examples/ecs/hierarchy.rs
- examples/3d/generate_custom_mesh.rs
- examples/stress_tests/many_sprites.rs
- examples/audio/spatial_audio_2d.rs
- examples/ui/relative_cursor_position.rs
- examples/ui/ui_material.rs
- examples/testbed/2d.rs
- examples/games/contributors.rs
- examples/ui/ui_scaling.rs
- examples/ui/ui_texture_atlas.rs
- examples/usage/cooldown.rs
- examples/window/multiple_windows.rs
- examples/stress_tests/many_animated_sprites.rs
- examples/2d/bloom_2d.rs
- examples/ui/ghost_nodes.rs
- examples/animation/animated_mesh_events.rs
- examples/animation/animated_ui.rs
- examples/asset/alter_sprite.rs
- examples/audio/spatial_audio_3d.rs
- examples/stress_tests/many_buttons.rs
- examples/shader/automatic_instancing.rs
- examples/camera/projection_zoom.rs
- examples/2d/rotation.rs
- examples/ui/ui_texture_slice.rs
- examples/camera/2d_on_ui.rs
- examples/3d/visibility_range.rs
- examples/2d/sprite_animation.rs
- examples/3d/volumetric_fog.rs
- examples/animation/animation_graph.rs
- examples/animation/animated_mesh_control.rs
- examples/3d/atmosphere.rs
- examples/3d/texture.rs
- examples/3d/motion_blur.rs
- examples/shader_advanced/custom_phase_item.rs
- examples/time/virtual_time.rs
- examples/shader_advanced/custom_post_processing.rs
- examples/ui/size_constraints.rs
- examples/3d/anti_aliasing.rs
- examples/ui/transparency_ui.rs
- examples/3d/manual_material.rs
- examples/transforms/align.rs
- examples/ui/ui_texture_atlas_slice.rs
- examples/picking/sprite_picking.rs
- examples/games/stepping.rs
- examples/3d/decal.rs
- examples/2d/mesh2d_alpha_mode.rs
- examples/2d/mesh2d_arcs.rs
- examples/asset/repeated_texture.rs
- examples/ui/text_wrap_debug.rs
- examples/games/alien_cake_addict.rs
- examples/asset/alter_mesh.rs
- examples/ui/text.rs
- examples/asset/asset_settings.rs
- examples/3d/meshlet.rs
- examples/2d/mesh2d_repeated_texture.rs
- examples/3d/pbr.rs
- examples/3d/auto_exposure.rs
- examples/asset/asset_loading.rs
- examples/ui/overflow.rs
- examples/ui/overflow_clip_margin.rs
- examples/ui/flex_layout.rs
- examples/shader/shader_prepass.rs
- examples/stress_tests/bevymark.rs
- examples/3d/solari.rs
- examples/ui/display_and_visibility.rs
- examples/3d/split_screen.rs
- examples/ui/scroll.rs
- examples/games/breakout.rs
- examples/stress_tests/many_foxes.rs
- examples/games/desk_toy.rs
- examples/3d/parallax_mapping.rs
- examples/2d/texture_atlas.rs
- examples/2d/sprite_scale.rs
- examples/3d/deferred_rendering.rs
- examples/ui/box_shadow.rs
- examples/2d/text2d.rs
- examples/animation/custom_skinned_mesh.rs
- examples/3d/blend_modes.rs
- examples/3d/lighting.rs
- examples/ui/text_debug.rs
- examples/ui/grid.rs
- examples/3d/transmission.rs
- examples/ui/ui_transform.rs
- examples/testbed/full_ui.rs
Sourcepub fn load_override<'a, A>(&self, path: impl Into<AssetPath<'a>>) -> Handle<A>where
A: Asset,
pub fn load_override<'a, A>(&self, path: impl Into<AssetPath<'a>>) -> Handle<A>where
A: Asset,
Same as load, but you can load assets from unapproved paths
if AssetPlugin::unapproved_path_mode
is Deny.
Sourcepub fn load_acquire<'a, A, G>(
&self,
path: impl Into<AssetPath<'a>>,
guard: G,
) -> Handle<A>
pub fn load_acquire<'a, A, G>( &self, path: impl Into<AssetPath<'a>>, guard: G, ) -> Handle<A>
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?
144fn setup_assets(mut commands: Commands, asset_server: Res<AssetServer>) {
145 let (barrier, guard) = AssetBarrier::new();
146 commands.insert_resource(OneHundredThings(std::array::from_fn(|i| match i % 5 {
147 0 => asset_server.load_acquire("models/GolfBall/GolfBall.glb", guard.clone()),
148 1 => asset_server.load_acquire("models/AlienCake/alien.glb", guard.clone()),
149 2 => asset_server.load_acquire("models/AlienCake/cakeBirthday.glb", guard.clone()),
150 3 => asset_server.load_acquire("models/FlightHelmet/FlightHelmet.gltf", guard.clone()),
151 4 => asset_server.load_acquire("models/torus/torus.gltf", guard.clone()),
152 _ => unreachable!(),
153 })));
154 let future = barrier.wait_async();
155 commands.insert_resource(barrier);
156
157 let loading_state = Arc::new(AtomicBool::new(false));
158 commands.insert_resource(AsyncLoadingState(loading_state.clone()));
159
160 // await the `AssetBarrierFuture`.
161 AsyncComputeTaskPool::get()
162 .spawn(async move {
163 future.await;
164 // Notify via `AsyncLoadingState`
165 loading_state.store(true, Ordering::Release);
166 })
167 .detach();
168}Sourcepub fn load_acquire_override<'a, A, G>(
&self,
path: impl Into<AssetPath<'a>>,
guard: G,
) -> Handle<A>
pub fn load_acquire_override<'a, A, G>( &self, path: impl Into<AssetPath<'a>>, guard: G, ) -> Handle<A>
Same as load, but you can load assets from unapproved paths
if AssetPlugin::unapproved_path_mode
is Deny.
Sourcepub fn load_with_settings<'a, A, S>(
&self,
path: impl Into<AssetPath<'a>>,
settings: impl Fn(&mut S) + Send + Sync + 'static,
) -> Handle<A>
pub fn load_with_settings<'a, A, S>( &self, path: impl Into<AssetPath<'a>>, settings: impl Fn(&mut S) + Send + Sync + 'static, ) -> Handle<A>
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?
61fn spawn_sphere(
62 commands: &mut Commands,
63 materials: &mut Assets<StandardMaterial>,
64 asset_server: &AssetServer,
65 sphere_mesh: &Handle<Mesh>,
66) {
67 commands.spawn((
68 Mesh3d(sphere_mesh.clone()),
69 MeshMaterial3d(materials.add(StandardMaterial {
70 clearcoat: 1.0,
71 clearcoat_perceptual_roughness: 0.3,
72 clearcoat_normal_texture: Some(asset_server.load_with_settings(
73 "textures/ScratchedGold-Normal.png",
74 |settings: &mut ImageLoaderSettings| settings.is_srgb = false,
75 )),
76 metallic: 0.9,
77 perceptual_roughness: 0.1,
78 base_color: GOLD.into(),
79 ..default()
80 })),
81 Transform::from_xyz(0.0, 0.0, 0.0).with_scale(Vec3::splat(1.25)),
82 ));
83}More examples
93fn spawn_car_paint_sphere(
94 commands: &mut Commands,
95 materials: &mut Assets<StandardMaterial>,
96 asset_server: &AssetServer,
97 sphere: &Handle<Mesh>,
98) {
99 commands
100 .spawn((
101 Mesh3d(sphere.clone()),
102 MeshMaterial3d(materials.add(StandardMaterial {
103 clearcoat: 1.0,
104 clearcoat_perceptual_roughness: 0.1,
105 normal_map_texture: Some(asset_server.load_with_settings(
106 "textures/BlueNoise-Normal.png",
107 |settings: &mut ImageLoaderSettings| settings.is_srgb = false,
108 )),
109 metallic: 0.9,
110 perceptual_roughness: 0.5,
111 base_color: BLUE.into(),
112 ..default()
113 })),
114 Transform::from_xyz(-1.0, 1.0, 0.0).with_scale(Vec3::splat(SPHERE_SCALE)),
115 ))
116 .insert(ExampleSphere);
117}
118
119/// Spawn a semitransparent object with a clearcoat layer.
120fn spawn_coated_glass_bubble_sphere(
121 commands: &mut Commands,
122 materials: &mut Assets<StandardMaterial>,
123 sphere: &Handle<Mesh>,
124) {
125 commands
126 .spawn((
127 Mesh3d(sphere.clone()),
128 MeshMaterial3d(materials.add(StandardMaterial {
129 clearcoat: 1.0,
130 clearcoat_perceptual_roughness: 0.1,
131 metallic: 0.5,
132 perceptual_roughness: 0.1,
133 base_color: Color::srgba(0.9, 0.9, 0.9, 0.3),
134 alpha_mode: AlphaMode::Blend,
135 ..default()
136 })),
137 Transform::from_xyz(-1.0, -1.0, 0.0).with_scale(Vec3::splat(SPHERE_SCALE)),
138 ))
139 .insert(ExampleSphere);
140}
141
142/// Spawns an object with both a clearcoat normal map (a scratched varnish) and
143/// a main layer normal map (the golf ball pattern).
144///
145/// This object is in glTF format, using the `KHR_materials_clearcoat`
146/// extension.
147fn spawn_golf_ball(commands: &mut Commands, asset_server: &AssetServer) {
148 commands.spawn((
149 SceneRoot(
150 asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/GolfBall/GolfBall.glb")),
151 ),
152 Transform::from_xyz(1.0, 1.0, 0.0).with_scale(Vec3::splat(SPHERE_SCALE)),
153 ExampleSphere,
154 ));
155}
156
157/// Spawns an object with only a clearcoat normal map (a scratch pattern) and no
158/// main layer normal map.
159fn spawn_scratched_gold_ball(
160 commands: &mut Commands,
161 materials: &mut Assets<StandardMaterial>,
162 asset_server: &AssetServer,
163 sphere: &Handle<Mesh>,
164) {
165 commands
166 .spawn((
167 Mesh3d(sphere.clone()),
168 MeshMaterial3d(materials.add(StandardMaterial {
169 clearcoat: 1.0,
170 clearcoat_perceptual_roughness: 0.3,
171 clearcoat_normal_texture: Some(asset_server.load_with_settings(
172 "textures/ScratchedGold-Normal.png",
173 |settings: &mut ImageLoaderSettings| settings.is_srgb = false,
174 )),
175 metallic: 0.9,
176 perceptual_roughness: 0.1,
177 base_color: GOLD.into(),
178 ..default()
179 })),
180 Transform::from_xyz(1.0, -1.0, 0.0).with_scale(Vec3::splat(SPHERE_SCALE)),
181 ))
182 .insert(ExampleSphere);
183}359 pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
360 commands.spawn((
361 Camera3d::default(),
362 Transform::from_xyz(-4.0, 4.0, -5.0).looking_at(Vec3::ZERO, Vec3::Y),
363 DespawnOnExit(CURRENT_SCENE),
364 ));
365
366 commands.spawn((
367 DirectionalLight {
368 color: BLUE.into(),
369 ..default()
370 },
371 Transform::IDENTITY.looking_to(Dir3::Z, Dir3::Y),
372 DespawnOnExit(CURRENT_SCENE),
373 ));
374
375 commands.spawn((
376 DirectionalLight {
377 color: RED.into(),
378 ..default()
379 },
380 Transform::IDENTITY.looking_to(Dir3::X, Dir3::Y),
381 DespawnOnExit(CURRENT_SCENE),
382 ));
383
384 commands.spawn((
385 DirectionalLight {
386 color: GREEN.into(),
387 ..default()
388 },
389 Transform::IDENTITY.looking_to(Dir3::NEG_Y, Dir3::X),
390 DespawnOnExit(CURRENT_SCENE),
391 ));
392
393 commands
394 .spawn((
395 SceneRoot(asset_server.load_with_settings(
396 GltfAssetLabel::Scene(0).from_asset("models/Faces/faces.glb"),
397 |s: &mut GltfLoaderSettings| {
398 s.use_model_forward_direction = Some(true);
399 },
400 )),
401 DespawnOnExit(CURRENT_SCENE),
402 ))
403 .observe(show_aabbs);
404 }211fn setup_parallax(
212 mut commands: Commands,
213 mut materials: ResMut<Assets<StandardMaterial>>,
214 mut meshes: ResMut<Assets<Mesh>>,
215 asset_server: Res<AssetServer>,
216) {
217 // The normal map. Note that to generate it in the GIMP image editor, you should
218 // open the depth map, and do Filters → Generic → Normal Map
219 // You should enable the "flip X" checkbox.
220 let normal_handle = asset_server.load_with_settings(
221 "textures/parallax_example/cube_normal.png",
222 // The normal map texture is in linear color space. Lighting won't look correct
223 // if `is_srgb` is `true`, which is the default.
224 |settings: &mut ImageLoaderSettings| settings.is_srgb = false,
225 );
226
227 let mut cube = Mesh::from(Cuboid::new(0.15, 0.15, 0.15));
228
229 // NOTE: for normal maps and depth maps to work, the mesh
230 // needs tangents generated.
231 cube.generate_tangents().unwrap();
232
233 let parallax_material = materials.add(StandardMaterial {
234 perceptual_roughness: 0.4,
235 base_color_texture: Some(asset_server.load("textures/parallax_example/cube_color.png")),
236 normal_map_texture: Some(normal_handle),
237 // The depth map is a grayscale texture where black is the highest level and
238 // white the lowest.
239 depth_map: Some(asset_server.load("textures/parallax_example/cube_depth.png")),
240 parallax_depth_scale: 0.09,
241 parallax_mapping_method: ParallaxMappingMethod::Relief { max_steps: 4 },
242 max_parallax_layer_count: ops::exp2(5.0f32),
243 ..default()
244 });
245 commands.spawn((
246 Mesh3d(meshes.add(cube)),
247 MeshMaterial3d(parallax_material),
248 Transform::from_xyz(0.4, 0.2, -0.8),
249 Spin { speed: 0.3 },
250 ));
251}180fn spawn_water(
181 commands: &mut Commands,
182 asset_server: &AssetServer,
183 meshes: &mut Assets<Mesh>,
184 water_materials: &mut Assets<ExtendedMaterial<StandardMaterial, Water>>,
185) {
186 commands.spawn((
187 Mesh3d(meshes.add(Plane3d::new(Vec3::Y, Vec2::splat(1.0)))),
188 MeshMaterial3d(water_materials.add(ExtendedMaterial {
189 base: StandardMaterial {
190 base_color: BLACK.into(),
191 perceptual_roughness: 0.0,
192 ..default()
193 },
194 extension: Water {
195 normals: asset_server.load_with_settings::<Image, ImageLoaderSettings>(
196 "textures/water_normals.png",
197 |settings| {
198 settings.is_srgb = false;
199 settings.sampler = ImageSampler::Descriptor(ImageSamplerDescriptor {
200 address_mode_u: ImageAddressMode::Repeat,
201 address_mode_v: ImageAddressMode::Repeat,
202 mag_filter: ImageFilterMode::Linear,
203 min_filter: ImageFilterMode::Linear,
204 ..default()
205 });
206 },
207 ),
208 // These water settings are just random values to create some
209 // variety.
210 settings: WaterSettings {
211 octave_vectors: [
212 vec4(0.080, 0.059, 0.073, -0.062),
213 vec4(0.153, 0.138, -0.149, -0.195),
214 ],
215 octave_scales: vec4(1.0, 2.1, 7.9, 14.9) * 5.0,
216 octave_strengths: vec4(0.16, 0.18, 0.093, 0.044),
217 },
218 },
219 })),
220 Transform::from_scale(Vec3::splat(100.0)),
221 ));
222}48fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
49 let bird_left = Bird::Normal;
50 let bird_right = Bird::Normal;
51 commands.spawn(Camera2d);
52
53 let texture_left = asset_server.load_with_settings(
54 bird_left.get_texture_path(),
55 // `RenderAssetUsages::all()` is already the default, so the line below could be omitted.
56 // It's helpful to know it exists, however.
57 //
58 // `RenderAssetUsages` tell Bevy whether to keep the data around:
59 // - for the GPU (`RenderAssetUsages::RENDER_WORLD`),
60 // - for the CPU (`RenderAssetUsages::MAIN_WORLD`),
61 // - or both.
62 // `RENDER_WORLD` is necessary to render the image, `MAIN_WORLD` is necessary to inspect
63 // and modify the image (via `ResMut<Assets<Image>>`).
64 //
65 // Since most games will not need to modify textures at runtime, many developers opt to pass
66 // only `RENDER_WORLD`. This is more memory efficient, as we don't need to keep the image in
67 // RAM. For this example however, this would not work, as we need to inspect and modify the
68 // image at runtime.
69 |settings: &mut ImageLoaderSettings| settings.asset_usage = RenderAssetUsages::all(),
70 );
71
72 commands.spawn((
73 Name::new("Bird Left"),
74 // This marker component ensures we can easily find either of the Birds by using With and
75 // Without query filters.
76 Left,
77 Sprite::from_image(texture_left),
78 Transform::from_xyz(-200.0, 0.0, 0.0),
79 bird_left,
80 ));
81
82 commands.spawn((
83 Name::new("Bird Right"),
84 // In contrast to the above, here we rely on the default `RenderAssetUsages` loader setting
85 Sprite::from_image(asset_server.load(bird_right.get_texture_path())),
86 Transform::from_xyz(200.0, 0.0, 0.0),
87 bird_right,
88 ));
89}Sourcepub fn load_with_settings_override<'a, A, S>(
&self,
path: impl Into<AssetPath<'a>>,
settings: impl Fn(&mut S) + Send + Sync + 'static,
) -> Handle<A>
pub fn load_with_settings_override<'a, A, S>( &self, path: impl Into<AssetPath<'a>>, settings: impl Fn(&mut S) + Send + Sync + 'static, ) -> Handle<A>
Same as load, but you can load assets from unapproved paths
if AssetPlugin::unapproved_path_mode
is Deny.
Sourcepub 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>
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>
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.
Sourcepub fn load_acquire_with_settings_override<'a, A, S, G>(
&self,
path: impl Into<AssetPath<'a>>,
settings: impl Fn(&mut S) + Send + Sync + 'static,
guard: G,
) -> Handle<A>
pub fn load_acquire_with_settings_override<'a, A, S, G>( &self, path: impl Into<AssetPath<'a>>, settings: impl Fn(&mut S) + Send + Sync + 'static, guard: G, ) -> Handle<A>
Same as load, but you can load assets from unapproved paths
if AssetPlugin::unapproved_path_mode
is Deny.
Sourcepub async fn load_untyped_async<'a>(
&self,
path: impl Into<AssetPath<'a>>,
) -> Result<UntypedHandle, AssetLoadError>
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.
Sourcepub fn load_untyped<'a>(
&self,
path: impl Into<AssetPath<'a>>,
) -> Handle<LoadedUntypedAsset>
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;
use bevy_ecs::resource::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.
Sourcepub fn reload<'a>(&self, path: impl Into<AssetPath<'a>>)
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.
Sourcepub fn add<A>(&self, asset: A) -> Handle<A>where
A: Asset,
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?
115fn decompress<T: Component + From<Handle<A>>, A: Asset>(
116 mut commands: Commands,
117 asset_server: Res<AssetServer>,
118 mut compressed_assets: ResMut<Assets<GzAsset>>,
119 query: Query<(Entity, &Compressed<A>)>,
120) {
121 for (entity, Compressed { compressed, .. }) in query.iter() {
122 let Some(GzAsset { uncompressed }) = compressed_assets.remove(compressed) else {
123 continue;
124 };
125
126 let uncompressed = uncompressed.take::<A>().unwrap();
127
128 commands
129 .entity(entity)
130 .remove::<Compressed<A>>()
131 .insert(T::from(asset_server.add(uncompressed)));
132 }
133}More examples
100fn setup(mut commands: Commands, asset_server: Res<AssetServer>, app_status: Res<AppStatus>) {
101 commands.spawn((
102 Camera3d::default(),
103 Transform::from_translation(CAMERA_INITIAL_POSITION).looking_at(Vec3::ZERO, Vec3::Y),
104 ));
105
106 spawn_directional_light(&mut commands);
107
108 commands.spawn((
109 SceneRoot(asset_server.load("models/AnisotropyBarnLamp/AnisotropyBarnLamp.gltf#Scene0")),
110 Transform::from_xyz(0.0, 0.07, -0.13),
111 Scene::BarnLamp,
112 ));
113
114 commands.spawn((
115 Mesh3d(
116 asset_server.add(
117 Mesh::from(Sphere::new(0.1))
118 .with_generated_tangents()
119 .unwrap(),
120 ),
121 ),
122 MeshMaterial3d(asset_server.add(StandardMaterial {
123 base_color: palettes::tailwind::GRAY_300.into(),
124 anisotropy_rotation: 0.5,
125 anisotropy_strength: 1.,
126 ..default()
127 })),
128 Scene::Sphere,
129 Visibility::Hidden,
130 ));
131
132 spawn_text(&mut commands, &app_status);
133}Sourcepub fn add_async<A, E>(
&self,
future: impl Future<Output = Result<A, E>> + Send + 'static,
) -> Handle<A>
pub fn add_async<A, E>( &self, future: impl Future<Output = Result<A, E>> + Send + 'static, ) -> Handle<A>
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, it will show up in the relevant Assets storage.
Sourcepub fn load_folder<'a>(
&self,
path: impl Into<AssetPath<'a>>,
) -> Handle<LoadedFolder>
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?
More examples
12fn setup(
13 mut commands: Commands,
14 asset_server: Res<AssetServer>,
15 meshes: Res<Assets<Mesh>>,
16 mut materials: ResMut<Assets<StandardMaterial>>,
17) {
18 // By default AssetServer will load assets from inside the "assets" folder.
19 // For example, the next line will load GltfAssetLabel::Primitive{mesh:0,primitive:0}.from_asset("ROOT/assets/models/cube/cube.gltf"),
20 // where "ROOT" is the directory of the Application.
21 //
22 // This can be overridden by setting [`AssetPlugin.file_path`].
23 let cube_handle = asset_server.load(
24 GltfAssetLabel::Primitive {
25 mesh: 0,
26 primitive: 0,
27 }
28 .from_asset("models/cube/cube.gltf"),
29 );
30 let sphere_handle = asset_server.load(
31 GltfAssetLabel::Primitive {
32 mesh: 0,
33 primitive: 0,
34 }
35 .from_asset("models/sphere/sphere.gltf"),
36 );
37
38 // All assets end up in their Assets<T> collection once they are done loading:
39 if let Some(sphere) = meshes.get(&sphere_handle) {
40 // You might notice that this doesn't run! This is because assets load in parallel without
41 // blocking. When an asset has loaded, it will appear in relevant Assets<T>
42 // collection.
43 info!("{:?}", sphere.primitive_topology());
44 } else {
45 info!("sphere hasn't loaded yet");
46 }
47
48 // You can load all assets in a folder like this. They will be loaded in parallel without
49 // blocking. The LoadedFolder asset holds handles to each asset in the folder. These are all
50 // dependencies of the LoadedFolder asset, meaning you can wait for the LoadedFolder asset to
51 // fire AssetEvent::LoadedWithDependencies if you want to wait for all assets in the folder
52 // to load.
53 // If you want to keep the assets in the folder alive, make sure you store the returned handle
54 // somewhere.
55 let _loaded_folder: Handle<LoadedFolder> = asset_server.load_folder("models/torus");
56
57 // If you want a handle to a specific asset in a loaded folder, the easiest way to get one is to call load.
58 // It will _not_ be loaded a second time.
59 // The LoadedFolder asset will ultimately also hold handles to the assets, but waiting for it to load
60 // and finding the right handle is more work!
61 let torus_handle = asset_server.load(
62 GltfAssetLabel::Primitive {
63 mesh: 0,
64 primitive: 0,
65 }
66 .from_asset("models/torus/torus.gltf"),
67 );
68
69 // You can also add assets directly to their Assets<T> storage:
70 let material_handle = materials.add(StandardMaterial {
71 base_color: Color::srgb(0.8, 0.7, 0.6),
72 ..default()
73 });
74
75 // torus
76 commands.spawn((
77 Mesh3d(torus_handle),
78 MeshMaterial3d(material_handle.clone()),
79 Transform::from_xyz(-3.0, 0.0, 0.0),
80 ));
81 // cube
82 commands.spawn((
83 Mesh3d(cube_handle),
84 MeshMaterial3d(material_handle.clone()),
85 Transform::from_xyz(0.0, 0.0, 0.0),
86 ));
87 // sphere
88 commands.spawn((
89 Mesh3d(sphere_handle),
90 MeshMaterial3d(material_handle),
91 Transform::from_xyz(3.0, 0.0, 0.0),
92 ));
93 // light
94 commands.spawn((PointLight::default(), Transform::from_xyz(4.0, 5.0, 4.0)));
95 // camera
96 commands.spawn((
97 Camera3d::default(),
98 Transform::from_xyz(0.0, 3.0, 10.0).looking_at(Vec3::ZERO, Vec3::Y),
99 ));
100}Sourcepub fn get_load_states(
&self,
id: impl Into<UntypedAssetId>,
) -> Option<(LoadState, DependencyLoadState, RecursiveDependencyLoadState)>
pub fn get_load_states( &self, id: impl Into<UntypedAssetId>, ) -> Option<(LoadState, DependencyLoadState, RecursiveDependencyLoadState)>
Retrieves all loads states for the given asset id.
Sourcepub fn get_load_state(&self, id: impl Into<UntypedAssetId>) -> Option<LoadState>
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 get the load state of
its dependencies or recursive dependencies, see AssetServer::get_dependency_load_state
and AssetServer::get_recursive_dependency_load_state respectively.
Sourcepub fn get_dependency_load_state(
&self,
id: impl Into<UntypedAssetId>,
) -> Option<DependencyLoadState>
pub fn get_dependency_load_state( &self, id: impl Into<UntypedAssetId>, ) -> Option<DependencyLoadState>
Retrieves the DependencyLoadState of a given asset id’s dependencies.
Note that this is only the load state of direct dependencies of the root asset. To get
the load state of the root asset itself or its recursive dependencies, see
AssetServer::get_load_state and AssetServer::get_recursive_dependency_load_state respectively.
Sourcepub fn get_recursive_dependency_load_state(
&self,
id: impl Into<UntypedAssetId>,
) -> Option<RecursiveDependencyLoadState>
pub fn get_recursive_dependency_load_state( &self, id: impl Into<UntypedAssetId>, ) -> Option<RecursiveDependencyLoadState>
Retrieves the main RecursiveDependencyLoadState of a given asset id’s recursive dependencies.
Note that this is only the load state of recursive dependencies of the root asset. To get
the load state of the root asset itself or its direct dependencies only, see
AssetServer::get_load_state and AssetServer::get_dependency_load_state respectively.
Examples found in repository?
196fn update_loading_data(
197 mut loading_data: ResMut<LoadingData>,
198 mut loading_state: ResMut<LoadingState>,
199 asset_server: Res<AssetServer>,
200 pipelines_ready: Res<PipelinesReady>,
201) {
202 if !loading_data.loading_assets.is_empty() || !pipelines_ready.0 {
203 // If we are still loading assets / pipelines are not fully compiled,
204 // we reset the confirmation frame count.
205 loading_data.confirmation_frames_count = 0;
206
207 loading_data.loading_assets.retain(|asset| {
208 asset_server
209 .get_recursive_dependency_load_state(asset)
210 .is_none_or(|state| !state.is_loaded())
211 });
212
213 // If there are no more assets being monitored, and pipelines
214 // are compiled, then start counting confirmation frames.
215 // Once enough confirmations have passed, everything will be
216 // considered to be fully loaded.
217 } else {
218 loading_data.confirmation_frames_count += 1;
219 if loading_data.confirmation_frames_count == loading_data.confirmation_frames_target {
220 *loading_state = LoadingState::LevelReady;
221 }
222 }
223}Sourcepub fn load_state(&self, id: impl Into<UntypedAssetId>) -> LoadState
pub fn load_state(&self, id: impl Into<UntypedAssetId>) -> LoadState
Retrieves the main LoadState of a given asset id.
This is the same as AssetServer::get_load_state except the result is unwrapped. If
the result is None, LoadState::NotLoaded is returned.
Examples found in repository?
127fn environment_map_load_finish(
128 mut commands: Commands,
129 asset_server: Res<AssetServer>,
130 environment_map: Single<&EnvironmentMapLight>,
131 label_entity: Option<Single<Entity, With<EnvironmentMapLabel>>>,
132) {
133 if asset_server
134 .load_state(&environment_map.diffuse_map)
135 .is_loaded()
136 && asset_server
137 .load_state(&environment_map.specular_map)
138 .is_loaded()
139 {
140 // Do not attempt to remove `label_entity` if it has already been removed.
141 if let Some(label_entity) = label_entity {
142 commands.entity(*label_entity).despawn();
143 }
144 }
145}More examples
148fn asset_loaded(
149 asset_server: Res<AssetServer>,
150 mut images: ResMut<Assets<Image>>,
151 mut cubemap: ResMut<Cubemap>,
152 mut skyboxes: Query<&mut Skybox>,
153) {
154 if !cubemap.is_loaded && asset_server.load_state(&cubemap.image_handle).is_loaded() {
155 info!("Swapping to {}...", CUBEMAPS[cubemap.index].0);
156 let image = images.get_mut(&cubemap.image_handle).unwrap();
157 // NOTE: PNGs do not have any metadata that could indicate they contain a cubemap texture,
158 // so they appear as one texture. The following code reconfigures the texture as necessary.
159 if image.texture_descriptor.array_layer_count() == 1 {
160 image.reinterpret_stacked_2d_as_array(image.height() / image.width());
161 image.texture_view_descriptor = Some(TextureViewDescriptor {
162 dimension: Some(TextureViewDimension::Cube),
163 ..default()
164 });
165 }
166
167 for mut skybox in &mut skyboxes {
168 skybox.image = cubemap.image_handle.clone();
169 }
170
171 cubemap.is_loaded = true;
172 }
173}48fn create_array_texture(
49 mut commands: Commands,
50 asset_server: Res<AssetServer>,
51 mut loading_texture: ResMut<LoadingTexture>,
52 mut images: ResMut<Assets<Image>>,
53 mut meshes: ResMut<Assets<Mesh>>,
54 mut materials: ResMut<Assets<ArrayTextureMaterial>>,
55) {
56 if loading_texture.is_loaded
57 || !asset_server
58 .load_state(loading_texture.handle.id())
59 .is_loaded()
60 {
61 return;
62 }
63 loading_texture.is_loaded = true;
64 let image = images.get_mut(&loading_texture.handle).unwrap();
65
66 // Create a new array texture asset from the loaded texture.
67 let array_layers = 4;
68 image.reinterpret_stacked_2d_as_array(array_layers);
69
70 // Spawn some cubes using the array texture
71 let mesh_handle = meshes.add(Cuboid::default());
72 let material_handle = materials.add(ArrayTextureMaterial {
73 array_texture: loading_texture.handle.clone(),
74 });
75 for x in -5..=5 {
76 commands.spawn((
77 Mesh3d(mesh_handle.clone()),
78 MeshMaterial3d(material_handle.clone()),
79 Transform::from_xyz(x as f32 + 0.5, 0.0, 0.0),
80 ));
81 }
82}Sourcepub fn dependency_load_state(
&self,
id: impl Into<UntypedAssetId>,
) -> DependencyLoadState
pub fn dependency_load_state( &self, id: impl Into<UntypedAssetId>, ) -> DependencyLoadState
Retrieves the DependencyLoadState of a given asset id.
This is the same as AssetServer::get_dependency_load_state except the result is unwrapped. If
the result is None, DependencyLoadState::NotLoaded is returned.
Sourcepub fn recursive_dependency_load_state(
&self,
id: impl Into<UntypedAssetId>,
) -> RecursiveDependencyLoadState
pub fn recursive_dependency_load_state( &self, id: impl Into<UntypedAssetId>, ) -> RecursiveDependencyLoadState
Retrieves the RecursiveDependencyLoadState of a given asset id.
This is the same as AssetServer::get_recursive_dependency_load_state except the result is unwrapped. If
the result is None, RecursiveDependencyLoadState::NotLoaded is returned.
Sourcepub fn is_loaded(&self, id: impl Into<UntypedAssetId>) -> bool
pub fn is_loaded(&self, id: impl Into<UntypedAssetId>) -> bool
Convenience method that returns true if the asset has been loaded.
Sourcepub fn is_loaded_with_direct_dependencies(
&self,
id: impl Into<UntypedAssetId>,
) -> bool
pub fn is_loaded_with_direct_dependencies( &self, id: impl Into<UntypedAssetId>, ) -> bool
Convenience method that returns true if the asset and all of its direct dependencies have been loaded.
Sourcepub fn is_loaded_with_dependencies(&self, id: impl Into<UntypedAssetId>) -> bool
pub fn is_loaded_with_dependencies(&self, id: impl Into<UntypedAssetId>) -> bool
Convenience method that returns true if the asset, all of its dependencies, and all of its recursive dependencies have been loaded.
Sourcepub fn get_handle<'a, A>(
&self,
path: impl Into<AssetPath<'a>>,
) -> Option<Handle<A>>where
A: Asset,
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?
50fn setup(
51 mut commands: Commands,
52 rpg_sprite_handles: Res<RpgSpriteFolder>,
53 asset_server: Res<AssetServer>,
54 mut texture_atlases: ResMut<Assets<TextureAtlasLayout>>,
55 loaded_folders: Res<Assets<LoadedFolder>>,
56 mut textures: ResMut<Assets<Image>>,
57) {
58 let loaded_folder = loaded_folders.get(&rpg_sprite_handles.0).unwrap();
59
60 // Create texture atlases with different padding and sampling
61
62 let (texture_atlas_linear, linear_sources, linear_texture) = create_texture_atlas(
63 loaded_folder,
64 None,
65 Some(ImageSampler::linear()),
66 &mut textures,
67 );
68 let atlas_linear_handle = texture_atlases.add(texture_atlas_linear);
69
70 let (texture_atlas_nearest, nearest_sources, nearest_texture) = create_texture_atlas(
71 loaded_folder,
72 None,
73 Some(ImageSampler::nearest()),
74 &mut textures,
75 );
76 let atlas_nearest_handle = texture_atlases.add(texture_atlas_nearest);
77
78 let (texture_atlas_linear_padded, linear_padded_sources, linear_padded_texture) =
79 create_texture_atlas(
80 loaded_folder,
81 Some(UVec2::new(6, 6)),
82 Some(ImageSampler::linear()),
83 &mut textures,
84 );
85 let atlas_linear_padded_handle = texture_atlases.add(texture_atlas_linear_padded.clone());
86
87 let (texture_atlas_nearest_padded, nearest_padded_sources, nearest_padded_texture) =
88 create_texture_atlas(
89 loaded_folder,
90 Some(UVec2::new(6, 6)),
91 Some(ImageSampler::nearest()),
92 &mut textures,
93 );
94 let atlas_nearest_padded_handle = texture_atlases.add(texture_atlas_nearest_padded);
95
96 commands.spawn(Camera2d);
97
98 // Padded textures are to the right, unpadded to the left
99
100 // Draw unpadded texture atlas
101 commands.spawn((
102 Sprite::from_image(linear_texture.clone()),
103 Transform {
104 translation: Vec3::new(-250.0, -160.0, 0.0),
105 scale: Vec3::splat(0.5),
106 ..default()
107 },
108 ));
109
110 // Draw padded texture atlas
111 commands.spawn((
112 Sprite::from_image(linear_padded_texture.clone()),
113 Transform {
114 translation: Vec3::new(250.0, -160.0, 0.0),
115 scale: Vec3::splat(0.5),
116 ..default()
117 },
118 ));
119
120 let font = asset_server.load("fonts/FiraSans-Bold.ttf");
121
122 // Padding label text style
123 let text_style: TextFont = TextFont {
124 font: font.clone(),
125 font_size: 42.0,
126 ..default()
127 };
128
129 // Labels to indicate padding
130
131 // No padding
132 create_label(
133 &mut commands,
134 (-250.0, 250.0, 0.0),
135 "No padding",
136 text_style.clone(),
137 );
138
139 // Padding
140 create_label(&mut commands, (250.0, 250.0, 0.0), "Padding", text_style);
141
142 // Get handle to a sprite to render
143 let vendor_handle: Handle<Image> = asset_server
144 .get_handle("textures/rpg/chars/vendor/generic-rpg-vendor.png")
145 .unwrap();
146
147 // Configuration array to render sprites through iteration
148 let configurations: [(
149 &str,
150 Handle<TextureAtlasLayout>,
151 TextureAtlasSources,
152 Handle<Image>,
153 f32,
154 ); 4] = [
155 (
156 "Linear",
157 atlas_linear_handle,
158 linear_sources,
159 linear_texture,
160 -350.0,
161 ),
162 (
163 "Nearest",
164 atlas_nearest_handle,
165 nearest_sources,
166 nearest_texture,
167 -150.0,
168 ),
169 (
170 "Linear",
171 atlas_linear_padded_handle,
172 linear_padded_sources,
173 linear_padded_texture,
174 150.0,
175 ),
176 (
177 "Nearest",
178 atlas_nearest_padded_handle,
179 nearest_padded_sources,
180 nearest_padded_texture,
181 350.0,
182 ),
183 ];
184
185 // Label text style
186 let sampling_label_style = TextFont {
187 font,
188 font_size: 25.0,
189 ..default()
190 };
191
192 let base_y = 80.0; // y position of the sprites
193
194 for (sampling, atlas_handle, atlas_sources, atlas_texture, x) in configurations {
195 // Render a sprite from the texture_atlas
196 create_sprite_from_atlas(
197 &mut commands,
198 (x, base_y, 0.0),
199 atlas_texture,
200 atlas_sources,
201 atlas_handle,
202 &vendor_handle,
203 );
204
205 // Render a label to indicate the sampling setting
206 create_label(
207 &mut commands,
208 (x, base_y + 110.0, 0.0), // Offset to y position of the sprite
209 sampling,
210 sampling_label_style.clone(),
211 );
212 }
213}Sourcepub fn get_id_handle<A>(&self, id: AssetId<A>) -> Option<Handle<A>>where
A: Asset,
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.
Sourcepub fn get_id_handle_untyped(&self, id: UntypedAssetId) -> Option<UntypedHandle>
pub fn get_id_handle_untyped(&self, id: UntypedAssetId) -> Option<UntypedHandle>
Get an UntypedHandle from an UntypedAssetId.
See AssetServer::get_id_handle for details.
Sourcepub fn is_managed(&self, id: impl Into<UntypedAssetId>) -> bool
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.
Sourcepub fn get_path_id<'a>(
&self,
path: impl Into<AssetPath<'a>>,
) -> Option<UntypedAssetId>
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.
Sourcepub fn get_path_ids<'a>(
&self,
path: impl Into<AssetPath<'a>>,
) -> Vec<UntypedAssetId>
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.
Sourcepub fn get_handle_untyped<'a>(
&self,
path: impl Into<AssetPath<'a>>,
) -> Option<UntypedHandle>
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.
Sourcepub fn get_handles_untyped<'a>(
&self,
path: impl Into<AssetPath<'a>>,
) -> Vec<UntypedHandle>
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.
Sourcepub fn get_path_and_type_id_handle(
&self,
path: &AssetPath<'_>,
type_id: TypeId,
) -> Option<UntypedHandle>
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”.
Sourcepub fn get_path(&self, id: impl Into<UntypedAssetId>) -> Option<AssetPath<'_>>
pub fn get_path(&self, id: impl Into<UntypedAssetId>) -> Option<AssetPath<'_>>
Returns the path for the given id, if it has one.
Examples found in repository?
80fn name_morphs(
81 asset_server: Res<AssetServer>,
82 mut events: MessageReader<AssetEvent<Mesh>>,
83 meshes: Res<Assets<Mesh>>,
84) {
85 for event in events.read() {
86 if let AssetEvent::<Mesh>::Added { id } = event
87 && let Some(path) = asset_server.get_path(*id)
88 && let Some(mesh) = meshes.get(*id)
89 && let Some(names) = mesh.morph_target_names()
90 {
91 info!("Morph target names for {path:?}:");
92
93 for name in names {
94 info!(" {name}");
95 }
96 }
97 }
98}Sourcepub fn mode(&self) -> AssetServerMode
pub fn mode(&self) -> AssetServerMode
Returns the AssetServerMode this server is currently in.
Sourcepub fn preregister_loader<L>(&self, extensions: &[&str])where
L: AssetLoader,
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.
Sourcepub async fn wait_for_asset<A>(
&self,
handle: &Handle<A>,
) -> Result<(), WaitForAssetError>where
A: Asset,
pub async fn wait_for_asset<A>(
&self,
handle: &Handle<A>,
) -> Result<(), WaitForAssetError>where
A: Asset,
Returns a future that will suspend until the specified asset and its dependencies finish loading.
§Errors
This will return an error if the asset or any of its dependencies fail to load, or if the asset has not been queued up to be loaded.
Sourcepub async fn wait_for_asset_untyped(
&self,
handle: &UntypedHandle,
) -> Result<(), WaitForAssetError>
pub async fn wait_for_asset_untyped( &self, handle: &UntypedHandle, ) -> Result<(), WaitForAssetError>
Returns a future that will suspend until the specified asset and its dependencies finish loading.
§Errors
This will return an error if the asset or any of its dependencies fail to load, or if the asset has not been queued up to be loaded.
Sourcepub async fn wait_for_asset_id(
&self,
id: impl Into<UntypedAssetId>,
) -> Result<(), WaitForAssetError>
pub async fn wait_for_asset_id( &self, id: impl Into<UntypedAssetId>, ) -> Result<(), WaitForAssetError>
Returns a future that will suspend until the specified asset and its dependencies finish loading.
Note that since an asset ID does not count as a reference to the asset, the future returned from this method will not keep the asset alive. This may lead to the asset unexpectedly being dropped while you are waiting for it to finish loading.
When calling this method, make sure a strong handle is stored elsewhere to prevent the
asset from being dropped.
If you have access to an asset’s strong Handle, you should prefer to call
AssetServer::wait_for_asset
or wait_for_asset_untyped to ensure the asset finishes
loading.
§Errors
This will return an error if the asset or any of its dependencies fail to load, or if the asset has not been queued up to be loaded.
Sourcepub async fn write_default_loader_meta_file_for_path(
&self,
path: impl Into<AssetPath<'_>>,
) -> Result<(), WriteDefaultMetaError>
pub async fn write_default_loader_meta_file_for_path( &self, path: impl Into<AssetPath<'_>>, ) -> Result<(), WriteDefaultMetaError>
Writes the default loader meta file for the provided path.
This function only generates meta files that simply load the path directly. To generate a
meta file that will use the default asset processor for the path, see
AssetProcessor::write_default_meta_file_for_path.
Note if there is already a meta file for path, this function returns
Err(WriteDefaultMetaError::MetaAlreadyExists).
Trait Implementations§
Source§impl Clone for AssetServer
impl Clone for AssetServer
Source§fn clone(&self) -> AssetServer
fn clone(&self) -> AssetServer
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for AssetServer
impl Debug for AssetServer
Source§impl GetAssetServer for AssetServer
impl GetAssetServer for AssetServer
fn get_asset_server(&self) -> &AssetServer
impl Resource for AssetServer
Auto Trait Implementations§
impl Freeze for AssetServer
impl !RefUnwindSafe for AssetServer
impl Send for AssetServer
impl Sync for AssetServer
impl Unpin for AssetServer
impl !UnwindSafe for AssetServer
Blanket Implementations§
Source§impl<T, U> AsBindGroupShaderType<U> for T
impl<T, U> AsBindGroupShaderType<U> for T
Source§fn as_bind_group_shader_type(&self, _images: &RenderAssets<GpuImage>) -> U
fn as_bind_group_shader_type(&self, _images: &RenderAssets<GpuImage>) -> U
T ShaderType for self. When used in AsBindGroup
derives, it is safe to assume that all images in self exist.Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
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.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSend for T
impl<T> DowncastSend for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<T> FmtForward for T
impl<T> FmtForward for T
Source§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.Source§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.Source§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.Source§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.Source§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.Source§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.Source§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.Source§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.Source§impl<S> FromSample<S> for S
impl<S> FromSample<S> for S
fn from_sample_(s: S) -> S
Source§impl<T, W> HasTypeWitness<W> for Twhere
W: MakeTypeWitness<Arg = T>,
T: ?Sized,
impl<T, W> HasTypeWitness<W> for Twhere
W: MakeTypeWitness<Arg = T>,
T: ?Sized,
Source§impl<T> Identity for Twhere
T: ?Sized,
impl<T> Identity for Twhere
T: ?Sized,
Source§impl<T> InitializeFromFunction<T> for T
impl<T> InitializeFromFunction<T> for T
Source§fn initialize_from_function(f: fn() -> T) -> T
fn initialize_from_function(f: fn() -> T) -> T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
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 moreSource§impl<T> IntoResult<T> for T
impl<T> IntoResult<T> for T
Source§fn into_result(self) -> Result<T, RunSystemError>
fn into_result(self) -> Result<T, RunSystemError>
Source§impl<F, T> IntoSample<T> for Fwhere
T: FromSample<F>,
impl<F, T> IntoSample<T> for Fwhere
T: FromSample<F>,
fn into_sample(self) -> T
Source§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
Source§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
Source§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
Source§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
Source§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.Source§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.Source§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.Source§impl<T> Pointable for T
impl<T> Pointable for T
Source§impl<Ret> SpawnIfAsync<(), Ret> for Ret
impl<Ret> SpawnIfAsync<(), Ret> for Ret
Source§impl<T, O> SuperFrom<T> for Owhere
O: From<T>,
impl<T, O> SuperFrom<T> for Owhere
O: From<T>,
Source§fn super_from(input: T) -> O
fn super_from(input: T) -> O
Source§impl<T, O, M> SuperInto<O, M> for Twhere
O: SuperFrom<T, M>,
impl<T, O, M> SuperInto<O, M> for Twhere
O: SuperFrom<T, M>,
Source§fn super_into(self) -> O
fn super_into(self) -> O
Source§impl<T> Tap for T
impl<T> Tap for T
Source§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read moreSource§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read moreSource§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read moreSource§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read moreSource§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.Source§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.Source§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.Source§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.Source§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.