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}