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 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}