Skip to main content

gltf/mesh/
mod.rs

1//! # Basic usage
2//!
3//! Listing the attributes of each mesh primitive in a glTF asset.
4//!
5//! ```
6//! # fn run() -> Result<(), Box<dyn std::error::Error>> {
7//! # let gltf = gltf::Gltf::open("examples/Box.gltf")?;
8//! for mesh in gltf.meshes() {
9//!    println!("Mesh #{}", mesh.index());
10//!    for primitive in mesh.primitives() {
11//!        println!("- Primitive #{}", primitive.index());
12//!        for (semantic, _) in primitive.attributes() {
13//!            println!("-- {:?}", semantic);
14//!        }
15//!    }
16//! }
17//! # Ok(())
18//! # }
19//! # fn main() {
20//! #    let _ = run().expect("runtime error");
21//! # }
22//! ```
23//!
24//! # Reader utility
25//!
26//! Printing the vertex positions of each primitive of each mesh in
27//! a glTF asset.
28//!
29//! ```
30//! # fn run() -> Result<(), Box<dyn std::error::Error>> {
31//! let (gltf, buffers, _) = gltf::import("examples/Box.gltf")?;
32//! for mesh in gltf.meshes() {
33//!    println!("Mesh #{}", mesh.index());
34//!    for primitive in mesh.primitives() {
35//!        println!("- Primitive #{}", primitive.index());
36//!        let reader = primitive.reader(|buffer| Some(&buffers[buffer.index()]));
37//!        if let Some(iter) = reader.read_positions() {
38//!            for vertex_position in iter {
39//!                println!("{:?}", vertex_position);
40//!            }
41//!        }
42//!    }
43//! }
44//! # Ok(())
45//! # }
46//! # fn main() {
47//! #    let _ = run().expect("runtime error");
48//! # }
49//! ```
50
51/// Iterators.
52pub mod iter;
53
54/// Utility functions.
55#[cfg(feature = "utils")]
56#[cfg_attr(docsrs, doc(cfg(feature = "utils")))]
57pub mod util;
58
59use crate::{Accessor, Buffer, Document, Material};
60
61#[cfg(feature = "utils")]
62use crate::accessor;
63
64pub use json::mesh::{Mode, Semantic};
65use json::validation::Checked;
66#[cfg(feature = "extensions")]
67use serde_json::{Map, Value};
68
69/// Vertex attribute data.
70pub type Attribute<'a> = (Semantic, Accessor<'a>);
71
72/// Vertex position bounding box.
73pub type BoundingBox = Bounds<[f32; 3]>;
74
75/// The minimum and maximum values for a generic accessor.
76#[derive(Clone, Debug, PartialEq)]
77pub struct Bounds<T> {
78    /// Minimum value.
79    pub min: T,
80
81    /// Maximum value.
82    pub max: T,
83}
84
85/// A set of primitives to be rendered.
86#[derive(Clone, Debug)]
87pub struct Mesh<'a> {
88    /// The parent `Document` struct.
89    document: &'a Document,
90
91    /// The corresponding JSON index.
92    index: usize,
93
94    /// The corresponding JSON struct.
95    json: &'a json::mesh::Mesh,
96}
97
98/// A single morph target for a mesh primitive.
99#[derive(Clone, Debug)]
100pub struct MorphTarget<'a> {
101    /// XYZ vertex position displacements.
102    positions: Option<Accessor<'a>>,
103
104    /// XYZ vertex normal displacements.
105    normals: Option<Accessor<'a>>,
106
107    /// XYZ vertex tangent displacements.
108    tangents: Option<Accessor<'a>>,
109}
110
111/// Geometry to be rendered with the given material.
112#[derive(Clone, Debug)]
113pub struct Primitive<'a> {
114    /// The parent `Mesh` struct.
115    mesh: Mesh<'a>,
116
117    /// The corresponding JSON index.
118    index: usize,
119
120    /// The corresponding JSON struct.
121    json: &'a json::mesh::Primitive,
122}
123
124/// Mesh primitive reader.
125#[derive(Clone, Debug)]
126pub struct Reader<'a, 's, F>
127where
128    F: Clone + Fn(Buffer<'a>) -> Option<&'s [u8]>,
129{
130    #[allow(dead_code)]
131    pub(crate) primitive: &'a Primitive<'a>,
132    #[allow(dead_code)]
133    pub(crate) get_buffer_data: F,
134}
135
136impl<'a> Mesh<'a> {
137    /// Constructs a `Mesh`.
138    pub(crate) fn new(document: &'a Document, index: usize, json: &'a json::mesh::Mesh) -> Self {
139        Self {
140            document,
141            index,
142            json,
143        }
144    }
145
146    /// Returns the internal JSON index.
147    pub fn index(&self) -> usize {
148        self.index
149    }
150
151    /// Returns extension data unknown to this crate version.
152    #[cfg(feature = "extensions")]
153    #[cfg_attr(docsrs, doc(cfg(feature = "extensions")))]
154    pub fn extensions(&self) -> Option<&Map<String, Value>> {
155        let ext = self.json.extensions.as_ref()?;
156        Some(&ext.others)
157    }
158
159    /// Queries extension data unknown to this crate version.
160    #[cfg(feature = "extensions")]
161    #[cfg_attr(docsrs, doc(cfg(feature = "extensions")))]
162    pub fn extension_value(&self, ext_name: &str) -> Option<&Value> {
163        let ext = self.json.extensions.as_ref()?;
164        ext.others.get(ext_name)
165    }
166
167    /// Optional application specific data.
168    pub fn extras(&self) -> &'a json::Extras {
169        &self.json.extras
170    }
171
172    /// Optional user-defined name for this object.
173    #[cfg(feature = "names")]
174    #[cfg_attr(docsrs, doc(cfg(feature = "names")))]
175    pub fn name(&self) -> Option<&'a str> {
176        self.json.name.as_deref()
177    }
178
179    /// Defines the geometry to be renderered with a material.
180    pub fn primitives(&self) -> iter::Primitives<'a> {
181        iter::Primitives {
182            mesh: self.clone(),
183            iter: self.json.primitives.iter().enumerate(),
184        }
185    }
186
187    /// Defines the weights to be applied to the morph targets.
188    pub fn weights(&self) -> Option<&'a [f32]> {
189        self.json.weights.as_deref()
190    }
191}
192
193impl<'a> Primitive<'a> {
194    /// Constructs a `Primitive`.
195    pub(crate) fn new(mesh: Mesh<'a>, index: usize, json: &'a json::mesh::Primitive) -> Self {
196        Self { mesh, index, json }
197    }
198
199    /// Returns the bounds of the `POSITION` vertex attribute.
200    pub fn bounding_box(&self) -> BoundingBox {
201        // NOTE: cannot panic if validated "minimally"
202        let pos_accessor_index = self
203            .json
204            .attributes
205            .get(&Checked::Valid(Semantic::Positions))
206            .unwrap();
207        let pos_accessor = self
208            .mesh
209            .document
210            .accessors()
211            .nth(pos_accessor_index.value())
212            .unwrap();
213        let min: [f32; 3] = json::deserialize::from_value(pos_accessor.min().unwrap()).unwrap();
214        let max: [f32; 3] = json::deserialize::from_value(pos_accessor.max().unwrap()).unwrap();
215        Bounds { min, max }
216    }
217
218    /// Returns extension data unknown to this crate version.
219    #[cfg(feature = "extensions")]
220    #[cfg_attr(docsrs, doc(cfg(feature = "extensions")))]
221    pub fn extensions(&self) -> Option<&Map<String, Value>> {
222        let ext = self.json.extensions.as_ref()?;
223        Some(&ext.others)
224    }
225
226    /// Queries extension data unknown to this crate version.
227    #[cfg(feature = "extensions")]
228    #[cfg_attr(docsrs, doc(cfg(feature = "extensions")))]
229    pub fn extension_value(&self, ext_name: &str) -> Option<&Value> {
230        let ext = self.json.extensions.as_ref()?;
231        ext.others.get(ext_name)
232    }
233
234    /// Optional application specific data.
235    pub fn extras(&self) -> &'a json::Extras {
236        &self.json.extras
237    }
238
239    /// Return the accessor with the given semantic.
240    pub fn get(&self, semantic: &Semantic) -> Option<Accessor<'a>> {
241        self.json
242            .attributes
243            .get(&json::validation::Checked::Valid(semantic.clone()))
244            .map(|index| self.mesh.document.accessors().nth(index.value()).unwrap())
245    }
246
247    /// Returns the internal JSON index.
248    pub fn index(&self) -> usize {
249        self.index
250    }
251
252    /// Returns the accessor containing the primitive indices, if provided.
253    pub fn indices(&self) -> Option<Accessor<'a>> {
254        self.json
255            .indices
256            .as_ref()
257            .map(|index| self.mesh.document.accessors().nth(index.value()).unwrap())
258    }
259
260    /// Returns an `Iterator` that visits the vertex attributes.
261    pub fn attributes(&self) -> iter::Attributes<'a> {
262        iter::Attributes {
263            document: self.mesh.document,
264            prim: self.clone(),
265            iter: self.json.attributes.iter(),
266        }
267    }
268
269    /// Returns the material to apply to this primitive when rendering
270    pub fn material(&self) -> Material<'a> {
271        self.json
272            .material
273            .as_ref()
274            .map(|index| self.mesh.document.materials().nth(index.value()).unwrap())
275            .unwrap_or_else(|| Material::default(self.mesh.document))
276    }
277
278    /// The type of primitives to render.
279    pub fn mode(&self) -> Mode {
280        self.json.mode.unwrap()
281    }
282
283    /// Returns an `Iterator` that visits the morph targets of the primitive.
284    pub fn morph_targets(&self) -> iter::MorphTargets<'a> {
285        if let Some(slice) = self.json.targets.as_ref() {
286            iter::MorphTargets {
287                document: self.mesh.document,
288                iter: slice.iter(),
289            }
290        } else {
291            iter::MorphTargets {
292                document: self.mesh.document,
293                iter: ([]).iter(),
294            }
295        }
296    }
297
298    /// Get the material variants.
299    #[cfg(feature = "KHR_materials_variants")]
300    #[cfg_attr(docsrs, doc(cfg(feature = "KHR_materials_variants")))]
301    pub fn mappings(&self) -> iter::Mappings<'a> {
302        let iter = self
303            .json
304            .extensions
305            .as_ref()
306            .and_then(|extensions| extensions.khr_materials_variants.as_ref())
307            .map(|variants| variants.mappings.iter())
308            .unwrap_or_else(|| ([]).iter());
309
310        iter::Mappings {
311            document: self.mesh.document,
312            iter,
313        }
314    }
315
316    /// Constructs the primitive reader.
317    #[cfg(feature = "utils")]
318    #[cfg_attr(docsrs, doc(cfg(feature = "utils")))]
319    pub fn reader<'s, F>(&'a self, get_buffer_data: F) -> Reader<'a, 's, F>
320    where
321        F: Clone + Fn(Buffer<'a>) -> Option<&'s [u8]>,
322    {
323        Reader {
324            primitive: self,
325            get_buffer_data,
326        }
327    }
328}
329
330#[cfg(feature = "utils")]
331impl<'a, 's, F> Reader<'a, 's, F>
332where
333    F: Clone + Fn(Buffer<'a>) -> Option<&'s [u8]>,
334{
335    /// Visits the vertex positions of a primitive.
336    pub fn read_positions(&self) -> Option<util::ReadPositions<'s>> {
337        self.primitive
338            .get(&Semantic::Positions)
339            .and_then(|accessor| {
340                #[cfg(feature = "KHR_mesh_quantization")]
341                {
342                    let normalized = accessor.normalized();
343                    match accessor.data_type() {
344                        json::accessor::ComponentType::I8 => {
345                            // POSITION supports both normalized and unnormalized BYTE
346                            accessor::Iter::new(accessor, self.get_buffer_data.clone())
347                                .map(|iter| util::ReadPositions::I8(iter, normalized))
348                        }
349                        json::accessor::ComponentType::U8 => {
350                            // POSITION supports both normalized and unnormalized UNSIGNED_BYTE
351                            accessor::Iter::new(accessor, self.get_buffer_data.clone())
352                                .map(|iter| util::ReadPositions::U8(iter, normalized))
353                        }
354                        json::accessor::ComponentType::I16 => {
355                            // POSITION supports both normalized and unnormalized SHORT
356                            accessor::Iter::new(accessor, self.get_buffer_data.clone())
357                                .map(|iter| util::ReadPositions::I16(iter, normalized))
358                        }
359                        json::accessor::ComponentType::U16 => {
360                            // POSITION supports both normalized and unnormalized UNSIGNED_SHORT
361                            accessor::Iter::new(accessor, self.get_buffer_data.clone())
362                                .map(|iter| util::ReadPositions::U16(iter, normalized))
363                        }
364                        json::accessor::ComponentType::F32 => {
365                            accessor::Iter::new(accessor, self.get_buffer_data.clone())
366                                .map(util::ReadPositions::F32)
367                        }
368                        _ => None, // Invalid component types for positions (e.g., U32)
369                    }
370                }
371                #[cfg(not(feature = "KHR_mesh_quantization"))]
372                accessor::Iter::new(accessor, self.get_buffer_data.clone())
373            })
374    }
375
376    /// Visits the vertex normals of a primitive.
377    pub fn read_normals(&self) -> Option<util::ReadNormals<'s>> {
378        self.primitive.get(&Semantic::Normals).and_then(|accessor| {
379            #[cfg(feature = "KHR_mesh_quantization")]
380            {
381                let normalized = accessor.normalized();
382                match accessor.data_type() {
383                    json::accessor::ComponentType::I8 => {
384                        // For KHR_mesh_quantization: NORMAL with BYTE must be normalized
385                        if !normalized {
386                            return None; // Invalid: unnormalized byte normals not allowed
387                        }
388                        accessor::Iter::new(accessor, self.get_buffer_data.clone())
389                            .map(|iter| util::ReadNormals::I8(iter, normalized))
390                    }
391                    json::accessor::ComponentType::I16 => {
392                        // For KHR_mesh_quantization: NORMAL with SHORT must be normalized
393                        if !normalized {
394                            return None; // Invalid: unnormalized short normals not allowed
395                        }
396                        accessor::Iter::new(accessor, self.get_buffer_data.clone())
397                            .map(|iter| util::ReadNormals::I16(iter, normalized))
398                    }
399                    json::accessor::ComponentType::F32 => {
400                        accessor::Iter::new(accessor, self.get_buffer_data.clone())
401                            .map(util::ReadNormals::F32)
402                    }
403                    _ => None, // Invalid component types for normals
404                }
405            }
406            #[cfg(not(feature = "KHR_mesh_quantization"))]
407            accessor::Iter::new(accessor, self.get_buffer_data.clone())
408        })
409    }
410
411    /// Visits the vertex tangents of a primitive.
412    pub fn read_tangents(&self) -> Option<util::ReadTangents<'s>> {
413        self.primitive
414            .get(&Semantic::Tangents)
415            .and_then(|accessor| {
416                #[cfg(feature = "KHR_mesh_quantization")]
417                {
418                    let normalized = accessor.normalized();
419                    match accessor.data_type() {
420                        json::accessor::ComponentType::I8 => {
421                            // For KHR_mesh_quantization: TANGENT with BYTE must be normalized
422                            if !normalized {
423                                return None; // Invalid: unnormalized byte tangents not allowed
424                            }
425                            accessor::Iter::new(accessor, self.get_buffer_data.clone())
426                                .map(|iter| util::ReadTangents::I8(iter, normalized))
427                        }
428                        json::accessor::ComponentType::I16 => {
429                            // For KHR_mesh_quantization: TANGENT with SHORT must be normalized
430                            if !normalized {
431                                return None; // Invalid: unnormalized short tangents not allowed
432                            }
433                            accessor::Iter::new(accessor, self.get_buffer_data.clone())
434                                .map(|iter| util::ReadTangents::I16(iter, normalized))
435                        }
436                        json::accessor::ComponentType::F32 => {
437                            accessor::Iter::new(accessor, self.get_buffer_data.clone())
438                                .map(util::ReadTangents::F32)
439                        }
440                        _ => None, // Invalid component types for tangents
441                    }
442                }
443                #[cfg(not(feature = "KHR_mesh_quantization"))]
444                accessor::Iter::new(accessor, self.get_buffer_data.clone())
445            })
446    }
447
448    /// Visits the vertex colors of a primitive.
449    pub fn read_colors(&self, set: u32) -> Option<util::ReadColors<'s>> {
450        use self::util::ReadColors;
451        use accessor::DataType::{F32, U16, U8};
452        use accessor::Dimensions::{Vec3, Vec4};
453        self.primitive
454            .get(&Semantic::Colors(set))
455            .and_then(
456                |accessor| match (accessor.data_type(), accessor.dimensions()) {
457                    (U8, Vec3) => accessor::Iter::new(accessor, self.get_buffer_data.clone())
458                        .map(ReadColors::RgbU8),
459                    (U16, Vec3) => accessor::Iter::new(accessor, self.get_buffer_data.clone())
460                        .map(ReadColors::RgbU16),
461                    (F32, Vec3) => accessor::Iter::new(accessor, self.get_buffer_data.clone())
462                        .map(ReadColors::RgbF32),
463                    (U8, Vec4) => accessor::Iter::new(accessor, self.get_buffer_data.clone())
464                        .map(ReadColors::RgbaU8),
465                    (U16, Vec4) => accessor::Iter::new(accessor, self.get_buffer_data.clone())
466                        .map(ReadColors::RgbaU16),
467                    (F32, Vec4) => accessor::Iter::new(accessor, self.get_buffer_data.clone())
468                        .map(ReadColors::RgbaF32),
469                    _ => unreachable!(),
470                },
471            )
472    }
473
474    /// Visits the vertex draw sequence of a primitive.
475    pub fn read_indices(&self) -> Option<util::ReadIndices<'s>> {
476        use self::util::ReadIndices;
477        use accessor::DataType;
478        self.primitive
479            .indices()
480            .and_then(|accessor| match accessor.data_type() {
481                DataType::U8 => {
482                    accessor::Iter::new(accessor, self.get_buffer_data.clone()).map(ReadIndices::U8)
483                }
484                DataType::U16 => accessor::Iter::new(accessor, self.get_buffer_data.clone())
485                    .map(ReadIndices::U16),
486                DataType::U32 => accessor::Iter::new(accessor, self.get_buffer_data.clone())
487                    .map(ReadIndices::U32),
488                _ => unreachable!(),
489            })
490    }
491
492    /// Visits the joint indices of the primitive.
493    pub fn read_joints(&self, set: u32) -> Option<util::ReadJoints<'s>> {
494        use self::util::ReadJoints;
495        use accessor::DataType;
496        self.primitive
497            .get(&Semantic::Joints(set))
498            .and_then(|accessor| match accessor.data_type() {
499                DataType::U8 => {
500                    accessor::Iter::new(accessor, self.get_buffer_data.clone()).map(ReadJoints::U8)
501                }
502                DataType::U16 => {
503                    accessor::Iter::new(accessor, self.get_buffer_data.clone()).map(ReadJoints::U16)
504                }
505                _ => unreachable!(),
506            })
507    }
508
509    /// Visits the vertex texture co-ordinates of a primitive.
510    pub fn read_tex_coords(&self, set: u32) -> Option<util::ReadTexCoords<'s>> {
511        use self::util::ReadTexCoords;
512        use accessor::DataType;
513        self.primitive
514            .get(&Semantic::TexCoords(set))
515            .and_then(|accessor| {
516                #[cfg(feature = "KHR_mesh_quantization")]
517                {
518                    let normalized = accessor.normalized();
519                    match accessor.data_type() {
520                        DataType::I8 => {
521                            // TEXCOORD supports both normalized and unnormalized BYTE
522                            accessor::Iter::new(accessor, self.get_buffer_data.clone())
523                                .map(|iter| ReadTexCoords::I8(iter, normalized))
524                        }
525                        DataType::U8 => {
526                            // TEXCOORD supports UNSIGNED_BYTE
527                            accessor::Iter::new(accessor, self.get_buffer_data.clone())
528                                .map(|iter| ReadTexCoords::U8(iter, normalized))
529                        }
530                        DataType::I16 => {
531                            // TEXCOORD supports both normalized and unnormalized SHORT
532                            accessor::Iter::new(accessor, self.get_buffer_data.clone())
533                                .map(|iter| ReadTexCoords::I16(iter, normalized))
534                        }
535                        DataType::U16 => {
536                            // TEXCOORD supports UNSIGNED_SHORT
537                            accessor::Iter::new(accessor, self.get_buffer_data.clone())
538                                .map(|iter| ReadTexCoords::U16(iter, normalized))
539                        }
540                        DataType::F32 => {
541                            accessor::Iter::new(accessor, self.get_buffer_data.clone())
542                                .map(ReadTexCoords::F32)
543                        }
544                        _ => None, // Invalid component types for texture coordinates (e.g., U32)
545                    }
546                }
547                #[cfg(not(feature = "KHR_mesh_quantization"))]
548                {
549                    // Base glTF requires U8/U16 texture coordinates to be normalized
550                    match accessor.data_type() {
551                        DataType::U8 => accessor::Iter::new(accessor, self.get_buffer_data.clone())
552                            .map(|iter| ReadTexCoords::U8(iter, true)),
553                        DataType::U16 => {
554                            accessor::Iter::new(accessor, self.get_buffer_data.clone())
555                                .map(|iter| ReadTexCoords::U16(iter, true))
556                        }
557                        DataType::F32 => {
558                            accessor::Iter::new(accessor, self.get_buffer_data.clone())
559                                .map(ReadTexCoords::F32)
560                        }
561                        _ => unreachable!(),
562                    }
563                }
564            })
565    }
566
567    /// Visits the joint weights of the primitive.
568    pub fn read_weights(&self, set: u32) -> Option<util::ReadWeights<'s>> {
569        use self::accessor::DataType;
570        use self::util::ReadWeights;
571        self.primitive
572            .get(&Semantic::Weights(set))
573            .and_then(|accessor| match accessor.data_type() {
574                DataType::U8 => {
575                    accessor::Iter::new(accessor, self.get_buffer_data.clone()).map(ReadWeights::U8)
576                }
577                DataType::U16 => accessor::Iter::new(accessor, self.get_buffer_data.clone())
578                    .map(ReadWeights::U16),
579                DataType::F32 => accessor::Iter::new(accessor, self.get_buffer_data.clone())
580                    .map(ReadWeights::F32),
581                _ => unreachable!(),
582            })
583    }
584
585    /// Visits the morph targets of the primitive.
586    pub fn read_morph_targets(&self) -> util::ReadMorphTargets<'a, 's, F> {
587        util::ReadMorphTargets {
588            index: 0,
589            reader: self.clone(),
590        }
591    }
592}
593
594impl<'a> MorphTarget<'a> {
595    /// Returns the XYZ vertex position displacements.
596    pub fn positions(&self) -> Option<Accessor<'a>> {
597        self.positions.clone()
598    }
599
600    /// Returns the XYZ vertex normal displacements.
601    pub fn normals(&self) -> Option<Accessor<'a>> {
602        self.normals.clone()
603    }
604
605    /// Returns the XYZ vertex tangent displacements.
606    pub fn tangents(&self) -> Option<Accessor<'a>> {
607        self.tangents.clone()
608    }
609}