Make adding a subasset label return a result for if there is a duplicate label. (#18013)
# Objective - Makes #18010 more easily debuggable. This doesn't solve that issue, but protects us from it in the future. ## Solution - Make `LoadContext::add_labeled_asset` and friends return an error if it finds a duplicate asset. ## Testing - Added a test - it fails before the fix. --- ## Migration Guide - `AssetLoader`s must now handle the case of a duplicate subasset label when using `LoadContext::add_labeled_asset` and its variants. If you know your subasset labels are unique by construction (e.g., they include an index number), you can simply unwrap this result.
This commit is contained in:
parent
6bae04ab36
commit
ed1143b26b
@ -639,7 +639,7 @@ mod tests {
|
||||
},
|
||||
loader::{AssetLoader, LoadContext},
|
||||
Asset, AssetApp, AssetEvent, AssetId, AssetLoadError, AssetLoadFailedEvent, AssetPath,
|
||||
AssetPlugin, AssetServer, Assets,
|
||||
AssetPlugin, AssetServer, Assets, DuplicateLabelAssetError, LoadState,
|
||||
};
|
||||
use alloc::{
|
||||
boxed::Box,
|
||||
@ -695,6 +695,8 @@ 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),
|
||||
}
|
||||
@ -740,7 +742,7 @@ mod tests {
|
||||
.sub_texts
|
||||
.drain(..)
|
||||
.map(|text| load_context.add_labeled_asset(text.clone(), SubText { text }))
|
||||
.collect(),
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
})
|
||||
}
|
||||
|
||||
@ -1778,6 +1780,49 @@ 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::<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:?}"),
|
||||
});
|
||||
}
|
||||
|
||||
// validate the Asset derive macro for various asset types
|
||||
#[derive(Asset, TypePath)]
|
||||
pub struct TestAsset;
|
||||
|
||||
@ -13,7 +13,6 @@ use alloc::{
|
||||
};
|
||||
use atomicow::CowArc;
|
||||
use bevy_ecs::world::World;
|
||||
use bevy_log::warn;
|
||||
use bevy_platform_support::collections::{HashMap, HashSet};
|
||||
use bevy_tasks::{BoxedFuture, ConditionalSendFuture};
|
||||
use core::any::{Any, TypeId};
|
||||
@ -458,7 +457,7 @@ impl<'a> LoadContext<'a> {
|
||||
&mut self,
|
||||
label: String,
|
||||
load: impl FnOnce(&mut LoadContext) -> A,
|
||||
) -> Handle<A> {
|
||||
) -> Result<Handle<A>, DuplicateLabelAssetError> {
|
||||
let mut context = self.begin_labeled_asset();
|
||||
let asset = load(&mut context);
|
||||
let complete_asset = context.finish(asset);
|
||||
@ -475,7 +474,11 @@ 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<A: Asset>(&mut self, label: String, asset: A) -> Handle<A> {
|
||||
pub fn add_labeled_asset<A: Asset>(
|
||||
&mut self,
|
||||
label: String,
|
||||
asset: A,
|
||||
) -> Result<Handle<A>, DuplicateLabelAssetError> {
|
||||
self.labeled_asset_scope(label, |_| asset)
|
||||
}
|
||||
|
||||
@ -488,7 +491,7 @@ impl<'a> LoadContext<'a> {
|
||||
&mut self,
|
||||
label: impl Into<CowArc<'static, str>>,
|
||||
loaded_asset: CompleteLoadedAsset<A>,
|
||||
) -> Handle<A> {
|
||||
) -> Result<Handle<A>, DuplicateLabelAssetError> {
|
||||
let label = label.into();
|
||||
let CompleteLoadedAsset {
|
||||
asset,
|
||||
@ -499,19 +502,25 @@ impl<'a> LoadContext<'a> {
|
||||
let handle = self
|
||||
.asset_server
|
||||
.get_or_create_path_handle(labeled_path, None);
|
||||
self.labeled_assets.insert(
|
||||
label,
|
||||
LabeledAsset {
|
||||
asset: loaded_asset,
|
||||
handle: handle.clone().untyped(),
|
||||
},
|
||||
);
|
||||
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() {
|
||||
warn!("A labeled asset with the label \"{label}\" already exists. Replacing with the new asset.");
|
||||
return Err(DuplicateLabelAssetError(label.to_string()));
|
||||
}
|
||||
}
|
||||
handle
|
||||
Ok(handle)
|
||||
}
|
||||
|
||||
/// Returns `true` if an asset with the label `label` exists in this context.
|
||||
@ -661,3 +670,8 @@ 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);
|
||||
|
||||
@ -517,10 +517,12 @@ async fn load_gltf<'a, 'b, 'c>(
|
||||
);
|
||||
}
|
||||
}
|
||||
let handle = load_context.add_labeled_asset(
|
||||
GltfAssetLabel::Animation(animation.index()).to_string(),
|
||||
animation_clip,
|
||||
);
|
||||
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");
|
||||
if let Some(name) = animation.name() {
|
||||
named_animations.insert(name.into(), handle.clone());
|
||||
}
|
||||
@ -540,9 +542,9 @@ async fn load_gltf<'a, 'b, 'c>(
|
||||
texture: ImageOrPath,
|
||||
) {
|
||||
let handle = match texture {
|
||||
ImageOrPath::Image { label, image } => {
|
||||
load_context.add_labeled_asset(label.to_string(), image)
|
||||
}
|
||||
ImageOrPath::Image { label, image } => load_context
|
||||
.add_labeled_asset(label.to_string(), image)
|
||||
.expect("texture indices are unique, so the label is unique"),
|
||||
ImageOrPath::Path {
|
||||
path,
|
||||
is_srgb,
|
||||
@ -696,7 +698,8 @@ 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);
|
||||
.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);
|
||||
let extras = gltf_mesh.extras().as_ref();
|
||||
@ -749,7 +752,9 @@ async fn load_gltf<'a, 'b, 'c>(
|
||||
});
|
||||
}
|
||||
|
||||
let mesh_handle = load_context.add_labeled_asset(primitive_label.to_string(), mesh);
|
||||
let mesh_handle = load_context
|
||||
.add_labeled_asset(primitive_label.to_string(), mesh)
|
||||
.expect("primitive indices are unique, so the label is unique");
|
||||
primitives.push(super::GltfPrimitive::new(
|
||||
&gltf_mesh,
|
||||
&primitive,
|
||||
@ -766,7 +771,9 @@ async fn load_gltf<'a, 'b, 'c>(
|
||||
let mesh =
|
||||
super::GltfMesh::new(&gltf_mesh, primitives, get_gltf_extras(gltf_mesh.extras()));
|
||||
|
||||
let handle = load_context.add_labeled_asset(mesh.asset_label().to_string(), mesh);
|
||||
let handle = load_context
|
||||
.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() {
|
||||
named_meshes.insert(name.into(), handle.clone());
|
||||
}
|
||||
@ -783,10 +790,12 @@ async fn load_gltf<'a, 'b, 'c>(
|
||||
.map(|mat| Mat4::from_cols_array_2d(&mat))
|
||||
.collect();
|
||||
|
||||
load_context.add_labeled_asset(
|
||||
inverse_bind_matrices_label(&gltf_skin),
|
||||
SkinnedMeshInverseBindposes::from(local_to_bone_bind_matrices),
|
||||
)
|
||||
load_context
|
||||
.add_labeled_asset(
|
||||
inverse_bind_matrices_label(&gltf_skin),
|
||||
SkinnedMeshInverseBindposes::from(local_to_bone_bind_matrices),
|
||||
)
|
||||
.expect("inverse bind matrix indices are unique, so the label is unique")
|
||||
})
|
||||
.collect();
|
||||
|
||||
@ -831,7 +840,9 @@ async fn load_gltf<'a, 'b, 'c>(
|
||||
get_gltf_extras(skin.extras()),
|
||||
);
|
||||
|
||||
let handle = load_context.add_labeled_asset(skin_label(&skin), gltf_skin);
|
||||
let handle = load_context
|
||||
.add_labeled_asset(skin_label(&skin), gltf_skin)
|
||||
.expect("skin indices are unique, so the label is unique");
|
||||
|
||||
skins.push(handle.clone());
|
||||
if let Some(name) = skin.name() {
|
||||
@ -863,7 +874,9 @@ 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);
|
||||
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");
|
||||
nodes.insert(node.index(), handle.clone());
|
||||
if let Some(name) = node.name() {
|
||||
named_nodes.insert(name.into(), handle);
|
||||
@ -952,7 +965,9 @@ 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(scene_label(&scene), loaded_scene);
|
||||
let scene_handle = load_context
|
||||
.add_loaded_labeled_asset(scene_label(&scene), loaded_scene)
|
||||
.expect("scene indices are unique, so the label is unique");
|
||||
|
||||
if let Some(name) = scene.name() {
|
||||
named_scenes.insert(name.into(), scene_handle.clone());
|
||||
@ -1119,74 +1134,78 @@ fn load_material(
|
||||
is_scale_inverted: bool,
|
||||
) -> Handle<StandardMaterial> {
|
||||
let material_label = material_label(material, is_scale_inverted);
|
||||
load_context.labeled_asset_scope(material_label, |load_context| {
|
||||
let pbr = material.pbr_metallic_roughness();
|
||||
load_context
|
||||
.labeled_asset_scope(material_label, |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| get_uv_channel(material, "base color", info.tex_coord()))
|
||||
.unwrap_or_default();
|
||||
let base_color_texture = pbr
|
||||
.base_color_texture()
|
||||
.map(|info| texture_handle(load_context, &info.texture()));
|
||||
// TODO: handle missing label handle errors here?
|
||||
let color = pbr.base_color_factor();
|
||||
let base_color_channel = pbr
|
||||
.base_color_texture()
|
||||
.map(|info| get_uv_channel(material, "base color", info.tex_coord()))
|
||||
.unwrap_or_default();
|
||||
let base_color_texture = pbr
|
||||
.base_color_texture()
|
||||
.map(|info| texture_handle(load_context, &info.texture()));
|
||||
|
||||
let uv_transform = pbr
|
||||
.base_color_texture()
|
||||
.and_then(|info| {
|
||||
info.texture_transform()
|
||||
.map(convert_texture_transform_to_affine2)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let uv_transform = pbr
|
||||
.base_color_texture()
|
||||
.and_then(|info| {
|
||||
info.texture_transform()
|
||||
.map(convert_texture_transform_to_affine2)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let normal_map_channel = material
|
||||
.normal_texture()
|
||||
.map(|info| get_uv_channel(material, "normal map", info.tex_coord()))
|
||||
.unwrap_or_default();
|
||||
let normal_map_texture: Option<Handle<Image>> =
|
||||
material.normal_texture().map(|normal_texture| {
|
||||
// TODO: handle normal_texture.scale
|
||||
texture_handle(load_context, &normal_texture.texture())
|
||||
let normal_map_channel = material
|
||||
.normal_texture()
|
||||
.map(|info| get_uv_channel(material, "normal map", info.tex_coord()))
|
||||
.unwrap_or_default();
|
||||
let normal_map_texture: Option<Handle<Image>> =
|
||||
material.normal_texture().map(|normal_texture| {
|
||||
// TODO: handle normal_texture.scale
|
||||
texture_handle(load_context, &normal_texture.texture())
|
||||
});
|
||||
|
||||
let metallic_roughness_channel = pbr
|
||||
.metallic_roughness_texture()
|
||||
.map(|info| get_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(load_context, &info.texture())
|
||||
});
|
||||
|
||||
let metallic_roughness_channel = pbr
|
||||
.metallic_roughness_texture()
|
||||
.map(|info| get_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(load_context, &info.texture())
|
||||
});
|
||||
let occlusion_channel = material
|
||||
.occlusion_texture()
|
||||
.map(|info| get_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(load_context, &occlusion_texture.texture())
|
||||
});
|
||||
|
||||
let occlusion_channel = material
|
||||
.occlusion_texture()
|
||||
.map(|info| get_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(load_context, &occlusion_texture.texture())
|
||||
});
|
||||
let emissive = material.emissive_factor();
|
||||
let emissive_channel = material
|
||||
.emissive_texture()
|
||||
.map(|info| get_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(load_context, &info.texture())
|
||||
});
|
||||
|
||||
let emissive = material.emissive_factor();
|
||||
let emissive_channel = material
|
||||
.emissive_texture()
|
||||
.map(|info| get_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(load_context, &info.texture())
|
||||
});
|
||||
|
||||
#[cfg(feature = "pbr_transmission_textures")]
|
||||
let (specular_transmission, specular_transmission_channel, specular_transmission_texture) =
|
||||
material
|
||||
#[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
|
||||
@ -1208,152 +1227,156 @@ 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| get_uv_channel(material, "thickness", info.tex_coord()))
|
||||
.unwrap_or_default();
|
||||
let thickness_texture: Option<Handle<Image>> =
|
||||
volume.thickness_texture().map(|thickness_texture| {
|
||||
texture_handle(load_context, &thickness_texture.texture())
|
||||
});
|
||||
#[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| get_uv_channel(material, "thickness", info.tex_coord()))
|
||||
.unwrap_or_default();
|
||||
let thickness_texture: Option<Handle<Image>> =
|
||||
volume.thickness_texture().map(|thickness_texture| {
|
||||
texture_handle(load_context, &thickness_texture.texture())
|
||||
});
|
||||
|
||||
(
|
||||
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(),
|
||||
)
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
let ior = material.ior().unwrap_or(1.5);
|
||||
#[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(),
|
||||
)
|
||||
});
|
||||
|
||||
// Parse the `KHR_materials_clearcoat` extension data if necessary.
|
||||
let clearcoat =
|
||||
ClearcoatExtension::parse(load_context, document, material).unwrap_or_default();
|
||||
let ior = material.ior().unwrap_or(1.5);
|
||||
|
||||
// Parse the `KHR_materials_anisotropy` extension data if necessary.
|
||||
let anisotropy =
|
||||
AnisotropyExtension::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_specular` extension data if necessary.
|
||||
let specular =
|
||||
SpecularExtension::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();
|
||||
|
||||
// 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);
|
||||
// Parse the `KHR_materials_specular` extension data if necessary.
|
||||
let specular =
|
||||
SpecularExtension::parse(load_context, document, material).unwrap_or_default();
|
||||
|
||||
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:
|
||||
// <https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_materials_specular#materials-with-reflectance-parameter>
|
||||
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()
|
||||
}
|
||||
})
|
||||
// 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:
|
||||
// <https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_materials_specular#materials-with-reflectance-parameter>
|
||||
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")
|
||||
}
|
||||
|
||||
fn get_uv_channel(material: &Material, texture_kind: &str, tex_coord: u32) -> UvChannel {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user