Add methods to directly load assets from World (#12023)
# Objective `FromWorld` is often used to group loading and creation of assets for resources. With this setup, users often end up repetitively calling `.resource::<AssetServer>` and `.resource_mut::<Assets<T>>`, and may have difficulties handling lifetimes of the returned references. ## Solution Add extension methods to `World` to add and load assets, through a new extension trait defined in `bevy_asset`. ### Other considerations * This might be a bit too "magic", as it makes implicit the resource access. * We could also implement `DirectAssetAccessExt` on `App`, but it didn't feel necessary, as `FromWorld` is the principal use-case here. --- ## Changelog * Add the `DirectAssetAccessExt` trait, which adds the `add_asset`, `load_asset` and `load_asset_with_settings` method to the `World` type.
This commit is contained in:
parent
5860e01432
commit
f7f7e326e5
50
crates/bevy_asset/src/direct_access_ext.rs
Normal file
50
crates/bevy_asset/src/direct_access_ext.rs
Normal file
@ -0,0 +1,50 @@
|
||||
//! Add methods on `World` to simplify loading assets when all
|
||||
//! you have is a `World`.
|
||||
|
||||
use bevy_ecs::world::World;
|
||||
|
||||
use crate::{meta::Settings, Asset, AssetPath, AssetServer, Assets, Handle};
|
||||
|
||||
pub trait DirectAssetAccessExt {
|
||||
/// Insert an asset similarly to [`Assets::add`].
|
||||
fn add_asset<A: Asset>(&mut self, asset: impl Into<A>) -> Handle<A>;
|
||||
|
||||
/// Load an asset similarly to [`AssetServer::load`].
|
||||
fn load_asset<'a, A: Asset>(&self, path: impl Into<AssetPath<'a>>) -> Handle<A>;
|
||||
|
||||
/// Load an asset with settings, similarly to [`AssetServer::load_with_settings`].
|
||||
fn load_asset_with_settings<'a, A: Asset, S: Settings>(
|
||||
&self,
|
||||
path: impl Into<AssetPath<'a>>,
|
||||
settings: impl Fn(&mut S) + Send + Sync + 'static,
|
||||
) -> Handle<A>;
|
||||
}
|
||||
impl DirectAssetAccessExt for World {
|
||||
/// Insert an asset similarly to [`Assets::add`].
|
||||
///
|
||||
/// # Panics
|
||||
/// If `self` doesn't have an [`AssetServer`] resource initialized yet.
|
||||
fn add_asset<'a, A: Asset>(&mut self, asset: impl Into<A>) -> Handle<A> {
|
||||
self.resource_mut::<Assets<A>>().add(asset)
|
||||
}
|
||||
|
||||
/// Load an asset similarly to [`AssetServer::load`].
|
||||
///
|
||||
/// # Panics
|
||||
/// If `self` doesn't have an [`AssetServer`] resource initialized yet.
|
||||
fn load_asset<'a, A: Asset>(&self, path: impl Into<AssetPath<'a>>) -> Handle<A> {
|
||||
self.resource::<AssetServer>().load(path)
|
||||
}
|
||||
/// Load an asset with settings, similarly to [`AssetServer::load_with_settings`].
|
||||
///
|
||||
/// # Panics
|
||||
/// If `self` doesn't have an [`AssetServer`] resource initialized yet.
|
||||
fn load_asset_with_settings<'a, A: Asset, S: Settings>(
|
||||
&self,
|
||||
path: impl Into<AssetPath<'a>>,
|
||||
settings: impl Fn(&mut S) + Send + Sync + 'static,
|
||||
) -> Handle<A> {
|
||||
self.resource::<AssetServer>()
|
||||
.load_with_settings(path, settings)
|
||||
}
|
||||
}
|
@ -10,12 +10,13 @@ pub mod transformer;
|
||||
pub mod prelude {
|
||||
#[doc(hidden)]
|
||||
pub use crate::{
|
||||
Asset, AssetApp, AssetEvent, AssetId, AssetMode, AssetPlugin, AssetServer, Assets, Handle,
|
||||
UntypedHandle,
|
||||
Asset, AssetApp, AssetEvent, AssetId, AssetMode, AssetPlugin, AssetServer, Assets,
|
||||
DirectAssetAccessExt, Handle, UntypedHandle,
|
||||
};
|
||||
}
|
||||
|
||||
mod assets;
|
||||
mod direct_access_ext;
|
||||
mod event;
|
||||
mod folder;
|
||||
mod handle;
|
||||
@ -27,6 +28,7 @@ mod server;
|
||||
|
||||
pub use assets::*;
|
||||
pub use bevy_asset_macros::Asset;
|
||||
pub use direct_access_ext::DirectAssetAccessExt;
|
||||
pub use event::*;
|
||||
pub use folder::*;
|
||||
pub use futures_lite::{AsyncReadExt, AsyncWriteExt};
|
||||
|
@ -512,36 +512,19 @@ fn handle_mouse_clicks(
|
||||
|
||||
impl FromWorld for ExampleAssets {
|
||||
fn from_world(world: &mut World) -> Self {
|
||||
// Load all the assets.
|
||||
let asset_server = world.resource::<AssetServer>();
|
||||
let fox = asset_server.load("models/animated/Fox.glb#Scene0");
|
||||
let main_scene =
|
||||
asset_server.load("models/IrradianceVolumeExample/IrradianceVolumeExample.glb#Scene0");
|
||||
let irradiance_volume = asset_server.load::<Image>("irradiance_volumes/Example.vxgi.ktx2");
|
||||
let fox_animation =
|
||||
asset_server.load::<AnimationClip>("models/animated/Fox.glb#Animation1");
|
||||
|
||||
// Just use a specular map for the skybox since it's not too blurry.
|
||||
// In reality you wouldn't do this--you'd use a real skybox texture--but
|
||||
// reusing the textures like this saves space in the Bevy repository.
|
||||
let skybox = asset_server.load::<Image>("environment_maps/pisa_specular_rgb9e5_zstd.ktx2");
|
||||
|
||||
let mut mesh_assets = world.resource_mut::<Assets<Mesh>>();
|
||||
let main_sphere = mesh_assets.add(Sphere::default().mesh().uv(32, 18));
|
||||
let voxel_cube = mesh_assets.add(Cuboid::default());
|
||||
|
||||
let mut standard_material_assets = world.resource_mut::<Assets<StandardMaterial>>();
|
||||
let main_material = standard_material_assets.add(LegacyColor::SILVER);
|
||||
|
||||
ExampleAssets {
|
||||
main_sphere,
|
||||
fox,
|
||||
main_sphere_material: main_material,
|
||||
main_scene,
|
||||
irradiance_volume,
|
||||
fox_animation,
|
||||
voxel_cube,
|
||||
skybox,
|
||||
main_sphere: world.add_asset(Sphere::default().mesh().uv(32, 18)),
|
||||
fox: world.load_asset("models/animated/Fox.glb#Scene0"),
|
||||
main_sphere_material: world.add_asset(LegacyColor::SILVER),
|
||||
main_scene: world
|
||||
.load_asset("models/IrradianceVolumeExample/IrradianceVolumeExample.glb#Scene0"),
|
||||
irradiance_volume: world.load_asset("irradiance_volumes/Example.vxgi.ktx2"),
|
||||
fox_animation: world.load_asset("models/animated/Fox.glb#Animation1"),
|
||||
voxel_cube: world.add_asset(Cuboid::default()),
|
||||
// Just use a specular map for the skybox since it's not too blurry.
|
||||
// In reality you wouldn't do this--you'd use a real skybox texture--but
|
||||
// reusing the textures like this saves space in the Bevy repository.
|
||||
skybox: world.load_asset("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -331,17 +331,15 @@ fn rotate_camera(
|
||||
// Loads the cubemaps from the assets directory.
|
||||
impl FromWorld for Cubemaps {
|
||||
fn from_world(world: &mut World) -> Self {
|
||||
let asset_server = world.resource::<AssetServer>();
|
||||
|
||||
// Just use the specular map for the skybox since it's not too blurry.
|
||||
// In reality you wouldn't do this--you'd use a real skybox texture--but
|
||||
// reusing the textures like this saves space in the Bevy repository.
|
||||
let specular_map = asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2");
|
||||
let specular_map = world.load_asset("environment_maps/pisa_specular_rgb9e5_zstd.ktx2");
|
||||
|
||||
Cubemaps {
|
||||
diffuse: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
|
||||
specular_reflection_probe: asset_server
|
||||
.load("environment_maps/cubes_reflection_probe_specular_rgb9e5_zstd.ktx2"),
|
||||
diffuse: world.load_asset("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
|
||||
specular_reflection_probe: world
|
||||
.load_asset("environment_maps/cubes_reflection_probe_specular_rgb9e5_zstd.ktx2"),
|
||||
specular_environment_map: specular_map.clone(),
|
||||
skybox: specular_map,
|
||||
}
|
||||
|
@ -131,9 +131,7 @@ impl FromWorld for GameOfLifePipeline {
|
||||
fn from_world(world: &mut World) -> Self {
|
||||
let render_device = world.resource::<RenderDevice>();
|
||||
let texture_bind_group_layout = GameOfLifeImage::bind_group_layout(render_device);
|
||||
let shader = world
|
||||
.resource::<AssetServer>()
|
||||
.load("shaders/game_of_life.wgsl");
|
||||
let shader = world.load_asset("shaders/game_of_life.wgsl");
|
||||
let pipeline_cache = world.resource::<PipelineCache>();
|
||||
let init_pipeline = pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor {
|
||||
label: None,
|
||||
|
@ -248,9 +248,7 @@ impl FromWorld for PostProcessPipeline {
|
||||
let sampler = render_device.create_sampler(&SamplerDescriptor::default());
|
||||
|
||||
// Get the shader handle
|
||||
let shader = world
|
||||
.resource::<AssetServer>()
|
||||
.load("shaders/post_processing.wgsl");
|
||||
let shader = world.load_asset("shaders/post_processing.wgsl");
|
||||
|
||||
let pipeline_id = world
|
||||
.resource_mut::<PipelineCache>()
|
||||
|
@ -182,13 +182,10 @@ struct CustomPipeline {
|
||||
|
||||
impl FromWorld for CustomPipeline {
|
||||
fn from_world(world: &mut World) -> Self {
|
||||
let asset_server = world.resource::<AssetServer>();
|
||||
let shader = asset_server.load("shaders/instancing.wgsl");
|
||||
|
||||
let mesh_pipeline = world.resource::<MeshPipeline>();
|
||||
|
||||
CustomPipeline {
|
||||
shader,
|
||||
shader: world.load_asset("shaders/instancing.wgsl"),
|
||||
mesh_pipeline: mesh_pipeline.clone(),
|
||||
}
|
||||
}
|
||||
|
@ -52,10 +52,9 @@ struct ButtonMaterials {
|
||||
}
|
||||
impl FromWorld for ButtonMaterials {
|
||||
fn from_world(world: &mut World) -> Self {
|
||||
let mut materials = world.resource_mut::<Assets<ColorMaterial>>();
|
||||
Self {
|
||||
normal: materials.add(NORMAL_BUTTON_COLOR),
|
||||
active: materials.add(ACTIVE_BUTTON_COLOR),
|
||||
normal: world.add_asset(NORMAL_BUTTON_COLOR),
|
||||
active: world.add_asset(ACTIVE_BUTTON_COLOR),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -68,12 +67,13 @@ struct ButtonMeshes {
|
||||
}
|
||||
impl FromWorld for ButtonMeshes {
|
||||
fn from_world(world: &mut World) -> Self {
|
||||
let mut meshes = world.resource_mut::<Assets<Mesh>>();
|
||||
Self {
|
||||
circle: meshes.add(Circle::new(BUTTON_RADIUS)).into(),
|
||||
triangle: meshes.add(RegularPolygon::new(BUTTON_RADIUS, 3)).into(),
|
||||
start_pause: meshes.add(Rectangle::from_size(START_SIZE)).into(),
|
||||
trigger: meshes.add(Rectangle::from_size(TRIGGER_SIZE)).into(),
|
||||
circle: world.add_asset(Circle::new(BUTTON_RADIUS)).into(),
|
||||
triangle: world
|
||||
.add_asset(RegularPolygon::new(BUTTON_RADIUS, 3))
|
||||
.into(),
|
||||
start_pause: world.add_asset(Rectangle::from_size(START_SIZE)).into(),
|
||||
trigger: world.add_asset(Rectangle::from_size(TRIGGER_SIZE)).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user