Skip to main content

gltf_reader/
lib.rs

1//! A simple glTF 2.0 reader using [`serde`] and [`serde_json`]. This crate is pretty much a 1:1
2//! implementation of the [glTF schema] in Rust. It is purely structural and performs no validation
3//! of the glTF asset.
4//!
5//! The structures try to borrow from the glTF JSON as much as possible. While not zero-copy, this
6//! crate is way more memory efficient than other glTF readers. You can still obtain an owned copy
7//! of any structure through `into_owned` methods, though.
8//!
9//! All of the types implement [`Deserialize`], which in theory allow them to be deserialized from
10//! any format. However, the only supported format intended is JSON through [`serde_json`] -
11//! anything else has no guarantees of working.
12//!
13//! You can easily parse a glTF JSON string with [`Root::from_str`], which is just a wrapper around
14//! [`serde_json::from_str`].
15//!
16//! # no-std
17//! This is a no-[`std`] crate. It does, however, still require [`alloc`].
18//!
19//! [glTF schema]: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#appendix-a-json-schema-reference
20
21#![cfg_attr(not(any(test, doc)), no_std)]
22
23extern crate alloc;
24
25pub use serde_json;
26
27pub mod json;
28
29mod accessor;
30mod animation;
31mod buffer;
32mod camera;
33mod image;
34mod material;
35mod mesh;
36mod node;
37mod sampler;
38mod scene;
39mod skin;
40mod texture;
41
42use alloc::borrow::Cow;
43use alloc::vec::Vec;
44use core::marker::PhantomData;
45
46use ownable::IntoOwned;
47use serde::Deserialize;
48
49pub use crate::accessor::*;
50pub use crate::animation::*;
51pub use crate::buffer::*;
52pub use crate::camera::*;
53pub use crate::image::*;
54pub use crate::material::*;
55pub use crate::mesh::*;
56pub use crate::node::*;
57pub use crate::sampler::*;
58pub use crate::scene::*;
59pub use crate::skin::*;
60pub use crate::texture::*;
61
62/// A glTF index. Since pratically all structures in the crate have a lifetime that doesn't matter
63/// for this type, it's simply set to `static` - in practice, just ignore it.
64#[derive(Clone, Copy, Deserialize)]
65#[serde(transparent)]
66pub struct Idx<T: 'static>(u32, PhantomData<T>);
67
68impl<T> ownable::traits::IntoOwned for Idx<T> {
69    type Owned = Self;
70
71    fn into_owned(self) -> Self::Owned {
72        self
73    }
74}
75
76impl<T> core::fmt::Debug for Idx<T> {
77    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
78        self.0.fmt(f)
79    }
80}
81
82impl<T: 'static> Idx<T> {
83    /// Creates a new index with the given value.
84    pub fn new(index: u32) -> Self {
85        Self(index, PhantomData)
86    }
87
88    /// Gets the value of this index.
89    pub fn get(self) -> u32 {
90        self.0
91    }
92
93    /// Casts this index type to another.
94    pub fn cast<U: 'static>(self) -> Idx<U> {
95        Idx(self.0, PhantomData)
96    }
97}
98
99/// Application-specific data.
100#[derive(Clone, Deserialize, IntoOwned)]
101#[serde(transparent)]
102pub struct Extras<'a>(#[serde(borrow)] pub json::Value<'a>);
103
104impl core::fmt::Debug for Extras<'_> {
105    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
106        self.0.fmt(f)
107    }
108}
109
110/// JSON object with extension-specific objects.
111#[derive(Clone, Deserialize, Default, IntoOwned)]
112#[serde(transparent)]
113pub struct Extensions<'a>(#[serde(borrow)] pub json::Object<'a>);
114
115impl core::fmt::Debug for Extensions<'_> {
116    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
117        self.0.fmt(f)
118    }
119}
120
121/// Metadata about a glTF asset.
122#[derive(Debug, Clone, Deserialize, IntoOwned)]
123pub struct Asset<'a> {
124    /// A copyright message suitable for display to credit the content creator.
125    #[serde(borrow)]
126    pub copyright: Option<Cow<'a, str>>,
127    /// Tool that generated this glTF model. Useful for debugging.
128    #[serde(borrow)]
129    pub generator: Option<Cow<'a, str>>,
130    /// The glTF version in the form of `<major>.<minor>` that this asset targets.
131    #[serde(borrow)]
132    pub version: Cow<'a, str>,
133    /// The minimum glTF version in the form of `<major>.<minor>` that this asset targets.
134    #[serde(rename = "minVersion")]
135    #[serde(borrow)]
136    pub min_version: Option<Cow<'a, str>>,
137
138    #[serde(borrow)]
139    pub extensions: Option<Extensions<'a>>,
140    #[serde(borrow)]
141    pub extras: Option<Extras<'a>>,
142}
143
144/// The root object of a glTF asset.
145#[derive(Debug, Clone, Deserialize, IntoOwned)]
146pub struct Root<'a> {
147    /// Metadata about this glTF asset.
148    pub asset: Asset<'a>,
149    /// Names of glTF extensions used in this asset.
150    #[serde(rename = "extensionsUsed")]
151    #[serde(default, borrow)]
152    pub extensions_used: Vec<Cow<'a, str>>,
153    /// Names of glTF extensions required to properly load this asset.
154    #[serde(rename = "extensionsRequired")]
155    #[serde(default, borrow)]
156    pub extensions_required: Vec<Cow<'a, str>>,
157    /// The index of the default scene.
158    pub scene: Option<Idx<Scene<'static>>>,
159
160    /// An array of accessors.
161    #[serde(default, borrow)]
162    pub accessors: Vec<Accessor<'a>>,
163    /// An array of animations.
164    #[serde(default, borrow)]
165    pub animations: Vec<Animation<'a>>,
166    /// An array of buffers.
167    #[serde(default, borrow)]
168    pub buffers: Vec<Buffer<'a>>,
169    /// An array of buffer views.
170    #[serde(rename = "bufferViews")]
171    #[serde(default, borrow)]
172    pub buffer_views: Vec<BufferView<'a>>,
173    /// An array of cameras.
174    #[serde(default, borrow)]
175    pub cameras: Vec<Camera<'a>>,
176    /// An array of images.
177    #[serde(default, borrow)]
178    pub images: Vec<Image<'a>>,
179    /// An array of materials.
180    #[serde(default, borrow)]
181    pub materials: Vec<Material<'a>>,
182    /// An array of meshes.
183    #[serde(default, borrow)]
184    pub meshes: Vec<Mesh<'a>>,
185    /// An array of nodes.
186    #[serde(default, borrow)]
187    pub nodes: Vec<Node<'a>>,
188    /// An array of samplers.
189    #[serde(default, borrow)]
190    pub samplers: Vec<Sampler<'a>>,
191    /// An array of scenes.
192    #[serde(default, borrow)]
193    pub scenes: Vec<Scene<'a>>,
194    /// An array of skins.
195    #[serde(default, borrow)]
196    pub skins: Vec<Skin<'a>>,
197    /// An array of textures.
198    #[serde(default, borrow)]
199    pub textures: Vec<Texture<'a>>,
200
201    #[serde(borrow)]
202    pub extensions: Option<Extensions<'a>>,
203    #[serde(borrow)]
204    pub extras: Option<Extras<'a>>,
205}
206
207impl<'a> Root<'a> {
208    /// Reads a glTF JSON from the given string.
209    #[expect(
210        clippy::should_implement_trait,
211        reason = "looks like FromStr but can't be implemented as it because of lifetimes"
212    )]
213    pub fn from_str(s: &'a str) -> serde_json::Result<Self> {
214        serde_json::from_str(s)
215    }
216}