- Fixes #18010. - Revert the offending PRs! These are #15481 and #18013. We now no longer get an error if there are duplicate subassets. - In theory we could untangle #18013 from #15481, but that may be tricky, and may still introduce regressions. To avoid this worry (since we're already in RC mode), I am just reverting both. - This is just a revert. --- <Remove the migration guides for #15481 and #18013> I will make a PR to the bevy_website repo after this is merged.
This commit is contained in:
parent
1a6b480ba2
commit
fe3656fa8b
@ -22,7 +22,6 @@ trace = []
|
|||||||
bevy_app = { path = "../bevy_app", version = "0.16.0-rc.2" }
|
bevy_app = { path = "../bevy_app", version = "0.16.0-rc.2" }
|
||||||
bevy_asset_macros = { path = "macros", 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_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 = [
|
bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-rc.2", features = [
|
||||||
"uuid",
|
"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]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
notify-debouncer-full = { version = "0.5.0", optional = true }
|
notify-debouncer-full = { version = "0.5.0", optional = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
bevy_log = { path = "../bevy_log", version = "0.16.0-rc.2" }
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
|
@ -670,7 +670,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
loader::{AssetLoader, LoadContext},
|
loader::{AssetLoader, LoadContext},
|
||||||
Asset, AssetApp, AssetEvent, AssetId, AssetLoadError, AssetLoadFailedEvent, AssetPath,
|
Asset, AssetApp, AssetEvent, AssetId, AssetLoadError, AssetLoadFailedEvent, AssetPath,
|
||||||
AssetPlugin, AssetServer, Assets, DuplicateLabelAssetError, LoadState, UnapprovedPathMode,
|
AssetPlugin, AssetServer, Assets, LoadState, UnapprovedPathMode,
|
||||||
};
|
};
|
||||||
use alloc::{
|
use alloc::{
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
@ -726,8 +726,6 @@ mod tests {
|
|||||||
CannotLoadDependency { dependency: AssetPath<'static> },
|
CannotLoadDependency { dependency: AssetPath<'static> },
|
||||||
#[error("A RON error occurred during loading")]
|
#[error("A RON error occurred during loading")]
|
||||||
RonSpannedError(#[from] ron::error::SpannedError),
|
RonSpannedError(#[from] ron::error::SpannedError),
|
||||||
#[error(transparent)]
|
|
||||||
DuplicateLabelAssetError(#[from] DuplicateLabelAssetError),
|
|
||||||
#[error("An IO error occurred during loading")]
|
#[error("An IO error occurred during loading")]
|
||||||
Io(#[from] std::io::Error),
|
Io(#[from] std::io::Error),
|
||||||
}
|
}
|
||||||
@ -758,7 +756,7 @@ mod tests {
|
|||||||
.map_err(|_| Self::Error::CannotLoadDependency {
|
.map_err(|_| Self::Error::CannotLoadDependency {
|
||||||
dependency: dep.into(),
|
dependency: dep.into(),
|
||||||
})?;
|
})?;
|
||||||
let cool = loaded.get_asset().get();
|
let cool = loaded.get();
|
||||||
embedded.push_str(&cool.text);
|
embedded.push_str(&cool.text);
|
||||||
}
|
}
|
||||||
Ok(CoolText {
|
Ok(CoolText {
|
||||||
@ -773,7 +771,7 @@ mod tests {
|
|||||||
.sub_texts
|
.sub_texts
|
||||||
.drain(..)
|
.drain(..)
|
||||||
.map(|text| load_context.add_labeled_asset(text.clone(), SubText { text }))
|
.map(|text| load_context.add_labeled_asset(text.clone(), SubText { text }))
|
||||||
.collect::<Result<Vec<_>, _>>()?,
|
.collect(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1811,49 +1809,6 @@ mod tests {
|
|||||||
app.world_mut().run_schedule(Update);
|
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::<CoolText>()
|
|
||||||
.init_asset::<SubText>()
|
|
||||||
.register_asset_loader(CoolTextLoader);
|
|
||||||
|
|
||||||
let asset_server = app.world().resource::<AssetServer>().clone();
|
|
||||||
let handle = asset_server.load::<CoolText>("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
|
// 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.
|
// not capable of loading subassets when doing nested immediate loads.
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -60,7 +60,7 @@ pub trait ErasedAssetLoader: Send + Sync + 'static {
|
|||||||
load_context: LoadContext<'a>,
|
load_context: LoadContext<'a>,
|
||||||
) -> BoxedFuture<
|
) -> BoxedFuture<
|
||||||
'a,
|
'a,
|
||||||
Result<CompleteErasedLoadedAsset, Box<dyn core::error::Error + Send + Sync + 'static>>,
|
Result<ErasedLoadedAsset, Box<dyn core::error::Error + Send + Sync + 'static>>,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
/// Returns a list of extensions supported by this asset loader, without the preceding dot.
|
/// Returns a list of extensions supported by this asset loader, without the preceding dot.
|
||||||
@ -91,7 +91,7 @@ where
|
|||||||
mut load_context: LoadContext<'a>,
|
mut load_context: LoadContext<'a>,
|
||||||
) -> BoxedFuture<
|
) -> BoxedFuture<
|
||||||
'a,
|
'a,
|
||||||
Result<CompleteErasedLoadedAsset, Box<dyn core::error::Error + Send + Sync + 'static>>,
|
Result<ErasedLoadedAsset, Box<dyn core::error::Error + Send + Sync + 'static>>,
|
||||||
> {
|
> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let settings = meta
|
let settings = meta
|
||||||
@ -152,6 +152,7 @@ pub struct LoadedAsset<A: Asset> {
|
|||||||
pub(crate) value: A,
|
pub(crate) value: A,
|
||||||
pub(crate) dependencies: HashSet<UntypedAssetId>,
|
pub(crate) dependencies: HashSet<UntypedAssetId>,
|
||||||
pub(crate) loader_dependencies: HashMap<AssetPath<'static>, AssetHash>,
|
pub(crate) loader_dependencies: HashMap<AssetPath<'static>, AssetHash>,
|
||||||
|
pub(crate) labeled_assets: HashMap<CowArc<'static, str>, LabeledAsset>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: Asset> LoadedAsset<A> {
|
impl<A: Asset> LoadedAsset<A> {
|
||||||
@ -165,6 +166,7 @@ impl<A: Asset> LoadedAsset<A> {
|
|||||||
value,
|
value,
|
||||||
dependencies,
|
dependencies,
|
||||||
loader_dependencies: HashMap::default(),
|
loader_dependencies: HashMap::default(),
|
||||||
|
labeled_assets: HashMap::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,6 +179,19 @@ impl<A: Asset> LoadedAsset<A> {
|
|||||||
pub fn get(&self) -> &A {
|
pub fn get(&self) -> &A {
|
||||||
&self.value
|
&self.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the [`ErasedLoadedAsset`] for the given label, if it exists.
|
||||||
|
pub fn get_labeled(
|
||||||
|
&self,
|
||||||
|
label: impl Into<CowArc<'static, str>>,
|
||||||
|
) -> 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<Item = &str> {
|
||||||
|
self.labeled_assets.keys().map(|s| &**s)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: Asset> From<A> for LoadedAsset<A> {
|
impl<A: Asset> From<A> for LoadedAsset<A> {
|
||||||
@ -190,6 +205,7 @@ pub struct ErasedLoadedAsset {
|
|||||||
pub(crate) value: Box<dyn AssetContainer>,
|
pub(crate) value: Box<dyn AssetContainer>,
|
||||||
pub(crate) dependencies: HashSet<UntypedAssetId>,
|
pub(crate) dependencies: HashSet<UntypedAssetId>,
|
||||||
pub(crate) loader_dependencies: HashMap<AssetPath<'static>, AssetHash>,
|
pub(crate) loader_dependencies: HashMap<AssetPath<'static>, AssetHash>,
|
||||||
|
pub(crate) labeled_assets: HashMap<CowArc<'static, str>, LabeledAsset>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: Asset> From<LoadedAsset<A>> for ErasedLoadedAsset {
|
impl<A: Asset> From<LoadedAsset<A>> for ErasedLoadedAsset {
|
||||||
@ -198,6 +214,7 @@ impl<A: Asset> From<LoadedAsset<A>> for ErasedLoadedAsset {
|
|||||||
value: Box::new(asset.value),
|
value: Box::new(asset.value),
|
||||||
dependencies: asset.dependencies,
|
dependencies: asset.dependencies,
|
||||||
loader_dependencies: asset.loader_dependencies,
|
loader_dependencies: asset.loader_dependencies,
|
||||||
|
labeled_assets: asset.labeled_assets,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -224,6 +241,19 @@ impl ErasedLoadedAsset {
|
|||||||
self.value.asset_type_name()
|
self.value.asset_type_name()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the [`ErasedLoadedAsset`] for the given label, if it exists.
|
||||||
|
pub fn get_labeled(
|
||||||
|
&self,
|
||||||
|
label: impl Into<CowArc<'static, str>>,
|
||||||
|
) -> 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<Item = &str> {
|
||||||
|
self.labeled_assets.keys().map(|s| &**s)
|
||||||
|
}
|
||||||
|
|
||||||
/// Cast this loaded asset as the given type. If the type does not match,
|
/// Cast this loaded asset as the given type. If the type does not match,
|
||||||
/// the original type-erased asset is returned.
|
/// the original type-erased asset is returned.
|
||||||
pub fn downcast<A: Asset>(mut self) -> Result<LoadedAsset<A>, ErasedLoadedAsset> {
|
pub fn downcast<A: Asset>(mut self) -> Result<LoadedAsset<A>, ErasedLoadedAsset> {
|
||||||
@ -232,6 +262,7 @@ impl ErasedLoadedAsset {
|
|||||||
value: *value,
|
value: *value,
|
||||||
dependencies: self.dependencies,
|
dependencies: self.dependencies,
|
||||||
loader_dependencies: self.loader_dependencies,
|
loader_dependencies: self.loader_dependencies,
|
||||||
|
labeled_assets: self.labeled_assets,
|
||||||
}),
|
}),
|
||||||
Err(value) => {
|
Err(value) => {
|
||||||
self.value = value;
|
self.value = value;
|
||||||
@ -259,100 +290,6 @@ impl<A: Asset> AssetContainer for A {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A loaded asset and all its loaded subassets.
|
|
||||||
pub struct CompleteLoadedAsset<A: Asset> {
|
|
||||||
/// The loaded asset.
|
|
||||||
pub(crate) asset: LoadedAsset<A>,
|
|
||||||
/// The subassets by their label.
|
|
||||||
pub(crate) labeled_assets: HashMap<CowArc<'static, str>, LabeledAsset>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A: Asset> CompleteLoadedAsset<A> {
|
|
||||||
/// 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<A> {
|
|
||||||
&self.asset
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the [`ErasedLoadedAsset`] for the given label, if it exists.
|
|
||||||
pub fn get_labeled(
|
|
||||||
&self,
|
|
||||||
label: impl Into<CowArc<'static, str>>,
|
|
||||||
) -> 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<Item = &str> {
|
|
||||||
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<CowArc<'static, str>, 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<A: Asset>(self) -> Option<A> {
|
|
||||||
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<CowArc<'static, str>>,
|
|
||||||
) -> 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<Item = &str> {
|
|
||||||
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<A: Asset>(
|
|
||||||
mut self,
|
|
||||||
) -> Result<CompleteLoadedAsset<A>, CompleteErasedLoadedAsset> {
|
|
||||||
match self.asset.downcast::<A>() {
|
|
||||||
Ok(asset) => Ok(CompleteLoadedAsset {
|
|
||||||
asset,
|
|
||||||
labeled_assets: self.labeled_assets,
|
|
||||||
}),
|
|
||||||
Err(asset) => {
|
|
||||||
self.asset = asset;
|
|
||||||
Err(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A: Asset> From<CompleteLoadedAsset<A>> for CompleteErasedLoadedAsset {
|
|
||||||
fn from(value: CompleteLoadedAsset<A>) -> Self {
|
|
||||||
Self {
|
|
||||||
asset: value.asset.into(),
|
|
||||||
labeled_assets: value.labeled_assets,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An error that occurs when attempting to call [`NestedLoader::load`] which
|
/// An error that occurs when attempting to call [`NestedLoader::load`] which
|
||||||
/// is configured to work [immediately].
|
/// is configured to work [immediately].
|
||||||
///
|
///
|
||||||
@ -461,11 +398,11 @@ impl<'a> LoadContext<'a> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
label: String,
|
label: String,
|
||||||
load: impl FnOnce(&mut LoadContext) -> A,
|
load: impl FnOnce(&mut LoadContext) -> A,
|
||||||
) -> Result<Handle<A>, DuplicateLabelAssetError> {
|
) -> Handle<A> {
|
||||||
let mut context = self.begin_labeled_asset();
|
let mut context = self.begin_labeled_asset();
|
||||||
let asset = load(&mut context);
|
let asset = load(&mut context);
|
||||||
let complete_asset = context.finish(asset);
|
let loaded_asset = context.finish(asset);
|
||||||
self.add_loaded_labeled_asset(label, complete_asset)
|
self.add_loaded_labeled_asset(label, loaded_asset)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This will add the given `asset` as a "labeled [`Asset`]" with the `label` label.
|
/// 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.
|
/// new [`LoadContext`] to track the dependencies for the labeled asset.
|
||||||
///
|
///
|
||||||
/// See [`AssetPath`] for more on labeled assets.
|
/// See [`AssetPath`] for more on labeled assets.
|
||||||
pub fn add_labeled_asset<A: Asset>(
|
pub fn add_labeled_asset<A: Asset>(&mut self, label: String, asset: A) -> Handle<A> {
|
||||||
&mut self,
|
|
||||||
label: String,
|
|
||||||
asset: A,
|
|
||||||
) -> Result<Handle<A>, DuplicateLabelAssetError> {
|
|
||||||
self.labeled_asset_scope(label, |_| asset)
|
self.labeled_asset_scope(label, |_| asset)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -494,37 +427,22 @@ impl<'a> LoadContext<'a> {
|
|||||||
pub fn add_loaded_labeled_asset<A: Asset>(
|
pub fn add_loaded_labeled_asset<A: Asset>(
|
||||||
&mut self,
|
&mut self,
|
||||||
label: impl Into<CowArc<'static, str>>,
|
label: impl Into<CowArc<'static, str>>,
|
||||||
loaded_asset: CompleteLoadedAsset<A>,
|
loaded_asset: LoadedAsset<A>,
|
||||||
) -> Result<Handle<A>, DuplicateLabelAssetError> {
|
) -> Handle<A> {
|
||||||
let label = label.into();
|
let label = label.into();
|
||||||
let CompleteLoadedAsset {
|
let loaded_asset: ErasedLoadedAsset = loaded_asset.into();
|
||||||
asset,
|
|
||||||
labeled_assets,
|
|
||||||
} = loaded_asset;
|
|
||||||
let loaded_asset: ErasedLoadedAsset = asset.into();
|
|
||||||
let labeled_path = self.asset_path.clone().with_label(label.clone());
|
let labeled_path = self.asset_path.clone().with_label(label.clone());
|
||||||
let handle = self
|
let handle = self
|
||||||
.asset_server
|
.asset_server
|
||||||
.get_or_create_path_handle(labeled_path, None);
|
.get_or_create_path_handle(labeled_path, None);
|
||||||
let has_duplicate = self
|
self.labeled_assets.insert(
|
||||||
.labeled_assets
|
label,
|
||||||
.insert(
|
LabeledAsset {
|
||||||
label.clone(),
|
asset: loaded_asset,
|
||||||
LabeledAsset {
|
handle: handle.clone().untyped(),
|
||||||
asset: loaded_asset,
|
},
|
||||||
handle: handle.clone().untyped(),
|
);
|
||||||
},
|
handle
|
||||||
)
|
|
||||||
.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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if an asset with the label `label` exists in this context.
|
/// 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.
|
/// "Finishes" this context by populating the final [`Asset`] value.
|
||||||
pub fn finish<A: Asset>(self, value: A) -> CompleteLoadedAsset<A> {
|
pub fn finish<A: Asset>(self, value: A) -> LoadedAsset<A> {
|
||||||
CompleteLoadedAsset {
|
LoadedAsset {
|
||||||
asset: LoadedAsset {
|
value,
|
||||||
value,
|
dependencies: self.dependencies,
|
||||||
dependencies: self.dependencies,
|
loader_dependencies: self.loader_dependencies,
|
||||||
loader_dependencies: self.loader_dependencies,
|
|
||||||
},
|
|
||||||
labeled_assets: self.labeled_assets,
|
labeled_assets: self.labeled_assets,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -613,8 +529,8 @@ impl<'a> LoadContext<'a> {
|
|||||||
meta: &dyn AssetMetaDyn,
|
meta: &dyn AssetMetaDyn,
|
||||||
loader: &dyn ErasedAssetLoader,
|
loader: &dyn ErasedAssetLoader,
|
||||||
reader: &mut dyn Reader,
|
reader: &mut dyn Reader,
|
||||||
) -> Result<CompleteErasedLoadedAsset, LoadDirectError> {
|
) -> Result<ErasedLoadedAsset, LoadDirectError> {
|
||||||
let complete_asset = self
|
let loaded_asset = self
|
||||||
.asset_server
|
.asset_server
|
||||||
.load_with_meta_loader_and_reader(
|
.load_with_meta_loader_and_reader(
|
||||||
&path,
|
&path,
|
||||||
@ -632,7 +548,7 @@ impl<'a> LoadContext<'a> {
|
|||||||
let info = meta.processed_info().as_ref();
|
let info = meta.processed_info().as_ref();
|
||||||
let hash = info.map(|i| i.full_hash).unwrap_or_default();
|
let hash = info.map(|i| i.full_hash).unwrap_or_default();
|
||||||
self.loader_dependencies.insert(path, hash);
|
self.loader_dependencies.insert(path, hash);
|
||||||
Ok(complete_asset)
|
Ok(loaded_asset)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a builder for loading nested assets in this context.
|
/// 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.")]
|
#[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,
|
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);
|
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
io::Reader,
|
io::Reader,
|
||||||
meta::{meta_transform_settings, AssetMetaDyn, MetaTransform, Settings},
|
meta::{meta_transform_settings, AssetMetaDyn, MetaTransform, Settings},
|
||||||
Asset, AssetLoadError, AssetPath, CompleteErasedLoadedAsset, CompleteLoadedAsset,
|
Asset, AssetLoadError, AssetPath, ErasedAssetLoader, ErasedLoadedAsset, Handle, LoadContext,
|
||||||
ErasedAssetLoader, Handle, LoadContext, LoadDirectError, LoadedUntypedAsset, UntypedHandle,
|
LoadDirectError, LoadedAsset, LoadedUntypedAsset, UntypedHandle,
|
||||||
};
|
};
|
||||||
use alloc::{borrow::ToOwned, boxed::Box, sync::Arc};
|
use alloc::{borrow::ToOwned, boxed::Box, sync::Arc};
|
||||||
use core::any::TypeId;
|
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,
|
/// 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
|
/// use [`with_dynamic_type`] followed by [`load`] to start loading an asset
|
||||||
/// of that type. This lets you get an [`UntypedHandle`] (via [`Deferred`]),
|
/// 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
|
/// - in [`UnknownTyped`]: loading either a type-erased version of the asset
|
||||||
/// ([`CompleteErasedLoadedAsset`]), or a handle *to a handle* of the actual
|
/// ([`ErasedLoadedAsset`]), or a handle *to a handle* of the actual asset
|
||||||
/// asset ([`LoadedUntypedAsset`]).
|
/// ([`LoadedUntypedAsset`]).
|
||||||
///
|
///
|
||||||
/// If you have no idea what type of asset you will be loading (not even at
|
/// If you have no idea what type of asset you will be loading (not even at
|
||||||
/// runtime with a [`TypeId`]), use this.
|
/// runtime with a [`TypeId`]), use this.
|
||||||
@ -389,7 +389,7 @@ impl<'builder, 'reader, T> NestedLoader<'_, '_, T, Immediate<'builder, 'reader>>
|
|||||||
self,
|
self,
|
||||||
path: &AssetPath<'static>,
|
path: &AssetPath<'static>,
|
||||||
asset_type_id: Option<TypeId>,
|
asset_type_id: Option<TypeId>,
|
||||||
) -> Result<(Arc<dyn ErasedAssetLoader>, CompleteErasedLoadedAsset), LoadDirectError> {
|
) -> Result<(Arc<dyn ErasedAssetLoader>, ErasedLoadedAsset), LoadDirectError> {
|
||||||
if path.label().is_some() {
|
if path.label().is_some() {
|
||||||
return Err(LoadDirectError::RequestedSubasset(path.clone()));
|
return Err(LoadDirectError::RequestedSubasset(path.clone()));
|
||||||
}
|
}
|
||||||
@ -454,7 +454,7 @@ impl NestedLoader<'_, '_, StaticTyped, Immediate<'_, '_>> {
|
|||||||
pub async fn load<'p, A: Asset>(
|
pub async fn load<'p, A: Asset>(
|
||||||
self,
|
self,
|
||||||
path: impl Into<AssetPath<'p>>,
|
path: impl Into<AssetPath<'p>>,
|
||||||
) -> Result<CompleteLoadedAsset<A>, LoadDirectError> {
|
) -> Result<LoadedAsset<A>, LoadDirectError> {
|
||||||
let path = path.into().into_owned();
|
let path = path.into().into_owned();
|
||||||
self.load_internal(&path, Some(TypeId::of::<A>()))
|
self.load_internal(&path, Some(TypeId::of::<A>()))
|
||||||
.await
|
.await
|
||||||
@ -484,7 +484,7 @@ impl NestedLoader<'_, '_, DynamicTyped, Immediate<'_, '_>> {
|
|||||||
pub async fn load<'p>(
|
pub async fn load<'p>(
|
||||||
self,
|
self,
|
||||||
path: impl Into<AssetPath<'p>>,
|
path: impl Into<AssetPath<'p>>,
|
||||||
) -> Result<CompleteErasedLoadedAsset, LoadDirectError> {
|
) -> Result<ErasedLoadedAsset, LoadDirectError> {
|
||||||
let path = path.into().into_owned();
|
let path = path.into().into_owned();
|
||||||
let asset_type_id = Some(self.typing.asset_type_id);
|
let asset_type_id = Some(self.typing.asset_type_id);
|
||||||
self.load_internal(&path, asset_type_id)
|
self.load_internal(&path, asset_type_id)
|
||||||
@ -500,7 +500,7 @@ impl NestedLoader<'_, '_, UnknownTyped, Immediate<'_, '_>> {
|
|||||||
pub async fn load<'p>(
|
pub async fn load<'p>(
|
||||||
self,
|
self,
|
||||||
path: impl Into<AssetPath<'p>>,
|
path: impl Into<AssetPath<'p>>,
|
||||||
) -> Result<CompleteErasedLoadedAsset, LoadDirectError> {
|
) -> Result<ErasedLoadedAsset, LoadDirectError> {
|
||||||
let path = path.into().into_owned();
|
let path = path.into().into_owned();
|
||||||
self.load_internal(&path, None)
|
self.load_internal(&path, None)
|
||||||
.await
|
.await
|
||||||
|
@ -7,7 +7,7 @@ use crate::{
|
|||||||
processor::AssetProcessor,
|
processor::AssetProcessor,
|
||||||
saver::{AssetSaver, SavedAsset},
|
saver::{AssetSaver, SavedAsset},
|
||||||
transformer::{AssetTransformer, IdentityAssetTransformer, TransformedAsset},
|
transformer::{AssetTransformer, IdentityAssetTransformer, TransformedAsset},
|
||||||
AssetLoadError, AssetLoader, AssetPath, CompleteErasedLoadedAsset, DeserializeMetaError,
|
AssetLoadError, AssetLoader, AssetPath, DeserializeMetaError, ErasedLoadedAsset,
|
||||||
MissingAssetLoaderForExtensionError, MissingAssetLoaderForTypeNameError,
|
MissingAssetLoaderForExtensionError, MissingAssetLoaderForTypeNameError,
|
||||||
};
|
};
|
||||||
use alloc::{
|
use alloc::{
|
||||||
@ -305,15 +305,15 @@ impl<'a> ProcessContext<'a> {
|
|||||||
pub async fn load_source_asset<L: AssetLoader>(
|
pub async fn load_source_asset<L: AssetLoader>(
|
||||||
&mut self,
|
&mut self,
|
||||||
meta: AssetMeta<L, ()>,
|
meta: AssetMeta<L, ()>,
|
||||||
) -> Result<CompleteErasedLoadedAsset, AssetLoadError> {
|
) -> Result<ErasedLoadedAsset, AssetLoadError> {
|
||||||
let server = &self.processor.server;
|
let server = &self.processor.server;
|
||||||
let loader_name = core::any::type_name::<L>();
|
let loader_name = core::any::type_name::<L>();
|
||||||
let loader = server.get_asset_loader_with_type_name(loader_name).await?;
|
let loader = server.get_asset_loader_with_type_name(loader_name).await?;
|
||||||
let mut reader = SliceReader::new(self.asset_bytes);
|
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)
|
.load_with_meta_loader_and_reader(self.path, &meta, &*loader, &mut reader, false, true)
|
||||||
.await?;
|
.await?;
|
||||||
for (path, full_hash) in &complete_asset.asset.loader_dependencies {
|
for (path, full_hash) in &loaded_asset.loader_dependencies {
|
||||||
self.new_processed_info
|
self.new_processed_info
|
||||||
.process_dependencies
|
.process_dependencies
|
||||||
.push(ProcessDependencyInfo {
|
.push(ProcessDependencyInfo {
|
||||||
@ -321,7 +321,7 @@ impl<'a> ProcessContext<'a> {
|
|||||||
path: path.to_owned(),
|
path: path.to_owned(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Ok(complete_asset)
|
Ok(loaded_asset)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The path of the asset being processed.
|
/// The path of the asset being processed.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
io::Writer, meta::Settings, transformer::TransformedAsset, Asset, AssetLoader,
|
io::Writer, meta::Settings, transformer::TransformedAsset, Asset, AssetLoader,
|
||||||
CompleteErasedLoadedAsset, ErasedLoadedAsset, Handle, LabeledAsset, UntypedHandle,
|
ErasedLoadedAsset, Handle, LabeledAsset, UntypedHandle,
|
||||||
};
|
};
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use atomicow::CowArc;
|
use atomicow::CowArc;
|
||||||
@ -44,7 +44,7 @@ pub trait ErasedAssetSaver: Send + Sync + 'static {
|
|||||||
fn save<'a>(
|
fn save<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
writer: &'a mut Writer,
|
writer: &'a mut Writer,
|
||||||
complete_asset: &'a CompleteErasedLoadedAsset,
|
asset: &'a ErasedLoadedAsset,
|
||||||
settings: &'a dyn Settings,
|
settings: &'a dyn Settings,
|
||||||
) -> BoxedFuture<'a, Result<(), Box<dyn core::error::Error + Send + Sync + 'static>>>;
|
) -> BoxedFuture<'a, Result<(), Box<dyn core::error::Error + Send + Sync + 'static>>>;
|
||||||
|
|
||||||
@ -56,14 +56,14 @@ impl<S: AssetSaver> ErasedAssetSaver for S {
|
|||||||
fn save<'a>(
|
fn save<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
writer: &'a mut Writer,
|
writer: &'a mut Writer,
|
||||||
complete_asset: &'a CompleteErasedLoadedAsset,
|
asset: &'a ErasedLoadedAsset,
|
||||||
settings: &'a dyn Settings,
|
settings: &'a dyn Settings,
|
||||||
) -> BoxedFuture<'a, Result<(), Box<dyn core::error::Error + Send + Sync + 'static>>> {
|
) -> BoxedFuture<'a, Result<(), Box<dyn core::error::Error + Send + Sync + 'static>>> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let settings = settings
|
let settings = settings
|
||||||
.downcast_ref::<S::Settings>()
|
.downcast_ref::<S::Settings>()
|
||||||
.expect("AssetLoader settings should match the loader type");
|
.expect("AssetLoader settings should match the loader type");
|
||||||
let saved_asset = SavedAsset::<S::Asset>::from_loaded(complete_asset).unwrap();
|
let saved_asset = SavedAsset::<S::Asset>::from_loaded(asset).unwrap();
|
||||||
if let Err(err) = self.save(writer, saved_asset, settings).await {
|
if let Err(err) = self.save(writer, saved_asset, settings).await {
|
||||||
return Err(err.into());
|
return Err(err.into());
|
||||||
}
|
}
|
||||||
@ -91,11 +91,11 @@ impl<'a, A: Asset> Deref for SavedAsset<'a, A> {
|
|||||||
|
|
||||||
impl<'a, A: Asset> SavedAsset<'a, A> {
|
impl<'a, A: Asset> SavedAsset<'a, A> {
|
||||||
/// Creates a new [`SavedAsset`] from `asset` if its internal value matches `A`.
|
/// Creates a new [`SavedAsset`] from `asset` if its internal value matches `A`.
|
||||||
pub fn from_loaded(complete_asset: &'a CompleteErasedLoadedAsset) -> Option<Self> {
|
pub fn from_loaded(asset: &'a ErasedLoadedAsset) -> Option<Self> {
|
||||||
let value = complete_asset.asset.value.downcast_ref::<A>()?;
|
let value = asset.value.downcast_ref::<A>()?;
|
||||||
Some(SavedAsset {
|
Some(SavedAsset {
|
||||||
value,
|
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.
|
/// Returns the labeled asset, if it exists and matches this type.
|
||||||
pub fn get_labeled<B: Asset, Q>(&self, label: &Q) -> Option<&B>
|
pub fn get_labeled<B: Asset, Q>(&self, label: &Q) -> Option<SavedAsset<B>>
|
||||||
where
|
where
|
||||||
CowArc<'static, str>: Borrow<Q>,
|
CowArc<'static, str>: Borrow<Q>,
|
||||||
Q: ?Sized + Hash + Eq,
|
Q: ?Sized + Hash + Eq,
|
||||||
{
|
{
|
||||||
let labeled = self.labeled_assets.get(label)?;
|
let labeled = self.labeled_assets.get(label)?;
|
||||||
labeled.asset.value.downcast_ref::<B>()
|
let value = labeled.asset.value.downcast_ref::<B>()?;
|
||||||
|
Some(SavedAsset {
|
||||||
|
value,
|
||||||
|
labeled_assets: &labeled.asset.labeled_assets,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the type-erased labeled asset, if it exists and matches this type.
|
/// Returns the type-erased labeled asset, if it exists and matches this type.
|
||||||
|
@ -15,8 +15,8 @@ use crate::{
|
|||||||
},
|
},
|
||||||
path::AssetPath,
|
path::AssetPath,
|
||||||
Asset, AssetEvent, AssetHandleProvider, AssetId, AssetLoadFailedEvent, AssetMetaCheck, Assets,
|
Asset, AssetEvent, AssetHandleProvider, AssetId, AssetLoadFailedEvent, AssetMetaCheck, Assets,
|
||||||
CompleteErasedLoadedAsset, DeserializeMetaError, ErasedLoadedAsset, Handle, LoadedUntypedAsset,
|
DeserializeMetaError, ErasedLoadedAsset, Handle, LoadedUntypedAsset, UnapprovedPathMode,
|
||||||
UnapprovedPathMode, UntypedAssetId, UntypedAssetLoadFailedEvent, UntypedHandle,
|
UntypedAssetId, UntypedAssetLoadFailedEvent, UntypedHandle,
|
||||||
};
|
};
|
||||||
use alloc::{borrow::ToOwned, boxed::Box, vec, vec::Vec};
|
use alloc::{borrow::ToOwned, boxed::Box, vec, vec::Vec};
|
||||||
use alloc::{
|
use alloc::{
|
||||||
@ -796,18 +796,12 @@ impl AssetServer {
|
|||||||
|
|
||||||
/// Sends a load event for the given `loaded_asset` and does the same recursively for all
|
/// Sends a load event for the given `loaded_asset` and does the same recursively for all
|
||||||
/// labeled assets.
|
/// labeled assets.
|
||||||
fn send_loaded_asset(&self, id: UntypedAssetId, mut complete_asset: CompleteErasedLoadedAsset) {
|
fn send_loaded_asset(&self, id: UntypedAssetId, mut loaded_asset: ErasedLoadedAsset) {
|
||||||
for (_, labeled_asset) in complete_asset.labeled_assets.drain() {
|
for (_, labeled_asset) in loaded_asset.labeled_assets.drain() {
|
||||||
self.send_asset_event(InternalAssetEvent::Loaded {
|
self.send_loaded_asset(labeled_asset.handle.id(), labeled_asset.asset);
|
||||||
id: labeled_asset.handle.id(),
|
|
||||||
loaded_asset: labeled_asset.asset,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.send_asset_event(InternalAssetEvent::Loaded {
|
self.send_asset_event(InternalAssetEvent::Loaded { id, loaded_asset });
|
||||||
id,
|
|
||||||
loaded_asset: complete_asset.asset,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Kicks off a reload of the asset stored at the given path. This will only reload the asset if it currently loaded.
|
/// 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,
|
reader: &mut dyn Reader,
|
||||||
load_dependencies: bool,
|
load_dependencies: bool,
|
||||||
populate_hashes: bool,
|
populate_hashes: bool,
|
||||||
) -> Result<CompleteErasedLoadedAsset, AssetLoadError> {
|
) -> Result<ErasedLoadedAsset, AssetLoadError> {
|
||||||
// TODO: experiment with this
|
// TODO: experiment with this
|
||||||
let asset_path = asset_path.clone_owned();
|
let asset_path = asset_path.clone_owned();
|
||||||
let load_context =
|
let load_context =
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
use crate::{
|
use crate::{meta::Settings, Asset, ErasedLoadedAsset, Handle, LabeledAsset, UntypedHandle};
|
||||||
meta::Settings, Asset, CompleteErasedLoadedAsset, ErasedLoadedAsset, Handle, LabeledAsset,
|
|
||||||
UntypedHandle,
|
|
||||||
};
|
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use atomicow::CowArc;
|
use atomicow::CowArc;
|
||||||
use bevy_platform_support::collections::HashMap;
|
use bevy_platform_support::collections::HashMap;
|
||||||
@ -59,11 +56,11 @@ impl<A: Asset> DerefMut for TransformedAsset<A> {
|
|||||||
|
|
||||||
impl<A: Asset> TransformedAsset<A> {
|
impl<A: Asset> TransformedAsset<A> {
|
||||||
/// Creates a new [`TransformedAsset`] from `asset` if its internal value matches `A`.
|
/// Creates a new [`TransformedAsset`] from `asset` if its internal value matches `A`.
|
||||||
pub fn from_loaded(complete_asset: CompleteErasedLoadedAsset) -> Option<Self> {
|
pub fn from_loaded(asset: ErasedLoadedAsset) -> Option<Self> {
|
||||||
if let Ok(value) = complete_asset.asset.value.downcast::<A>() {
|
if let Ok(value) = asset.value.downcast::<A>() {
|
||||||
return Some(TransformedAsset {
|
return Some(TransformedAsset {
|
||||||
value: *value,
|
value: *value,
|
||||||
labeled_assets: complete_asset.labeled_assets,
|
labeled_assets: asset.labeled_assets,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
@ -90,13 +87,117 @@ impl<A: Asset> TransformedAsset<A> {
|
|||||||
&mut self.value
|
&mut self.value
|
||||||
}
|
}
|
||||||
/// Returns the labeled asset, if it exists and matches this type.
|
/// Returns the labeled asset, if it exists and matches this type.
|
||||||
pub fn get_labeled<B: Asset, Q>(&mut self, label: &'_ Q) -> Option<&mut B>
|
pub fn get_labeled<B: Asset, Q>(&mut self, label: &Q) -> Option<TransformedSubAsset<B>>
|
||||||
where
|
where
|
||||||
CowArc<'static, str>: Borrow<Q>,
|
CowArc<'static, str>: Borrow<Q>,
|
||||||
Q: ?Sized + Hash + Eq,
|
Q: ?Sized + Hash + Eq,
|
||||||
{
|
{
|
||||||
let labeled = self.labeled_assets.get_mut(label)?;
|
let labeled = self.labeled_assets.get_mut(label)?;
|
||||||
labeled.asset.value.downcast_mut::<B>()
|
let value = labeled.asset.value.downcast_mut::<B>()?;
|
||||||
|
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<Q>(&self, label: &Q) -> Option<&ErasedLoadedAsset>
|
||||||
|
where
|
||||||
|
CowArc<'static, str>: Borrow<Q>,
|
||||||
|
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<Q>(&self, label: &Q) -> Option<UntypedHandle>
|
||||||
|
where
|
||||||
|
CowArc<'static, str>: Borrow<Q>,
|
||||||
|
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<Q, B: Asset>(&self, label: &Q) -> Option<Handle<B>>
|
||||||
|
where
|
||||||
|
CowArc<'static, str>: Borrow<Q>,
|
||||||
|
Q: ?Sized + Hash + Eq,
|
||||||
|
{
|
||||||
|
let labeled = self.labeled_assets.get(label)?;
|
||||||
|
if let Ok(handle) = labeled.handle.clone().try_typed::<B>() {
|
||||||
|
return Some(handle);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
/// Adds `asset` as a labeled sub asset using `label` and `handle`
|
||||||
|
pub fn insert_labeled(
|
||||||
|
&mut self,
|
||||||
|
label: impl Into<CowArc<'static, str>>,
|
||||||
|
handle: impl Into<UntypedHandle>,
|
||||||
|
asset: impl Into<ErasedLoadedAsset>,
|
||||||
|
) {
|
||||||
|
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<Item = &str> {
|
||||||
|
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<CowArc<'static, str>, 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<Self> {
|
||||||
|
let value = asset.value.downcast_mut::<A>()?;
|
||||||
|
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<B: Asset, Q>(&mut self, label: &Q) -> Option<TransformedSubAsset<B>>
|
||||||
|
where
|
||||||
|
CowArc<'static, str>: Borrow<Q>,
|
||||||
|
Q: ?Sized + Hash + Eq,
|
||||||
|
{
|
||||||
|
let labeled = self.labeled_assets.get_mut(label)?;
|
||||||
|
let value = labeled.asset.value.downcast_mut::<B>()?;
|
||||||
|
Some(TransformedSubAsset {
|
||||||
|
value,
|
||||||
|
labeled_assets: &mut labeled.asset.labeled_assets,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
/// Returns the type-erased labeled asset, if it exists and matches this type.
|
/// Returns the type-erased labeled asset, if it exists and matches this type.
|
||||||
pub fn get_erased_labeled<Q>(&self, label: &Q) -> Option<&ErasedLoadedAsset>
|
pub fn get_erased_labeled<Q>(&self, label: &Q) -> Option<&ErasedLoadedAsset>
|
||||||
|
@ -494,12 +494,10 @@ async fn load_gltf<'a, 'b, 'c>(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let handle = load_context
|
let handle = load_context.add_labeled_asset(
|
||||||
.add_labeled_asset(
|
GltfAssetLabel::Animation(animation.index()).to_string(),
|
||||||
GltfAssetLabel::Animation(animation.index()).to_string(),
|
animation_clip,
|
||||||
animation_clip,
|
);
|
||||||
)
|
|
||||||
.expect("animation indices are unique, so the label is unique");
|
|
||||||
if let Some(name) = animation.name() {
|
if let Some(name) = animation.name() {
|
||||||
named_animations.insert(name.into(), handle.clone());
|
named_animations.insert(name.into(), handle.clone());
|
||||||
}
|
}
|
||||||
@ -646,8 +644,7 @@ async fn load_gltf<'a, 'b, 'c>(
|
|||||||
RenderAssetUsages::default(),
|
RenderAssetUsages::default(),
|
||||||
)?;
|
)?;
|
||||||
let handle = load_context
|
let handle = load_context
|
||||||
.add_labeled_asset(morph_targets_label.to_string(), morph_target_image.0)
|
.add_labeled_asset(morph_targets_label.to_string(), morph_target_image.0);
|
||||||
.expect("morph target indices are unique, so the label is unique");
|
|
||||||
|
|
||||||
mesh.set_morph_targets(handle);
|
mesh.set_morph_targets(handle);
|
||||||
let extras = gltf_mesh.extras().as_ref();
|
let extras = gltf_mesh.extras().as_ref();
|
||||||
@ -700,9 +697,7 @@ async fn load_gltf<'a, 'b, 'c>(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let mesh_handle = load_context
|
let mesh_handle = load_context.add_labeled_asset(primitive_label.to_string(), mesh);
|
||||||
.add_labeled_asset(primitive_label.to_string(), mesh)
|
|
||||||
.expect("primitive indices are unique, so the label is unique");
|
|
||||||
primitives.push(super::GltfPrimitive::new(
|
primitives.push(super::GltfPrimitive::new(
|
||||||
&gltf_mesh,
|
&gltf_mesh,
|
||||||
&primitive,
|
&primitive,
|
||||||
@ -726,9 +721,7 @@ async fn load_gltf<'a, 'b, 'c>(
|
|||||||
gltf_mesh.extras().as_deref().map(GltfExtras::from),
|
gltf_mesh.extras().as_deref().map(GltfExtras::from),
|
||||||
);
|
);
|
||||||
|
|
||||||
let handle = load_context
|
let handle = load_context.add_labeled_asset(mesh.asset_label().to_string(), mesh);
|
||||||
.add_labeled_asset(mesh.asset_label().to_string(), mesh)
|
|
||||||
.expect("mesh indices are unique, so the label is unique");
|
|
||||||
if let Some(name) = gltf_mesh.name() {
|
if let Some(name) = gltf_mesh.name() {
|
||||||
named_meshes.insert(name.into(), handle.clone());
|
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()
|
core::iter::repeat_n(Mat4::IDENTITY, gltf_skin.joints().len()).collect()
|
||||||
});
|
});
|
||||||
|
|
||||||
load_context
|
load_context.add_labeled_asset(
|
||||||
.add_labeled_asset(
|
GltfAssetLabel::InverseBindMatrices(gltf_skin.index()).to_string(),
|
||||||
GltfAssetLabel::InverseBindMatrices(gltf_skin.index()).to_string(),
|
SkinnedMeshInverseBindposes::from(local_to_bone_bind_matrices),
|
||||||
SkinnedMeshInverseBindposes::from(local_to_bone_bind_matrices),
|
)
|
||||||
)
|
|
||||||
.expect("inverse bind matrix indices are unique, so the label is unique")
|
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@ -800,8 +791,7 @@ async fn load_gltf<'a, 'b, 'c>(
|
|||||||
);
|
);
|
||||||
|
|
||||||
let handle = load_context
|
let handle = load_context
|
||||||
.add_labeled_asset(gltf_skin.asset_label().to_string(), gltf_skin)
|
.add_labeled_asset(gltf_skin.asset_label().to_string(), gltf_skin);
|
||||||
.expect("skin indices are unique, so the label is unique");
|
|
||||||
|
|
||||||
if let Some(name) = skin.name() {
|
if let Some(name) = skin.name() {
|
||||||
named_skins.insert(name.into(), handle.clone());
|
named_skins.insert(name.into(), handle.clone());
|
||||||
@ -834,9 +824,7 @@ async fn load_gltf<'a, 'b, 'c>(
|
|||||||
#[cfg(feature = "bevy_animation")]
|
#[cfg(feature = "bevy_animation")]
|
||||||
let gltf_node = gltf_node.with_animation_root(animation_roots.contains(&node.index()));
|
let gltf_node = gltf_node.with_animation_root(animation_roots.contains(&node.index()));
|
||||||
|
|
||||||
let handle = load_context
|
let handle = load_context.add_labeled_asset(gltf_node.asset_label().to_string(), gltf_node);
|
||||||
.add_labeled_asset(gltf_node.asset_label().to_string(), gltf_node)
|
|
||||||
.expect("node indices are unique, so the label is unique");
|
|
||||||
nodes.insert(node.index(), handle.clone());
|
nodes.insert(node.index(), handle.clone());
|
||||||
if let Some(name) = node.name() {
|
if let Some(name) = node.name() {
|
||||||
named_nodes.insert(name.into(), handle);
|
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 loaded_scene = scene_load_context.finish(Scene::new(world));
|
||||||
let scene_handle = load_context
|
let scene_handle = load_context.add_loaded_labeled_asset(
|
||||||
.add_loaded_labeled_asset(
|
GltfAssetLabel::Scene(scene.index()).to_string(),
|
||||||
GltfAssetLabel::Scene(scene.index()).to_string(),
|
loaded_scene,
|
||||||
loaded_scene,
|
);
|
||||||
)
|
|
||||||
.expect("scene indices are unique, so the label is unique");
|
|
||||||
|
|
||||||
if let Some(name) = scene.name() {
|
if let Some(name) = scene.name() {
|
||||||
named_scenes.insert(name.into(), scene_handle.clone());
|
named_scenes.insert(name.into(), scene_handle.clone());
|
||||||
@ -1041,75 +1027,71 @@ fn load_material(
|
|||||||
is_scale_inverted: bool,
|
is_scale_inverted: bool,
|
||||||
) -> Handle<StandardMaterial> {
|
) -> Handle<StandardMaterial> {
|
||||||
let material_label = material_label(material, is_scale_inverted);
|
let material_label = material_label(material, is_scale_inverted);
|
||||||
load_context
|
load_context.labeled_asset_scope(material_label.to_string(), |load_context| {
|
||||||
.labeled_asset_scope(material_label.to_string(), |load_context| {
|
let pbr = material.pbr_metallic_roughness();
|
||||||
let pbr = material.pbr_metallic_roughness();
|
|
||||||
|
|
||||||
// TODO: handle missing label handle errors here?
|
// TODO: handle missing label handle errors here?
|
||||||
let color = pbr.base_color_factor();
|
let color = pbr.base_color_factor();
|
||||||
let base_color_channel = pbr
|
let base_color_channel = pbr
|
||||||
.base_color_texture()
|
.base_color_texture()
|
||||||
.map(|info| uv_channel(material, "base color", info.tex_coord()))
|
.map(|info| uv_channel(material, "base color", info.tex_coord()))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let base_color_texture = pbr
|
let base_color_texture = pbr
|
||||||
.base_color_texture()
|
.base_color_texture()
|
||||||
.map(|info| texture_handle(&info.texture(), load_context));
|
.map(|info| texture_handle(&info.texture(), load_context));
|
||||||
|
|
||||||
let uv_transform = pbr
|
let uv_transform = pbr
|
||||||
.base_color_texture()
|
.base_color_texture()
|
||||||
.and_then(|info| info.texture_transform().map(texture_transform_to_affine2))
|
.and_then(|info| info.texture_transform().map(texture_transform_to_affine2))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let normal_map_channel = material
|
let normal_map_channel = material
|
||||||
.normal_texture()
|
.normal_texture()
|
||||||
.map(|info| uv_channel(material, "normal map", info.tex_coord()))
|
.map(|info| uv_channel(material, "normal map", info.tex_coord()))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let normal_map_texture: Option<Handle<Image>> =
|
let normal_map_texture: Option<Handle<Image>> =
|
||||||
material.normal_texture().map(|normal_texture| {
|
material.normal_texture().map(|normal_texture| {
|
||||||
// TODO: handle normal_texture.scale
|
// TODO: handle normal_texture.scale
|
||||||
texture_handle(&normal_texture.texture(), load_context)
|
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 occlusion_channel = material
|
let metallic_roughness_channel = pbr
|
||||||
.occlusion_texture()
|
.metallic_roughness_texture()
|
||||||
.map(|info| uv_channel(material, "occlusion", info.tex_coord()))
|
.map(|info| uv_channel(material, "metallic/roughness", info.tex_coord()))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let occlusion_texture = material.occlusion_texture().map(|occlusion_texture| {
|
let metallic_roughness_texture = pbr.metallic_roughness_texture().map(|info| {
|
||||||
// TODO: handle occlusion_texture.strength() (a scalar multiplier for occlusion strength)
|
warn_on_differing_texture_transforms(
|
||||||
texture_handle(&occlusion_texture.texture(), load_context)
|
material,
|
||||||
});
|
&info,
|
||||||
|
uv_transform,
|
||||||
|
"metallic/roughness",
|
||||||
|
);
|
||||||
|
texture_handle(&info.texture(), load_context)
|
||||||
|
});
|
||||||
|
|
||||||
let emissive = material.emissive_factor();
|
let occlusion_channel = material
|
||||||
let emissive_channel = material
|
.occlusion_texture()
|
||||||
.emissive_texture()
|
.map(|info| uv_channel(material, "occlusion", info.tex_coord()))
|
||||||
.map(|info| uv_channel(material, "emissive", info.tex_coord()))
|
.unwrap_or_default();
|
||||||
.unwrap_or_default();
|
let occlusion_texture = material.occlusion_texture().map(|occlusion_texture| {
|
||||||
let emissive_texture = material.emissive_texture().map(|info| {
|
// TODO: handle occlusion_texture.strength() (a scalar multiplier for occlusion strength)
|
||||||
// TODO: handle occlusion_texture.strength() (a scalar multiplier for occlusion strength)
|
texture_handle(&occlusion_texture.texture(), load_context)
|
||||||
warn_on_differing_texture_transforms(material, &info, uv_transform, "emissive");
|
});
|
||||||
texture_handle(&info.texture(), load_context)
|
|
||||||
});
|
|
||||||
|
|
||||||
#[cfg(feature = "pbr_transmission_textures")]
|
let emissive = material.emissive_factor();
|
||||||
let (
|
let emissive_channel = material
|
||||||
specular_transmission,
|
.emissive_texture()
|
||||||
specular_transmission_channel,
|
.map(|info| uv_channel(material, "emissive", info.tex_coord()))
|
||||||
specular_transmission_texture,
|
.unwrap_or_default();
|
||||||
) = material
|
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()
|
.transmission()
|
||||||
.map_or((0.0, UvChannel::Uv0, None), |transmission| {
|
.map_or((0.0, UvChannel::Uv0, None), |transmission| {
|
||||||
let specular_transmission_channel = transmission
|
let specular_transmission_channel = transmission
|
||||||
@ -1129,156 +1111,152 @@ fn load_material(
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(not(feature = "pbr_transmission_textures"))]
|
#[cfg(not(feature = "pbr_transmission_textures"))]
|
||||||
let specular_transmission = material
|
let specular_transmission = material
|
||||||
.transmission()
|
.transmission()
|
||||||
.map_or(0.0, |transmission| transmission.transmission_factor());
|
.map_or(0.0, |transmission| transmission.transmission_factor());
|
||||||
|
|
||||||
#[cfg(feature = "pbr_transmission_textures")]
|
#[cfg(feature = "pbr_transmission_textures")]
|
||||||
let (
|
let (
|
||||||
thickness,
|
thickness,
|
||||||
thickness_channel,
|
thickness_channel,
|
||||||
thickness_texture,
|
thickness_texture,
|
||||||
attenuation_distance,
|
attenuation_distance,
|
||||||
attenuation_color,
|
attenuation_color,
|
||||||
) = material.volume().map_or(
|
) = material.volume().map_or(
|
||||||
(0.0, UvChannel::Uv0, None, f32::INFINITY, [1.0, 1.0, 1.0]),
|
(0.0, UvChannel::Uv0, None, f32::INFINITY, [1.0, 1.0, 1.0]),
|
||||||
|volume| {
|
|volume| {
|
||||||
let thickness_channel = volume
|
let thickness_channel = volume
|
||||||
.thickness_texture()
|
.thickness_texture()
|
||||||
.map(|info| uv_channel(material, "thickness", info.tex_coord()))
|
.map(|info| uv_channel(material, "thickness", info.tex_coord()))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let thickness_texture: Option<Handle<Image>> =
|
let thickness_texture: Option<Handle<Image>> =
|
||||||
volume.thickness_texture().map(|thickness_texture| {
|
volume.thickness_texture().map(|thickness_texture| {
|
||||||
texture_handle(&thickness_texture.texture(), load_context)
|
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(),
|
volume.thickness_factor(),
|
||||||
thickness_channel,
|
|
||||||
thickness_texture,
|
|
||||||
volume.attenuation_distance(),
|
volume.attenuation_distance(),
|
||||||
volume.attenuation_color(),
|
volume.attenuation_color(),
|
||||||
)
|
)
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "pbr_transmission_textures"))]
|
let ior = material.ior().unwrap_or(1.5);
|
||||||
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);
|
// 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.
|
// Parse the `KHR_materials_anisotropy` extension data if necessary.
|
||||||
let clearcoat =
|
let anisotropy =
|
||||||
ClearcoatExtension::parse(load_context, document, material).unwrap_or_default();
|
AnisotropyExtension::parse(load_context, document, material).unwrap_or_default();
|
||||||
|
|
||||||
// Parse the `KHR_materials_anisotropy` extension data if necessary.
|
// Parse the `KHR_materials_specular` extension data if necessary.
|
||||||
let anisotropy =
|
let specular =
|
||||||
AnisotropyExtension::parse(load_context, document, material).unwrap_or_default();
|
SpecularExtension::parse(load_context, document, material).unwrap_or_default();
|
||||||
|
|
||||||
// Parse the `KHR_materials_specular` extension data if necessary.
|
// We need to operate in the Linear color space and be willing to exceed 1.0 in our channels
|
||||||
let specular =
|
let base_emissive = LinearRgba::rgb(emissive[0], emissive[1], emissive[2]);
|
||||||
SpecularExtension::parse(load_context, document, material).unwrap_or_default();
|
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
|
StandardMaterial {
|
||||||
let base_emissive = LinearRgba::rgb(emissive[0], emissive[1], emissive[2]);
|
base_color: Color::linear_rgba(color[0], color[1], color[2], color[3]),
|
||||||
let emissive = base_emissive * material.emissive_strength().unwrap_or(1.0);
|
base_color_channel,
|
||||||
|
base_color_texture,
|
||||||
StandardMaterial {
|
perceptual_roughness: pbr.roughness_factor(),
|
||||||
base_color: Color::linear_rgba(color[0], color[1], color[2], color[3]),
|
metallic: pbr.metallic_factor(),
|
||||||
base_color_channel,
|
metallic_roughness_channel,
|
||||||
base_color_texture,
|
metallic_roughness_texture,
|
||||||
perceptual_roughness: pbr.roughness_factor(),
|
normal_map_channel,
|
||||||
metallic: pbr.metallic_factor(),
|
normal_map_texture,
|
||||||
metallic_roughness_channel,
|
double_sided: material.double_sided(),
|
||||||
metallic_roughness_texture,
|
cull_mode: if material.double_sided() {
|
||||||
normal_map_channel,
|
None
|
||||||
normal_map_texture,
|
} else if is_scale_inverted {
|
||||||
double_sided: material.double_sided(),
|
Some(Face::Front)
|
||||||
cull_mode: if material.double_sided() {
|
} else {
|
||||||
None
|
Some(Face::Back)
|
||||||
} else if is_scale_inverted {
|
},
|
||||||
Some(Face::Front)
|
occlusion_channel,
|
||||||
} else {
|
occlusion_texture,
|
||||||
Some(Face::Back)
|
emissive,
|
||||||
},
|
emissive_channel,
|
||||||
occlusion_channel,
|
emissive_texture,
|
||||||
occlusion_texture,
|
specular_transmission,
|
||||||
emissive,
|
#[cfg(feature = "pbr_transmission_textures")]
|
||||||
emissive_channel,
|
specular_transmission_channel,
|
||||||
emissive_texture,
|
#[cfg(feature = "pbr_transmission_textures")]
|
||||||
specular_transmission,
|
specular_transmission_texture,
|
||||||
#[cfg(feature = "pbr_transmission_textures")]
|
thickness,
|
||||||
specular_transmission_channel,
|
#[cfg(feature = "pbr_transmission_textures")]
|
||||||
#[cfg(feature = "pbr_transmission_textures")]
|
thickness_channel,
|
||||||
specular_transmission_texture,
|
#[cfg(feature = "pbr_transmission_textures")]
|
||||||
thickness,
|
thickness_texture,
|
||||||
#[cfg(feature = "pbr_transmission_textures")]
|
ior,
|
||||||
thickness_channel,
|
attenuation_distance,
|
||||||
#[cfg(feature = "pbr_transmission_textures")]
|
attenuation_color: Color::linear_rgb(
|
||||||
thickness_texture,
|
attenuation_color[0],
|
||||||
ior,
|
attenuation_color[1],
|
||||||
attenuation_distance,
|
attenuation_color[2],
|
||||||
attenuation_color: Color::linear_rgb(
|
),
|
||||||
attenuation_color[0],
|
unlit: material.unlit(),
|
||||||
attenuation_color[1],
|
alpha_mode: alpha_mode(material),
|
||||||
attenuation_color[2],
|
uv_transform,
|
||||||
),
|
clearcoat: clearcoat.clearcoat_factor.unwrap_or_default() as f32,
|
||||||
unlit: material.unlit(),
|
clearcoat_perceptual_roughness: clearcoat.clearcoat_roughness_factor.unwrap_or_default()
|
||||||
alpha_mode: alpha_mode(material),
|
as f32,
|
||||||
uv_transform,
|
#[cfg(feature = "pbr_multi_layer_material_textures")]
|
||||||
clearcoat: clearcoat.clearcoat_factor.unwrap_or_default() as f32,
|
clearcoat_channel: clearcoat.clearcoat_channel,
|
||||||
clearcoat_perceptual_roughness: clearcoat
|
#[cfg(feature = "pbr_multi_layer_material_textures")]
|
||||||
.clearcoat_roughness_factor
|
clearcoat_texture: clearcoat.clearcoat_texture,
|
||||||
.unwrap_or_default() as f32,
|
#[cfg(feature = "pbr_multi_layer_material_textures")]
|
||||||
#[cfg(feature = "pbr_multi_layer_material_textures")]
|
clearcoat_roughness_channel: clearcoat.clearcoat_roughness_channel,
|
||||||
clearcoat_channel: clearcoat.clearcoat_channel,
|
#[cfg(feature = "pbr_multi_layer_material_textures")]
|
||||||
#[cfg(feature = "pbr_multi_layer_material_textures")]
|
clearcoat_roughness_texture: clearcoat.clearcoat_roughness_texture,
|
||||||
clearcoat_texture: clearcoat.clearcoat_texture,
|
#[cfg(feature = "pbr_multi_layer_material_textures")]
|
||||||
#[cfg(feature = "pbr_multi_layer_material_textures")]
|
clearcoat_normal_channel: clearcoat.clearcoat_normal_channel,
|
||||||
clearcoat_roughness_channel: clearcoat.clearcoat_roughness_channel,
|
#[cfg(feature = "pbr_multi_layer_material_textures")]
|
||||||
#[cfg(feature = "pbr_multi_layer_material_textures")]
|
clearcoat_normal_texture: clearcoat.clearcoat_normal_texture,
|
||||||
clearcoat_roughness_texture: clearcoat.clearcoat_roughness_texture,
|
anisotropy_strength: anisotropy.anisotropy_strength.unwrap_or_default() as f32,
|
||||||
#[cfg(feature = "pbr_multi_layer_material_textures")]
|
anisotropy_rotation: anisotropy.anisotropy_rotation.unwrap_or_default() as f32,
|
||||||
clearcoat_normal_channel: clearcoat.clearcoat_normal_channel,
|
#[cfg(feature = "pbr_anisotropy_texture")]
|
||||||
#[cfg(feature = "pbr_multi_layer_material_textures")]
|
anisotropy_channel: anisotropy.anisotropy_channel,
|
||||||
clearcoat_normal_texture: clearcoat.clearcoat_normal_texture,
|
#[cfg(feature = "pbr_anisotropy_texture")]
|
||||||
anisotropy_strength: anisotropy.anisotropy_strength.unwrap_or_default() as f32,
|
anisotropy_texture: anisotropy.anisotropy_texture,
|
||||||
anisotropy_rotation: anisotropy.anisotropy_rotation.unwrap_or_default() as f32,
|
// From the `KHR_materials_specular` spec:
|
||||||
#[cfg(feature = "pbr_anisotropy_texture")]
|
// <https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_materials_specular#materials-with-reflectance-parameter>
|
||||||
anisotropy_channel: anisotropy.anisotropy_channel,
|
reflectance: specular.specular_factor.unwrap_or(1.0) as f32 * 0.5,
|
||||||
#[cfg(feature = "pbr_anisotropy_texture")]
|
#[cfg(feature = "pbr_specular_textures")]
|
||||||
anisotropy_texture: anisotropy.anisotropy_texture,
|
specular_channel: specular.specular_channel,
|
||||||
// From the `KHR_materials_specular` spec:
|
#[cfg(feature = "pbr_specular_textures")]
|
||||||
// <https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_materials_specular#materials-with-reflectance-parameter>
|
specular_texture: specular.specular_texture,
|
||||||
reflectance: specular.specular_factor.unwrap_or(1.0) as f32 * 0.5,
|
specular_tint: match specular.specular_color_factor {
|
||||||
#[cfg(feature = "pbr_specular_textures")]
|
Some(color) => Color::linear_rgb(color[0] as f32, color[1] as f32, color[2] as f32),
|
||||||
specular_channel: specular.specular_channel,
|
None => Color::WHITE,
|
||||||
#[cfg(feature = "pbr_specular_textures")]
|
},
|
||||||
specular_texture: specular.specular_texture,
|
#[cfg(feature = "pbr_specular_textures")]
|
||||||
specular_tint: match specular.specular_color_factor {
|
specular_tint_channel: specular.specular_color_channel,
|
||||||
Some(color) => {
|
#[cfg(feature = "pbr_specular_textures")]
|
||||||
Color::linear_rgb(color[0] as f32, color[1] as f32, color[2] as f32)
|
specular_tint_texture: specular.specular_color_texture,
|
||||||
}
|
..Default::default()
|
||||||
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")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads a glTF node.
|
/// Loads a glTF node.
|
||||||
@ -1697,9 +1675,9 @@ impl ImageOrPath {
|
|||||||
handles: &mut Vec<Handle<Image>>,
|
handles: &mut Vec<Handle<Image>>,
|
||||||
) {
|
) {
|
||||||
let handle = match self {
|
let handle = match self {
|
||||||
ImageOrPath::Image { label, image } => load_context
|
ImageOrPath::Image { label, image } => {
|
||||||
.add_labeled_asset(label.to_string(), image)
|
load_context.add_labeled_asset(label.to_string(), image)
|
||||||
.expect("texture indices are unique, so the label is unique"),
|
}
|
||||||
ImageOrPath::Path {
|
ImageOrPath::Path {
|
||||||
path,
|
path,
|
||||||
is_srgb,
|
is_srgb,
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
use bevy::{
|
use bevy::{
|
||||||
asset::{
|
asset::{
|
||||||
io::{Reader, VecReader},
|
io::{Reader, VecReader},
|
||||||
AssetLoader, CompleteErasedLoadedAsset, LoadContext, LoadDirectError,
|
AssetLoader, ErasedLoadedAsset, LoadContext, LoadDirectError,
|
||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
reflect::TypePath,
|
reflect::TypePath,
|
||||||
@ -14,7 +14,7 @@ use thiserror::Error;
|
|||||||
|
|
||||||
#[derive(Asset, TypePath)]
|
#[derive(Asset, TypePath)]
|
||||||
struct GzAsset {
|
struct GzAsset {
|
||||||
uncompressed: CompleteErasedLoadedAsset,
|
uncompressed: ErasedLoadedAsset,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -149,15 +149,15 @@ impl AssetLoader for CoolTextLoader {
|
|||||||
let ron: CoolTextRon = ron::de::from_bytes(&bytes)?;
|
let ron: CoolTextRon = ron::de::from_bytes(&bytes)?;
|
||||||
let mut base_text = ron.text;
|
let mut base_text = ron.text;
|
||||||
for embedded in ron.embedded_dependencies {
|
for embedded in ron.embedded_dependencies {
|
||||||
let complete_loaded = load_context
|
let loaded = load_context
|
||||||
.loader()
|
.loader()
|
||||||
.immediate()
|
.immediate()
|
||||||
.load::<Text>(&embedded)
|
.load::<Text>(&embedded)
|
||||||
.await?;
|
.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 {
|
for (path, settings_override) in ron.dependencies_with_settings {
|
||||||
let complete_loaded = load_context
|
let loaded = load_context
|
||||||
.loader()
|
.loader()
|
||||||
.with_settings(move |settings| {
|
.with_settings(move |settings| {
|
||||||
*settings = settings_override.clone();
|
*settings = settings_override.clone();
|
||||||
@ -165,7 +165,7 @@ impl AssetLoader for CoolTextLoader {
|
|||||||
.immediate()
|
.immediate()
|
||||||
.load::<Text>(&path)
|
.load::<Text>(&path)
|
||||||
.await?;
|
.await?;
|
||||||
base_text.push_str(&complete_loaded.get_asset().get().0);
|
base_text.push_str(&loaded.get().0);
|
||||||
}
|
}
|
||||||
Ok(CoolText {
|
Ok(CoolText {
|
||||||
text: base_text,
|
text: base_text,
|
||||||
|
Loading…
Reference in New Issue
Block a user