three_d_asset/io/
gltf.rs

1use crate::{animation::*, geometry::*, io::*, material::*, Error, Node, Result, Scene};
2use ::gltf::Gltf;
3use std::collections::HashSet;
4use std::path::{Path, PathBuf};
5
6pub fn dependencies(raw_assets: &RawAssets, path: &PathBuf) -> HashSet<PathBuf> {
7    let mut dependencies = HashSet::new();
8    if let Ok(Gltf { document, .. }) = Gltf::from_slice(raw_assets.get(path).unwrap()) {
9        let base_path = path.parent().unwrap_or(Path::new(""));
10        for buffer in document.buffers() {
11            match buffer.source() {
12                ::gltf::buffer::Source::Uri(uri) => {
13                    if uri.starts_with("data:") {
14                        dependencies.insert(PathBuf::from(uri));
15                    } else {
16                        dependencies.insert(base_path.join(uri));
17                    }
18                }
19                _ => {}
20            };
21        }
22
23        for texture in document.textures() {
24            match texture.source().source() {
25                ::gltf::image::Source::Uri { uri, .. } => {
26                    if uri.starts_with("data:") {
27                        use std::str::FromStr;
28                        dependencies.insert(PathBuf::from_str(uri).unwrap());
29                    } else {
30                        dependencies.insert(base_path.join(uri));
31                    }
32                }
33                _ => {}
34            };
35        }
36    }
37    dependencies
38}
39
40pub fn deserialize_gltf(raw_assets: &mut RawAssets, path: &PathBuf) -> Result<Scene> {
41    let Gltf { document, mut blob } = Gltf::from_slice(&raw_assets.remove(path)?)?;
42    let base_path = path.parent().unwrap_or(Path::new(""));
43
44    let mut buffers = Vec::new();
45    for buffer in document.buffers() {
46        let mut data = match buffer.source() {
47            ::gltf::buffer::Source::Uri(uri) => {
48                if uri.starts_with("data:") {
49                    raw_assets.remove(uri)?
50                } else {
51                    raw_assets.remove(base_path.join(uri))?
52                }
53            }
54            ::gltf::buffer::Source::Bin => blob.take().ok_or(Error::GltfMissingData)?,
55        };
56        if data.len() < buffer.length() {
57            Err(Error::GltfCorruptData)?;
58        }
59        while data.len() % 4 != 0 {
60            data.push(0);
61        }
62        buffers.push(::gltf::buffer::Data(data));
63    }
64
65    let mut materials = Vec::new();
66    for material in document.materials() {
67        if let Some(_) = material.index() {
68            materials.push(parse_material(
69                raw_assets,
70                &base_path,
71                &mut buffers,
72                &material,
73            )?);
74        }
75    }
76
77    let mut nodes = Vec::new();
78    for gltf_node in document.nodes() {
79        let transformation = parse_transform(gltf_node.transform());
80        // glTF say that if the scale is all zeroes, the node should be ignored.
81        if transformation.determinant() != 0.0 {
82            let name = gltf_node
83                .name()
84                .map(|s| s.to_string())
85                .unwrap_or(format!("index {}", gltf_node.index()));
86            let children = if let Some(mesh) = gltf_node.mesh() {
87                parse_model(&mesh, &buffers)?
88            } else {
89                Vec::new()
90            };
91            nodes.push(Some(Node {
92                name,
93                transformation,
94                children,
95                ..Default::default()
96            }));
97        } else {
98            nodes.push(None);
99        }
100    }
101
102    for animation in document.animations() {
103        let mut key_frames = Vec::new();
104        let mut loop_time = 0.0f32;
105        for channel in animation.channels() {
106            let reader = channel.reader(|buffer| Some(&buffers[buffer.index()]));
107            let interpolation = match channel.sampler().interpolation() {
108                ::gltf::animation::Interpolation::Step => Interpolation::Nearest,
109                ::gltf::animation::Interpolation::Linear => Interpolation::Linear,
110                ::gltf::animation::Interpolation::CubicSpline => Interpolation::CubicSpline,
111            };
112            let target_node = channel.target().node().index();
113            let key = (
114                target_node,
115                channel.sampler().input().index(),
116                interpolation,
117            );
118            let i = key_frames
119                .iter_mut()
120                .position(|(_, k, _)| k == &key)
121                .unwrap_or_else(|| {
122                    let times = reader.read_inputs().unwrap().collect::<Vec<_>>();
123                    loop_time = loop_time.max(*times.last().unwrap_or(&0.0));
124                    key_frames.push((
125                        target_node,
126                        key,
127                        (
128                            animation.name().map(|s| s.to_owned()),
129                            KeyFrames {
130                                times,
131                                interpolation,
132                                ..Default::default()
133                            },
134                        ),
135                    ));
136                    key_frames.len() - 1
137                });
138            let kf = &mut key_frames[i].2 .1;
139
140            match reader.read_outputs().unwrap() {
141                ::gltf::animation::util::ReadOutputs::Rotations(rotations) => {
142                    kf.rotations = Some(
143                        rotations
144                            .into_f32()
145                            .into_iter()
146                            .map(|r| Quat::from_sv(r[3], vec3(r[0], r[1], r[2])))
147                            .collect(),
148                    );
149                }
150                ::gltf::animation::util::ReadOutputs::Translations(translations) => {
151                    kf.translations = Some(
152                        translations
153                            .into_iter()
154                            .map(|r| vec3(r[0], r[1], r[2]))
155                            .collect(),
156                    );
157                }
158                ::gltf::animation::util::ReadOutputs::Scales(scales) => {
159                    kf.scales = Some(scales.into_iter().map(|r| vec3(r[0], r[1], r[2])).collect());
160                }
161                ::gltf::animation::util::ReadOutputs::MorphTargetWeights(weights) => {
162                    let weights = weights.into_f32().collect::<Vec<_>>();
163                    let count = weights.len() / kf.times.len();
164                    kf.weights = Some(
165                        weights
166                            .chunks(count)
167                            .map(|c| c.into_iter().map(|v| *v).collect::<Vec<_>>())
168                            .collect(),
169                    );
170                }
171            }
172        }
173        for (target_node, _, mut kf) in key_frames {
174            nodes[target_node].as_mut().map(|n| {
175                kf.1.loop_time = Some(loop_time);
176                n.animations.push(kf);
177            });
178        }
179    }
180
181    let gltf_scene = document.scenes().nth(0).unwrap();
182    let mut scene = Scene {
183        name: gltf_scene
184            .name()
185            .unwrap_or(&format!("Scene {}", gltf_scene.index()))
186            .to_owned(),
187        materials,
188        children: Vec::new(),
189    };
190    for c in gltf_scene.nodes() {
191        if let Some(mut node) = nodes[c.index()].take() {
192            visit(c, &mut nodes, &mut node.children);
193            scene.children.push(node);
194        }
195    }
196    Ok(scene)
197}
198
199fn visit(gltf_node: ::gltf::Node, nodes: &mut Vec<Option<Node>>, children: &mut Vec<Node>) {
200    for c in gltf_node.children() {
201        if let Some(mut node) = nodes[c.index()].take() {
202            visit(c, nodes, &mut node.children);
203            children.push(node);
204        }
205    }
206}
207
208fn parse_model(mesh: &::gltf::mesh::Mesh, buffers: &[::gltf::buffer::Data]) -> Result<Vec<Node>> {
209    let mut children = Vec::new();
210    for primitive in mesh.primitives() {
211        let reader = primitive.reader(|buffer| Some(&buffers[buffer.index()]));
212        if let Some(read_positions) = reader.read_positions() {
213            let positions: Vec<_> = read_positions.map(|p| p.into()).collect();
214
215            let normals = reader
216                .read_normals()
217                .map(|values| values.map(|n| n.into()).collect());
218
219            let tangents = reader
220                .read_tangents()
221                .map(|values| values.map(|t| t.into()).collect());
222
223            let indices = reader
224                .read_indices()
225                .map(|values| match values {
226                    ::gltf::mesh::util::ReadIndices::U8(iter) => Indices::U8(iter.collect()),
227                    ::gltf::mesh::util::ReadIndices::U16(iter) => Indices::U16(iter.collect()),
228                    ::gltf::mesh::util::ReadIndices::U32(iter) => Indices::U32(iter.collect()),
229                })
230                .unwrap_or(Indices::None);
231
232            let colors = reader.read_colors(0).map(|values| {
233                values
234                    .into_rgba_u8()
235                    .map(|c| Srgba::new(c[0], c[1], c[2], c[3]))
236                    .collect()
237            });
238
239            let uvs = reader
240                .read_tex_coords(0)
241                .map(|values| values.into_f32().map(|uv| uv.into()).collect());
242
243            children.push(Node {
244                geometry: Some(Geometry::Triangles(TriMesh {
245                    positions: Positions::F32(positions),
246                    normals,
247                    tangents,
248                    indices,
249                    colors,
250                    uvs,
251                })),
252                material_index: primitive.material().index(),
253                ..Default::default()
254            });
255        }
256    }
257    Ok(children)
258}
259
260fn material_name(material: &::gltf::material::Material) -> String {
261    material.name().map(|s| s.to_string()).unwrap_or(
262        material
263            .index()
264            .map(|i| format!("index {}", i))
265            .unwrap_or("default".to_string()),
266    )
267}
268
269fn parse_material(
270    raw_assets: &mut RawAssets,
271    path: &Path,
272    buffers: &[::gltf::buffer::Data],
273    material: &::gltf::material::Material,
274) -> Result<PbrMaterial> {
275    let pbr = material.pbr_metallic_roughness();
276    let color = pbr.base_color_factor();
277    let albedo_texture = if let Some(info) = pbr.base_color_texture() {
278        Some(parse_texture(raw_assets, path, buffers, info.texture())?)
279    } else {
280        None
281    };
282    let metallic_roughness_texture = if let Some(info) = pbr.metallic_roughness_texture() {
283        Some(parse_texture(raw_assets, path, buffers, info.texture())?)
284    } else {
285        None
286    };
287    let (normal_texture, normal_scale) = if let Some(normal) = material.normal_texture() {
288        (
289            Some(parse_texture(raw_assets, path, buffers, normal.texture())?),
290            normal.scale(),
291        )
292    } else {
293        (None, 1.0)
294    };
295    let (occlusion_texture, occlusion_strength) =
296        if let Some(occlusion) = material.occlusion_texture() {
297            (
298                Some(parse_texture(
299                    raw_assets,
300                    path,
301                    buffers,
302                    occlusion.texture(),
303                )?),
304                occlusion.strength(),
305            )
306        } else {
307            (None, 1.0)
308        };
309    let emissive_texture = if let Some(info) = material.emissive_texture() {
310        Some(parse_texture(raw_assets, path, buffers, info.texture())?)
311    } else {
312        None
313    };
314    let transmission_texture =
315        if let Some(Some(info)) = material.transmission().map(|t| t.transmission_texture()) {
316            Some(parse_texture(raw_assets, path, buffers, info.texture())?)
317        } else {
318            None
319        };
320    Ok(PbrMaterial {
321        name: material_name(material),
322        albedo: color.into(),
323        albedo_texture,
324        metallic: pbr.metallic_factor(),
325        roughness: pbr.roughness_factor(),
326        metallic_roughness_texture,
327        normal_texture,
328        normal_scale,
329        occlusion_texture,
330        occlusion_strength,
331        occlusion_metallic_roughness_texture: None,
332        emissive: material.emissive_factor().into(),
333        emissive_texture,
334        transmission: material
335            .transmission()
336            .map(|t| t.transmission_factor())
337            .unwrap_or(0.0),
338        transmission_texture,
339        index_of_refraction: material.ior().unwrap_or(1.5),
340        alpha_cutout: material.alpha_cutoff(),
341        lighting_model: LightingModel::Cook(
342            NormalDistributionFunction::TrowbridgeReitzGGX,
343            GeometryFunction::SmithSchlickGGX,
344        ),
345    })
346}
347
348impl Into<Wrapping> for ::gltf::texture::WrappingMode {
349    fn into(self) -> Wrapping {
350        match self {
351            ::gltf::texture::WrappingMode::ClampToEdge => Wrapping::ClampToEdge,
352            ::gltf::texture::WrappingMode::MirroredRepeat => Wrapping::MirroredRepeat,
353            ::gltf::texture::WrappingMode::Repeat => Wrapping::Repeat,
354        }
355    }
356}
357
358fn parse_texture<'a>(
359    raw_assets: &mut RawAssets,
360    path: &Path,
361    buffers: &[::gltf::buffer::Data],
362    gltf_texture: ::gltf::texture::Texture,
363) -> Result<Texture2D> {
364    let gltf_image = gltf_texture.source();
365    let gltf_source = gltf_image.source();
366    let mut tex: Texture2D = match gltf_source {
367        ::gltf::image::Source::Uri { uri, .. } => {
368            if uri.starts_with("data:") {
369                raw_assets.deserialize(uri)?
370            } else {
371                raw_assets.deserialize(path.join(uri))?
372            }
373        }
374        ::gltf::image::Source::View { view, .. } => {
375            if view.stride() != None {
376                unimplemented!();
377            }
378            #[allow(unused_variables)]
379            let buffer = &buffers[view.buffer().index()];
380            #[cfg(not(feature = "image"))]
381            return Err(Error::FeatureMissing("image".to_string()));
382            #[cfg(feature = "image")]
383            super::img::deserialize_img("", &buffer[view.offset()..view.offset() + view.length()])?
384        }
385    };
386
387    let sampler = gltf_texture.sampler();
388    tex.mag_filter = match sampler.mag_filter() {
389        Some(::gltf::texture::MagFilter::Nearest) => Interpolation::Nearest,
390        Some(::gltf::texture::MagFilter::Linear) => Interpolation::Linear,
391        None => tex.mag_filter,
392    };
393    (tex.min_filter, tex.mipmap) = match sampler.min_filter() {
394        Some(::gltf::texture::MinFilter::Nearest) => (Interpolation::Nearest, None),
395        Some(::gltf::texture::MinFilter::Linear) => (Interpolation::Linear, None),
396        Some(::gltf::texture::MinFilter::NearestMipmapNearest) => (
397            Interpolation::Nearest,
398            Some(Mipmap {
399                filter: Interpolation::Nearest,
400                ..Default::default()
401            }),
402        ),
403        Some(::gltf::texture::MinFilter::LinearMipmapNearest) => (
404            Interpolation::Linear,
405            Some(Mipmap {
406                filter: Interpolation::Nearest,
407                ..Default::default()
408            }),
409        ),
410        Some(::gltf::texture::MinFilter::NearestMipmapLinear) => (
411            Interpolation::Nearest,
412            Some(Mipmap {
413                filter: Interpolation::Linear,
414                ..Default::default()
415            }),
416        ),
417        Some(::gltf::texture::MinFilter::LinearMipmapLinear) => (
418            Interpolation::Linear,
419            Some(Mipmap {
420                filter: Interpolation::Linear,
421                ..Default::default()
422            }),
423        ),
424        None => (tex.min_filter, tex.mipmap),
425    };
426    tex.wrap_s = sampler.wrap_s().into();
427    tex.wrap_t = sampler.wrap_t().into();
428
429    Ok(tex)
430}
431
432fn parse_transform(transform: ::gltf::scene::Transform) -> Mat4 {
433    let [c0, c1, c2, c3] = transform.matrix();
434    Mat4::from_cols(c0.into(), c1.into(), c2.into(), c3.into())
435}
436
437#[cfg(test)]
438mod test {
439    use super::*;
440    use crate::Model;
441
442    #[test]
443    pub fn load_gltf() {
444        let mut loaded = crate::io::load(&["test_data/Cube.gltf"]).unwrap();
445        let model: Model = loaded.deserialize(".gltf").unwrap();
446        assert_eq!(
447            model.materials[0]
448                .albedo_texture
449                .as_ref()
450                .map(|t| std::path::PathBuf::from(&t.name)),
451            Some(std::path::PathBuf::from("test_data/Cube_BaseColor.png"))
452        );
453        assert_eq!(
454            model.materials[0]
455                .metallic_roughness_texture
456                .as_ref()
457                .map(|t| std::path::PathBuf::from(&t.name)),
458            Some(std::path::PathBuf::from(
459                "test_data/Cube_MetallicRoughness.png"
460            ))
461        );
462    }
463
464    #[test]
465    pub fn deserialize_gltf() {
466        let model: Model = crate::io::RawAssets::new()
467            .insert(
468                "Cube.gltf",
469                include_bytes!("../../test_data/Cube.gltf").to_vec(),
470            )
471            .insert(
472                "Cube.bin",
473                include_bytes!("../../test_data/Cube.bin").to_vec(),
474            )
475            .insert(
476                "Cube_BaseColor.png",
477                include_bytes!("../../test_data/Cube_BaseColor.png").to_vec(),
478            )
479            .insert(
480                "Cube_MetallicRoughness.png",
481                include_bytes!("../../test_data/Cube_MetallicRoughness.png").to_vec(),
482            )
483            .deserialize("gltf")
484            .unwrap();
485        assert_eq!(model.geometries.len(), 1);
486        assert_eq!(model.materials.len(), 1);
487        assert_eq!(
488            model.materials[0]
489                .albedo_texture
490                .as_ref()
491                .map(|t| t.name.as_str()),
492            Some("Cube_BaseColor.png")
493        );
494        assert_eq!(
495            model.materials[0]
496                .metallic_roughness_texture
497                .as_ref()
498                .map(|t| t.name.as_str()),
499            Some("Cube_MetallicRoughness.png")
500        );
501    }
502
503    #[test]
504    pub fn deserialize_gltf_with_data_url() {
505        let model: Model = crate::io::load_and_deserialize("test_data/data_url.gltf").unwrap();
506        assert_eq!(model.geometries.len(), 1);
507        assert_eq!(model.materials.len(), 1);
508    }
509
510    #[test]
511    pub fn deserialize_gltf_with_animations() {
512        let model: Model =
513            crate::io::load_and_deserialize("test_data/AnimatedTriangle.gltf").unwrap();
514        assert_eq!(model.geometries.len(), 1);
515        assert_eq!(model.materials.len(), 0);
516        assert_eq!(model.geometries[0].animations.len(), 1);
517        let animation = &model.geometries[0].animations[0];
518        assert_eq!(animation.transformation(0.0), Mat4::identity());
519        assert_eq!(
520            animation.transformation(0.25),
521            Mat4::from_cols(
522                vec4(5.9604645e-8, 0.99999994, 0.0, 0.0),
523                vec4(-0.99999994, 5.9604645e-8, 0.0, 0.0),
524                vec4(0.0, 0.0, 1.0, 0.0),
525                vec4(0.0, 0.0, 0.0, 1.0)
526            )
527        );
528        assert_eq!(
529            animation.transformation(0.5),
530            Mat4::from_cols(
531                vec4(-1.0, 0.0, 0.0, 0.0),
532                vec4(0.0, -1.0, 0.0, 0.0),
533                vec4(0.0, 0.0, 1.0, 0.0),
534                vec4(0.0, 0.0, 0.0, 1.0)
535            )
536        );
537        assert_eq!(
538            animation.transformation(0.75),
539            Mat4::from_cols(
540                vec4(5.9604645e-8, -0.99999994, 0.0, 0.0),
541                vec4(0.99999994, 5.9604645e-8, 0.0, 0.0),
542                vec4(0.0, 0.0, 1.0, 0.0),
543                vec4(0.0, 0.0, 0.0, 1.0)
544            )
545        );
546        assert_eq!(animation.transformation(1.0), Mat4::identity());
547    }
548
549    #[test]
550    pub fn deserialize_gltf_with_morphing() {
551        let model: Model = crate::io::load_and_deserialize("test_data/AnimatedMorph.gltf").unwrap();
552        assert_eq!(model.geometries.len(), 1);
553        assert_eq!(model.materials.len(), 0);
554    }
555
556    #[test]
557    pub fn deserialize_gltf_with_skinning() {
558        let model: Model = crate::io::load_and_deserialize("test_data/AnimatedSkin.gltf").unwrap();
559        assert_eq!(model.geometries.len(), 1);
560        assert_eq!(model.materials.len(), 0);
561    }
562}