mod3d_base/
material.rs

1//a Documentation
2
3/*!
4
5This provides for abstract Materials which can be used by any 3D model
6
7!*/
8
9//a Imports
10use crate::{Material, MaterialAspect, ShortIndex};
11
12//a BaseData
13//tp BaseData
14/// The basic data for a material; the most simple material is
15/// actually just RGB, but to keep the system simple the [BaseData]
16/// includes an alpha, metallicness and roughness.
17///
18/// For a simple material the alpha should be 1.0, and the metallic 0,
19/// and roughness 1
20///
21/// The simplest of shaders will use just the RGB values
22///
23/// Gltf
24#[derive(Debug, Clone)]
25pub struct BaseData {
26    /// Color of the material
27    ///
28    /// Least signficant is R, most A (0 transparent, of course)
29    pub rgba: u32,
30    /// Metallic nature of the material: 0 is fully dielectric, 1.0 is fully metallic
31    ///
32    /// Roughness of the material:  0.5 is specular, no specular down to 0 full reflection, up to 1 fully matt
33    ///
34    /// Each is 16 bits; least significant is metallic
35    pub metallic_roughness: u32,
36    /// The emissive color of the texture; 0 if unused
37    ///
38    /// Least signficant is R
39    pub emissive_rgb: u32,
40    /// The alpha data for the texture (alpha mode, alpha cutoff, etc)
41    ///
42    /// effectively extensions
43    pub alpha_etc: u32,
44}
45
46//ip Default for BaseData
47impl Default for BaseData {
48    fn default() -> Self {
49        0xffffffff_u32.into()
50    }
51}
52
53//ip BaseData
54impl BaseData {
55    //cp of_rgba
56    /// Create a new material with a given RGBA
57    pub fn of_rgba((r, g, b, a): (u8, u8, u8, u8)) -> Self {
58        let rgba: u32 = (r as u32) | ((g as u32) << 8) | ((b as u32) << 16) | ((a as u32) << 24);
59        rgba.into()
60    }
61
62    //mp set_rgba
63    /// Set the r,g,b,a
64    pub fn set_rgba(&mut self, (r, g, b, a): (u8, u8, u8, u8)) {
65        let rgba: u32 = (r as u32) | ((g as u32) << 8) | ((b as u32) << 16) | ((a as u32) << 24);
66        self.rgba = rgba;
67    }
68
69    //mp set_emissive_rgb
70    /// Set the emissive r,g,b
71    pub fn set_emissive_rgb(&mut self, (r, g, b): (u8, u8, u8)) {
72        let rgb: u32 = (r as u32) | ((g as u32) << 8) | ((b as u32) << 16);
73        self.emissive_rgb = rgb
74    }
75
76    //mp set_mr
77    /// Set the metallic and roughness of a material
78    pub fn set_mr(&mut self, metallic: f32, roughness: f32) {
79        let metallic = (metallic * 65535.0) as u32;
80        let roughness = (roughness * 65535.0) as u32;
81        self.metallic_roughness = (roughness << 16) | metallic;
82    }
83
84    //mp metallic_roughness
85    /// Get the metallic and roughness of a material
86    pub fn metallic_roughness(&self) -> (f32, f32) {
87        let metallic = self.metallic_roughness & 65535;
88        let roughness = (self.metallic_roughness >> 16) & 65535;
89        let metallic = (metallic as f32) / 65535.0;
90        let roughness = (roughness as f32) / 65535.0;
91        (metallic, roughness)
92    }
93
94    //ap rgba_tuple
95    /// Return a tuple of R, G, B, A of the color
96    pub fn rgba_tuple(&self) -> (u8, u8, u8, u8) {
97        let r = self.rgba & 0xff;
98        let g = (self.rgba >> 8) & 0xff;
99        let b = (self.rgba >> 16) & 0xff;
100        let a = (self.rgba >> 24) & 0xff;
101        (r as u8, g as u8, b as u8, a as u8)
102    }
103
104    //zz All done
105}
106
107impl From<u32> for BaseData {
108    fn from(rgba: u32) -> Self {
109        Self {
110            rgba,
111            metallic_roughness: 0,
112            emissive_rgb: 0,
113            alpha_etc: 0,
114        }
115    }
116}
117
118//a BaseMaterial
119//tp BaseMaterial
120/// Base material that provides simply color and constant metallicness/roughness
121#[derive(Debug)]
122pub struct BaseMaterial {
123    /// Base material data
124    base_data: BaseData,
125}
126
127//ip BaseMaterial
128impl BaseMaterial {
129    //fp of_rgba
130    /// Create a new [BaseMaterial] of an RGB color and alpha
131    pub fn of_rgba(rgba: u32) -> Self {
132        let base_data: BaseData = rgba.into();
133        Self { base_data }
134    }
135
136    //cp set_mr
137    /// Set the metallicness and roughness value for the [BaseMaterial]
138    pub fn set_mr(&mut self, metallic: f32, roughness: f32) {
139        self.base_data.set_mr(metallic, roughness);
140    }
141}
142
143//ip Material for BaseMaterial
144impl Material for BaseMaterial {
145    fn base_data(&self) -> &BaseData {
146        &self.base_data
147    }
148}
149
150//a PbrMaterial
151//tp PbrMaterial
152/// A physically-based rendered material with full set of textures
153#[derive(Debug, Default)]
154pub struct PbrMaterial {
155    base_data: BaseData,
156    base_texture: ShortIndex,
157    normal_texture: ShortIndex,
158    mr_texture: ShortIndex,
159    occlusion_texture: ShortIndex,
160    emission_texture: ShortIndex,
161}
162
163//ip PbrMaterial
164impl PbrMaterial {
165    //fp of_rgba
166    /// Create a new [BaseMaterial] of an RGB color and alpha
167    pub fn of_rgba(rgba: u32) -> Self {
168        let base_data: BaseData = rgba.into();
169        Self {
170            base_data,
171            ..Default::default()
172        }
173    }
174
175    //mp set_rgba
176    /// Set the RGBA
177    //mp set_rgba
178    /// Set the r,g,b,a
179    pub fn set_rgba(&mut self, (r, g, b, a): (u8, u8, u8, u8)) {
180        self.base_data.set_rgba((r, g, b, a));
181    }
182
183    //mp set_emissive_rgb
184    /// Set the emission RGB
185    pub fn set_emissive_rgb(&mut self, (r, g, b): (u8, u8, u8)) {
186        self.base_data.set_emissive_rgb((r, g, b));
187    }
188
189    //mp set_mr
190    /// Set the metallicness and roughness value for the [BaseMaterial]
191    pub fn set_mr(&mut self, metallic: f32, roughness: f32) {
192        self.base_data.set_mr(metallic, roughness);
193    }
194
195    //mp set_base_data
196    /// Set the base data
197    pub fn set_base_data(&mut self, base_data: &BaseData) {
198        self.base_data = base_data.clone();
199    }
200
201    //mp set_texture
202    /// Set a texture (currently just base texture) to an index in the Textures of an object
203    pub fn set_texture(&mut self, aspect: MaterialAspect, index: ShortIndex) {
204        use MaterialAspect::*;
205        #[allow(unreachable_patterns)]
206        match aspect {
207            Color => {
208                self.base_texture = index;
209            }
210            Normal => {
211                self.normal_texture = index;
212            }
213            MetallicRoughness => {
214                self.mr_texture = index;
215            }
216            Occlusion => {
217                self.occlusion_texture = index;
218            }
219            Emission => {
220                self.emission_texture = index;
221            }
222            _ => (),
223        }
224    }
225}
226
227//ip Material for PbrMaterial
228impl Material for PbrMaterial {
229    fn base_data(&self) -> &BaseData {
230        &self.base_data
231    }
232
233    fn texture(&self, aspect: MaterialAspect) -> ShortIndex {
234        use MaterialAspect::*;
235        #[allow(unreachable_patterns)]
236        match aspect {
237            Color => self.base_texture,
238            Normal => self.normal_texture,
239            MetallicRoughness => self.mr_texture,
240            Occlusion => self.occlusion_texture,
241            Emission => self.emission_texture,
242            _ => ShortIndex::none(),
243        }
244    }
245}