Skip to main content

goth_gltf/
lib.rs

1//! Goth-gltf aims to be a low-level, unopinionated reader for gltf files.
2//!
3//! Basic example:
4//! ```no_run
5//! let filename = std::env::args().nth(1).unwrap();
6//! let bytes = std::fs::read(&filename).unwrap();
7//! let (gltf, _): (
8//!     goth_gltf::Gltf<goth_gltf::default_extensions::Extensions>,
9//!     _,
10//! ) = goth_gltf::Gltf::from_bytes(&bytes).unwrap();
11//! println!("{:#?}", gltf);
12//! ```
13//!
14//! # In comparison with [gltf-rs], it:
15//!
16//! - Represents the gltf JSON structure transparently
17//! - Uses nanoserde instead of serde
18//! - Supports a wider range of extensions
19//! - Has no code specific for loading images or reading attributes out of buffers
20//!
21//! # Extensions Implemented
22//!
23//! - `KHR_lights_punctual`
24//! - `KHR_materials_emissive_strength`
25//! - `KHR_materials_ior`
26//! - `KHR_materials_sheen`
27//! - `KHR_materials_unlit`
28//! - `KHR_texture_basisu`
29//! - `KHR_texture_transform`
30//! - `KHR_materials_transmission`
31//! - `EXT_mesh_gpu_instancing`
32//! - `EXT_meshopt_compression`
33//! - `MSFT_lod`
34//! - `MSFT_screencoverage`
35//!
36//! [gltf-rs]: https://github.com/gltf-rs/gltf
37
38#![allow(clippy::question_mark)]
39
40pub mod extensions;
41
42use nanoserde::{DeJson, SerJson};
43use std::fmt::Debug;
44
45pub trait Extensions: DeJson + SerJson {
46    type RootExtensions: DeJson + SerJson + Default + Debug + Clone;
47    type TextureExtensions: DeJson + SerJson + Default + Debug + Clone;
48    type TextureInfoExtensions: DeJson + SerJson + Default + Debug + Clone;
49    type MaterialExtensions: DeJson + SerJson + Default + Debug + Clone;
50    type BufferExtensions: DeJson + SerJson + Default + Debug + Clone;
51    type NodeExtensions: DeJson + SerJson + Default + Debug + Clone;
52    type NodeExtras: DeJson + SerJson + Default + Debug + Clone;
53    type BufferViewExtensions: DeJson + SerJson + Default + Debug + Clone;
54}
55
56impl Extensions for () {
57    type RootExtensions = ();
58    type TextureExtensions = ();
59    type TextureInfoExtensions = ();
60    type MaterialExtensions = ();
61    type BufferExtensions = ();
62    type NodeExtensions = ();
63    type NodeExtras = ();
64    type BufferViewExtensions = ();
65}
66
67/// A parsed gltf document.
68#[derive(Debug, DeJson, SerJson)]
69pub struct Gltf<E: Extensions> {
70    pub asset: Asset,
71    #[cfg(feature = "names")]
72    #[nserde(default, rename = "extensionsUsed")]
73    pub extensions_used: Vec<String>,
74    #[cfg(feature = "names")]
75    #[nserde(default, rename = "extensionsRequired")]
76    pub extensions_required: Vec<String>,
77    #[nserde(default)]
78    pub extensions: E::RootExtensions,
79    #[nserde(default)]
80    pub scene: usize,
81    #[nserde(default)]
82    pub scenes: Vec<Scene>,
83    #[nserde(default)]
84    pub nodes: Vec<Node<E>>,
85    #[nserde(default)]
86    pub materials: Vec<Material<E>>,
87    #[nserde(default)]
88    pub meshes: Vec<Mesh>,
89    #[nserde(default)]
90    pub textures: Vec<Texture<E>>,
91    #[nserde(default)]
92    pub images: Vec<Image>,
93    #[nserde(default)]
94    pub accessors: Vec<Accessor>,
95    #[nserde(rename = "bufferViews")]
96    #[nserde(default)]
97    pub buffer_views: Vec<BufferView<E>>,
98    #[nserde(default)]
99    pub animations: Vec<Animation>,
100    #[nserde(default)]
101    pub skins: Vec<Skin>,
102    #[nserde(default)]
103    pub samplers: Vec<Sampler>,
104    #[nserde(default)]
105    pub buffers: Vec<Buffer<E>>,
106    #[nserde(default)]
107    pub cameras: Vec<Camera>,
108}
109
110impl<E: Extensions> Gltf<E> {
111    /// Load a gltf from either a gltf or a glb file.
112    ///
113    /// In the case of a .glb, the binary buffer chunk will be returned as well.
114    pub fn from_bytes(bytes: &[u8]) -> Result<(Self, Option<&[u8]>), nanoserde::DeJsonErr> {
115        // Check for the 4-byte magic.
116        if !bytes.starts_with(b"glTF") {
117            return Ok((Self::from_json_bytes(bytes)?, None));
118        }
119
120        // There's always a json chunk at the start:
121        // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#structured-json-content
122
123        let json_chunk_length = u32::from_le_bytes(bytes[12..16].try_into().unwrap());
124
125        let json_chunk_end = 20 + json_chunk_length as usize;
126
127        let json_chunk_bytes = &bytes[20..20 + json_chunk_length as usize];
128
129        let json = Self::from_json_bytes(json_chunk_bytes)?;
130
131        let binary_buffer = if bytes.len() != json_chunk_end {
132            Some(&bytes[json_chunk_end + 8..])
133        } else {
134            None
135        };
136
137        Ok((json, binary_buffer))
138    }
139
140    pub fn write_to_glb<W: std::io::Write>(
141        &self,
142        bytes: &[u8],
143        mut writer: W,
144    ) -> std::io::Result<()> {
145        writer.write_all(b"glTF")?;
146        let version = 2_u32;
147        writer.write_all(&version.to_le_bytes())?;
148        let string = self.serialize_json();
149        let header_len = 4 + 4 + 4;
150        let json_chunk_len = 4 + 4 + string.len() as u32;
151        let binary_chunk_len = 4 + 4 + bytes.len() as u32;
152        let total_len = header_len + json_chunk_len + binary_chunk_len;
153        writer.write_all(&total_len.to_le_bytes())?;
154
155        writer.write_all(&(string.len() as u32).to_le_bytes())?;
156        writer.write_all(b"JSON")?;
157        writer.write_all(string.as_bytes())?;
158
159        writer.write_all(&(bytes.len() as u32).to_le_bytes())?;
160        writer.write_all(b"BIN\0")?;
161        writer.write_all(bytes)?;
162        Ok(())
163    }
164
165    pub fn from_json_bytes(bytes: &[u8]) -> Result<Self, nanoserde::DeJsonErr> {
166        match std::str::from_utf8(bytes) {
167            Ok(string) => Self::from_json_string(string),
168            Err(error) => Err(nanoserde::DeJsonState::default().err_parse(&error.to_string())),
169        }
170    }
171
172    pub fn from_json_string(string: &str) -> Result<Self, nanoserde::DeJsonErr> {
173        Self::deserialize_json(string)
174    }
175}
176
177#[derive(Debug, DeJson, SerJson)]
178pub struct Skin {
179    #[nserde(rename = "inverseBindMatrices")]
180    pub inverse_bind_matrices: Option<usize>,
181    pub skeleton: Option<usize>,
182    pub joints: Vec<usize>,
183    #[cfg(feature = "names")]
184    pub name: Option<String>,
185}
186
187#[derive(Debug, DeJson, SerJson)]
188pub struct Animation {
189    pub channels: Vec<Channel>,
190    pub samplers: Vec<AnimationSampler>,
191    #[cfg(feature = "names")]
192    pub name: Option<String>,
193}
194
195#[derive(Debug, DeJson, SerJson)]
196pub struct Channel {
197    pub sampler: usize,
198    pub target: Target,
199}
200
201#[derive(Debug, DeJson, SerJson)]
202pub struct Target {
203    pub node: Option<usize>,
204    pub path: TargetPath,
205}
206
207#[derive(Debug, DeJson, SerJson)]
208pub struct AnimationSampler {
209    pub input: usize,
210    #[nserde(default)]
211    pub interpolation: Interpolation,
212    pub output: usize,
213}
214
215#[derive(Debug, DeJson, SerJson)]
216pub struct Asset {
217    #[cfg(feature = "names")]
218    generator: Option<String>,
219    version: String,
220}
221
222#[derive(Default, Debug, DeJson, SerJson, Clone, Copy)]
223pub enum Interpolation {
224    #[default]
225    #[nserde(rename = "LINEAR")]
226    Linear,
227    #[nserde(rename = "STEP")]
228    Step,
229    #[nserde(rename = "CUBICSPLINE")]
230    CubicSpline,
231}
232
233#[derive(Debug, DeJson, SerJson)]
234pub enum TargetPath {
235    #[nserde(rename = "translation")]
236    Translation,
237    #[nserde(rename = "rotation")]
238    Rotation,
239    #[nserde(rename = "scale")]
240    Scale,
241    #[nserde(rename = "weights")]
242    Weights,
243}
244
245#[derive(Debug, DeJson, SerJson)]
246pub struct Buffer<E: Extensions> {
247    pub uri: Option<String>,
248    #[nserde(rename = "byteLength")]
249    pub byte_length: usize,
250    #[cfg(feature = "names")]
251    pub name: Option<String>,
252    #[nserde(default)]
253    pub extensions: E::BufferExtensions,
254}
255
256#[derive(Debug, DeJson, SerJson)]
257pub struct Node<E: Extensions> {
258    pub camera: Option<usize>,
259    #[nserde(default)]
260    pub children: Vec<usize>,
261    pub skin: Option<usize>,
262    pub matrix: Option<[f32; 16]>,
263    pub mesh: Option<usize>,
264    #[cfg(feature = "names")]
265    pub name: Option<String>,
266    pub rotation: Option<[f32; 4]>,
267    pub scale: Option<[f32; 3]>,
268    pub translation: Option<[f32; 3]>,
269    #[nserde(default)]
270    pub extensions: E::NodeExtensions,
271    #[nserde(default)]
272    pub extras: E::NodeExtras,
273}
274
275impl<E: Extensions> Node<E> {
276    pub fn transform(&self) -> NodeTransform {
277        match self.matrix {
278            Some(matrix) => match (self.translation, self.rotation, self.scale) {
279                // If both a matrix and a full transform set is specified, then just use the transform.
280                (Some(translation), Some(rotation), Some(scale)) => NodeTransform::Set {
281                    translation,
282                    rotation,
283                    scale,
284                },
285                _ => NodeTransform::Matrix(matrix),
286            },
287            None => {
288                let translation = self.translation.unwrap_or([0.0; 3]);
289                let rotation = self.rotation.unwrap_or([0.0, 0.0, 0.0, 1.0]);
290                let scale = self.scale.unwrap_or([1.0; 3]);
291                NodeTransform::Set {
292                    translation,
293                    rotation,
294                    scale,
295                }
296            }
297        }
298    }
299}
300
301#[derive(Debug)]
302pub enum NodeTransform {
303    Matrix([f32; 16]),
304    Set {
305        translation: [f32; 3],
306        rotation: [f32; 4],
307        scale: [f32; 3],
308    },
309}
310
311#[derive(Debug, DeJson, SerJson)]
312pub struct Mesh {
313    #[cfg(feature = "names")]
314    pub name: Option<String>,
315    pub primitives: Vec<Primitive>,
316    pub weights: Option<Vec<f32>>,
317}
318
319#[derive(Debug, DeJson, SerJson)]
320pub struct Primitive {
321    pub attributes: Attributes,
322    pub indices: Option<usize>,
323    pub material: Option<usize>,
324    #[nserde(default)]
325    pub mode: PrimitiveMode,
326    pub targets: Option<Vec<Attributes>>,
327}
328
329#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
330pub enum PrimitiveMode {
331    Points,
332    Lines,
333    LineLoop,
334    LineStrip,
335    #[default]
336    Triangles,
337    TriangleStrip,
338    TriangleFan,
339}
340
341impl DeJson for PrimitiveMode {
342    fn de_json(
343        state: &mut nanoserde::DeJsonState,
344        input: &mut core::str::Chars,
345    ) -> Result<Self, nanoserde::DeJsonErr> {
346        let ty = match &state.tok {
347            nanoserde::DeJsonTok::U64(ty) => match ty {
348                0 => Self::Points,
349                1 => Self::Lines,
350                2 => Self::LineLoop,
351                3 => Self::LineStrip,
352                4 => Self::Triangles,
353                5 => Self::TriangleStrip,
354                6 => Self::TriangleFan,
355                _ => return Err(state.err_range(&ty.to_string())),
356            },
357            _ => return Err(state.err_token("U64")),
358        };
359
360        state.next_tok(input)?;
361
362        Ok(ty)
363    }
364}
365
366impl SerJson for PrimitiveMode {
367    fn ser_json(&self, d: usize, s: &mut nanoserde::SerJsonState) {
368        match self {
369            Self::Points => 0,
370            Self::Lines => 1,
371            Self::LineLoop => 2,
372            Self::LineStrip => 3,
373            Self::Triangles => 4,
374            Self::TriangleStrip => 5,
375            Self::TriangleFan => 6,
376        }
377        .ser_json(d, s)
378    }
379}
380
381#[derive(Debug, DeJson, SerJson)]
382pub struct Attributes {
383    #[nserde(rename = "POSITION")]
384    pub position: Option<usize>,
385    #[nserde(rename = "TANGENT")]
386    pub tangent: Option<usize>,
387    #[nserde(rename = "NORMAL")]
388    pub normal: Option<usize>,
389    #[nserde(rename = "TEXCOORD_0")]
390    pub texcoord_0: Option<usize>,
391    #[nserde(rename = "TEXCOORD_1")]
392    pub texcoord_1: Option<usize>,
393    #[nserde(rename = "JOINTS_0")]
394    pub joints_0: Option<usize>,
395    #[nserde(rename = "WEIGHTS_0")]
396    pub weights_0: Option<usize>,
397}
398
399#[derive(Debug, DeJson, SerJson, Clone)]
400pub struct Image {
401    #[nserde(rename = "bufferView")]
402    pub buffer_view: Option<usize>,
403    pub uri: Option<String>,
404    #[nserde(rename = "mimeType")]
405    pub mime_type: Option<String>,
406    #[cfg(feature = "names")]
407    pub name: Option<String>,
408}
409
410#[derive(Debug, DeJson, SerJson)]
411pub struct Texture<E: Extensions> {
412    pub sampler: Option<usize>,
413    pub source: Option<usize>,
414    #[cfg(feature = "names")]
415    pub name: Option<String>,
416    #[nserde(default)]
417    pub extensions: E::TextureExtensions,
418}
419
420#[derive(Debug, DeJson, SerJson)]
421pub struct BufferView<E: Extensions> {
422    pub buffer: usize,
423    #[nserde(rename = "byteLength")]
424    pub byte_length: usize,
425    #[nserde(rename = "byteOffset")]
426    #[nserde(default)]
427    pub byte_offset: usize,
428    #[nserde(rename = "byteStride")]
429    pub byte_stride: Option<usize>,
430    #[nserde(default)]
431    pub target: u32,
432    #[cfg(feature = "names")]
433    pub name: Option<String>,
434    #[nserde(default)]
435    pub extensions: E::BufferViewExtensions,
436}
437
438#[derive(Debug, DeJson, SerJson)]
439pub struct Accessor {
440    #[nserde(rename = "bufferView")]
441    pub buffer_view: Option<usize>,
442    #[nserde(rename = "componentType")]
443    pub component_type: ComponentType,
444    pub count: usize,
445    pub sparse: Option<Sparse>,
446    // todo: these could be changed to enum { Int, Float }.
447    pub max: Option<Vec<f32>>,
448    pub min: Option<Vec<f32>>,
449    #[nserde(rename = "type")]
450    pub accessor_type: AccessorType,
451    #[cfg(feature = "names")]
452    pub name: Option<String>,
453    #[nserde(rename = "byteOffset")]
454    #[nserde(default)]
455    pub byte_offset: usize,
456    #[nserde(default)]
457    pub normalized: bool,
458}
459
460impl Accessor {
461    pub fn byte_length<E: Extensions>(&self, buffer_view: &BufferView<E>) -> usize {
462        self.count
463            * buffer_view.byte_stride.unwrap_or_else(|| {
464                self.component_type.byte_size() * self.accessor_type.num_components()
465            })
466    }
467}
468
469#[derive(Debug, DeJson, SerJson)]
470pub struct Sparse {
471    pub count: usize,
472    pub indices: SparseIndices,
473    pub values: SparseValues,
474}
475
476#[derive(Debug, DeJson, SerJson)]
477pub struct SparseIndices {
478    #[nserde(rename = "bufferView")]
479    pub buffer_view: usize,
480    #[nserde(rename = "byteOffset")]
481    #[nserde(default)]
482    pub byte_offset: usize,
483    #[nserde(rename = "componentType")]
484    pub component_type: ComponentType,
485}
486
487#[derive(Debug, DeJson, SerJson)]
488pub struct SparseValues {
489    #[nserde(rename = "bufferView")]
490    pub buffer_view: usize,
491    #[nserde(rename = "byteOffset")]
492    #[nserde(default)]
493    pub byte_offset: usize,
494}
495
496#[derive(Debug, Clone, Copy, PartialEq, Eq)]
497pub enum ComponentType {
498    UnsignedByte,
499    Byte,
500    UnsignedShort,
501    Short,
502    UnsignedInt,
503    Float,
504}
505
506impl ComponentType {
507    pub fn byte_size(&self) -> usize {
508        match self {
509            Self::UnsignedByte | Self::Byte => 1,
510            Self::UnsignedShort | Self::Short => 2,
511            Self::UnsignedInt | Self::Float => 4,
512        }
513    }
514}
515
516impl DeJson for ComponentType {
517    fn de_json(
518        state: &mut nanoserde::DeJsonState,
519        input: &mut core::str::Chars,
520    ) -> Result<Self, nanoserde::DeJsonErr> {
521        let ty = match &state.tok {
522            nanoserde::DeJsonTok::U64(ty) => match ty {
523                5120 => Self::Byte,
524                5121 => Self::UnsignedByte,
525                5122 => Self::Short,
526                5123 => Self::UnsignedShort,
527                5125 => Self::UnsignedInt,
528                5126 => Self::Float,
529                _ => return Err(state.err_range(&ty.to_string())),
530            },
531            _ => return Err(state.err_token("U64")),
532        };
533
534        state.next_tok(input)?;
535
536        Ok(ty)
537    }
538}
539
540impl SerJson for ComponentType {
541    fn ser_json(&self, d: usize, s: &mut nanoserde::SerJsonState) {
542        match self {
543            Self::Byte => 5120,
544            Self::UnsignedByte => 5121,
545            Self::Short => 5122,
546            Self::UnsignedShort => 5123,
547            Self::UnsignedInt => 5125,
548            Self::Float => 5126,
549        }
550        .ser_json(d, s)
551    }
552}
553
554#[derive(Debug, DeJson, SerJson, PartialEq, Clone, Copy)]
555pub enum AccessorType {
556    #[nserde(rename = "SCALAR")]
557    Scalar,
558    #[nserde(rename = "VEC2")]
559    Vec2,
560    #[nserde(rename = "VEC3")]
561    Vec3,
562    #[nserde(rename = "VEC4")]
563    Vec4,
564    #[nserde(rename = "MAT2")]
565    Mat2,
566    #[nserde(rename = "MAT3")]
567    Mat3,
568    #[nserde(rename = "MAT4")]
569    Mat4,
570}
571
572impl AccessorType {
573    pub fn num_components(&self) -> usize {
574        match self {
575            Self::Scalar => 1,
576            Self::Vec2 => 2,
577            Self::Vec3 => 3,
578            Self::Vec4 | Self::Mat2 => 4,
579            Self::Mat3 => 9,
580            Self::Mat4 => 16,
581        }
582    }
583}
584
585#[derive(Debug, DeJson, SerJson, Clone)]
586pub struct Material<E: Extensions> {
587    #[nserde(rename = "alphaMode")]
588    #[nserde(default)]
589    pub alpha_mode: AlphaMode,
590    #[nserde(default)]
591    pub extensions: E::MaterialExtensions,
592    #[cfg(feature = "names")]
593    pub name: Option<String>,
594    #[nserde(rename = "pbrMetallicRoughness")]
595    #[nserde(default)]
596    pub pbr_metallic_roughness: PbrMetallicRoughness<E>,
597    #[nserde(rename = "normalTexture")]
598    pub normal_texture: Option<NormalTextureInfo<E>>,
599    #[nserde(rename = "occlusionTexture")]
600    pub occlusion_texture: Option<OcclusionTextureInfo<E>>,
601    #[nserde(rename = "emissiveTexture")]
602    pub emissive_texture: Option<TextureInfo<E>>,
603    #[nserde(rename = "emissiveFactor")]
604    #[nserde(default)]
605    pub emissive_factor: [f32; 3],
606    #[nserde(rename = "alphaCutoff")]
607    #[nserde(default = "0.5")]
608    pub alpha_cutoff: f32,
609    #[nserde(rename = "doubleSided")]
610    #[nserde(default)]
611    pub double_sided: bool,
612}
613
614#[derive(Default, Debug, DeJson, SerJson, Clone, Copy)]
615pub enum AlphaMode {
616    #[default]
617    #[nserde(rename = "OPAQUE")]
618    Opaque,
619    #[nserde(rename = "MASK")]
620    Mask,
621    #[nserde(rename = "BLEND")]
622    Blend,
623}
624
625#[derive(Debug, DeJson, SerJson, Clone)]
626pub struct PbrMetallicRoughness<E: Extensions> {
627    #[nserde(rename = "baseColorFactor")]
628    #[nserde(default = "[1.0, 1.0, 1.0, 1.0]")]
629    pub base_color_factor: [f32; 4],
630    #[nserde(rename = "baseColorTexture")]
631    pub base_color_texture: Option<TextureInfo<E>>,
632    #[nserde(rename = "metallicFactor")]
633    #[nserde(default = "1.0")]
634    pub metallic_factor: f32,
635    #[nserde(rename = "roughnessFactor")]
636    #[nserde(default = "1.0")]
637    pub roughness_factor: f32,
638    #[nserde(rename = "metallicRoughnessTexture")]
639    pub metallic_roughness_texture: Option<TextureInfo<E>>,
640}
641
642impl<E: Extensions> Default for PbrMetallicRoughness<E> {
643    fn default() -> Self {
644        Self {
645            base_color_factor: [1.0; 4],
646            base_color_texture: None,
647            metallic_factor: 1.0,
648            roughness_factor: 1.0,
649            metallic_roughness_texture: None,
650        }
651    }
652}
653
654#[derive(Debug, DeJson, SerJson, Clone)]
655pub struct TextureInfo<E: Extensions> {
656    pub index: usize,
657    #[nserde(rename = "texCoord")]
658    #[nserde(default)]
659    pub tex_coord: usize,
660    #[nserde(default)]
661    pub extensions: E::TextureInfoExtensions,
662}
663
664#[derive(Debug, DeJson, SerJson, Clone)]
665pub struct NormalTextureInfo<E: Extensions> {
666    pub index: usize,
667    #[nserde(rename = "texCoord")]
668    #[nserde(default)]
669    pub tex_coord: usize,
670    #[nserde(default = "1.0")]
671    pub scale: f32,
672    #[nserde(default)]
673    pub extensions: E::TextureInfoExtensions,
674}
675
676#[derive(Debug, DeJson, SerJson, Clone)]
677pub struct OcclusionTextureInfo<E: Extensions> {
678    pub index: usize,
679    #[nserde(rename = "texCoord")]
680    #[nserde(default)]
681    pub tex_coord: usize,
682    #[nserde(default = "1.0")]
683    pub strength: f32,
684    #[nserde(default)]
685    pub extensions: E::TextureInfoExtensions,
686}
687
688#[derive(Debug, DeJson, SerJson)]
689pub struct Sampler {
690    #[nserde(rename = "magFilter")]
691    pub mag_filter: Option<FilterMode>,
692    #[nserde(rename = "minFilter")]
693    pub min_filter: Option<MinFilter>,
694    #[nserde(rename = "wrapS")]
695    #[nserde(default)]
696    pub wrap_s: SamplerWrap,
697    #[nserde(rename = "wrapT")]
698    #[nserde(default)]
699    pub wrap_t: SamplerWrap,
700    #[cfg(feature = "names")]
701    pub name: Option<String>,
702}
703
704#[derive(Debug, PartialEq)]
705pub enum FilterMode {
706    Nearest,
707    Linear,
708}
709
710impl DeJson for FilterMode {
711    fn de_json(
712        state: &mut nanoserde::DeJsonState,
713        input: &mut core::str::Chars,
714    ) -> Result<Self, nanoserde::DeJsonErr> {
715        let ty = match &state.tok {
716            nanoserde::DeJsonTok::U64(ty) => match ty {
717                9728 => Self::Nearest,
718                9729 => Self::Linear,
719                _ => return Err(state.err_range(&ty.to_string())),
720            },
721            _ => return Err(state.err_token("U64")),
722        };
723
724        state.next_tok(input)?;
725
726        Ok(ty)
727    }
728}
729
730impl SerJson for FilterMode {
731    fn ser_json(&self, d: usize, s: &mut nanoserde::SerJsonState) {
732        match self {
733            Self::Nearest => 9728,
734            Self::Linear => 9729,
735        }
736        .ser_json(d, s)
737    }
738}
739
740#[derive(Debug, PartialEq)]
741pub struct MinFilter {
742    pub mode: FilterMode,
743    pub mipmap: Option<FilterMode>,
744}
745
746impl DeJson for MinFilter {
747    fn de_json(
748        state: &mut nanoserde::DeJsonState,
749        input: &mut core::str::Chars,
750    ) -> Result<Self, nanoserde::DeJsonErr> {
751        let ty = match &state.tok {
752            nanoserde::DeJsonTok::U64(ty) => match ty {
753                9728 => Self {
754                    mode: FilterMode::Nearest,
755                    mipmap: None,
756                },
757                9729 => Self {
758                    mode: FilterMode::Linear,
759                    mipmap: None,
760                },
761                9984 => Self {
762                    mode: FilterMode::Nearest,
763                    mipmap: Some(FilterMode::Nearest),
764                },
765                9985 => Self {
766                    mode: FilterMode::Linear,
767                    mipmap: Some(FilterMode::Nearest),
768                },
769                9986 => Self {
770                    mode: FilterMode::Nearest,
771                    mipmap: Some(FilterMode::Linear),
772                },
773                9987 => Self {
774                    mode: FilterMode::Linear,
775                    mipmap: Some(FilterMode::Linear),
776                },
777                _ => return Err(state.err_range(&ty.to_string())),
778            },
779            _ => return Err(state.err_token("U64")),
780        };
781
782        state.next_tok(input)?;
783
784        Ok(ty)
785    }
786}
787
788impl SerJson for MinFilter {
789    fn ser_json(&self, d: usize, s: &mut nanoserde::SerJsonState) {
790        match &self {
791            MinFilter {
792                mode: FilterMode::Nearest,
793                mipmap: None,
794            } => 9728,
795            MinFilter {
796                mode: FilterMode::Linear,
797                mipmap: None,
798            } => 9729,
799            MinFilter {
800                mode: FilterMode::Nearest,
801                mipmap: Some(FilterMode::Nearest),
802            } => 9984,
803            MinFilter {
804                mode: FilterMode::Linear,
805                mipmap: Some(FilterMode::Nearest),
806            } => 9985,
807            MinFilter {
808                mode: FilterMode::Nearest,
809                mipmap: Some(FilterMode::Linear),
810            } => 9986,
811            MinFilter {
812                mode: FilterMode::Linear,
813                mipmap: Some(FilterMode::Linear),
814            } => 9987,
815        }
816        .ser_json(d, s)
817    }
818}
819
820#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
821pub enum SamplerWrap {
822    ClampToEdge,
823    MirroredRepeat,
824    #[default]
825    Repeat,
826}
827
828impl DeJson for SamplerWrap {
829    fn de_json(
830        state: &mut nanoserde::DeJsonState,
831        input: &mut core::str::Chars,
832    ) -> Result<Self, nanoserde::DeJsonErr> {
833        let ty = match &state.tok {
834            nanoserde::DeJsonTok::U64(ty) => match ty {
835                33071 => Self::ClampToEdge,
836                33648 => Self::MirroredRepeat,
837                10497 => Self::Repeat,
838                _ => return Err(state.err_range(&ty.to_string())),
839            },
840            _ => return Err(state.err_token("U64")),
841        };
842
843        state.next_tok(input)?;
844
845        Ok(ty)
846    }
847}
848
849impl SerJson for SamplerWrap {
850    fn ser_json(&self, d: usize, s: &mut nanoserde::SerJsonState) {
851        match self {
852            Self::ClampToEdge => 33071,
853            Self::MirroredRepeat => 33648,
854            Self::Repeat => 10497,
855        }
856        .ser_json(d, s)
857    }
858}
859
860#[derive(Debug, DeJson, SerJson, Clone)]
861pub struct Camera {
862    pub perspective: Option<CameraPerspective>,
863    pub orthographic: Option<CameraOrthographic>,
864    #[nserde(rename = "type")]
865    pub ty: CameraType,
866    #[cfg(feature = "names")]
867    pub name: Option<String>,
868}
869
870#[derive(Debug, DeJson, SerJson, Clone, Copy)]
871pub struct CameraPerspective {
872    pub yfov: f32,
873    pub znear: f32,
874    pub zfar: Option<f32>,
875    #[nserde(rename = "aspectRatio")]
876    pub aspect_ratio: Option<f32>,
877}
878
879#[derive(Debug, DeJson, SerJson, Clone, Copy)]
880pub struct CameraOrthographic {
881    pub xmag: f32,
882    pub ymag: f32,
883    pub zfar: f32,
884    pub znear: f32,
885}
886
887#[derive(Debug, DeJson, SerJson, Clone, Copy, PartialEq, Eq)]
888pub enum CameraType {
889    #[nserde(rename = "perspective")]
890    Perspective,
891    #[nserde(rename = "orthographic")]
892    Orthographic,
893}
894
895#[derive(Debug, DeJson, SerJson, Clone)]
896pub struct Scene {
897    #[cfg(feature = "names")]
898    pub name: Option<String>,
899    pub nodes: Vec<usize>,
900}
901
902pub mod default_extensions {
903    use crate::extensions;
904    use nanoserde::{DeJson, SerJson};
905
906    #[derive(Debug, Default, Clone, Copy, DeJson, SerJson)]
907    pub struct Extensions;
908
909    impl super::Extensions for Extensions {
910        type RootExtensions = RootExtensions;
911        type TextureExtensions = TextureExtensions;
912        type TextureInfoExtensions = TextureInfoExtensions;
913        type MaterialExtensions = MaterialExtensions<Self>;
914        type BufferExtensions = BufferExtensions;
915        type NodeExtensions = NodeExtensions;
916        type NodeExtras = NodeExtras;
917        type BufferViewExtensions = BufferViewExtensions;
918    }
919
920    #[derive(Debug, DeJson, SerJson, Default, Clone)]
921    pub struct RootExtensions {
922        #[nserde(rename = "KHR_lights_punctual")]
923        pub khr_lights_punctual: Option<extensions::khr_lights_punctual::Root>,
924    }
925
926    #[derive(Debug, DeJson, SerJson, Default, Clone)]
927    pub struct BufferExtensions {
928        #[nserde(rename = "EXT_meshopt_compression")]
929        pub ext_meshopt_compression: Option<extensions::ExtMeshoptCompressionBuffer>,
930    }
931
932    #[derive(Debug, DeJson, SerJson, Default, Clone)]
933    pub struct NodeExtensions {
934        #[nserde(rename = "EXT_mesh_gpu_instancing")]
935        pub ext_mesh_gpu_instancing: Option<extensions::ExtMeshGpuInstancing>,
936        #[nserde(rename = "MSFT_lod")]
937        pub msft_lod: Option<extensions::MsftLod>,
938        #[nserde(rename = "KHR_lights_punctual")]
939        pub khr_lights_punctual: Option<extensions::khr_lights_punctual::Node>,
940    }
941
942    #[derive(Debug, DeJson, SerJson, Default, Clone)]
943    pub struct NodeExtras {
944        #[nserde(rename = "MSFT_screencoverage")]
945        pub msft_screencoverage: Option<Vec<f32>>,
946    }
947
948    #[derive(Debug, DeJson, SerJson, Default, Clone)]
949    pub struct TextureExtensions {
950        #[nserde(rename = "KHR_texture_basisu")]
951        pub khr_texture_basisu: Option<extensions::KhrTextureBasisu>,
952    }
953
954    #[derive(Debug, DeJson, SerJson, Default, Clone)]
955    pub struct BufferViewExtensions {
956        #[nserde(rename = "EXT_meshopt_compression")]
957        pub ext_meshopt_compression: Option<extensions::ExtMeshoptCompression>,
958    }
959
960    #[derive(Debug, DeJson, SerJson, Default, Clone)]
961    pub struct MaterialExtensions<E: super::Extensions> {
962        #[nserde(rename = "KHR_materials_sheen")]
963        pub khr_materials_sheen: Option<extensions::KhrMaterialsSheen<E>>,
964        #[nserde(rename = "KHR_materials_emissive_strength")]
965        pub khr_materials_emissive_strength: Option<extensions::KhrMaterialsEmissiveStrength>,
966        #[nserde(rename = "KHR_materials_unlit")]
967        pub khr_materials_unlit: Option<extensions::KhrMaterialsUnlit>,
968        #[nserde(rename = "KHR_materials_ior")]
969        pub khr_materials_ior: Option<extensions::KhrMaterialsIor>,
970        #[nserde(rename = "KHR_materials_specular")]
971        pub khr_materials_specular: Option<extensions::KhrMaterialsSpecular<E>>,
972        #[nserde(rename = "KHR_materials_transmission")]
973        pub khr_materials_transmission: Option<extensions::KhrMaterialsTransmission<E>>,
974    }
975
976    #[derive(Debug, DeJson, SerJson, Default, Clone, Copy)]
977    pub struct TextureInfoExtensions {
978        #[nserde(rename = "KHR_texture_transform")]
979        pub khr_texture_transform: Option<extensions::KhrTextureTransform>,
980    }
981}
982
983#[cfg(test)]
984mod tests {
985    use super::*;
986    use nanoserde::{DeJson, SerJson};
987
988    fn test_roundtrip<T: DeJson + SerJson + PartialEq + Debug>(value: &T) {
989        assert_eq!(
990            *value,
991            T::deserialize_json(&value.serialize_json()).unwrap()
992        );
993    }
994
995    #[test]
996    fn test_primitive_mode_roundtrip() {
997        for variant in &[
998            PrimitiveMode::Points,
999            PrimitiveMode::Lines,
1000            PrimitiveMode::LineLoop,
1001            PrimitiveMode::LineStrip,
1002            PrimitiveMode::Triangles,
1003            PrimitiveMode::TriangleStrip,
1004            PrimitiveMode::TriangleFan,
1005        ] {
1006            test_roundtrip(variant);
1007        }
1008    }
1009
1010    #[test]
1011    fn test_component_type_roundtrip() {
1012        for variant in &[
1013            ComponentType::UnsignedByte,
1014            ComponentType::Byte,
1015            ComponentType::UnsignedShort,
1016            ComponentType::Short,
1017            ComponentType::UnsignedInt,
1018            ComponentType::Float,
1019        ] {
1020            test_roundtrip(variant);
1021        }
1022    }
1023
1024    #[test]
1025    fn test_filter_mode_roundtrip() {
1026        for variant in &[FilterMode::Nearest, FilterMode::Linear] {
1027            test_roundtrip(variant);
1028        }
1029    }
1030
1031    #[test]
1032    fn test_min_filter_roundtrip() {
1033        for variant in &[
1034            MinFilter {
1035                mode: FilterMode::Nearest,
1036                mipmap: None,
1037            },
1038            MinFilter {
1039                mode: FilterMode::Linear,
1040                mipmap: None,
1041            },
1042            MinFilter {
1043                mode: FilterMode::Nearest,
1044                mipmap: Some(FilterMode::Nearest),
1045            },
1046            MinFilter {
1047                mode: FilterMode::Linear,
1048                mipmap: Some(FilterMode::Nearest),
1049            },
1050            MinFilter {
1051                mode: FilterMode::Nearest,
1052                mipmap: Some(FilterMode::Linear),
1053            },
1054            MinFilter {
1055                mode: FilterMode::Linear,
1056                mipmap: Some(FilterMode::Linear),
1057            },
1058        ] {
1059            test_roundtrip(variant);
1060        }
1061    }
1062
1063    #[test]
1064    fn test_sampler_wrap_roundtrip() {
1065        for variant in &[
1066            SamplerWrap::ClampToEdge,
1067            SamplerWrap::MirroredRepeat,
1068            SamplerWrap::Repeat,
1069        ] {
1070            test_roundtrip(variant);
1071        }
1072    }
1073}