bevy_gltf/loader/gltf_ext/
material.rs1use bevy_math::Affine2;
2use bevy_pbr::UvChannel;
3use bevy_render::alpha::AlphaMode;
4
5use gltf::{json::texture::Info, Material};
6
7use serde_json::value;
8
9use crate::GltfAssetLabel;
10
11use super::texture::texture_transform_to_affine2;
12
13#[cfg(any(
14 feature = "pbr_anisotropy_texture",
15 feature = "pbr_specular_textures",
16 feature = "pbr_multi_layer_material_textures"
17))]
18use {
19 bevy_asset::{AssetPath, Handle},
20 bevy_image::Image,
21 serde_json::{Map, Value},
22};
23
24#[cfg(any(
27 feature = "pbr_anisotropy_texture",
28 feature = "pbr_specular_textures",
29 feature = "pbr_multi_layer_material_textures"
30))]
31pub(crate) fn parse_material_extension_texture(
32 material: &Material,
33 extension: &Map<String, Value>,
34 texture_name: &str,
35 texture_kind: &str,
36 textures: &[Handle<Image>],
37 asset_path: AssetPath<'_>,
38) -> (UvChannel, Option<Handle<Image>>) {
39 match extension
40 .get(texture_name)
41 .and_then(|value| value::from_value::<Info>(value.clone()).ok())
42 {
43 Some(json_info) => (
44 uv_channel(material, texture_kind, json_info.tex_coord),
45 Some({
46 match textures.get(json_info.index.value()).cloned() {
47 None => {
48 tracing::warn!("Gltf at path \"{asset_path}\" contains invalid texture index <{}> for texture {texture_name}. Using default image.", json_info.index.value());
49 Handle::default()
50 }
51 Some(handle) => handle,
52 }
53 }),
54 ),
55 None => (UvChannel::default(), None),
56 }
57}
58
59pub(crate) fn uv_channel(material: &Material, texture_kind: &str, tex_coord: u32) -> UvChannel {
60 match tex_coord {
61 0 => UvChannel::Uv0,
62 1 => UvChannel::Uv1,
63 _ => {
64 let material_name = material
65 .name()
66 .map(|n| format!("the material \"{n}\""))
67 .unwrap_or_else(|| "an unnamed material".to_string());
68 let material_index = material
69 .index()
70 .map(|i| format!("index {i}"))
71 .unwrap_or_else(|| "default".to_string());
72 tracing::warn!(
73 "Only 2 UV Channels are supported, but {material_name} ({material_index}) \
74 has the TEXCOORD attribute {} on texture kind {texture_kind}, which will fallback to 0.",
75 tex_coord,
76 );
77 UvChannel::Uv0
78 }
79 }
80}
81
82pub(crate) fn alpha_mode(material: &Material) -> AlphaMode {
83 match material.alpha_mode() {
84 gltf::material::AlphaMode::Opaque => AlphaMode::Opaque,
85 gltf::material::AlphaMode::Mask => AlphaMode::Mask(material.alpha_cutoff().unwrap_or(0.5)),
86 gltf::material::AlphaMode::Blend => AlphaMode::Blend,
87 }
88}
89
90pub(crate) fn extension_texture_index(
94 material: &Material,
95 extension_name: &str,
96 texture_field_name: &str,
97) -> Option<usize> {
98 Some(
99 value::from_value::<Info>(
100 material
101 .extensions()?
102 .get(extension_name)?
103 .as_object()?
104 .get(texture_field_name)?
105 .clone(),
106 )
107 .ok()?
108 .index
109 .value(),
110 )
111}
112
113pub(crate) fn needs_tangents(material: &Material) -> bool {
118 [
119 material.normal_texture().is_some(),
120 #[cfg(feature = "pbr_multi_layer_material_textures")]
121 extension_texture_index(
122 material,
123 "KHR_materials_clearcoat",
124 "clearcoatNormalTexture",
125 )
126 .is_some(),
127 ]
128 .into_iter()
129 .reduce(|a, b| a || b)
130 .unwrap_or(false)
131}
132
133pub(crate) fn warn_on_differing_texture_transforms(
134 material: &Material,
135 info: &gltf::texture::Info,
136 texture_transform: Affine2,
137 texture_kind: &str,
138) {
139 let has_differing_texture_transform = info
140 .texture_transform()
141 .map(texture_transform_to_affine2)
142 .is_some_and(|t| t != texture_transform);
143 if has_differing_texture_transform {
144 let material_name = material
145 .name()
146 .map(|n| format!("the material \"{n}\""))
147 .unwrap_or_else(|| "an unnamed material".to_string());
148 let texture_name = info
149 .texture()
150 .name()
151 .map(|n| format!("its {texture_kind} texture \"{n}\""))
152 .unwrap_or_else(|| format!("its unnamed {texture_kind} texture"));
153 let material_index = material
154 .index()
155 .map(|i| format!("index {i}"))
156 .unwrap_or_else(|| "default".to_string());
157 tracing::warn!(
158 "Only texture transforms on base color textures are supported, but {material_name} ({material_index}) \
159 has a texture transform on {texture_name} (index {}), which will be ignored.", info.texture().index()
160 );
161 }
162}
163
164pub(crate) fn material_label(material: &Material, is_scale_inverted: bool) -> GltfAssetLabel {
165 if let Some(index) = material.index() {
166 GltfAssetLabel::Material {
167 index,
168 is_scale_inverted,
169 }
170 } else {
171 GltfAssetLabel::DefaultMaterial
172 }
173}