easy_gltf/scene/model/material/
mod.rs1mod emissive;
2mod normal;
3mod occlusion;
4mod pbr;
5
6use crate::utils::*;
7use cgmath::*;
8use core::ops::Deref;
9use image::{ImageBuffer, Pixel};
10use std::sync::Arc;
11
12pub use emissive::Emissive;
13pub use normal::NormalMap;
14pub use occlusion::Occlusion;
15pub use pbr::PbrMaterial;
16
17#[derive(Clone, Debug, Default)]
19pub struct Material {
20 #[cfg(feature = "names")]
21 pub name: Option<String>,
23
24 #[cfg(feature = "extras")]
25 pub extras: gltf::json::extras::Extras,
27
28 pub pbr: PbrMaterial,
31
32 pub normal: Option<NormalMap>,
34
35 pub occlusion: Option<Occlusion>,
37
38 pub emissive: Emissive,
40}
41
42impl Material {
43 pub fn get_base_color_alpha(&self, tex_coords: Vector2<f32>) -> Vector4<f32> {
50 let mut res = self.pbr.base_color_factor;
51 if let Some(texture) = &self.pbr.base_color_texture {
52 let px_u = Self::get_pixel(tex_coords, texture);
53 let mut px_f = Vector4::new(0., 0., 0., 0.);
55 for i in 0..4 {
56 px_f[i] = (px_u[i] as f32) / 255.;
57 }
58 let pixel = Vector4::new(px_f.x.powf(2.2), px_f.y.powf(2.2), px_f.z.powf(2.2), px_f.w);
60 for i in 0..4 {
62 res[i] *= pixel[i];
63 }
64 }
65 res
66 }
67
68 pub fn get_base_color(&self, tex_coords: Vector2<f32>) -> Vector3<f32> {
75 self.get_base_color_alpha(tex_coords).truncate()
76 }
77
78 pub fn get_metallic(&self, tex_coords: Vector2<f32>) -> f32 {
84 self.pbr.metallic_factor
85 * if let Some(texture) = &self.pbr.metallic_texture {
86 Self::get_pixel(tex_coords, texture)[0] as f32 / 255.
87 } else {
88 1.
89 }
90 }
91
92 pub fn get_roughness(&self, tex_coords: Vector2<f32>) -> f32 {
98 self.pbr.roughness_factor
99 * if let Some(texture) = &self.pbr.roughness_texture {
100 Self::get_pixel(tex_coords, texture)[0] as f32 / 255.
101 } else {
102 1.
103 }
104 }
105
106 pub fn get_normal(&self, tex_coords: Vector2<f32>) -> Option<Vector3<f32>> {
112 let normal = self.normal.as_ref()?;
113 let pixel = Self::get_pixel(tex_coords, &normal.texture);
114 Some(
115 normal.factor
116 * Vector3::new(
117 (pixel[0] as f32) / 127.5 - 1.,
118 (pixel[1] as f32) / 127.5 - 1.,
119 (pixel[2] as f32) / 127.5 - 1.,
120 ),
121 )
122 }
123
124 pub fn get_occlusion(&self, tex_coords: Vector2<f32>) -> Option<f32> {
130 let occlusion = self.occlusion.as_ref()?;
131 Some(occlusion.factor * (Self::get_pixel(tex_coords, &occlusion.texture)[0] as f32 / 255.))
132 }
133
134 pub fn get_emissive(&self, tex_coords: Vector2<f32>) -> Vector3<f32> {
141 let mut res = self.emissive.factor;
142 if let Some(texture) = &self.emissive.texture {
143 let pixel = Self::get_pixel(tex_coords, texture);
144 for i in 0..3 {
145 res[i] *= (pixel[i] as f32) / 255.;
146 }
147 }
148 res
149 }
150
151 fn get_pixel<P, Container>(tex_coords: Vector2<f32>, texture: &ImageBuffer<P, Container>) -> P
152 where
153 P: Pixel + 'static,
154 P::Subpixel: 'static,
155 Container: Deref<Target = [P::Subpixel]>,
156 {
157 let coords = tex_coords.mul_element_wise(Vector2::new(
158 texture.width() as f32,
159 texture.height() as f32,
160 ));
161
162 texture[(
163 (coords.x as i64).rem_euclid(texture.width() as i64) as u32,
164 (coords.y as i64).rem_euclid(texture.height() as i64) as u32,
165 )]
166 }
167
168 pub(crate) fn load(gltf_mat: gltf::Material, data: &mut GltfData) -> Arc<Self> {
169 if let Some(material) = data.materials.get(&gltf_mat.index()) {
170 return material.clone();
171 }
172
173 let material = Arc::new(Material {
174 #[cfg(feature = "names")]
175 name: gltf_mat.name().map(String::from),
176 #[cfg(feature = "extras")]
177 extras: gltf_mat.extras().clone(),
178
179 pbr: PbrMaterial::load(gltf_mat.pbr_metallic_roughness(), data),
180 normal: NormalMap::load(&gltf_mat, data),
181 occlusion: Occlusion::load(&gltf_mat, data),
182 emissive: Emissive::load(&gltf_mat, data),
183 });
184
185 data.materials.insert(gltf_mat.index(), material.clone());
187 material
188 }
189}