Skip to main content

gltf_json/
material.rs

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