Skip to main content

gltf_json/
texture.rs

1use crate::validation::{Checked, Validate};
2use crate::{extensions, extras::Void, image, Extras, Index};
3use gltf_derive::Validate;
4use serde::{de, ser};
5use serde_derive::{Deserialize, Serialize};
6use std::fmt;
7
8/// Corresponds to `GL_NEAREST`.
9pub const NEAREST: u32 = 9728;
10
11/// Corresponds to `GL_LINEAR`.
12pub const LINEAR: u32 = 9729;
13
14/// Corresponds to `GL_NEAREST_MIPMAP_NEAREST`.
15pub const NEAREST_MIPMAP_NEAREST: u32 = 9984;
16
17/// Corresponds to `GL_LINEAR_MIPMAP_NEAREST`.
18pub const LINEAR_MIPMAP_NEAREST: u32 = 9985;
19
20/// Corresponds to `GL_NEAREST_MIPMAP_LINEAR`.
21pub const NEAREST_MIPMAP_LINEAR: u32 = 9986;
22
23/// Corresponds to `GL_LINEAR_MIPMAP_LINEAR`.
24pub const LINEAR_MIPMAP_LINEAR: u32 = 9987;
25
26/// Corresponds to `GL_CLAMP_TO_EDGE`.
27pub const CLAMP_TO_EDGE: u32 = 33_071;
28
29/// Corresponds to `GL_MIRRORED_REPEAT`.
30pub const MIRRORED_REPEAT: u32 = 33_648;
31
32/// Corresponds to `GL_REPEAT`.
33pub const REPEAT: u32 = 10_497;
34
35/// All valid magnification filters.
36pub const VALID_MAG_FILTERS: &[u32] = &[NEAREST, LINEAR];
37
38/// All valid minification filters.
39pub const VALID_MIN_FILTERS: &[u32] = &[
40    NEAREST,
41    LINEAR,
42    NEAREST_MIPMAP_NEAREST,
43    LINEAR_MIPMAP_NEAREST,
44    NEAREST_MIPMAP_LINEAR,
45    LINEAR_MIPMAP_LINEAR,
46];
47
48/// All valid wrapping modes.
49pub const VALID_WRAPPING_MODES: &[u32] = &[CLAMP_TO_EDGE, MIRRORED_REPEAT, REPEAT];
50
51/// Magnification filter.
52#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
53pub enum MagFilter {
54    /// Corresponds to `GL_NEAREST`.
55    Nearest = 1,
56
57    /// Corresponds to `GL_LINEAR`.
58    Linear,
59}
60
61impl MagFilter {
62    /// OpenGL enum
63    pub fn as_gl_enum(&self) -> u32 {
64        match *self {
65            MagFilter::Nearest => NEAREST,
66            MagFilter::Linear => LINEAR,
67        }
68    }
69}
70
71/// Minification filter.
72#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
73pub enum MinFilter {
74    /// Corresponds to `GL_NEAREST`.
75    Nearest = 1,
76
77    /// Corresponds to `GL_LINEAR`.
78    Linear,
79
80    /// Corresponds to `GL_NEAREST_MIPMAP_NEAREST`.
81    NearestMipmapNearest,
82
83    /// Corresponds to `GL_LINEAR_MIPMAP_NEAREST`.
84    LinearMipmapNearest,
85
86    /// Corresponds to `GL_NEAREST_MIPMAP_LINEAR`.
87    NearestMipmapLinear,
88
89    /// Corresponds to `GL_LINEAR_MIPMAP_LINEAR`.
90    LinearMipmapLinear,
91}
92
93impl MinFilter {
94    /// Returns the corresponding OpenGL enum value.
95    pub fn as_gl_enum(&self) -> u32 {
96        match *self {
97            MinFilter::Nearest => NEAREST,
98            MinFilter::Linear => LINEAR,
99            MinFilter::NearestMipmapNearest => NEAREST_MIPMAP_NEAREST,
100            MinFilter::LinearMipmapNearest => LINEAR_MIPMAP_NEAREST,
101            MinFilter::NearestMipmapLinear => NEAREST_MIPMAP_LINEAR,
102            MinFilter::LinearMipmapLinear => LINEAR_MIPMAP_LINEAR,
103        }
104    }
105}
106
107/// Texture co-ordinate wrapping mode.
108#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
109pub enum WrappingMode {
110    /// Corresponds to `GL_CLAMP_TO_EDGE`.
111    ClampToEdge = 1,
112
113    /// Corresponds to `GL_MIRRORED_REPEAT`.
114    MirroredRepeat,
115
116    /// Corresponds to `GL_REPEAT`.
117    Repeat,
118}
119
120impl WrappingMode {
121    /// Returns the corresponding OpenGL enum value.
122    pub fn as_gl_enum(&self) -> u32 {
123        match *self {
124            WrappingMode::ClampToEdge => CLAMP_TO_EDGE,
125            WrappingMode::MirroredRepeat => MIRRORED_REPEAT,
126            WrappingMode::Repeat => REPEAT,
127        }
128    }
129}
130
131/// Texture sampler properties for filtering and wrapping modes.
132#[derive(Clone, Debug, Default, Deserialize, Serialize, Validate)]
133#[serde(default)]
134pub struct Sampler {
135    /// Magnification filter.
136    #[serde(rename = "magFilter")]
137    #[serde(skip_serializing_if = "Option::is_none")]
138    pub mag_filter: Option<Checked<MagFilter>>,
139
140    /// Minification filter.
141    #[serde(rename = "minFilter")]
142    #[serde(skip_serializing_if = "Option::is_none")]
143    pub min_filter: Option<Checked<MinFilter>>,
144
145    /// Optional user-defined name for this object.
146    #[cfg(feature = "names")]
147    #[cfg_attr(feature = "names", serde(skip_serializing_if = "Option::is_none"))]
148    pub name: Option<String>,
149
150    /// `s` wrapping mode.
151    #[serde(default, rename = "wrapS")]
152    pub wrap_s: Checked<WrappingMode>,
153
154    /// `t` wrapping mode.
155    #[serde(default, rename = "wrapT")]
156    pub wrap_t: Checked<WrappingMode>,
157
158    /// Extension specific data.
159    #[serde(default, skip_serializing_if = "Option::is_none")]
160    pub extensions: Option<extensions::texture::Sampler>,
161
162    /// Optional application specific data.
163    #[serde(default)]
164    #[cfg_attr(feature = "extras", serde(skip_serializing_if = "Option::is_none"))]
165    #[cfg_attr(not(feature = "extras"), serde(skip_serializing))]
166    pub extras: Extras,
167}
168
169impl Sampler {
170    // A const version of the material returned when calling [`Sampler::default`]
171    pub const DEFAULT_SAMPLER: Sampler = Sampler {
172        mag_filter: None,
173        min_filter: None,
174        #[cfg(feature = "names")]
175        name: None,
176        wrap_s: Checked::Valid(WrappingMode::Repeat),
177        wrap_t: Checked::Valid(WrappingMode::Repeat),
178        extensions: None,
179        #[cfg(feature = "extras")]
180        extras: None,
181        #[cfg(not(feature = "extras"))]
182        extras: Void {
183            _allow_unknown_fields: (),
184        },
185    };
186}
187
188fn source_default() -> Index<image::Image> {
189    Index::new(u32::MAX)
190}
191
192fn source_is_empty(source: &Index<image::Image>) -> bool {
193    source.value() == u32::MAX as usize
194}
195
196fn source_validate<P, R>(source: &Index<image::Image>, root: &crate::Root, path: P, report: &mut R)
197where
198    P: Fn() -> crate::Path,
199    R: FnMut(&dyn Fn() -> crate::Path, crate::validation::Error),
200{
201    if cfg!(any(feature = "allow_empty_texture",)) {
202        if !source_is_empty(source) {
203            source.validate(root, path, report);
204        }
205    } else if source_is_empty(source) {
206        report(&path, crate::validation::Error::Missing);
207    } else {
208        source.validate(root, &path, report);
209    }
210}
211
212/// A texture and its sampler.
213#[derive(Clone, Debug, Deserialize, Serialize)]
214pub struct Texture {
215    /// Optional user-defined name for this object.
216    #[cfg(feature = "names")]
217    #[cfg_attr(feature = "names", serde(skip_serializing_if = "Option::is_none"))]
218    pub name: Option<String>,
219
220    /// The index of the sampler used by this texture.
221    #[serde(skip_serializing_if = "Option::is_none")]
222    pub sampler: Option<Index<Sampler>>,
223
224    /// The index of the image used by this texture.
225    #[serde(default = "source_default", skip_serializing_if = "source_is_empty")]
226    pub source: Index<image::Image>,
227
228    /// Extension specific data.
229    #[serde(default, skip_serializing_if = "Option::is_none")]
230    pub extensions: Option<extensions::texture::Texture>,
231
232    /// Optional application specific data.
233    #[serde(default)]
234    #[cfg_attr(feature = "extras", serde(skip_serializing_if = "Option::is_none"))]
235    #[cfg_attr(not(feature = "extras"), serde(skip_serializing))]
236    pub extras: Extras,
237}
238
239impl Texture {
240    /// The index of the image used by this texture.
241    pub fn primary_source(&self) -> Index<image::Image> {
242        #[allow(unused_mut)]
243        let mut source = self.source;
244        #[cfg(feature = "EXT_texture_webp")]
245        {
246            if let Some(texture_webp) = &self.extensions {
247                if let Some(texture_webp) = &texture_webp.texture_webp {
248                    // Only use the webp source if the source is not empty
249                    // Otherwise, fallback to whatever was there originally
250                    if !source_is_empty(&texture_webp.source) {
251                        source = texture_webp.source;
252                    }
253                }
254            }
255        }
256        source
257    }
258}
259
260impl Validate for Texture {
261    fn validate<P, R>(&self, root: &crate::Root, path: P, report: &mut R)
262    where
263        P: Fn() -> crate::Path,
264        R: FnMut(&dyn Fn() -> crate::Path, crate::validation::Error),
265    {
266        self.sampler
267            .validate(root, || path().field("sampler"), report);
268        self.extensions
269            .validate(root, || path().field("extensions"), report);
270
271        source_validate(
272            &self.primary_source(),
273            root,
274            || path().field("source"),
275            report,
276        );
277    }
278}
279
280#[derive(Clone, Debug, Deserialize, Serialize, Validate)]
281/// Reference to a `Texture`.
282pub struct Info {
283    /// The index of the texture.
284    pub index: Index<Texture>,
285
286    /// The set index of the texture's `TEXCOORD` attribute.
287    #[serde(default, rename = "texCoord")]
288    pub tex_coord: u32,
289
290    /// Extension specific data.
291    #[serde(default, skip_serializing_if = "Option::is_none")]
292    pub extensions: Option<extensions::texture::Info>,
293
294    /// Optional application specific data.
295    #[serde(default)]
296    #[cfg_attr(feature = "extras", serde(skip_serializing_if = "Option::is_none"))]
297    #[cfg_attr(not(feature = "extras"), serde(skip_serializing))]
298    pub extras: Extras,
299}
300
301impl<'de> de::Deserialize<'de> for Checked<MagFilter> {
302    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
303    where
304        D: de::Deserializer<'de>,
305    {
306        struct Visitor;
307        impl de::Visitor<'_> for Visitor {
308            type Value = Checked<MagFilter>;
309
310            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
311                write!(f, "any of: {:?}", VALID_MAG_FILTERS)
312            }
313
314            fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
315            where
316                E: de::Error,
317            {
318                use self::MagFilter::*;
319                use crate::validation::Checked::*;
320                Ok(match value as u32 {
321                    NEAREST => Valid(Nearest),
322                    LINEAR => Valid(Linear),
323                    _ => Invalid,
324                })
325            }
326        }
327        deserializer.deserialize_u64(Visitor)
328    }
329}
330
331impl<'de> de::Deserialize<'de> for Checked<MinFilter> {
332    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
333    where
334        D: de::Deserializer<'de>,
335    {
336        struct Visitor;
337        impl de::Visitor<'_> for Visitor {
338            type Value = Checked<MinFilter>;
339
340            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
341                write!(f, "any of: {:?}", VALID_MIN_FILTERS)
342            }
343
344            fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
345            where
346                E: de::Error,
347            {
348                use self::MinFilter::*;
349                use crate::validation::Checked::*;
350                Ok(match value as u32 {
351                    NEAREST => Valid(Nearest),
352                    LINEAR => Valid(Linear),
353                    NEAREST_MIPMAP_NEAREST => Valid(NearestMipmapNearest),
354                    LINEAR_MIPMAP_NEAREST => Valid(LinearMipmapNearest),
355                    NEAREST_MIPMAP_LINEAR => Valid(NearestMipmapLinear),
356                    LINEAR_MIPMAP_LINEAR => Valid(LinearMipmapLinear),
357                    _ => Invalid,
358                })
359            }
360        }
361        deserializer.deserialize_u64(Visitor)
362    }
363}
364
365impl ser::Serialize for MinFilter {
366    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
367    where
368        S: ser::Serializer,
369    {
370        serializer.serialize_u32(self.as_gl_enum())
371    }
372}
373
374impl<'de> de::Deserialize<'de> for Checked<WrappingMode> {
375    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
376    where
377        D: de::Deserializer<'de>,
378    {
379        struct Visitor;
380        impl de::Visitor<'_> for Visitor {
381            type Value = Checked<WrappingMode>;
382
383            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
384                write!(f, "any of: {:?}", VALID_WRAPPING_MODES)
385            }
386
387            fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
388            where
389                E: de::Error,
390            {
391                use self::WrappingMode::*;
392                use crate::validation::Checked::*;
393                Ok(match value as u32 {
394                    CLAMP_TO_EDGE => Valid(ClampToEdge),
395                    MIRRORED_REPEAT => Valid(MirroredRepeat),
396                    REPEAT => Valid(Repeat),
397                    _ => Invalid,
398                })
399            }
400        }
401        deserializer.deserialize_u64(Visitor)
402    }
403}
404
405impl ser::Serialize for MagFilter {
406    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
407    where
408        S: ser::Serializer,
409    {
410        serializer.serialize_u32(self.as_gl_enum())
411    }
412}
413
414impl Default for WrappingMode {
415    fn default() -> Self {
416        WrappingMode::Repeat
417    }
418}
419
420impl ser::Serialize for WrappingMode {
421    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
422    where
423        S: ser::Serializer,
424    {
425        serializer.serialize_u32(self.as_gl_enum())
426    }
427}
428
429#[cfg(test)]
430mod tests {
431    #[test]
432    fn deserialize_source() {
433        let json = r#"{"asset":{"version":"2.0"},"textures":[{"source": 0}]}"#;
434        let root = serde_json::from_str::<crate::Root>(json).unwrap();
435        assert_eq!(0, root.textures[0].source.value());
436    }
437
438    #[test]
439    fn deserialize_empty_source() {
440        let json = r#"{"asset":{"version":"2.0"},"textures":[{}]}"#;
441        let root = serde_json::from_str::<crate::Root>(json).unwrap();
442        assert_eq!(u32::MAX as usize, root.textures[0].source.value());
443    }
444
445    #[test]
446    fn serialize_source() {
447        let root = crate::Root {
448            textures: vec![crate::Texture {
449                #[cfg(feature = "names")]
450                name: None,
451                sampler: None,
452                source: crate::Index::new(0),
453                extensions: None,
454                extras: Default::default(),
455            }],
456            ..Default::default()
457        };
458        let json = serde_json::to_string(&root).unwrap();
459        assert_eq!(
460            r#"{"asset":{"version":"2.0"},"textures":[{"source":0}]}"#,
461            &json
462        );
463    }
464
465    #[test]
466    fn serialize_empty_source() {
467        let root = crate::Root {
468            textures: vec![crate::Texture {
469                #[cfg(feature = "names")]
470                name: None,
471                sampler: None,
472                source: crate::Index::new(u32::MAX),
473                extensions: None,
474                extras: Default::default(),
475            }],
476            ..Default::default()
477        };
478        let json = serde_json::to_string(&root).unwrap();
479        assert_eq!(r#"{"asset":{"version":"2.0"},"textures":[{}]}"#, &json);
480    }
481
482    #[test]
483    fn validate_source() {
484        use crate::validation::{Error, Validate};
485        use crate::Path;
486        let json = r#"{"asset":{"version":"2.0"},"textures":[{"source":0}]}"#;
487        let root = serde_json::from_str::<crate::Root>(json).unwrap();
488        let mut errors = Vec::new();
489        root.textures[0].validate(
490            &root,
491            || Path::new().field("textures").index(0),
492            &mut |path, error| {
493                errors.push((path(), error));
494            },
495        );
496        assert_eq!(1, errors.len());
497        let (path, error) = &errors[0];
498        assert_eq!("textures[0].source", path.as_str());
499        assert_eq!(Error::IndexOutOfBounds, *error);
500    }
501
502    #[test]
503    fn validate_empty_source() {
504        use crate::validation::{Error, Validate};
505        use crate::Path;
506        let json = r#"{"asset":{"version":"2.0"},"textures":[{}]}"#;
507        let root = serde_json::from_str::<crate::Root>(json).unwrap();
508        let mut errors = Vec::new();
509        root.textures[0].validate(
510            &root,
511            || Path::new().field("textures").index(0),
512            &mut |path, error| {
513                errors.push((path(), error));
514            },
515        );
516        if cfg!(feature = "allow_empty_texture") {
517            assert!(errors.is_empty());
518        } else {
519            assert_eq!(1, errors.len());
520            let (path, error) = &errors[0];
521            assert_eq!("textures[0].source", path.as_str());
522            assert_eq!(Error::Missing, *error);
523        }
524    }
525}