diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index 71bd60b94f..d6db4b988a 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -27,6 +27,7 @@ mod folder; mod handle; mod id; mod loader; +mod loader_builders; mod path; mod reflect; mod server; @@ -40,6 +41,9 @@ pub use futures_lite::{AsyncReadExt, AsyncWriteExt}; pub use handle::*; pub use id::*; pub use loader::*; +pub use loader_builders::{ + DirectNestedLoader, NestedLoader, UntypedDirectNestedLoader, UntypedNestedLoader, +}; pub use path::*; pub use reflect::*; pub use server::*; @@ -511,7 +515,9 @@ mod tests { let mut embedded = String::new(); for dep in ron.embedded_dependencies { let loaded = load_context - .load_direct::(&dep) + .loader() + .direct() + .load::(&dep) .await .map_err(|_| Self::Error::CannotLoadDependency { dependency: dep.into(), diff --git a/crates/bevy_asset/src/loader.rs b/crates/bevy_asset/src/loader.rs index 03530d191b..4c27dd1e57 100644 --- a/crates/bevy_asset/src/loader.rs +++ b/crates/bevy_asset/src/loader.rs @@ -1,12 +1,10 @@ use crate::{ io::{AssetReaderError, MissingAssetSourceError, MissingProcessedAssetReaderError, Reader}, - meta::{ - loader_settings_meta_transform, meta_transform_settings, AssetHash, AssetMeta, - AssetMetaDyn, ProcessedInfoMinimal, Settings, - }, + loader_builders::NestedLoader, + meta::{AssetHash, AssetMeta, AssetMetaDyn, ProcessedInfoMinimal, Settings}, path::AssetPath, - Asset, AssetLoadError, AssetServer, AssetServerMode, Assets, Handle, LoadedUntypedAsset, - UntypedAssetId, UntypedHandle, + Asset, AssetLoadError, AssetServer, AssetServerMode, Assets, Handle, UntypedAssetId, + UntypedHandle, }; use bevy_ecs::world::World; use bevy_utils::{BoxedFuture, ConditionalSendFuture, CowArc, HashMap, HashSet}; @@ -310,14 +308,14 @@ pub enum DeserializeMetaError { /// A context that provides access to assets in [`AssetLoader`]s, tracks dependencies, and collects asset load state. /// Any asset state accessed by [`LoadContext`] will be tracked and stored for use in dependency events and asset preprocessing. pub struct LoadContext<'a> { - asset_server: &'a AssetServer, - should_load_dependencies: bool, + pub(crate) asset_server: &'a AssetServer, + pub(crate) should_load_dependencies: bool, populate_hashes: bool, asset_path: AssetPath<'static>, - dependencies: HashSet, + pub(crate) dependencies: HashSet, /// Direct dependencies used by this loader. - loader_dependencies: HashMap, AssetHash>, - labeled_assets: HashMap, LabeledAsset>, + pub(crate) loader_dependencies: HashMap, AssetHash>, + pub(crate) labeled_assets: HashMap, LabeledAsset>, } impl<'a> LoadContext<'a> { @@ -503,57 +501,6 @@ impl<'a> LoadContext<'a> { Ok(bytes) } - /// Retrieves a handle for the asset at the given path and adds that path as a dependency of the asset. - /// If the current context is a normal [`AssetServer::load`], an actual asset load will be kicked off immediately, which ensures the load happens - /// as soon as possible. - /// "Normal loads" kicked from within a normal Bevy App will generally configure the context to kick off loads immediately. - /// If the current context is configured to not load dependencies automatically (ex: [`AssetProcessor`](crate::processor::AssetProcessor)), - /// a load will not be kicked off automatically. It is then the calling context's responsibility to begin a load if necessary. - pub fn load<'b, A: Asset>(&mut self, path: impl Into>) -> Handle { - let path = path.into().to_owned(); - let handle = if self.should_load_dependencies { - self.asset_server.load(path) - } else { - self.asset_server.get_or_create_path_handle(path, None) - }; - self.dependencies.insert(handle.id().untyped()); - handle - } - - /// Retrieves a handle for the asset at the given path and adds that path as a dependency of the asset without knowing its type. - pub fn load_untyped<'b>( - &mut self, - path: impl Into>, - ) -> Handle { - let path = path.into().to_owned(); - let handle = if self.should_load_dependencies { - self.asset_server.load_untyped(path) - } else { - self.asset_server.get_or_create_path_handle(path, None) - }; - self.dependencies.insert(handle.id().untyped()); - handle - } - - /// Loads the [`Asset`] of type `A` at the given `path` with the given [`AssetLoader::Settings`] settings `S`. This is a "deferred" - /// load. If the settings type `S` does not match the settings expected by `A`'s asset loader, an error will be printed to the log - /// and the asset load will fail. - pub fn load_with_settings<'b, A: Asset, S: Settings + Default>( - &mut self, - path: impl Into>, - settings: impl Fn(&mut S) + Send + Sync + 'static, - ) -> Handle { - let path = path.into(); - let handle = if self.should_load_dependencies { - self.asset_server.load_with_settings(path.clone(), settings) - } else { - self.asset_server - .get_or_create_path_handle(path, Some(loader_settings_meta_transform(settings))) - }; - self.dependencies.insert(handle.id().untyped()); - handle - } - /// Returns a handle to an asset of type `A` with the label `label`. This [`LoadContext`] must produce an asset of the /// given type and the given label or the dependencies of this asset will never be considered "fully loaded". However you /// can call this method before _or_ after adding the labeled asset. @@ -567,7 +514,7 @@ impl<'a> LoadContext<'a> { handle } - async fn load_direct_untyped_internal( + pub(crate) async fn load_direct_internal( &mut self, path: AssetPath<'static>, meta: Box, @@ -598,223 +545,22 @@ impl<'a> LoadContext<'a> { Ok(loaded_asset) } - async fn load_direct_internal( - &mut self, - path: AssetPath<'static>, - meta: Box, - loader: &dyn ErasedAssetLoader, - reader: &mut Reader<'_>, - ) -> Result, LoadDirectError> { - self.load_direct_untyped_internal(path.clone(), meta, loader, &mut *reader) - .await - .and_then(move |untyped_asset| { - untyped_asset.downcast::().map_err(|_| LoadDirectError { - dependency: path.clone(), - error: AssetLoadError::RequestedHandleTypeMismatch { - path, - requested: TypeId::of::(), - actual_asset_name: loader.asset_type_name(), - loader_name: loader.type_name(), - }, - }) - }) + /// Create a builder for loading nested assets in this context. + #[must_use] + pub fn loader(&mut self) -> NestedLoader<'a, '_> { + NestedLoader::new(self) } - async fn load_direct_untyped_with_transform( - &mut self, - path: AssetPath<'static>, - meta_transform: impl FnOnce(&mut dyn AssetMetaDyn), - ) -> Result { - let (mut meta, loader, mut reader) = self - .asset_server - .get_meta_loader_and_reader(&path, None) - .await - .map_err(|error| LoadDirectError { - dependency: path.clone(), - error, - })?; - meta_transform(&mut *meta); - self.load_direct_untyped_internal(path.clone(), meta, &*loader, &mut *reader) - .await - } - - async fn load_direct_with_transform( - &mut self, - path: AssetPath<'static>, - meta_transform: impl FnOnce(&mut dyn AssetMetaDyn), - ) -> Result, LoadDirectError> { - let (mut meta, loader, mut reader) = self - .asset_server - .get_meta_loader_and_reader(&path, Some(TypeId::of::())) - .await - .map_err(|error| LoadDirectError { - dependency: path.clone(), - error, - })?; - meta_transform(&mut *meta); - self.load_direct_internal(path.clone(), meta, &*loader, &mut *reader) - .await - } - - /// Loads the asset at the given `path` directly. This is an async function that will wait until the asset is fully loaded before - /// returning. Use this if you need the _value_ of another asset in order to load the current asset. For example, if you are - /// deriving a new asset from the referenced asset, or you are building a collection of assets. This will add the `path` as a - /// "load dependency". + /// Retrieves a handle for the asset at the given path and adds that path as a dependency of the asset. + /// If the current context is a normal [`AssetServer::load`], an actual asset load will be kicked off immediately, which ensures the load happens + /// as soon as possible. + /// "Normal loads" kicked from within a normal Bevy App will generally configure the context to kick off loads immediately. + /// If the current context is configured to not load dependencies automatically (ex: [`AssetProcessor`](crate::processor::AssetProcessor)), + /// a load will not be kicked off automatically. It is then the calling context's responsibility to begin a load if necessary. /// - /// If the current loader is used in a [`Process`] "asset preprocessor", such as a [`LoadTransformAndSave`] preprocessor, - /// changing a "load dependency" will result in re-processing of the asset. - /// - /// [`Process`]: crate::processor::Process - /// [`LoadTransformAndSave`]: crate::processor::LoadTransformAndSave - pub async fn load_direct<'b, A: Asset>( - &mut self, - path: impl Into>, - ) -> Result, LoadDirectError> { - self.load_direct_with_transform(path.into().into_owned(), |_| {}) - .await - } - - /// Loads the asset at the given `path` directly. This is an async function that will wait until the asset is fully loaded before - /// returning. Use this if you need the _value_ of another asset in order to load the current asset. For example, if you are - /// deriving a new asset from the referenced asset, or you are building a collection of assets. This will add the `path` as a - /// "load dependency". - /// - /// If the current loader is used in a [`Process`] "asset preprocessor", such as a [`LoadTransformAndSave`] preprocessor, - /// changing a "load dependency" will result in re-processing of the asset. - /// - /// If the settings type `S` does not match the settings expected by `A`'s asset loader, an error will be printed to the log - /// and the asset load will fail. - /// - /// [`Process`]: crate::processor::Process - /// [`LoadTransformAndSave`]: crate::processor::LoadTransformAndSave - pub async fn load_direct_with_settings<'b, A: Asset, S: Settings + Default>( - &mut self, - path: impl Into>, - settings: impl Fn(&mut S) + Send + Sync + 'static, - ) -> Result, LoadDirectError> { - self.load_direct_with_transform(path.into().into_owned(), move |meta| { - meta_transform_settings(meta, &settings); - }) - .await - } - - /// Loads the asset at the given `path` directly. This is an async function that will wait until the asset is fully loaded before - /// returning. Use this if you need the _value_ of another asset in order to load the current asset. For example, if you are - /// deriving a new asset from the referenced asset, or you are building a collection of assets. This will add the `path` as a - /// "load dependency". - /// - /// If the current loader is used in a [`Process`] "asset preprocessor", such as a [`LoadTransformAndSave`] preprocessor, - /// changing a "load dependency" will result in re-processing of the asset. - /// - /// [`Process`]: crate::processor::Process - /// [`LoadTransformAndSave`]: crate::processor::LoadTransformAndSave - pub async fn load_direct_untyped<'b>( - &mut self, - path: impl Into>, - ) -> Result { - self.load_direct_untyped_with_transform(path.into().into_owned(), |_| {}) - .await - } - - /// Loads the asset at the given `path` directly from the provided `reader`. This is an async function that will wait until the asset is fully loaded before - /// returning. Use this if you need the _value_ of another asset in order to load the current asset, and that value comes from your [`Reader`]. - /// For example, if you are deriving a new asset from the referenced asset, or you are building a collection of assets. This will add the `path` as a - /// "load dependency". - /// - /// If the current loader is used in a [`Process`] "asset preprocessor", such as a [`LoadTransformAndSave`] preprocessor, - /// changing a "load dependency" will result in re-processing of the asset. - /// - /// [`Process`]: crate::processor::Process - /// [`LoadTransformAndSave`]: crate::processor::LoadTransformAndSave - pub async fn load_direct_with_reader<'b, A: Asset>( - &mut self, - reader: &mut Reader<'_>, - path: impl Into>, - ) -> Result, LoadDirectError> { - let path = path.into().into_owned(); - - let loader = self - .asset_server - .get_asset_loader_with_asset_type::() - .await - .map_err(|error| LoadDirectError { - dependency: path.clone(), - error: error.into(), - })?; - - let meta = loader.default_meta(); - - self.load_direct_internal(path, meta, &*loader, reader) - .await - } - - /// Loads the asset at the given `path` directly from the provided `reader`. This is an async function that will wait until the asset is fully loaded before - /// returning. Use this if you need the _value_ of another asset in order to load the current asset, and that value comes from your [`Reader`]. - /// For example, if you are deriving a new asset from the referenced asset, or you are building a collection of assets. This will add the `path` as a - /// "load dependency". - /// - /// If the current loader is used in a [`Process`] "asset preprocessor", such as a [`LoadTransformAndSave`] preprocessor, - /// changing a "load dependency" will result in re-processing of the asset. - /// - /// If the settings type `S` does not match the settings expected by `A`'s asset loader, an error will be printed to the log - /// and the asset load will fail. - /// - /// [`Process`]: crate::processor::Process - /// [`LoadTransformAndSave`]: crate::processor::LoadTransformAndSave - pub async fn load_direct_with_reader_and_settings<'b, A: Asset, S: Settings + Default>( - &mut self, - reader: &mut Reader<'_>, - path: impl Into>, - settings: impl Fn(&mut S) + Send + Sync + 'static, - ) -> Result, LoadDirectError> { - let path = path.into().into_owned(); - - let loader = self - .asset_server - .get_asset_loader_with_asset_type::() - .await - .map_err(|error| LoadDirectError { - dependency: path.clone(), - error: error.into(), - })?; - - let mut meta = loader.default_meta(); - meta_transform_settings(&mut *meta, &settings); - - self.load_direct_internal(path, meta, &*loader, reader) - .await - } - - /// Loads the asset at the given `path` directly from the provided `reader`. This is an async function that will wait until the asset is fully loaded before - /// returning. Use this if you need the _value_ of another asset in order to load the current asset, and that value comes from your [`Reader`]. - /// For example, if you are deriving a new asset from the referenced asset, or you are building a collection of assets. This will add the `path` as a - /// "load dependency". - /// - /// If the current loader is used in a [`Process`] "asset preprocessor", such as a [`LoadTransformAndSave`] preprocessor, - /// changing a "load dependency" will result in re-processing of the asset. - /// - /// [`Process`]: crate::processor::Process - /// [`LoadTransformAndSave`]: crate::processor::LoadTransformAndSave - pub async fn load_direct_untyped_with_reader<'b>( - &mut self, - reader: &mut Reader<'_>, - path: impl Into>, - ) -> Result { - let path = path.into().into_owned(); - - let loader = self - .asset_server - .get_path_asset_loader(&path) - .await - .map_err(|error| LoadDirectError { - dependency: path.clone(), - error: error.into(), - })?; - - let meta = loader.default_meta(); - - self.load_direct_untyped_internal(path, meta, &*loader, reader) - .await + /// If you need to override asset settings, asset type, or load directly, please see [`LoadContext::loader`]. + pub fn load<'b, A: Asset>(&mut self, path: impl Into>) -> Handle { + self.loader().load(path) } } diff --git a/crates/bevy_asset/src/loader_builders.rs b/crates/bevy_asset/src/loader_builders.rs new file mode 100644 index 0000000000..e1479670d1 --- /dev/null +++ b/crates/bevy_asset/src/loader_builders.rs @@ -0,0 +1,305 @@ +//! Implementations of the builder-pattern used for loading dependent assets via +//! [`LoadContext::loader`]. + +use crate::{ + io::Reader, + meta::{meta_transform_settings, AssetMetaDyn, MetaTransform, Settings}, + Asset, AssetLoadError, AssetPath, ErasedAssetLoader, ErasedLoadedAsset, Handle, LoadContext, + LoadDirectError, LoadedAsset, LoadedUntypedAsset, +}; +use std::any::TypeId; +use std::sync::Arc; + +// Utility type for handling the sources of reader references +enum ReaderRef<'a, 'b> { + Borrowed(&'a mut Reader<'b>), + Boxed(Box>), +} + +impl<'a, 'b> ReaderRef<'a, 'b> { + pub fn as_mut(&mut self) -> &mut Reader { + match self { + ReaderRef::Borrowed(r) => r, + ReaderRef::Boxed(b) => &mut *b, + } + } +} + +/// A builder for loading nested assets inside a `LoadContext`. +/// +/// # Lifetimes +/// - `ctx`: the lifetime of the associated [`AssetServer`] reference +/// - `builder`: the lifetime of the temporary builder structs +pub struct NestedLoader<'ctx, 'builder> { + load_context: &'builder mut LoadContext<'ctx>, + meta_transform: Option, + asset_type_id: Option, +} + +impl<'ctx, 'builder> NestedLoader<'ctx, 'builder> { + pub(crate) fn new( + load_context: &'builder mut LoadContext<'ctx>, + ) -> NestedLoader<'ctx, 'builder> { + NestedLoader { + load_context, + meta_transform: None, + asset_type_id: None, + } + } + + fn with_transform( + mut self, + transform: impl Fn(&mut dyn AssetMetaDyn) + Send + Sync + 'static, + ) -> Self { + if let Some(prev_transform) = self.meta_transform { + self.meta_transform = Some(Box::new(move |meta| { + prev_transform(meta); + transform(meta); + })); + } else { + self.meta_transform = Some(Box::new(transform)); + } + self + } + + /// Configure the settings used to load the asset. + /// + /// If the settings type `S` does not match the settings expected by `A`'s asset loader, an error will be printed to the log + /// and the asset load will fail. + #[must_use] + pub fn with_settings( + self, + settings: impl Fn(&mut S) + Send + Sync + 'static, + ) -> Self { + self.with_transform(move |meta| meta_transform_settings(meta, &settings)) + } + + /// Specify the output asset type. + #[must_use] + pub fn with_asset_type(mut self) -> Self { + self.asset_type_id = Some(TypeId::of::()); + self + } + + /// Specify the output asset type. + #[must_use] + pub fn with_asset_type_id(mut self, asset_type_id: TypeId) -> Self { + self.asset_type_id = Some(asset_type_id); + self + } + + /// Load assets directly, rather than creating handles. + #[must_use] + pub fn direct<'c>(self) -> DirectNestedLoader<'ctx, 'builder, 'c> { + DirectNestedLoader { + base: self, + reader: None, + } + } + + /// Load assets without static type information. + /// + /// If you need to specify the type of asset, but cannot do it statically, + /// use `.with_asset_type_id()`. + #[must_use] + pub fn untyped(self) -> UntypedNestedLoader<'ctx, 'builder> { + UntypedNestedLoader { base: self } + } + + /// Retrieves a handle for the asset at the given path and adds that path as a dependency of the asset. + /// If the current context is a normal [`AssetServer::load`], an actual asset load will be kicked off immediately, which ensures the load happens + /// as soon as possible. + /// "Normal loads" kicked from within a normal Bevy App will generally configure the context to kick off loads immediately. + /// If the current context is configured to not load dependencies automatically (ex: [`AssetProcessor`](crate::processor::AssetProcessor)), + /// a load will not be kicked off automatically. It is then the calling context's responsibility to begin a load if necessary. + pub fn load<'c, A: Asset>(self, path: impl Into>) -> Handle { + let path = path.into().to_owned(); + let handle = if self.load_context.should_load_dependencies { + self.load_context + .asset_server + .load_with_meta_transform(path, self.meta_transform) + } else { + self.load_context + .asset_server + .get_or_create_path_handle(path, None) + }; + self.load_context.dependencies.insert(handle.id().untyped()); + handle + } +} + +/// A builder for loading untyped nested assets inside a [`LoadContext`]. +/// +/// # Lifetimes +/// - `ctx`: the lifetime of the associated [`AssetServer`] reference +/// - `builder`: the lifetime of the temporary builder structs +pub struct UntypedNestedLoader<'ctx, 'builder> { + base: NestedLoader<'ctx, 'builder>, +} + +impl<'ctx, 'builder> UntypedNestedLoader<'ctx, 'builder> { + /// Retrieves a handle for the asset at the given path and adds that path as a dependency of the asset without knowing its type. + pub fn load<'p>(self, path: impl Into>) -> Handle { + let path = path.into().to_owned(); + let handle = if self.base.load_context.should_load_dependencies { + self.base + .load_context + .asset_server + .load_untyped_with_meta_transform(path, self.base.meta_transform) + } else { + self.base + .load_context + .asset_server + .get_or_create_path_handle(path, self.base.meta_transform) + }; + self.base + .load_context + .dependencies + .insert(handle.id().untyped()); + handle + } +} + +/// A builder for directly loading nested assets inside a `LoadContext`. +/// +/// # Lifetimes +/// - `ctx`: the lifetime of the associated [`AssetServer`] reference +/// - `builder`: the lifetime of the temporary builder structs +/// - `reader`: the lifetime of the [`Reader`] reference used to read the asset data +pub struct DirectNestedLoader<'ctx, 'builder, 'reader> { + base: NestedLoader<'ctx, 'builder>, + reader: Option<&'builder mut Reader<'reader>>, +} + +impl<'ctx: 'reader, 'builder, 'reader> DirectNestedLoader<'ctx, 'builder, 'reader> { + /// Specify the reader to use to read the asset data. + #[must_use] + pub fn with_reader(mut self, reader: &'builder mut Reader<'reader>) -> Self { + self.reader = Some(reader); + self + } + + /// Load the asset without providing static type information. + /// + /// If you need to specify the type of asset, but cannot do it statically, + /// use `.with_asset_type_id()`. + #[must_use] + pub fn untyped(self) -> UntypedDirectNestedLoader<'ctx, 'builder, 'reader> { + UntypedDirectNestedLoader { base: self } + } + + async fn load_internal( + self, + path: &AssetPath<'static>, + ) -> Result<(Arc, ErasedLoadedAsset), LoadDirectError> { + let (mut meta, loader, mut reader) = if let Some(reader) = self.reader { + let loader = if let Some(asset_type_id) = self.base.asset_type_id { + self.base + .load_context + .asset_server + .get_asset_loader_with_asset_type_id(asset_type_id) + .await + .map_err(|error| LoadDirectError { + dependency: path.clone(), + error: error.into(), + })? + } else { + self.base + .load_context + .asset_server + .get_path_asset_loader(path) + .await + .map_err(|error| LoadDirectError { + dependency: path.clone(), + error: error.into(), + })? + }; + let meta = loader.default_meta(); + (meta, loader, ReaderRef::Borrowed(reader)) + } else { + let (meta, loader, reader) = self + .base + .load_context + .asset_server + .get_meta_loader_and_reader(path, self.base.asset_type_id) + .await + .map_err(|error| LoadDirectError { + dependency: path.clone(), + error, + })?; + (meta, loader, ReaderRef::Boxed(reader)) + }; + + if let Some(meta_transform) = self.base.meta_transform { + meta_transform(&mut *meta); + } + + let asset = self + .base + .load_context + .load_direct_internal(path.clone(), meta, &*loader, reader.as_mut()) + .await?; + Ok((loader, asset)) + } + + /// Loads the asset at the given `path` directly. This is an async function that will wait until the asset is fully loaded before + /// returning. Use this if you need the _value_ of another asset in order to load the current asset. For example, if you are + /// deriving a new asset from the referenced asset, or you are building a collection of assets. This will add the `path` as a + /// "load dependency". + /// + /// If the current loader is used in a [`Process`] "asset preprocessor", such as a [`LoadTransformAndSave`] preprocessor, + /// changing a "load dependency" will result in re-processing of the asset. + /// + /// [`Process`]: crate::processor::Process + /// [`LoadTransformAndSave`]: crate::processor::LoadTransformAndSave + pub async fn load<'p, A: Asset>( + mut self, + path: impl Into>, + ) -> Result, LoadDirectError> { + self.base.asset_type_id = Some(TypeId::of::()); + let path = path.into().into_owned(); + self.load_internal(&path) + .await + .and_then(move |(loader, untyped_asset)| { + untyped_asset.downcast::().map_err(|_| LoadDirectError { + dependency: path.clone(), + error: AssetLoadError::RequestedHandleTypeMismatch { + path, + requested: TypeId::of::(), + actual_asset_name: loader.asset_type_name(), + loader_name: loader.type_name(), + }, + }) + }) + } +} + +/// A builder for directly loading untyped nested assets inside a `LoadContext`. +/// +/// # Lifetimes +/// - `ctx`: the lifetime of the associated [`AssetServer`] reference +/// - `builder`: the lifetime of the temporary builder structs +/// - `reader`: the lifetime of the [`Reader`] reference used to read the asset data +pub struct UntypedDirectNestedLoader<'ctx, 'builder, 'reader> { + base: DirectNestedLoader<'ctx, 'builder, 'reader>, +} + +impl<'ctx: 'reader, 'builder, 'reader> UntypedDirectNestedLoader<'ctx, 'builder, 'reader> { + /// Loads the asset at the given `path` directly. This is an async function that will wait until the asset is fully loaded before + /// returning. Use this if you need the _value_ of another asset in order to load the current asset. For example, if you are + /// deriving a new asset from the referenced asset, or you are building a collection of assets. This will add the `path` as a + /// "load dependency". + /// + /// If the current loader is used in a [`Process`] "asset preprocessor", such as a [`LoadTransformAndSave`] preprocessor, + /// changing a "load dependency" will result in re-processing of the asset. + /// + /// [`Process`]: crate::processor::Process + /// [`LoadTransformAndSave`]: crate::processor::LoadTransformAndSave + pub async fn load<'p>( + self, + path: impl Into>, + ) -> Result { + let path = path.into().into_owned(); + self.base.load_internal(&path).await.map(|(_, asset)| asset) + } +} diff --git a/crates/bevy_asset/src/server/mod.rs b/crates/bevy_asset/src/server/mod.rs index 0e4ae6aab1..ac6007103d 100644 --- a/crates/bevy_asset/src/server/mod.rs +++ b/crates/bevy_asset/src/server/mod.rs @@ -285,7 +285,7 @@ impl AssetServer { self.load_with_meta_transform(path, Some(loader_settings_meta_transform(settings))) } - fn load_with_meta_transform<'a, A: Asset>( + pub(crate) fn load_with_meta_transform<'a, A: Asset>( &self, path: impl Into>, meta_transform: Option, @@ -324,30 +324,11 @@ impl AssetServer { self.load_internal(None, path, false, None).await } - /// 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); - /// - /// fn resolve_loaded_untyped_handle(loading_handle: Res, loaded_untyped_assets: Res>) { - /// 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. - #[must_use = "not using the returned strong handle may result in the unexpected release of the assets"] - pub fn load_untyped<'a>(&self, path: impl Into>) -> Handle { + pub(crate) fn load_untyped_with_meta_transform<'a>( + &self, + path: impl Into>, + meta_transform: Option, + ) -> Handle { let path = path.into().into_owned(); let untyped_source = AssetSourceId::Name(match path.source() { AssetSourceId::Default => CowArc::Borrowed(UNTYPED_SOURCE_SUFFIX), @@ -362,7 +343,7 @@ impl AssetServer { .get_or_create_path_handle::( path.clone().with_source(untyped_source), HandleLoadingMode::Request, - None, + meta_transform, ); if !should_load { return handle; @@ -393,10 +374,36 @@ impl AssetServer { } }) .detach(); - handle } + /// 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); + /// + /// fn resolve_loaded_untyped_handle(loading_handle: Res, loaded_untyped_assets: Res>) { + /// 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. + #[must_use = "not using the returned strong handle may result in the unexpected release of the assets"] + pub fn load_untyped<'a>(&self, path: impl Into>) -> Handle { + self.load_untyped_with_meta_transform(path, None) + } + /// Performs an async asset load. /// /// `input_handle` must only be [`Some`] if `should_load` was true when retrieving `input_handle`. This is an optimization to diff --git a/crates/bevy_gltf/src/loader.rs b/crates/bevy_gltf/src/loader.rs index a16b958870..5687675422 100644 --- a/crates/bevy_gltf/src/loader.rs +++ b/crates/bevy_gltf/src/loader.rs @@ -342,12 +342,13 @@ async fn load_gltf<'a, 'b, 'c>( path, is_srgb, sampler_descriptor, - } => { - load_context.load_with_settings(path, move |settings: &mut ImageLoaderSettings| { + } => load_context + .loader() + .with_settings(move |settings: &mut ImageLoaderSettings| { settings.is_srgb = is_srgb; settings.sampler = ImageSampler::Descriptor(sampler_descriptor.clone()); }) - } + .load(path), }; handles.push(handle); } diff --git a/examples/asset/asset_decompression.rs b/examples/asset/asset_decompression.rs index b806e7d742..b17d4c2259 100644 --- a/examples/asset/asset_decompression.rs +++ b/examples/asset/asset_decompression.rs @@ -72,7 +72,11 @@ impl AssetLoader for GzAssetLoader { let mut reader = VecReader::new(bytes_uncompressed); let uncompressed = load_context - .load_direct_untyped_with_reader(&mut reader, contained_path) + .loader() + .direct() + .with_reader(&mut reader) + .untyped() + .load(contained_path) .await?; Ok(GzAsset { uncompressed }) diff --git a/examples/asset/processing/asset_processing.rs b/examples/asset/processing/asset_processing.rs index ebe5f62898..cceb959ee7 100644 --- a/examples/asset/processing/asset_processing.rs +++ b/examples/asset/processing/asset_processing.rs @@ -146,14 +146,21 @@ impl AssetLoader for CoolTextLoader { let ron: CoolTextRon = ron::de::from_bytes(&bytes)?; let mut base_text = ron.text; for embedded in ron.embedded_dependencies { - let loaded = load_context.load_direct::(&embedded).await?; + let loaded = load_context + .loader() + .direct() + .load::(&embedded) + .await?; base_text.push_str(&loaded.get().0); } for (path, settings_override) in ron.dependencies_with_settings { let loaded = load_context - .load_direct_with_settings::(&path, move |settings| { + .loader() + .with_settings(move |settings| { *settings = settings_override.clone(); }) + .direct() + .load::(&path) .await?; base_text.push_str(&loaded.get().0); }