blender_mesh/
material.rs

1/// Material data for a mesh
2///
3/// # Blender
4///
5/// When exporting from Blender we read this data from the first Principled BSDF node in the
6/// node editor for the material
7///
8/// https://docs.blender.org/manual/en/latest/render/cycles/nodes/types/shaders/principled.html
9#[derive(Debug, Serialize, Deserialize, PartialEq, Default, Clone)]
10pub struct PrincipledBSDF {
11    /// [r, g, b]
12    pub(crate) base_color: MaterialInput<[f32; 3], String>,
13    /// roughness
14    pub(crate) roughness: MaterialInput<f32, (String, Channel)>,
15    /// metallic
16    pub(crate) metallic: MaterialInput<f32, (String, Channel)>,
17    /// The filename for the material's normal map
18    pub(crate) normal_map: Option<String>,
19}
20
21/// An input to a material property.
22///
23/// This can either be some uniform value that will get used across all vertices / fragments
24/// in your shader, or a texture.
25#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
26pub enum MaterialInput<U, I> {
27    /// Some value that is uniform across all vertices / fragments in your mesh.
28    Uniform(U),
29    /// The name of the texture image (excluding the full path) from an image texture node
30    /// in Blender's material editor.
31    ///
32    /// So a texture stored at /Users/me/hello-world.png
33    /// becomes MaterialInput::Texture("hello-world.png".to_string())
34    ///
35    /// This means that it is important to have different texture names across all unique textures
36    /// in your application.
37    ///
38    /// ## Note
39    ///
40    /// This is different from the other built in texture nodes, such as brick texture and
41    /// sky texture. We do not currently support these. If these would be useful for you,
42    /// open an issue!
43    ///
44    /// ## Examples
45    ///
46    /// ```
47    /// // Metalness can be read from the green channel of metal.jpg
48    /// use blender_mesh::{MaterialInput, Channel};
49    /// let metalness: MaterialInput<f32, (String, Channel)> =
50    ///     MaterialInput::ImageTexture((String::from("metal.jpg"), Channel::Green));
51    /// ```
52    ImageTexture(I),
53}
54
55/// An individual channel within an image.
56/// Red, Green, or Blue.
57#[derive(Debug, Serialize, Deserialize, PartialEq, Copy, Clone)]
58pub enum Channel {
59    #[serde(rename = "R")]
60    Red,
61    #[serde(rename = "G")]
62    Green,
63    #[serde(rename = "B")]
64    Blue,
65}
66
67impl<U, I> Default for MaterialInput<U, I>
68where
69    U: Default,
70{
71    fn default() -> Self {
72        MaterialInput::Uniform(U::default())
73    }
74}
75
76impl PrincipledBSDF {
77    /// Create a new physically-based material.
78    pub fn new(
79        base_color: MaterialInput<[f32; 3], String>,
80        roughness: MaterialInput<f32, (String, Channel)>,
81        metallic: MaterialInput<f32, (String, Channel)>,
82        normal_map: Option<String>,
83    ) -> Self {
84        PrincipledBSDF {
85            base_color,
86            roughness,
87            metallic,
88            normal_map,
89        }
90    }
91
92    /// The base_color of the material.
93    ///
94    /// https://docs.blender.org/api/blender2.8/bpy.types.Material.html#bpy.types.Material.diffuse_color
95    #[inline]
96    pub fn base_color(&self) -> &MaterialInput<[f32; 3], String> {
97        &self.base_color
98    }
99
100    /// The roughness of the material.
101    ///
102    /// https://docs.blender.org/api/blender2.8/bpy.types.Material.html#bpy.types.Material.roughness
103    #[inline]
104    pub fn roughness(&self) -> &MaterialInput<f32, (String, Channel)> {
105        &self.roughness
106    }
107
108    /// How metallic the material is. Most materials should be 0.0 or 1.0.
109    ///
110    /// https://docs.blender.org/api/blender2.8/bpy.types.Material.html#bpy.types.Material.metallic
111    #[inline]
112    pub fn metallic(&self) -> &MaterialInput<f32, (String, Channel)> {
113        &self.metallic
114    }
115
116    /// The normal map
117    #[inline]
118    pub fn normal_map(&self) -> Option<&String> {
119        self.normal_map.as_ref()
120    }
121}