diff --git a/crates/bevy_asset/Cargo.toml b/crates/bevy_asset/Cargo.toml index 01404cf6e7..0692c5f6dc 100644 --- a/crates/bevy_asset/Cargo.toml +++ b/crates/bevy_asset/Cargo.toml @@ -22,7 +22,6 @@ trace = [] bevy_app = { path = "../bevy_app", version = "0.16.0-rc.2" } bevy_asset_macros = { path = "macros", version = "0.16.0-rc.2" } bevy_ecs = { path = "../bevy_ecs", version = "0.16.0-rc.2" } -bevy_log = { path = "../bevy_log", version = "0.16.0-rc.2" } bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-rc.2", features = [ "uuid", ] } @@ -80,6 +79,9 @@ bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-rc.2", default-feat [target.'cfg(not(target_arch = "wasm32"))'.dependencies] notify-debouncer-full = { version = "0.5.0", optional = true } +[dev-dependencies] +bevy_log = { path = "../bevy_log", version = "0.16.0-rc.2" } + [lints] workspace = true diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index 9273ae0b86..d88c4a8729 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -670,7 +670,7 @@ mod tests { }, loader::{AssetLoader, LoadContext}, Asset, AssetApp, AssetEvent, AssetId, AssetLoadError, AssetLoadFailedEvent, AssetPath, - AssetPlugin, AssetServer, Assets, DuplicateLabelAssetError, LoadState, UnapprovedPathMode, + AssetPlugin, AssetServer, Assets, LoadState, UnapprovedPathMode, }; use alloc::{ boxed::Box, @@ -726,8 +726,6 @@ mod tests { CannotLoadDependency { dependency: AssetPath<'static> }, #[error("A RON error occurred during loading")] RonSpannedError(#[from] ron::error::SpannedError), - #[error(transparent)] - DuplicateLabelAssetError(#[from] DuplicateLabelAssetError), #[error("An IO error occurred during loading")] Io(#[from] std::io::Error), } @@ -758,7 +756,7 @@ mod tests { .map_err(|_| Self::Error::CannotLoadDependency { dependency: dep.into(), })?; - let cool = loaded.get_asset().get(); + let cool = loaded.get(); embedded.push_str(&cool.text); } Ok(CoolText { @@ -773,7 +771,7 @@ mod tests { .sub_texts .drain(..) .map(|text| load_context.add_labeled_asset(text.clone(), SubText { text })) - .collect::, _>>()?, + .collect(), }) } @@ -1811,49 +1809,6 @@ mod tests { app.world_mut().run_schedule(Update); } - #[test] - fn fails_to_load_for_duplicate_subasset_labels() { - let mut app = App::new(); - - let dir = Dir::default(); - dir.insert_asset_text( - Path::new("a.ron"), - r#"( - text: "b", - dependencies: [], - embedded_dependencies: [], - sub_texts: ["A", "A"], -)"#, - ); - - app.register_asset_source( - AssetSourceId::Default, - AssetSource::build() - .with_reader(move || Box::new(MemoryAssetReader { root: dir.clone() })), - ) - .add_plugins(( - TaskPoolPlugin::default(), - LogPlugin::default(), - AssetPlugin::default(), - )); - - app.init_asset::() - .init_asset::() - .register_asset_loader(CoolTextLoader); - - let asset_server = app.world().resource::().clone(); - let handle = asset_server.load::("a.ron"); - - run_app_until(&mut app, |_world| match asset_server.load_state(&handle) { - LoadState::Loading => None, - LoadState::Failed(err) => { - assert!(matches!(*err, AssetLoadError::AssetLoaderError(_))); - Some(()) - } - state => panic!("Unexpected asset state: {state:?}"), - }); - } - // This test is not checking a requirement, but documenting a current limitation. We simply are // not capable of loading subassets when doing nested immediate loads. #[test] diff --git a/crates/bevy_asset/src/loader.rs b/crates/bevy_asset/src/loader.rs index 8b02e5b3db..2f520c7584 100644 --- a/crates/bevy_asset/src/loader.rs +++ b/crates/bevy_asset/src/loader.rs @@ -60,7 +60,7 @@ pub trait ErasedAssetLoader: Send + Sync + 'static { load_context: LoadContext<'a>, ) -> BoxedFuture< 'a, - Result>, + Result>, >; /// Returns a list of extensions supported by this asset loader, without the preceding dot. @@ -91,7 +91,7 @@ where mut load_context: LoadContext<'a>, ) -> BoxedFuture< 'a, - Result>, + Result>, > { Box::pin(async move { let settings = meta @@ -152,6 +152,7 @@ pub struct LoadedAsset { pub(crate) value: A, pub(crate) dependencies: HashSet, pub(crate) loader_dependencies: HashMap, AssetHash>, + pub(crate) labeled_assets: HashMap, LabeledAsset>, } impl LoadedAsset { @@ -165,6 +166,7 @@ impl LoadedAsset { value, dependencies, loader_dependencies: HashMap::default(), + labeled_assets: HashMap::default(), } } @@ -177,6 +179,19 @@ impl LoadedAsset { pub fn get(&self) -> &A { &self.value } + + /// Returns the [`ErasedLoadedAsset`] for the given label, if it exists. + pub fn get_labeled( + &self, + label: impl Into>, + ) -> Option<&ErasedLoadedAsset> { + self.labeled_assets.get(&label.into()).map(|a| &a.asset) + } + + /// Iterate over all labels for "labeled assets" in the loaded asset + pub fn iter_labels(&self) -> impl Iterator { + self.labeled_assets.keys().map(|s| &**s) + } } impl From for LoadedAsset { @@ -190,6 +205,7 @@ pub struct ErasedLoadedAsset { pub(crate) value: Box, pub(crate) dependencies: HashSet, pub(crate) loader_dependencies: HashMap, AssetHash>, + pub(crate) labeled_assets: HashMap, LabeledAsset>, } impl From> for ErasedLoadedAsset { @@ -198,6 +214,7 @@ impl From> for ErasedLoadedAsset { value: Box::new(asset.value), dependencies: asset.dependencies, loader_dependencies: asset.loader_dependencies, + labeled_assets: asset.labeled_assets, } } } @@ -224,6 +241,19 @@ impl ErasedLoadedAsset { self.value.asset_type_name() } + /// Returns the [`ErasedLoadedAsset`] for the given label, if it exists. + pub fn get_labeled( + &self, + label: impl Into>, + ) -> Option<&ErasedLoadedAsset> { + self.labeled_assets.get(&label.into()).map(|a| &a.asset) + } + + /// Iterate over all labels for "labeled assets" in the loaded asset + pub fn iter_labels(&self) -> impl Iterator { + self.labeled_assets.keys().map(|s| &**s) + } + /// Cast this loaded asset as the given type. If the type does not match, /// the original type-erased asset is returned. pub fn downcast(mut self) -> Result, ErasedLoadedAsset> { @@ -232,6 +262,7 @@ impl ErasedLoadedAsset { value: *value, dependencies: self.dependencies, loader_dependencies: self.loader_dependencies, + labeled_assets: self.labeled_assets, }), Err(value) => { self.value = value; @@ -259,100 +290,6 @@ impl AssetContainer for A { } } -/// A loaded asset and all its loaded subassets. -pub struct CompleteLoadedAsset { - /// The loaded asset. - pub(crate) asset: LoadedAsset, - /// The subassets by their label. - pub(crate) labeled_assets: HashMap, LabeledAsset>, -} - -impl CompleteLoadedAsset { - /// Take ownership of the stored [`Asset`] value. - pub fn take(self) -> A { - self.asset.value - } - - /// Returns the stored asset. - pub fn get_asset(&self) -> &LoadedAsset { - &self.asset - } - - /// Returns the [`ErasedLoadedAsset`] for the given label, if it exists. - pub fn get_labeled( - &self, - label: impl Into>, - ) -> Option<&ErasedLoadedAsset> { - self.labeled_assets.get(&label.into()).map(|a| &a.asset) - } - - /// Iterate over all labels for "labeled assets" in the loaded asset - pub fn iter_labels(&self) -> impl Iterator { - self.labeled_assets.keys().map(|s| &**s) - } -} - -/// A "type erased / boxed" counterpart to [`CompleteLoadedAsset`]. This is used in places where the -/// loaded type is not statically known. -pub struct CompleteErasedLoadedAsset { - /// The loaded asset. - pub(crate) asset: ErasedLoadedAsset, - /// The subassets by their label. - pub(crate) labeled_assets: HashMap, LabeledAsset>, -} - -impl CompleteErasedLoadedAsset { - /// Cast (and take ownership) of the [`Asset`] value of the given type. This will return - /// [`Some`] if the stored type matches `A` and [`None`] if it does not. - pub fn take(self) -> Option { - self.asset.take() - } - - /// Returns the stored asset. - pub fn get_asset(&self) -> &ErasedLoadedAsset { - &self.asset - } - - /// Returns the [`ErasedLoadedAsset`] for the given label, if it exists. - pub fn get_labeled( - &self, - label: impl Into>, - ) -> Option<&ErasedLoadedAsset> { - self.labeled_assets.get(&label.into()).map(|a| &a.asset) - } - - /// Iterate over all labels for "labeled assets" in the loaded asset - pub fn iter_labels(&self) -> impl Iterator { - self.labeled_assets.keys().map(|s| &**s) - } - - /// Cast this loaded asset as the given type. If the type does not match, - /// the original type-erased asset is returned. - pub fn downcast( - mut self, - ) -> Result, CompleteErasedLoadedAsset> { - match self.asset.downcast::() { - Ok(asset) => Ok(CompleteLoadedAsset { - asset, - labeled_assets: self.labeled_assets, - }), - Err(asset) => { - self.asset = asset; - Err(self) - } - } - } -} - -impl From> for CompleteErasedLoadedAsset { - fn from(value: CompleteLoadedAsset) -> Self { - Self { - asset: value.asset.into(), - labeled_assets: value.labeled_assets, - } - } -} - /// An error that occurs when attempting to call [`NestedLoader::load`] which /// is configured to work [immediately]. /// @@ -461,11 +398,11 @@ impl<'a> LoadContext<'a> { &mut self, label: String, load: impl FnOnce(&mut LoadContext) -> A, - ) -> Result, DuplicateLabelAssetError> { + ) -> Handle { let mut context = self.begin_labeled_asset(); let asset = load(&mut context); - let complete_asset = context.finish(asset); - self.add_loaded_labeled_asset(label, complete_asset) + let loaded_asset = context.finish(asset); + self.add_loaded_labeled_asset(label, loaded_asset) } /// This will add the given `asset` as a "labeled [`Asset`]" with the `label` label. @@ -478,11 +415,7 @@ impl<'a> LoadContext<'a> { /// new [`LoadContext`] to track the dependencies for the labeled asset. /// /// See [`AssetPath`] for more on labeled assets. - pub fn add_labeled_asset( - &mut self, - label: String, - asset: A, - ) -> Result, DuplicateLabelAssetError> { + pub fn add_labeled_asset(&mut self, label: String, asset: A) -> Handle { self.labeled_asset_scope(label, |_| asset) } @@ -494,37 +427,22 @@ impl<'a> LoadContext<'a> { pub fn add_loaded_labeled_asset( &mut self, label: impl Into>, - loaded_asset: CompleteLoadedAsset, - ) -> Result, DuplicateLabelAssetError> { + loaded_asset: LoadedAsset, + ) -> Handle { let label = label.into(); - let CompleteLoadedAsset { - asset, - labeled_assets, - } = loaded_asset; - let loaded_asset: ErasedLoadedAsset = asset.into(); + let loaded_asset: ErasedLoadedAsset = loaded_asset.into(); let labeled_path = self.asset_path.clone().with_label(label.clone()); let handle = self .asset_server .get_or_create_path_handle(labeled_path, None); - let has_duplicate = self - .labeled_assets - .insert( - label.clone(), - LabeledAsset { - asset: loaded_asset, - handle: handle.clone().untyped(), - }, - ) - .is_some(); - if has_duplicate { - return Err(DuplicateLabelAssetError(label.to_string())); - } - for (label, asset) in labeled_assets { - if self.labeled_assets.insert(label.clone(), asset).is_some() { - return Err(DuplicateLabelAssetError(label.to_string())); - } - } - Ok(handle) + self.labeled_assets.insert( + label, + LabeledAsset { + asset: loaded_asset, + handle: handle.clone().untyped(), + }, + ); + handle } /// Returns `true` if an asset with the label `label` exists in this context. @@ -536,13 +454,11 @@ impl<'a> LoadContext<'a> { } /// "Finishes" this context by populating the final [`Asset`] value. - pub fn finish(self, value: A) -> CompleteLoadedAsset { - CompleteLoadedAsset { - asset: LoadedAsset { - value, - dependencies: self.dependencies, - loader_dependencies: self.loader_dependencies, - }, + pub fn finish(self, value: A) -> LoadedAsset { + LoadedAsset { + value, + dependencies: self.dependencies, + loader_dependencies: self.loader_dependencies, labeled_assets: self.labeled_assets, } } @@ -613,8 +529,8 @@ impl<'a> LoadContext<'a> { meta: &dyn AssetMetaDyn, loader: &dyn ErasedAssetLoader, reader: &mut dyn Reader, - ) -> Result { - let complete_asset = self + ) -> Result { + let loaded_asset = self .asset_server .load_with_meta_loader_and_reader( &path, @@ -632,7 +548,7 @@ impl<'a> LoadContext<'a> { let info = meta.processed_info().as_ref(); let hash = info.map(|i| i.full_hash).unwrap_or_default(); self.loader_dependencies.insert(path, hash); - Ok(complete_asset) + Ok(loaded_asset) } /// Create a builder for loading nested assets in this context. @@ -674,8 +590,3 @@ pub enum ReadAssetBytesError { #[error("The LoadContext for this read_asset_bytes call requires hash metadata, but it was not provided. This is likely an internal implementation error.")] MissingAssetHash, } - -/// An error when labeled assets have the same label, containing the duplicate label. -#[derive(Error, Debug)] -#[error("Encountered a duplicate label while loading an asset: \"{0}\"")] -pub struct DuplicateLabelAssetError(pub String); diff --git a/crates/bevy_asset/src/loader_builders.rs b/crates/bevy_asset/src/loader_builders.rs index db7c5de8f3..13bea2b71d 100644 --- a/crates/bevy_asset/src/loader_builders.rs +++ b/crates/bevy_asset/src/loader_builders.rs @@ -4,8 +4,8 @@ use crate::{ io::Reader, meta::{meta_transform_settings, AssetMetaDyn, MetaTransform, Settings}, - Asset, AssetLoadError, AssetPath, CompleteErasedLoadedAsset, CompleteLoadedAsset, - ErasedAssetLoader, Handle, LoadContext, LoadDirectError, LoadedUntypedAsset, UntypedHandle, + Asset, AssetLoadError, AssetPath, ErasedAssetLoader, ErasedLoadedAsset, Handle, LoadContext, + LoadDirectError, LoadedAsset, LoadedUntypedAsset, UntypedHandle, }; use alloc::{borrow::ToOwned, boxed::Box, sync::Arc}; use core::any::TypeId; @@ -57,11 +57,11 @@ impl ReaderRef<'_> { /// If you know the type ID of the asset at runtime, but not at compile time, /// use [`with_dynamic_type`] followed by [`load`] to start loading an asset /// of that type. This lets you get an [`UntypedHandle`] (via [`Deferred`]), -/// or a [`CompleteErasedLoadedAsset`] (via [`Immediate`]). +/// or a [`ErasedLoadedAsset`] (via [`Immediate`]). /// /// - in [`UnknownTyped`]: loading either a type-erased version of the asset -/// ([`CompleteErasedLoadedAsset`]), or a handle *to a handle* of the actual -/// asset ([`LoadedUntypedAsset`]). +/// ([`ErasedLoadedAsset`]), or a handle *to a handle* of the actual asset +/// ([`LoadedUntypedAsset`]). /// /// If you have no idea what type of asset you will be loading (not even at /// runtime with a [`TypeId`]), use this. @@ -389,7 +389,7 @@ impl<'builder, 'reader, T> NestedLoader<'_, '_, T, Immediate<'builder, 'reader>> self, path: &AssetPath<'static>, asset_type_id: Option, - ) -> Result<(Arc, CompleteErasedLoadedAsset), LoadDirectError> { + ) -> Result<(Arc, ErasedLoadedAsset), LoadDirectError> { if path.label().is_some() { return Err(LoadDirectError::RequestedSubasset(path.clone())); } @@ -454,7 +454,7 @@ impl NestedLoader<'_, '_, StaticTyped, Immediate<'_, '_>> { pub async fn load<'p, A: Asset>( self, path: impl Into>, - ) -> Result, LoadDirectError> { + ) -> Result, LoadDirectError> { let path = path.into().into_owned(); self.load_internal(&path, Some(TypeId::of::())) .await @@ -484,7 +484,7 @@ impl NestedLoader<'_, '_, DynamicTyped, Immediate<'_, '_>> { pub async fn load<'p>( self, path: impl Into>, - ) -> Result { + ) -> Result { let path = path.into().into_owned(); let asset_type_id = Some(self.typing.asset_type_id); self.load_internal(&path, asset_type_id) @@ -500,7 +500,7 @@ impl NestedLoader<'_, '_, UnknownTyped, Immediate<'_, '_>> { pub async fn load<'p>( self, path: impl Into>, - ) -> Result { + ) -> Result { let path = path.into().into_owned(); self.load_internal(&path, None) .await diff --git a/crates/bevy_asset/src/processor/process.rs b/crates/bevy_asset/src/processor/process.rs index d2202d2334..b37265d0fb 100644 --- a/crates/bevy_asset/src/processor/process.rs +++ b/crates/bevy_asset/src/processor/process.rs @@ -7,7 +7,7 @@ use crate::{ processor::AssetProcessor, saver::{AssetSaver, SavedAsset}, transformer::{AssetTransformer, IdentityAssetTransformer, TransformedAsset}, - AssetLoadError, AssetLoader, AssetPath, CompleteErasedLoadedAsset, DeserializeMetaError, + AssetLoadError, AssetLoader, AssetPath, DeserializeMetaError, ErasedLoadedAsset, MissingAssetLoaderForExtensionError, MissingAssetLoaderForTypeNameError, }; use alloc::{ @@ -305,15 +305,15 @@ impl<'a> ProcessContext<'a> { pub async fn load_source_asset( &mut self, meta: AssetMeta, - ) -> Result { + ) -> Result { let server = &self.processor.server; let loader_name = core::any::type_name::(); let loader = server.get_asset_loader_with_type_name(loader_name).await?; let mut reader = SliceReader::new(self.asset_bytes); - let complete_asset = server + let loaded_asset = server .load_with_meta_loader_and_reader(self.path, &meta, &*loader, &mut reader, false, true) .await?; - for (path, full_hash) in &complete_asset.asset.loader_dependencies { + for (path, full_hash) in &loaded_asset.loader_dependencies { self.new_processed_info .process_dependencies .push(ProcessDependencyInfo { @@ -321,7 +321,7 @@ impl<'a> ProcessContext<'a> { path: path.to_owned(), }); } - Ok(complete_asset) + Ok(loaded_asset) } /// The path of the asset being processed. diff --git a/crates/bevy_asset/src/saver.rs b/crates/bevy_asset/src/saver.rs index 3b187e96ef..c8b308a544 100644 --- a/crates/bevy_asset/src/saver.rs +++ b/crates/bevy_asset/src/saver.rs @@ -1,6 +1,6 @@ use crate::{ io::Writer, meta::Settings, transformer::TransformedAsset, Asset, AssetLoader, - CompleteErasedLoadedAsset, ErasedLoadedAsset, Handle, LabeledAsset, UntypedHandle, + ErasedLoadedAsset, Handle, LabeledAsset, UntypedHandle, }; use alloc::boxed::Box; use atomicow::CowArc; @@ -44,7 +44,7 @@ pub trait ErasedAssetSaver: Send + Sync + 'static { fn save<'a>( &'a self, writer: &'a mut Writer, - complete_asset: &'a CompleteErasedLoadedAsset, + asset: &'a ErasedLoadedAsset, settings: &'a dyn Settings, ) -> BoxedFuture<'a, Result<(), Box>>; @@ -56,14 +56,14 @@ impl ErasedAssetSaver for S { fn save<'a>( &'a self, writer: &'a mut Writer, - complete_asset: &'a CompleteErasedLoadedAsset, + asset: &'a ErasedLoadedAsset, settings: &'a dyn Settings, ) -> BoxedFuture<'a, Result<(), Box>> { Box::pin(async move { let settings = settings .downcast_ref::() .expect("AssetLoader settings should match the loader type"); - let saved_asset = SavedAsset::::from_loaded(complete_asset).unwrap(); + let saved_asset = SavedAsset::::from_loaded(asset).unwrap(); if let Err(err) = self.save(writer, saved_asset, settings).await { return Err(err.into()); } @@ -91,11 +91,11 @@ impl<'a, A: Asset> Deref for SavedAsset<'a, A> { impl<'a, A: Asset> SavedAsset<'a, A> { /// Creates a new [`SavedAsset`] from `asset` if its internal value matches `A`. - pub fn from_loaded(complete_asset: &'a CompleteErasedLoadedAsset) -> Option { - let value = complete_asset.asset.value.downcast_ref::()?; + pub fn from_loaded(asset: &'a ErasedLoadedAsset) -> Option { + let value = asset.value.downcast_ref::()?; Some(SavedAsset { value, - labeled_assets: &complete_asset.labeled_assets, + labeled_assets: &asset.labeled_assets, }) } @@ -114,13 +114,17 @@ impl<'a, A: Asset> SavedAsset<'a, A> { } /// Returns the labeled asset, if it exists and matches this type. - pub fn get_labeled(&self, label: &Q) -> Option<&B> + pub fn get_labeled(&self, label: &Q) -> Option> where CowArc<'static, str>: Borrow, Q: ?Sized + Hash + Eq, { let labeled = self.labeled_assets.get(label)?; - labeled.asset.value.downcast_ref::() + let value = labeled.asset.value.downcast_ref::()?; + Some(SavedAsset { + value, + labeled_assets: &labeled.asset.labeled_assets, + }) } /// Returns the type-erased labeled asset, if it exists and matches this type. diff --git a/crates/bevy_asset/src/server/mod.rs b/crates/bevy_asset/src/server/mod.rs index bee0993ccf..fa0f33eda0 100644 --- a/crates/bevy_asset/src/server/mod.rs +++ b/crates/bevy_asset/src/server/mod.rs @@ -15,8 +15,8 @@ use crate::{ }, path::AssetPath, Asset, AssetEvent, AssetHandleProvider, AssetId, AssetLoadFailedEvent, AssetMetaCheck, Assets, - CompleteErasedLoadedAsset, DeserializeMetaError, ErasedLoadedAsset, Handle, LoadedUntypedAsset, - UnapprovedPathMode, UntypedAssetId, UntypedAssetLoadFailedEvent, UntypedHandle, + DeserializeMetaError, ErasedLoadedAsset, Handle, LoadedUntypedAsset, UnapprovedPathMode, + UntypedAssetId, UntypedAssetLoadFailedEvent, UntypedHandle, }; use alloc::{borrow::ToOwned, boxed::Box, vec, vec::Vec}; use alloc::{ @@ -796,18 +796,12 @@ impl AssetServer { /// Sends a load event for the given `loaded_asset` and does the same recursively for all /// labeled assets. - fn send_loaded_asset(&self, id: UntypedAssetId, mut complete_asset: CompleteErasedLoadedAsset) { - for (_, labeled_asset) in complete_asset.labeled_assets.drain() { - self.send_asset_event(InternalAssetEvent::Loaded { - id: labeled_asset.handle.id(), - loaded_asset: labeled_asset.asset, - }); + fn send_loaded_asset(&self, id: UntypedAssetId, mut loaded_asset: ErasedLoadedAsset) { + for (_, labeled_asset) in loaded_asset.labeled_assets.drain() { + self.send_loaded_asset(labeled_asset.handle.id(), labeled_asset.asset); } - self.send_asset_event(InternalAssetEvent::Loaded { - id, - loaded_asset: complete_asset.asset, - }); + self.send_asset_event(InternalAssetEvent::Loaded { id, loaded_asset }); } /// Kicks off a reload of the asset stored at the given path. This will only reload the asset if it currently loaded. @@ -1431,7 +1425,7 @@ impl AssetServer { reader: &mut dyn Reader, load_dependencies: bool, populate_hashes: bool, - ) -> Result { + ) -> Result { // TODO: experiment with this let asset_path = asset_path.clone_owned(); let load_context = diff --git a/crates/bevy_asset/src/transformer.rs b/crates/bevy_asset/src/transformer.rs index d2573582f1..6e76fce4dc 100644 --- a/crates/bevy_asset/src/transformer.rs +++ b/crates/bevy_asset/src/transformer.rs @@ -1,7 +1,4 @@ -use crate::{ - meta::Settings, Asset, CompleteErasedLoadedAsset, ErasedLoadedAsset, Handle, LabeledAsset, - UntypedHandle, -}; +use crate::{meta::Settings, Asset, ErasedLoadedAsset, Handle, LabeledAsset, UntypedHandle}; use alloc::boxed::Box; use atomicow::CowArc; use bevy_platform_support::collections::HashMap; @@ -59,11 +56,11 @@ impl DerefMut for TransformedAsset { impl TransformedAsset { /// Creates a new [`TransformedAsset`] from `asset` if its internal value matches `A`. - pub fn from_loaded(complete_asset: CompleteErasedLoadedAsset) -> Option { - if let Ok(value) = complete_asset.asset.value.downcast::() { + pub fn from_loaded(asset: ErasedLoadedAsset) -> Option { + if let Ok(value) = asset.value.downcast::() { return Some(TransformedAsset { value: *value, - labeled_assets: complete_asset.labeled_assets, + labeled_assets: asset.labeled_assets, }); } None @@ -90,13 +87,117 @@ impl TransformedAsset { &mut self.value } /// Returns the labeled asset, if it exists and matches this type. - pub fn get_labeled(&mut self, label: &'_ Q) -> Option<&mut B> + pub fn get_labeled(&mut self, label: &Q) -> Option> where CowArc<'static, str>: Borrow, Q: ?Sized + Hash + Eq, { let labeled = self.labeled_assets.get_mut(label)?; - labeled.asset.value.downcast_mut::() + let value = labeled.asset.value.downcast_mut::()?; + Some(TransformedSubAsset { + value, + labeled_assets: &mut labeled.asset.labeled_assets, + }) + } + /// Returns the type-erased labeled asset, if it exists and matches this type. + pub fn get_erased_labeled(&self, label: &Q) -> Option<&ErasedLoadedAsset> + where + CowArc<'static, str>: Borrow, + Q: ?Sized + Hash + Eq, + { + let labeled = self.labeled_assets.get(label)?; + Some(&labeled.asset) + } + /// Returns the [`UntypedHandle`] of the labeled asset with the provided 'label', if it exists. + pub fn get_untyped_handle(&self, label: &Q) -> Option + where + CowArc<'static, str>: Borrow, + Q: ?Sized + Hash + Eq, + { + let labeled = self.labeled_assets.get(label)?; + Some(labeled.handle.clone()) + } + /// Returns the [`Handle`] of the labeled asset with the provided 'label', if it exists and is an asset of type `B` + pub fn get_handle(&self, label: &Q) -> Option> + where + CowArc<'static, str>: Borrow, + Q: ?Sized + Hash + Eq, + { + let labeled = self.labeled_assets.get(label)?; + if let Ok(handle) = labeled.handle.clone().try_typed::() { + return Some(handle); + } + None + } + /// Adds `asset` as a labeled sub asset using `label` and `handle` + pub fn insert_labeled( + &mut self, + label: impl Into>, + handle: impl Into, + asset: impl Into, + ) { + let labeled = LabeledAsset { + asset: asset.into(), + handle: handle.into(), + }; + self.labeled_assets.insert(label.into(), labeled); + } + /// Iterate over all labels for "labeled assets" in the loaded asset + pub fn iter_labels(&self) -> impl Iterator { + self.labeled_assets.keys().map(|s| &**s) + } +} + +/// A labeled sub-asset of [`TransformedAsset`] +pub struct TransformedSubAsset<'a, A: Asset> { + value: &'a mut A, + labeled_assets: &'a mut HashMap, LabeledAsset>, +} + +impl<'a, A: Asset> Deref for TransformedSubAsset<'a, A> { + type Target = A; + fn deref(&self) -> &Self::Target { + self.value + } +} + +impl<'a, A: Asset> DerefMut for TransformedSubAsset<'a, A> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.value + } +} + +impl<'a, A: Asset> TransformedSubAsset<'a, A> { + /// Creates a new [`TransformedSubAsset`] from `asset` if its internal value matches `A`. + pub fn from_loaded(asset: &'a mut ErasedLoadedAsset) -> Option { + let value = asset.value.downcast_mut::()?; + Some(TransformedSubAsset { + value, + labeled_assets: &mut asset.labeled_assets, + }) + } + /// Retrieves the value of this asset. + #[inline] + pub fn get(&self) -> &A { + self.value + } + /// Mutably retrieves the value of this asset. + #[inline] + pub fn get_mut(&mut self) -> &mut A { + self.value + } + /// Returns the labeled asset, if it exists and matches this type. + pub fn get_labeled(&mut self, label: &Q) -> Option> + where + CowArc<'static, str>: Borrow, + Q: ?Sized + Hash + Eq, + { + let labeled = self.labeled_assets.get_mut(label)?; + let value = labeled.asset.value.downcast_mut::()?; + Some(TransformedSubAsset { + value, + labeled_assets: &mut labeled.asset.labeled_assets, + }) } /// Returns the type-erased labeled asset, if it exists and matches this type. pub fn get_erased_labeled(&self, label: &Q) -> Option<&ErasedLoadedAsset> diff --git a/crates/bevy_gltf/src/loader/mod.rs b/crates/bevy_gltf/src/loader/mod.rs index b4cac76132..4b5a4622d9 100644 --- a/crates/bevy_gltf/src/loader/mod.rs +++ b/crates/bevy_gltf/src/loader/mod.rs @@ -494,12 +494,10 @@ async fn load_gltf<'a, 'b, 'c>( ); } } - let handle = load_context - .add_labeled_asset( - GltfAssetLabel::Animation(animation.index()).to_string(), - animation_clip, - ) - .expect("animation indices are unique, so the label is unique"); + let handle = load_context.add_labeled_asset( + GltfAssetLabel::Animation(animation.index()).to_string(), + animation_clip, + ); if let Some(name) = animation.name() { named_animations.insert(name.into(), handle.clone()); } @@ -646,8 +644,7 @@ async fn load_gltf<'a, 'b, 'c>( RenderAssetUsages::default(), )?; let handle = load_context - .add_labeled_asset(morph_targets_label.to_string(), morph_target_image.0) - .expect("morph target indices are unique, so the label is unique"); + .add_labeled_asset(morph_targets_label.to_string(), morph_target_image.0); mesh.set_morph_targets(handle); let extras = gltf_mesh.extras().as_ref(); @@ -700,9 +697,7 @@ async fn load_gltf<'a, 'b, 'c>( }); } - let mesh_handle = load_context - .add_labeled_asset(primitive_label.to_string(), mesh) - .expect("primitive indices are unique, so the label is unique"); + let mesh_handle = load_context.add_labeled_asset(primitive_label.to_string(), mesh); primitives.push(super::GltfPrimitive::new( &gltf_mesh, &primitive, @@ -726,9 +721,7 @@ async fn load_gltf<'a, 'b, 'c>( gltf_mesh.extras().as_deref().map(GltfExtras::from), ); - let handle = load_context - .add_labeled_asset(mesh.asset_label().to_string(), mesh) - .expect("mesh indices are unique, so the label is unique"); + let handle = load_context.add_labeled_asset(mesh.asset_label().to_string(), mesh); if let Some(name) = gltf_mesh.name() { named_meshes.insert(name.into(), handle.clone()); } @@ -746,12 +739,10 @@ async fn load_gltf<'a, 'b, 'c>( core::iter::repeat_n(Mat4::IDENTITY, gltf_skin.joints().len()).collect() }); - load_context - .add_labeled_asset( - GltfAssetLabel::InverseBindMatrices(gltf_skin.index()).to_string(), - SkinnedMeshInverseBindposes::from(local_to_bone_bind_matrices), - ) - .expect("inverse bind matrix indices are unique, so the label is unique") + load_context.add_labeled_asset( + GltfAssetLabel::InverseBindMatrices(gltf_skin.index()).to_string(), + SkinnedMeshInverseBindposes::from(local_to_bone_bind_matrices), + ) }) .collect(); @@ -800,8 +791,7 @@ async fn load_gltf<'a, 'b, 'c>( ); let handle = load_context - .add_labeled_asset(gltf_skin.asset_label().to_string(), gltf_skin) - .expect("skin indices are unique, so the label is unique"); + .add_labeled_asset(gltf_skin.asset_label().to_string(), gltf_skin); if let Some(name) = skin.name() { named_skins.insert(name.into(), handle.clone()); @@ -834,9 +824,7 @@ async fn load_gltf<'a, 'b, 'c>( #[cfg(feature = "bevy_animation")] let gltf_node = gltf_node.with_animation_root(animation_roots.contains(&node.index())); - let handle = load_context - .add_labeled_asset(gltf_node.asset_label().to_string(), gltf_node) - .expect("node indices are unique, so the label is unique"); + let handle = load_context.add_labeled_asset(gltf_node.asset_label().to_string(), gltf_node); nodes.insert(node.index(), handle.clone()); if let Some(name) = node.name() { named_nodes.insert(name.into(), handle); @@ -925,12 +913,10 @@ async fn load_gltf<'a, 'b, 'c>( }); } let loaded_scene = scene_load_context.finish(Scene::new(world)); - let scene_handle = load_context - .add_loaded_labeled_asset( - GltfAssetLabel::Scene(scene.index()).to_string(), - loaded_scene, - ) - .expect("scene indices are unique, so the label is unique"); + let scene_handle = load_context.add_loaded_labeled_asset( + GltfAssetLabel::Scene(scene.index()).to_string(), + loaded_scene, + ); if let Some(name) = scene.name() { named_scenes.insert(name.into(), scene_handle.clone()); @@ -1041,75 +1027,71 @@ fn load_material( is_scale_inverted: bool, ) -> Handle { let material_label = material_label(material, is_scale_inverted); - load_context - .labeled_asset_scope(material_label.to_string(), |load_context| { - let pbr = material.pbr_metallic_roughness(); + load_context.labeled_asset_scope(material_label.to_string(), |load_context| { + let pbr = material.pbr_metallic_roughness(); - // TODO: handle missing label handle errors here? - let color = pbr.base_color_factor(); - let base_color_channel = pbr - .base_color_texture() - .map(|info| uv_channel(material, "base color", info.tex_coord())) - .unwrap_or_default(); - let base_color_texture = pbr - .base_color_texture() - .map(|info| texture_handle(&info.texture(), load_context)); + // TODO: handle missing label handle errors here? + let color = pbr.base_color_factor(); + let base_color_channel = pbr + .base_color_texture() + .map(|info| uv_channel(material, "base color", info.tex_coord())) + .unwrap_or_default(); + let base_color_texture = pbr + .base_color_texture() + .map(|info| texture_handle(&info.texture(), load_context)); - let uv_transform = pbr - .base_color_texture() - .and_then(|info| info.texture_transform().map(texture_transform_to_affine2)) - .unwrap_or_default(); + let uv_transform = pbr + .base_color_texture() + .and_then(|info| info.texture_transform().map(texture_transform_to_affine2)) + .unwrap_or_default(); - let normal_map_channel = material - .normal_texture() - .map(|info| uv_channel(material, "normal map", info.tex_coord())) - .unwrap_or_default(); - let normal_map_texture: Option> = - material.normal_texture().map(|normal_texture| { - // TODO: handle normal_texture.scale - texture_handle(&normal_texture.texture(), load_context) - }); - - let metallic_roughness_channel = pbr - .metallic_roughness_texture() - .map(|info| uv_channel(material, "metallic/roughness", info.tex_coord())) - .unwrap_or_default(); - let metallic_roughness_texture = pbr.metallic_roughness_texture().map(|info| { - warn_on_differing_texture_transforms( - material, - &info, - uv_transform, - "metallic/roughness", - ); - texture_handle(&info.texture(), load_context) + let normal_map_channel = material + .normal_texture() + .map(|info| uv_channel(material, "normal map", info.tex_coord())) + .unwrap_or_default(); + let normal_map_texture: Option> = + material.normal_texture().map(|normal_texture| { + // TODO: handle normal_texture.scale + texture_handle(&normal_texture.texture(), load_context) }); - let occlusion_channel = material - .occlusion_texture() - .map(|info| uv_channel(material, "occlusion", info.tex_coord())) - .unwrap_or_default(); - let occlusion_texture = material.occlusion_texture().map(|occlusion_texture| { - // TODO: handle occlusion_texture.strength() (a scalar multiplier for occlusion strength) - texture_handle(&occlusion_texture.texture(), load_context) - }); + let metallic_roughness_channel = pbr + .metallic_roughness_texture() + .map(|info| uv_channel(material, "metallic/roughness", info.tex_coord())) + .unwrap_or_default(); + let metallic_roughness_texture = pbr.metallic_roughness_texture().map(|info| { + warn_on_differing_texture_transforms( + material, + &info, + uv_transform, + "metallic/roughness", + ); + texture_handle(&info.texture(), load_context) + }); - let emissive = material.emissive_factor(); - let emissive_channel = material - .emissive_texture() - .map(|info| uv_channel(material, "emissive", info.tex_coord())) - .unwrap_or_default(); - let emissive_texture = material.emissive_texture().map(|info| { - // TODO: handle occlusion_texture.strength() (a scalar multiplier for occlusion strength) - warn_on_differing_texture_transforms(material, &info, uv_transform, "emissive"); - texture_handle(&info.texture(), load_context) - }); + let occlusion_channel = material + .occlusion_texture() + .map(|info| uv_channel(material, "occlusion", info.tex_coord())) + .unwrap_or_default(); + let occlusion_texture = material.occlusion_texture().map(|occlusion_texture| { + // TODO: handle occlusion_texture.strength() (a scalar multiplier for occlusion strength) + texture_handle(&occlusion_texture.texture(), load_context) + }); - #[cfg(feature = "pbr_transmission_textures")] - let ( - specular_transmission, - specular_transmission_channel, - specular_transmission_texture, - ) = material + let emissive = material.emissive_factor(); + let emissive_channel = material + .emissive_texture() + .map(|info| uv_channel(material, "emissive", info.tex_coord())) + .unwrap_or_default(); + let emissive_texture = material.emissive_texture().map(|info| { + // TODO: handle occlusion_texture.strength() (a scalar multiplier for occlusion strength) + warn_on_differing_texture_transforms(material, &info, uv_transform, "emissive"); + texture_handle(&info.texture(), load_context) + }); + + #[cfg(feature = "pbr_transmission_textures")] + let (specular_transmission, specular_transmission_channel, specular_transmission_texture) = + material .transmission() .map_or((0.0, UvChannel::Uv0, None), |transmission| { let specular_transmission_channel = transmission @@ -1129,156 +1111,152 @@ fn load_material( ) }); - #[cfg(not(feature = "pbr_transmission_textures"))] - let specular_transmission = material - .transmission() - .map_or(0.0, |transmission| transmission.transmission_factor()); + #[cfg(not(feature = "pbr_transmission_textures"))] + let specular_transmission = material + .transmission() + .map_or(0.0, |transmission| transmission.transmission_factor()); - #[cfg(feature = "pbr_transmission_textures")] - let ( - thickness, - thickness_channel, - thickness_texture, - attenuation_distance, - attenuation_color, - ) = material.volume().map_or( - (0.0, UvChannel::Uv0, None, f32::INFINITY, [1.0, 1.0, 1.0]), - |volume| { - let thickness_channel = volume - .thickness_texture() - .map(|info| uv_channel(material, "thickness", info.tex_coord())) - .unwrap_or_default(); - let thickness_texture: Option> = - volume.thickness_texture().map(|thickness_texture| { - texture_handle(&thickness_texture.texture(), load_context) - }); + #[cfg(feature = "pbr_transmission_textures")] + let ( + thickness, + thickness_channel, + thickness_texture, + attenuation_distance, + attenuation_color, + ) = material.volume().map_or( + (0.0, UvChannel::Uv0, None, f32::INFINITY, [1.0, 1.0, 1.0]), + |volume| { + let thickness_channel = volume + .thickness_texture() + .map(|info| uv_channel(material, "thickness", info.tex_coord())) + .unwrap_or_default(); + let thickness_texture: Option> = + volume.thickness_texture().map(|thickness_texture| { + texture_handle(&thickness_texture.texture(), load_context) + }); + ( + volume.thickness_factor(), + thickness_channel, + thickness_texture, + volume.attenuation_distance(), + volume.attenuation_color(), + ) + }, + ); + + #[cfg(not(feature = "pbr_transmission_textures"))] + let (thickness, attenuation_distance, attenuation_color) = + material + .volume() + .map_or((0.0, f32::INFINITY, [1.0, 1.0, 1.0]), |volume| { ( volume.thickness_factor(), - thickness_channel, - thickness_texture, volume.attenuation_distance(), volume.attenuation_color(), ) - }, - ); + }); - #[cfg(not(feature = "pbr_transmission_textures"))] - let (thickness, attenuation_distance, attenuation_color) = - material - .volume() - .map_or((0.0, f32::INFINITY, [1.0, 1.0, 1.0]), |volume| { - ( - volume.thickness_factor(), - volume.attenuation_distance(), - volume.attenuation_color(), - ) - }); + let ior = material.ior().unwrap_or(1.5); - let ior = material.ior().unwrap_or(1.5); + // Parse the `KHR_materials_clearcoat` extension data if necessary. + let clearcoat = + ClearcoatExtension::parse(load_context, document, material).unwrap_or_default(); - // Parse the `KHR_materials_clearcoat` extension data if necessary. - let clearcoat = - ClearcoatExtension::parse(load_context, document, material).unwrap_or_default(); + // Parse the `KHR_materials_anisotropy` extension data if necessary. + let anisotropy = + AnisotropyExtension::parse(load_context, document, material).unwrap_or_default(); - // Parse the `KHR_materials_anisotropy` extension data if necessary. - let anisotropy = - AnisotropyExtension::parse(load_context, document, material).unwrap_or_default(); + // Parse the `KHR_materials_specular` extension data if necessary. + let specular = + SpecularExtension::parse(load_context, document, material).unwrap_or_default(); - // Parse the `KHR_materials_specular` extension data if necessary. - let specular = - SpecularExtension::parse(load_context, document, material).unwrap_or_default(); + // We need to operate in the Linear color space and be willing to exceed 1.0 in our channels + let base_emissive = LinearRgba::rgb(emissive[0], emissive[1], emissive[2]); + let emissive = base_emissive * material.emissive_strength().unwrap_or(1.0); - // We need to operate in the Linear color space and be willing to exceed 1.0 in our channels - let base_emissive = LinearRgba::rgb(emissive[0], emissive[1], emissive[2]); - let emissive = base_emissive * material.emissive_strength().unwrap_or(1.0); - - StandardMaterial { - base_color: Color::linear_rgba(color[0], color[1], color[2], color[3]), - base_color_channel, - base_color_texture, - perceptual_roughness: pbr.roughness_factor(), - metallic: pbr.metallic_factor(), - metallic_roughness_channel, - metallic_roughness_texture, - normal_map_channel, - normal_map_texture, - double_sided: material.double_sided(), - cull_mode: if material.double_sided() { - None - } else if is_scale_inverted { - Some(Face::Front) - } else { - Some(Face::Back) - }, - occlusion_channel, - occlusion_texture, - emissive, - emissive_channel, - emissive_texture, - specular_transmission, - #[cfg(feature = "pbr_transmission_textures")] - specular_transmission_channel, - #[cfg(feature = "pbr_transmission_textures")] - specular_transmission_texture, - thickness, - #[cfg(feature = "pbr_transmission_textures")] - thickness_channel, - #[cfg(feature = "pbr_transmission_textures")] - thickness_texture, - ior, - attenuation_distance, - attenuation_color: Color::linear_rgb( - attenuation_color[0], - attenuation_color[1], - attenuation_color[2], - ), - unlit: material.unlit(), - alpha_mode: alpha_mode(material), - uv_transform, - clearcoat: clearcoat.clearcoat_factor.unwrap_or_default() as f32, - clearcoat_perceptual_roughness: clearcoat - .clearcoat_roughness_factor - .unwrap_or_default() as f32, - #[cfg(feature = "pbr_multi_layer_material_textures")] - clearcoat_channel: clearcoat.clearcoat_channel, - #[cfg(feature = "pbr_multi_layer_material_textures")] - clearcoat_texture: clearcoat.clearcoat_texture, - #[cfg(feature = "pbr_multi_layer_material_textures")] - clearcoat_roughness_channel: clearcoat.clearcoat_roughness_channel, - #[cfg(feature = "pbr_multi_layer_material_textures")] - clearcoat_roughness_texture: clearcoat.clearcoat_roughness_texture, - #[cfg(feature = "pbr_multi_layer_material_textures")] - clearcoat_normal_channel: clearcoat.clearcoat_normal_channel, - #[cfg(feature = "pbr_multi_layer_material_textures")] - clearcoat_normal_texture: clearcoat.clearcoat_normal_texture, - anisotropy_strength: anisotropy.anisotropy_strength.unwrap_or_default() as f32, - anisotropy_rotation: anisotropy.anisotropy_rotation.unwrap_or_default() as f32, - #[cfg(feature = "pbr_anisotropy_texture")] - anisotropy_channel: anisotropy.anisotropy_channel, - #[cfg(feature = "pbr_anisotropy_texture")] - anisotropy_texture: anisotropy.anisotropy_texture, - // From the `KHR_materials_specular` spec: - // - reflectance: specular.specular_factor.unwrap_or(1.0) as f32 * 0.5, - #[cfg(feature = "pbr_specular_textures")] - specular_channel: specular.specular_channel, - #[cfg(feature = "pbr_specular_textures")] - specular_texture: specular.specular_texture, - specular_tint: match specular.specular_color_factor { - Some(color) => { - Color::linear_rgb(color[0] as f32, color[1] as f32, color[2] as f32) - } - None => Color::WHITE, - }, - #[cfg(feature = "pbr_specular_textures")] - specular_tint_channel: specular.specular_color_channel, - #[cfg(feature = "pbr_specular_textures")] - specular_tint_texture: specular.specular_color_texture, - ..Default::default() - } - }) - .expect("material indices are unique, so the label is unique") + StandardMaterial { + base_color: Color::linear_rgba(color[0], color[1], color[2], color[3]), + base_color_channel, + base_color_texture, + perceptual_roughness: pbr.roughness_factor(), + metallic: pbr.metallic_factor(), + metallic_roughness_channel, + metallic_roughness_texture, + normal_map_channel, + normal_map_texture, + double_sided: material.double_sided(), + cull_mode: if material.double_sided() { + None + } else if is_scale_inverted { + Some(Face::Front) + } else { + Some(Face::Back) + }, + occlusion_channel, + occlusion_texture, + emissive, + emissive_channel, + emissive_texture, + specular_transmission, + #[cfg(feature = "pbr_transmission_textures")] + specular_transmission_channel, + #[cfg(feature = "pbr_transmission_textures")] + specular_transmission_texture, + thickness, + #[cfg(feature = "pbr_transmission_textures")] + thickness_channel, + #[cfg(feature = "pbr_transmission_textures")] + thickness_texture, + ior, + attenuation_distance, + attenuation_color: Color::linear_rgb( + attenuation_color[0], + attenuation_color[1], + attenuation_color[2], + ), + unlit: material.unlit(), + alpha_mode: alpha_mode(material), + uv_transform, + clearcoat: clearcoat.clearcoat_factor.unwrap_or_default() as f32, + clearcoat_perceptual_roughness: clearcoat.clearcoat_roughness_factor.unwrap_or_default() + as f32, + #[cfg(feature = "pbr_multi_layer_material_textures")] + clearcoat_channel: clearcoat.clearcoat_channel, + #[cfg(feature = "pbr_multi_layer_material_textures")] + clearcoat_texture: clearcoat.clearcoat_texture, + #[cfg(feature = "pbr_multi_layer_material_textures")] + clearcoat_roughness_channel: clearcoat.clearcoat_roughness_channel, + #[cfg(feature = "pbr_multi_layer_material_textures")] + clearcoat_roughness_texture: clearcoat.clearcoat_roughness_texture, + #[cfg(feature = "pbr_multi_layer_material_textures")] + clearcoat_normal_channel: clearcoat.clearcoat_normal_channel, + #[cfg(feature = "pbr_multi_layer_material_textures")] + clearcoat_normal_texture: clearcoat.clearcoat_normal_texture, + anisotropy_strength: anisotropy.anisotropy_strength.unwrap_or_default() as f32, + anisotropy_rotation: anisotropy.anisotropy_rotation.unwrap_or_default() as f32, + #[cfg(feature = "pbr_anisotropy_texture")] + anisotropy_channel: anisotropy.anisotropy_channel, + #[cfg(feature = "pbr_anisotropy_texture")] + anisotropy_texture: anisotropy.anisotropy_texture, + // From the `KHR_materials_specular` spec: + // + reflectance: specular.specular_factor.unwrap_or(1.0) as f32 * 0.5, + #[cfg(feature = "pbr_specular_textures")] + specular_channel: specular.specular_channel, + #[cfg(feature = "pbr_specular_textures")] + specular_texture: specular.specular_texture, + specular_tint: match specular.specular_color_factor { + Some(color) => Color::linear_rgb(color[0] as f32, color[1] as f32, color[2] as f32), + None => Color::WHITE, + }, + #[cfg(feature = "pbr_specular_textures")] + specular_tint_channel: specular.specular_color_channel, + #[cfg(feature = "pbr_specular_textures")] + specular_tint_texture: specular.specular_color_texture, + ..Default::default() + } + }) } /// Loads a glTF node. @@ -1697,9 +1675,9 @@ impl ImageOrPath { handles: &mut Vec>, ) { let handle = match self { - ImageOrPath::Image { label, image } => load_context - .add_labeled_asset(label.to_string(), image) - .expect("texture indices are unique, so the label is unique"), + ImageOrPath::Image { label, image } => { + load_context.add_labeled_asset(label.to_string(), image) + } ImageOrPath::Path { path, is_srgb, diff --git a/examples/asset/asset_decompression.rs b/examples/asset/asset_decompression.rs index edc42ab853..e514924ca9 100644 --- a/examples/asset/asset_decompression.rs +++ b/examples/asset/asset_decompression.rs @@ -3,7 +3,7 @@ use bevy::{ asset::{ io::{Reader, VecReader}, - AssetLoader, CompleteErasedLoadedAsset, LoadContext, LoadDirectError, + AssetLoader, ErasedLoadedAsset, LoadContext, LoadDirectError, }, prelude::*, reflect::TypePath, @@ -14,7 +14,7 @@ use thiserror::Error; #[derive(Asset, TypePath)] struct GzAsset { - uncompressed: CompleteErasedLoadedAsset, + uncompressed: ErasedLoadedAsset, } #[derive(Default)] diff --git a/examples/asset/processing/asset_processing.rs b/examples/asset/processing/asset_processing.rs index b24f7afb14..47a12fc6bb 100644 --- a/examples/asset/processing/asset_processing.rs +++ b/examples/asset/processing/asset_processing.rs @@ -149,15 +149,15 @@ 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 complete_loaded = load_context + let loaded = load_context .loader() .immediate() .load::(&embedded) .await?; - base_text.push_str(&complete_loaded.get_asset().get().0); + base_text.push_str(&loaded.get().0); } for (path, settings_override) in ron.dependencies_with_settings { - let complete_loaded = load_context + let loaded = load_context .loader() .with_settings(move |settings| { *settings = settings_override.clone(); @@ -165,7 +165,7 @@ impl AssetLoader for CoolTextLoader { .immediate() .load::(&path) .await?; - base_text.push_str(&complete_loaded.get_asset().get().0); + base_text.push_str(&loaded.get().0); } Ok(CoolText { text: base_text,