shine_gltf/
material.rs

1use crate::validation::{Checked, Error, Validate};
2use crate::{extensions, texture, Index, Path, Root};
3use serde::{de, ser};
4use serde_derive::{Deserialize, Serialize};
5use shine_gltf_macro::Validate;
6use std::fmt;
7
8/// All valid alpha modes.
9pub const VALID_ALPHA_MODES: &[&str] = &["OPAQUE", "MASK", "BLEND"];
10
11/// The alpha rendering mode of a material.
12#[derive(Clone, Copy, Eq, PartialEq, Debug)]
13pub enum AlphaMode {
14    /// The alpha value is ignored and the rendered output is fully opaque.
15    Opaque = 1,
16
17    /// The rendered output is either fully opaque or fully transparent depending on
18    /// the alpha value and the specified alpha cutoff value.
19    Mask,
20
21    /// The rendered output is either fully opaque or fully transparent depending on
22    /// the alpha value and the specified alpha cutoff value.
23    Blend,
24}
25
26impl ser::Serialize for AlphaMode {
27    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
28    where
29        S: ser::Serializer,
30    {
31        match *self {
32            AlphaMode::Opaque => serializer.serialize_str("OPAQUE"),
33            AlphaMode::Mask => serializer.serialize_str("MASK"),
34            AlphaMode::Blend => serializer.serialize_str("BLEND"),
35        }
36    }
37}
38
39/// The material appearance of a primitive.
40#[derive(Clone, Debug, Default, Deserialize, Serialize, Validate)]
41#[serde(default)]
42pub struct Material {
43    /// The alpha cutoff value of the material.
44    #[serde(rename = "alphaCutoff")]
45    pub alpha_cutoff: AlphaCutoff,
46
47    /// The alpha rendering mode of the material.
48    ///
49    /// The material's alpha rendering mode enumeration specifying the
50    /// interpretation of the alpha value of the main factor and texture.
51    ///
52    /// * In `Opaque` mode (default) the alpha value is ignored and the rendered
53    ///   output is fully opaque.
54    ///
55    /// * In `Mask` mode, the rendered output is either fully opaque or fully
56    ///   transparent depending on the alpha value and the specified alpha cutoff
57    ///   value.
58    ///
59    /// * In `Blend` mode, the alpha value is used to composite the source and
60    ///   destination areas and the rendered output is combined with the
61    ///   background using the normal painting operation (i.e. the Porter and
62    ///   Duff over operator).
63    #[serde(rename = "alphaMode")]
64    pub alpha_mode: Checked<AlphaMode>,
65
66    /// Specifies whether the material is double-sided.
67    ///
68    /// * When this value is false, back-face culling is enabled.
69    ///
70    /// * When this value is true, back-face culling is disabled and double sided
71    ///   lighting is enabled.
72    ///
73    /// The back-face must have its normals reversed before the lighting
74    /// equation is evaluated.
75    #[serde(rename = "doubleSided")]
76    pub double_sided: bool,
77
78    /// A set of parameter values that are used to define the metallic-roughness
79    /// material model from Physically-Based Rendering (PBR) methodology. When not
80    /// specified, all the default values of `pbrMetallicRoughness` apply.
81    #[serde(default, rename = "pbrMetallicRoughness")]
82    pub pbr_metallic_roughness: PbrMetallicRoughness,
83
84    /// A tangent space normal map. The texture contains RGB components in linear
85    /// space. Each texel represents the XYZ components of a normal vector in
86    /// tangent space. Red [0 to 255] maps to X [-1 to 1]. Green [0 to 255] maps to
87    /// Y [-1 to 1]. Blue [128 to 255] maps to Z [1/255 to 1]. The normal vectors
88    /// use OpenGL conventions where +X is right and +Y is up. +Z points toward the
89    /// viewer.
90    #[serde(rename = "normalTexture")]
91    #[serde(skip_serializing_if = "Option::is_none")]
92    pub normal_texture: Option<NormalTexture>,
93
94    /// The occlusion map texture. The occlusion values are sampled from the R
95    /// channel. Higher values indicate areas that should receive full indirect
96    /// lighting and lower values indicate no indirect lighting. These values are
97    /// linear. If other channels are present (GBA), they are ignored for occlusion
98    /// calculations.
99    #[serde(rename = "occlusionTexture")]
100    #[serde(skip_serializing_if = "Option::is_none")]
101    pub occlusion_texture: Option<OcclusionTexture>,
102
103    /// The emissive map controls the color and intensity of the light being emitted
104    /// by the material. This texture contains RGB components in sRGB color space.
105    /// If a fourth component (A) is present, it is ignored.
106    #[serde(rename = "emissiveTexture")]
107    #[serde(skip_serializing_if = "Option::is_none")]
108    pub emissive_texture: Option<texture::Info>,
109
110    /// The emissive color of the material.
111    #[serde(rename = "emissiveFactor")]
112    pub emissive_factor: EmissiveFactor,
113
114    /// Extension specific data.
115    #[serde(default, skip_serializing_if = "Option::is_none")]
116    pub extensions: Option<extensions::material::Material>,
117}
118
119/// A set of parameter values that are used to define the metallic-roughness
120/// material model from Physically-Based Rendering (PBR) methodology.
121#[derive(Clone, Debug, Default, Deserialize, Serialize, Validate)]
122#[serde(default)]
123pub struct PbrMetallicRoughness {
124    /// The material's base color factor.
125    #[serde(rename = "baseColorFactor")]
126    pub base_color_factor: PbrBaseColorFactor,
127
128    /// The base color texture.
129    #[serde(rename = "baseColorTexture")]
130    #[serde(skip_serializing_if = "Option::is_none")]
131    pub base_color_texture: Option<texture::Info>,
132
133    /// The metalness of the material.
134    #[serde(rename = "metallicFactor")]
135    pub metallic_factor: StrengthFactor,
136
137    /// The roughness of the material.
138    ///
139    /// * A value of 1.0 means the material is completely rough.
140    /// * A value of 0.0 means the material is completely smooth.
141    #[serde(rename = "roughnessFactor")]
142    pub roughness_factor: StrengthFactor,
143
144    /// The metallic-roughness texture.
145    ///
146    /// This texture has two components:
147    ///
148    /// The metalness values are sampled from the B channel.
149    /// The roughness values are sampled from the G channel.
150    /// These values are linear. If other channels are present (R or A),
151    /// they are ignored for metallic-roughness calculations.
152    #[serde(rename = "metallicRoughnessTexture")]
153    #[serde(skip_serializing_if = "Option::is_none")]
154    pub metallic_roughness_texture: Option<texture::Info>,
155
156    /// Extension specific data.
157    #[serde(default, skip_serializing_if = "Option::is_none")]
158    pub extensions: Option<extensions::material::PbrMetallicRoughness>,
159}
160
161/// Defines the normal texture of a material.
162#[derive(Clone, Debug, Deserialize, Serialize, Validate)]
163pub struct NormalTexture {
164    /// The index of the texture.
165    pub index: Index<texture::Texture>,
166
167    /// The scalar multiplier applied to each normal vector of the texture.
168    ///
169    /// This value is ignored if normalTexture is not specified.
170    #[serde(default = "material_normal_texture_scale_default")]
171    pub scale: f32,
172
173    /// The set index of the texture's `TEXCOORD` attribute.
174    #[serde(default, rename = "texCoord")]
175    pub tex_coord: u32,
176
177    /// Extension specific data.
178    #[serde(default, skip_serializing_if = "Option::is_none")]
179    pub extensions: Option<extensions::material::NormalTexture>,
180}
181
182fn material_normal_texture_scale_default() -> f32 {
183    1.0
184}
185
186/// Defines the occlusion texture of a material.
187#[derive(Clone, Debug, Deserialize, Serialize, Validate)]
188pub struct OcclusionTexture {
189    /// The index of the texture.
190    pub index: Index<texture::Texture>,
191
192    /// The scalar multiplier controlling the amount of occlusion applied.
193    #[serde(default)]
194    pub strength: StrengthFactor,
195
196    /// The set index of the texture's `TEXCOORD` attribute.
197    #[serde(default, rename = "texCoord")]
198    pub tex_coord: u32,
199
200    /// Extension specific data.
201    #[serde(default, skip_serializing_if = "Option::is_none")]
202    pub extensions: Option<extensions::material::OcclusionTexture>,
203}
204
205/// The alpha cutoff value of a material.
206#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
207pub struct AlphaCutoff(pub f32);
208
209/// The emissive color of a material.
210#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize)]
211pub struct EmissiveFactor(pub [f32; 3]);
212
213/// The base color factor of a material.
214#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
215pub struct PbrBaseColorFactor(pub [f32; 4]);
216
217/// A number in the inclusive range [0.0, 1.0] with a default value of 1.0.
218#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
219pub struct StrengthFactor(pub f32);
220
221impl Default for AlphaCutoff {
222    fn default() -> Self {
223        AlphaCutoff(0.5)
224    }
225}
226
227impl Default for AlphaMode {
228    fn default() -> Self {
229        AlphaMode::Opaque
230    }
231}
232
233impl Validate for AlphaCutoff {
234    fn validate_completely<P, R>(&self, _: &Root, path: P, report: &mut R)
235    where
236        P: Fn() -> Path,
237        R: FnMut(&dyn Fn() -> Path, Error),
238    {
239        if self.0 < 0.0 {
240            report(&path, Error::Invalid);
241        }
242    }
243}
244
245impl<'de> de::Deserialize<'de> for Checked<AlphaMode> {
246    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
247    where
248        D: de::Deserializer<'de>,
249    {
250        struct Visitor;
251        impl<'de> de::Visitor<'de> for Visitor {
252            type Value = Checked<AlphaMode>;
253
254            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
255                write!(f, "any of: {:?}", VALID_ALPHA_MODES)
256            }
257
258            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
259            where
260                E: de::Error,
261            {
262                use self::AlphaMode::*;
263                use crate::validation::Checked::*;
264                Ok(match value {
265                    "OPAQUE" => Valid(Opaque),
266                    "MASK" => Valid(Mask),
267                    "BLEND" => Valid(Blend),
268                    _ => Invalid,
269                })
270            }
271        }
272        deserializer.deserialize_str(Visitor)
273    }
274}
275
276impl Validate for EmissiveFactor {
277    fn validate_completely<P, R>(&self, _: &Root, path: P, report: &mut R)
278    where
279        P: Fn() -> Path,
280        R: FnMut(&dyn Fn() -> Path, Error),
281    {
282        for x in &self.0 {
283            if *x < 0.0 || *x > 1.0 {
284                report(&path, Error::Invalid);
285                // Only report once
286                break;
287            }
288        }
289    }
290}
291
292impl Default for PbrBaseColorFactor {
293    fn default() -> Self {
294        PbrBaseColorFactor([1.0, 1.0, 1.0, 1.0])
295    }
296}
297
298impl Validate for PbrBaseColorFactor {
299    fn validate_completely<P, R>(&self, _: &Root, path: P, report: &mut R)
300    where
301        P: Fn() -> Path,
302        R: FnMut(&dyn Fn() -> Path, Error),
303    {
304        for x in &self.0 {
305            if *x < 0.0 || *x > 1.0 {
306                report(&path, Error::Invalid);
307                // Only report once
308                break;
309            }
310        }
311    }
312}
313
314impl Default for StrengthFactor {
315    fn default() -> Self {
316        StrengthFactor(1.0)
317    }
318}
319
320impl Validate for StrengthFactor {
321    fn validate_completely<P, R>(&self, _: &Root, path: P, report: &mut R)
322    where
323        P: Fn() -> Path,
324        R: FnMut(&dyn Fn() -> Path, Error),
325    {
326        if self.0 < 0.0 || self.0 > 1.0 {
327            report(&path, Error::Invalid);
328        }
329    }
330}