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
8pub const NEAREST: u32 = 9728;
10
11pub const LINEAR: u32 = 9729;
13
14pub const NEAREST_MIPMAP_NEAREST: u32 = 9984;
16
17pub const LINEAR_MIPMAP_NEAREST: u32 = 9985;
19
20pub const NEAREST_MIPMAP_LINEAR: u32 = 9986;
22
23pub const LINEAR_MIPMAP_LINEAR: u32 = 9987;
25
26pub const CLAMP_TO_EDGE: u32 = 33_071;
28
29pub const MIRRORED_REPEAT: u32 = 33_648;
31
32pub const REPEAT: u32 = 10_497;
34
35pub const VALID_MAG_FILTERS: &[u32] = &[NEAREST, LINEAR];
37
38pub 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
48pub const VALID_WRAPPING_MODES: &[u32] = &[CLAMP_TO_EDGE, MIRRORED_REPEAT, REPEAT];
50
51#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
53pub enum MagFilter {
54 Nearest = 1,
56
57 Linear,
59}
60
61impl MagFilter {
62 pub fn as_gl_enum(&self) -> u32 {
64 match *self {
65 MagFilter::Nearest => NEAREST,
66 MagFilter::Linear => LINEAR,
67 }
68 }
69}
70
71#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
73pub enum MinFilter {
74 Nearest = 1,
76
77 Linear,
79
80 NearestMipmapNearest,
82
83 LinearMipmapNearest,
85
86 NearestMipmapLinear,
88
89 LinearMipmapLinear,
91}
92
93impl MinFilter {
94 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#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
109pub enum WrappingMode {
110 ClampToEdge = 1,
112
113 MirroredRepeat,
115
116 Repeat,
118}
119
120impl WrappingMode {
121 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#[derive(Clone, Debug, Default, Deserialize, Serialize, Validate)]
133#[serde(default)]
134pub struct Sampler {
135 #[serde(rename = "magFilter")]
137 #[serde(skip_serializing_if = "Option::is_none")]
138 pub mag_filter: Option<Checked<MagFilter>>,
139
140 #[serde(rename = "minFilter")]
142 #[serde(skip_serializing_if = "Option::is_none")]
143 pub min_filter: Option<Checked<MinFilter>>,
144
145 #[cfg(feature = "names")]
147 #[cfg_attr(feature = "names", serde(skip_serializing_if = "Option::is_none"))]
148 pub name: Option<String>,
149
150 #[serde(default, rename = "wrapS")]
152 pub wrap_s: Checked<WrappingMode>,
153
154 #[serde(default, rename = "wrapT")]
156 pub wrap_t: Checked<WrappingMode>,
157
158 #[serde(default, skip_serializing_if = "Option::is_none")]
160 pub extensions: Option<extensions::texture::Sampler>,
161
162 #[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 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#[derive(Clone, Debug, Deserialize, Serialize)]
214pub struct Texture {
215 #[cfg(feature = "names")]
217 #[cfg_attr(feature = "names", serde(skip_serializing_if = "Option::is_none"))]
218 pub name: Option<String>,
219
220 #[serde(skip_serializing_if = "Option::is_none")]
222 pub sampler: Option<Index<Sampler>>,
223
224 #[serde(default = "source_default", skip_serializing_if = "source_is_empty")]
226 pub source: Index<image::Image>,
227
228 #[serde(default, skip_serializing_if = "Option::is_none")]
230 pub extensions: Option<extensions::texture::Texture>,
231
232 #[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 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 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)]
281pub struct Info {
283 pub index: Index<Texture>,
285
286 #[serde(default, rename = "texCoord")]
288 pub tex_coord: u32,
289
290 #[serde(default, skip_serializing_if = "Option::is_none")]
292 pub extensions: Option<extensions::texture::Info>,
293
294 #[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}