gltf_viewer_lib/render/
material.rs

1use std::rc::Rc;
2use std::path::Path;
3
4use gltf;
5
6use crate::render::math::*;
7use crate::render::{ Root };
8use crate::render::texture::Texture;
9use crate::shader::*;
10use crate::importdata::ImportData;
11
12pub struct Material {
13    pub index: Option<usize>, /// glTF index
14    pub name: Option<String>,
15
16    // pbr_metallic_roughness properties
17    pub base_color_factor: Vector4,
18    pub base_color_texture: Option<Rc<Texture>>,
19    pub metallic_factor: f32,
20    pub roughness_factor: f32,
21    pub metallic_roughness_texture: Option<Rc<Texture>>,
22
23    pub normal_texture: Option<Rc<Texture>>,
24    pub normal_scale: Option<f32>,
25
26    pub occlusion_texture: Option<Rc<Texture>>,
27    pub occlusion_strength: f32,
28    pub emissive_factor: Vector3,
29    pub emissive_texture: Option<Rc<Texture>>,
30
31    pub alpha_cutoff: f32,
32    pub alpha_mode: gltf::material::AlphaMode,
33
34    pub double_sided: bool,
35
36}
37
38impl Material {
39    pub fn from_gltf(
40        g_material: &gltf::material::Material<'_>,
41        root: &mut Root,
42        imp: &ImportData,
43        base_path: &Path
44    ) -> Material {
45        let pbr = g_material.pbr_metallic_roughness();
46
47        let mut material = Material {
48            index: g_material.index(),
49            name: g_material.name().map(|s| s.into()),
50            base_color_factor: pbr.base_color_factor().into(),
51            // TODO: perhaps RC only the underlying image? no, also opengl id...
52            base_color_texture: None,
53            metallic_factor: pbr.metallic_factor(),
54            roughness_factor: pbr.roughness_factor(),
55            metallic_roughness_texture: None,
56
57            normal_texture: None,
58            normal_scale: None,
59
60            occlusion_texture: None,
61            occlusion_strength: 0.0,
62
63            emissive_factor: g_material.emissive_factor().into(),
64            emissive_texture: None,
65
66            alpha_cutoff: g_material.alpha_cutoff(),
67            alpha_mode: g_material.alpha_mode(),
68
69            double_sided: g_material.double_sided(),
70        };
71
72        if let Some(color_info) = pbr.base_color_texture() {
73            material.base_color_texture = Some(
74                load_texture(&color_info.texture(), color_info.tex_coord(), root, imp, base_path));
75        }
76        if let Some(mr_info) = pbr.metallic_roughness_texture() {
77            material.metallic_roughness_texture = Some(
78                load_texture(&mr_info.texture(), mr_info.tex_coord(), root, imp, base_path));
79        }
80        if let Some(normal_texture) = g_material.normal_texture() {
81            material.normal_texture = Some(
82                load_texture(&normal_texture.texture(), normal_texture.tex_coord(), root, imp, base_path));
83            material.normal_scale = Some(normal_texture.scale());
84        }
85        if let Some(occ_texture) = g_material.occlusion_texture() {
86            material.occlusion_texture = Some(
87                load_texture(&occ_texture.texture(), occ_texture.tex_coord(), root, imp, base_path));
88            material.occlusion_strength = occ_texture.strength();
89        }
90        if let Some(em_info) = g_material.emissive_texture() {
91            material.emissive_texture = Some(
92                load_texture(&em_info.texture(), em_info.tex_coord(), root, imp, base_path));
93        }
94
95        material
96    }
97
98    pub fn shader_flags(&self) -> ShaderFlags {
99        let mut flags = ShaderFlags::empty();
100        if self.base_color_texture.is_some() {
101            flags |= ShaderFlags::HAS_BASECOLORMAP;
102        }
103        if self.normal_texture.is_some() {
104            flags |= ShaderFlags::HAS_NORMALMAP;
105        }
106        if self.emissive_texture.is_some() {
107            flags |= ShaderFlags::HAS_EMISSIVEMAP;
108        }
109        if self.metallic_roughness_texture.is_some() {
110            flags |= ShaderFlags::HAS_METALROUGHNESSMAP;
111        }
112        if self.occlusion_texture.is_some() {
113            flags |= ShaderFlags::HAS_OCCLUSIONMAP;
114        }
115        flags
116    }
117
118}
119
120fn load_texture(
121    g_texture: &gltf::texture::Texture<'_>,
122    tex_coord: u32,
123    root: &mut Root,
124    imp: &ImportData,
125    base_path: &Path) -> Rc<Texture>
126{
127    if let Some(tex) = root.textures.iter().find(|tex| (***tex).index == g_texture.index()) {
128        return Rc::clone(tex)
129    }
130
131    let texture = Rc::new(Texture::from_gltf(g_texture, tex_coord, imp, base_path));
132    root.textures.push(Rc::clone(&texture));
133    texture
134}