Make LoadContext use the builder pattern for loading dependent assets (#13465)
# Objective - Fixes #13445. ## Solution - Removes all `load_` methods from `LoadContext`. - Introduces `fn loader()` which returns a builder. ## Testing - I've tested with `cargo test --package=bevy_asset` and run the two relevant examples (`asset_processing` & `asset_decompression`). --- ## Changelog - Replaced all `load_` methods on `LoadContext` with the new `loader()` pattern. ## Migration Guide - Several LoadContext method calls will need to be updated: - `load_context.load_with_settings(path, settings)` => `load_context.loader().with_settings(settings).load(path)` - `load_context.load_untyped(path)` => `load_context.loader().untyped().load(path)` - `load_context.load_direct(path)` => `load_context.loader().direct().load(path)` - `load_context.load_direct_untyped(path)` => `load_context.loader().direct().untyped().load(path)` - `load_context.load_direct_with_settings(path, settings)` => `load_context.loader().with_settings(settings).direct().load(path)` - `load_context.load_direct_with_reader(reader, path)` => `load_context.loader().direct().with_reader(reader).load(path)` - `load_context.load_direct_with_reader_and_settings(reader, path, settings)` => `load_context.loader().with_settings(settings).direct().with_reader(reader).load(path)` - `load_context.load_direct_untyped_with_reader(reader, path)` => `load_context.loader().direct().with_reader(reader).untyped().load(path)` --- CC @alice-i-cecile / @bushrat011899 Examples: ```rust load_context.loader() .with_asset_type::<A>() .with_asset_type_id(TypeId::of::<A>()) .with_settings(|mut settings| { settings.key = value; }) // Then, for a Handle<A>: .load::<A>() // Or, for a Handle<LoadedUntypedAsset>: .untyped() .load() // Or, to load an `A` directly: .direct() .load::<A>() .await // Or, to load an `ErasedLoadedAsset` directly: .direct() .untyped() .load() .await ```
This commit is contained in:
parent
faf003fc9d
commit
efcb6d6c11
@ -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::<CoolText>(&dep)
|
||||
.loader()
|
||||
.direct()
|
||||
.load::<CoolText>(&dep)
|
||||
.await
|
||||
.map_err(|_| Self::Error::CannotLoadDependency {
|
||||
dependency: dep.into(),
|
||||
|
@ -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<UntypedAssetId>,
|
||||
pub(crate) dependencies: HashSet<UntypedAssetId>,
|
||||
/// Direct dependencies used by this loader.
|
||||
loader_dependencies: HashMap<AssetPath<'static>, AssetHash>,
|
||||
labeled_assets: HashMap<CowArc<'static, str>, LabeledAsset>,
|
||||
pub(crate) loader_dependencies: HashMap<AssetPath<'static>, AssetHash>,
|
||||
pub(crate) labeled_assets: HashMap<CowArc<'static, str>, 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<AssetPath<'b>>) -> Handle<A> {
|
||||
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<AssetPath<'b>>,
|
||||
) -> Handle<LoadedUntypedAsset> {
|
||||
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<AssetPath<'b>>,
|
||||
settings: impl Fn(&mut S) + Send + Sync + 'static,
|
||||
) -> Handle<A> {
|
||||
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<dyn AssetMetaDyn>,
|
||||
@ -598,223 +545,22 @@ impl<'a> LoadContext<'a> {
|
||||
Ok(loaded_asset)
|
||||
}
|
||||
|
||||
async fn load_direct_internal<A: Asset>(
|
||||
&mut self,
|
||||
path: AssetPath<'static>,
|
||||
meta: Box<dyn AssetMetaDyn>,
|
||||
loader: &dyn ErasedAssetLoader,
|
||||
reader: &mut Reader<'_>,
|
||||
) -> Result<LoadedAsset<A>, LoadDirectError> {
|
||||
self.load_direct_untyped_internal(path.clone(), meta, loader, &mut *reader)
|
||||
.await
|
||||
.and_then(move |untyped_asset| {
|
||||
untyped_asset.downcast::<A>().map_err(|_| LoadDirectError {
|
||||
dependency: path.clone(),
|
||||
error: AssetLoadError::RequestedHandleTypeMismatch {
|
||||
path,
|
||||
requested: TypeId::of::<A>(),
|
||||
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<ErasedLoadedAsset, LoadDirectError> {
|
||||
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<A: Asset>(
|
||||
&mut self,
|
||||
path: AssetPath<'static>,
|
||||
meta_transform: impl FnOnce(&mut dyn AssetMetaDyn),
|
||||
) -> Result<LoadedAsset<A>, LoadDirectError> {
|
||||
let (mut meta, loader, mut reader) = self
|
||||
.asset_server
|
||||
.get_meta_loader_and_reader(&path, Some(TypeId::of::<A>()))
|
||||
.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<AssetPath<'b>>,
|
||||
) -> Result<LoadedAsset<A>, 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<AssetPath<'b>>,
|
||||
settings: impl Fn(&mut S) + Send + Sync + 'static,
|
||||
) -> Result<LoadedAsset<A>, 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<AssetPath<'b>>,
|
||||
) -> Result<ErasedLoadedAsset, LoadDirectError> {
|
||||
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<AssetPath<'b>>,
|
||||
) -> Result<LoadedAsset<A>, LoadDirectError> {
|
||||
let path = path.into().into_owned();
|
||||
|
||||
let loader = self
|
||||
.asset_server
|
||||
.get_asset_loader_with_asset_type::<A>()
|
||||
.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<AssetPath<'b>>,
|
||||
settings: impl Fn(&mut S) + Send + Sync + 'static,
|
||||
) -> Result<LoadedAsset<A>, LoadDirectError> {
|
||||
let path = path.into().into_owned();
|
||||
|
||||
let loader = self
|
||||
.asset_server
|
||||
.get_asset_loader_with_asset_type::<A>()
|
||||
.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<AssetPath<'b>>,
|
||||
) -> Result<ErasedLoadedAsset, LoadDirectError> {
|
||||
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<AssetPath<'b>>) -> Handle<A> {
|
||||
self.loader().load(path)
|
||||
}
|
||||
}
|
||||
|
||||
|
305
crates/bevy_asset/src/loader_builders.rs
Normal file
305
crates/bevy_asset/src/loader_builders.rs
Normal file
@ -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<Reader<'b>>),
|
||||
}
|
||||
|
||||
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<MetaTransform>,
|
||||
asset_type_id: Option<TypeId>,
|
||||
}
|
||||
|
||||
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<S: 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<A: Asset>(mut self) -> Self {
|
||||
self.asset_type_id = Some(TypeId::of::<A>());
|
||||
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<AssetPath<'c>>) -> Handle<A> {
|
||||
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<AssetPath<'p>>) -> Handle<LoadedUntypedAsset> {
|
||||
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<dyn ErasedAssetLoader>, 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<AssetPath<'p>>,
|
||||
) -> Result<LoadedAsset<A>, LoadDirectError> {
|
||||
self.base.asset_type_id = Some(TypeId::of::<A>());
|
||||
let path = path.into().into_owned();
|
||||
self.load_internal(&path)
|
||||
.await
|
||||
.and_then(move |(loader, untyped_asset)| {
|
||||
untyped_asset.downcast::<A>().map_err(|_| LoadDirectError {
|
||||
dependency: path.clone(),
|
||||
error: AssetLoadError::RequestedHandleTypeMismatch {
|
||||
path,
|
||||
requested: TypeId::of::<A>(),
|
||||
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<AssetPath<'p>>,
|
||||
) -> Result<ErasedLoadedAsset, LoadDirectError> {
|
||||
let path = path.into().into_owned();
|
||||
self.base.load_internal(&path).await.map(|(_, asset)| asset)
|
||||
}
|
||||
}
|
@ -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<AssetPath<'a>>,
|
||||
meta_transform: Option<MetaTransform>,
|
||||
@ -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<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.
|
||||
#[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<AssetPath<'a>>) -> Handle<LoadedUntypedAsset> {
|
||||
pub(crate) fn load_untyped_with_meta_transform<'a>(
|
||||
&self,
|
||||
path: impl Into<AssetPath<'a>>,
|
||||
meta_transform: Option<MetaTransform>,
|
||||
) -> Handle<LoadedUntypedAsset> {
|
||||
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::<LoadedUntypedAsset>(
|
||||
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<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.
|
||||
#[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<AssetPath<'a>>) -> Handle<LoadedUntypedAsset> {
|
||||
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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 })
|
||||
|
@ -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::<Text>(&embedded).await?;
|
||||
let loaded = load_context
|
||||
.loader()
|
||||
.direct()
|
||||
.load::<Text>(&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::<Text, _>(&path, move |settings| {
|
||||
.loader()
|
||||
.with_settings(move |settings| {
|
||||
*settings = settings_override.clone();
|
||||
})
|
||||
.direct()
|
||||
.load::<Text>(&path)
|
||||
.await?;
|
||||
base_text.push_str(&loaded.get().0);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user