1mod document;
30pub mod error;
31mod scene;
32
33use crate::material::MaterialTextureBinding;
34use crate::{
35 asset::manager::ResourceManager,
36 core::{
37 algebra::{Matrix4, Point3, UnitQuaternion, Vector2, Vector3, Vector4},
38 instant::Instant,
39 log::{Log, MessageKind},
40 math::curve::{CurveKey, CurveKeyKind},
41 math::{self, triangulator::triangulate, RotationOrder},
42 pool::Handle,
43 },
44 graph::SceneGraph,
45 material,
46 material::MaterialResourceBinding,
47 resource::{
48 fbx::{
49 document::FbxDocument,
50 error::FbxError,
51 scene::{
52 animation::{FbxAnimationCurveNode, FbxAnimationCurveNodeType},
53 geometry::FbxMeshGeometry,
54 model::FbxModel,
55 FbxComponent, FbxMapping, FbxScene,
56 },
57 },
58 model::{MaterialSearchOptions, ModelImportOptions},
59 texture::{Texture, TextureImportOptions, TextureResource, TextureResourceExtension},
60 },
61 scene::{
62 animation::{Animation, AnimationContainer, AnimationPlayerBuilder, Track},
63 base::BaseBuilder,
64 graph::Graph,
65 mesh::{
66 buffer::{VertexAttributeUsage, VertexBuffer, VertexWriteTrait},
67 surface::{
68 BlendShape, BlendShapesContainer, InputBlendShapeData, Surface, SurfaceData,
69 SurfaceResource, VertexWeightSet,
70 },
71 vertex::{AnimatedVertex, StaticVertex},
72 Mesh, MeshBuilder,
73 },
74 node::Node,
75 pivot::PivotBuilder,
76 transform::TransformBuilder,
77 Scene,
78 },
79 utils::{self, raw_mesh::RawMeshBuilder},
80};
81use fxhash::{FxHashMap, FxHashSet};
82use fyrox_animation::track::TrackBinding;
83use fyrox_core::{err, Uuid};
84use fyrox_material::shader::{ShaderResource, ShaderResourceExtension};
85use fyrox_material::MaterialResource;
86use fyrox_resource::io::ResourceIo;
87use fyrox_resource::untyped::ResourceKind;
88use std::{cmp::Ordering, path::Path};
89
90fn quat_from_euler(euler: Vector3<f32>) -> UnitQuaternion<f32> {
92 math::quat_from_euler(
93 Vector3::new(
94 euler.x.to_radians(),
95 euler.y.to_radians(),
96 euler.z.to_radians(),
97 ),
98 RotationOrder::XYZ,
99 )
100}
101
102fn fix_index(index: i32) -> usize {
106 if index < 0 {
107 (index ^ -1) as usize
108 } else {
109 index as usize
110 }
111}
112
113fn prepare_next_face(
116 vertices: &[Vector3<f32>],
117 indices: &[i32],
118 temp_vertices: &mut Vec<Vector3<f32>>,
119 out_triangles: &mut Vec<[usize; 3]>,
120 out_face_triangles: &mut Vec<[usize; 3]>,
121) -> usize {
122 out_triangles.clear();
123 out_face_triangles.clear();
124
125 let mut vertex_per_face = 0;
127 for &index in indices {
128 vertex_per_face += 1;
129 if index < 0 {
130 break;
131 }
132 }
133
134 match vertex_per_face.cmp(&3) {
135 Ordering::Less => {
136 }
138 Ordering::Equal => {
139 let a = fix_index(indices[0]);
140 let b = fix_index(indices[1]);
141 let c = fix_index(indices[2]);
142
143 if a < vertices.len() && b < vertices.len() && c < vertices.len() {
146 out_triangles.push([a, b, c]);
148 out_face_triangles.push([0, 1, 2]);
149 }
150 }
151 Ordering::Greater => {
152 temp_vertices.clear();
154 for i in 0..vertex_per_face {
155 temp_vertices.push(vertices[fix_index(indices[i])]);
156 }
157 triangulate(temp_vertices, out_face_triangles);
158 for triangle in out_face_triangles.iter() {
159 out_triangles.push([
160 fix_index(indices[triangle[0]]),
161 fix_index(indices[triangle[1]]),
162 fix_index(indices[triangle[2]]),
163 ]);
164 }
165 }
166 }
167
168 vertex_per_face
169}
170
171#[derive(Clone)]
172struct UnpackedVertex {
173 surface_index: usize,
175 position: Vector3<f32>,
176 normal: Vector3<f32>,
177 tangent: Vector3<f32>,
178 uv: Vector2<f32>,
179 weights: Option<VertexWeightSet>,
181}
182
183impl Into<AnimatedVertex> for UnpackedVertex {
184 fn into(self) -> AnimatedVertex {
185 AnimatedVertex {
186 position: self.position,
187 tex_coord: self.uv,
188 normal: self.normal,
189 tangent: Vector4::new(self.tangent.x, self.tangent.y, self.tangent.z, 1.0),
190 bone_weights: Default::default(),
193 bone_indices: Default::default(),
194 }
195 }
196}
197
198impl Into<StaticVertex> for UnpackedVertex {
199 fn into(self) -> StaticVertex {
200 StaticVertex {
201 position: self.position,
202 tex_coord: self.uv,
203 normal: self.normal,
204 tangent: Vector4::new(self.tangent.x, self.tangent.y, self.tangent.z, 1.0),
205 }
206 }
207}
208
209fn convert_vertex(
210 geom: &FbxMeshGeometry,
211 geometric_transform: &Matrix4<f32>,
212 material_index: usize,
213 index: usize,
214 index_in_polygon: usize,
215 skin_data: &[VertexWeightSet],
216) -> Result<UnpackedVertex, FbxError> {
217 let position = *geom.vertices.get(index).ok_or(FbxError::IndexOutOfBounds)?;
218
219 let normal = match geom.normals.as_ref() {
220 Some(normals) => *normals.get(index, index_in_polygon)?,
221 None => Vector3::y(),
222 };
223
224 let tangent = match geom.tangents.as_ref() {
225 Some(tangents) => *tangents.get(index, index_in_polygon)?,
226 None => Vector3::y(),
227 };
228
229 let uv = match geom.uvs.as_ref() {
230 Some(uvs) => *uvs.get(index, index_in_polygon)?,
231 None => Vector2::default(),
232 };
233
234 let material = match geom.materials.as_ref() {
235 Some(materials) => *materials.get(material_index, index_in_polygon)?,
236 None => 0,
237 };
238
239 Ok(UnpackedVertex {
240 position: geometric_transform
241 .transform_point(&Point3::from(position))
242 .coords,
243 normal: geometric_transform.transform_vector(&normal),
244 tangent: geometric_transform.transform_vector(&tangent),
245 uv: Vector2::new(uv.x, 1.0 - uv.y), surface_index: material as usize,
247 weights: if geom.deformers.is_empty() {
248 None
249 } else {
250 Some(*skin_data.get(index).ok_or(FbxError::IndexOutOfBounds)?)
251 },
252 })
253}
254
255#[derive(Clone)]
256enum FbxMeshBuilder {
257 Static(RawMeshBuilder<StaticVertex>),
258 Animated(RawMeshBuilder<AnimatedVertex>),
259}
260
261impl FbxMeshBuilder {
262 fn build(self) -> SurfaceData {
263 match self {
264 FbxMeshBuilder::Static(builder) => SurfaceData::from_raw_mesh(builder.build()),
265 FbxMeshBuilder::Animated(builder) => SurfaceData::from_raw_mesh(builder.build()),
266 }
267 }
268}
269
270#[derive(Clone)]
271struct FbxSurfaceData {
272 base_mesh_builder: FbxMeshBuilder,
273 blend_shapes: Vec<InputBlendShapeData>,
274 skin_data: Vec<VertexWeightSet>,
275}
276
277fn make_blend_shapes_container(
278 base_shape: &VertexBuffer,
279 blend_shapes: Vec<InputBlendShapeData>,
280) -> Option<BlendShapesContainer> {
281 if blend_shapes.is_empty() {
282 None
283 } else {
284 Some(BlendShapesContainer::from_lists(base_shape, &blend_shapes))
285 }
286}
287
288type EngineMaterial = material::Material;
289type MaterialMap = FxHashMap<Handle<FbxComponent>, MaterialResource>;
290
291async fn create_materials(
292 fbx_scene: &FbxScene,
293 resource_manager: &ResourceManager,
294 model_import_options: &ModelImportOptions,
295 model_path: &Path,
296) -> Result<MaterialMap, FbxError> {
297 let mut map = MaterialMap::default();
298 for (component_handle, component) in fbx_scene.pair_iter() {
299 if let FbxComponent::Material(fbx_material) = component {
300 let mut material = EngineMaterial::from_shader(ShaderResource::standard());
301
302 material.set_property("diffuseColor", fbx_material.diffuse_color);
303
304 let io = resource_manager.resource_io();
305
306 for (name, texture_handle) in fbx_material.textures.iter() {
307 let texture = fbx_scene.get(*texture_handle).as_texture()?;
308 let path = texture.get_root_file_path(&fbx_scene.components);
309
310 if let Some(filename) = path.file_name() {
311 let texture_path = if texture.content.is_empty() {
312 match model_import_options.material_search_options {
313 MaterialSearchOptions::MaterialsDirectory(ref directory) => {
314 Some(directory.join(filename))
315 }
316 MaterialSearchOptions::RecursiveUp => {
317 let mut texture_path = None;
318 let mut path = model_path.to_owned();
319 while let Some(parent) = path.parent() {
320 let candidate = parent.join(filename);
321 if io.exists(&candidate).await {
322 texture_path = Some(candidate);
323 break;
324 }
325 path.pop();
326 }
327 texture_path
328 }
329 MaterialSearchOptions::WorkingDirectory => {
330 let mut texture_path = None;
331
332 let path = Path::new(".");
333
334 if let Ok(iter) = io.walk_directory(path, usize::MAX).await {
335 for dir in iter {
336 if io.is_dir(&dir).await {
337 let candidate = dir.join(filename);
338 if candidate.exists() {
339 texture_path = Some(candidate);
340 break;
341 }
342 }
343 }
344 }
345
346 texture_path
347 }
348 MaterialSearchOptions::UsePathDirectly => Some(path.clone()),
349 }
350 } else {
351 Some(path.clone())
352 };
353
354 if let Some(texture_path) = texture_path {
355 let texture = if texture.content.is_empty() {
356 resource_manager.request::<Texture>(texture_path.as_path())
357 } else {
358 TextureResource::load_from_memory(
359 Uuid::new_v4(),
360 ResourceKind::External,
361 &texture.content,
362 TextureImportOptions::default(),
363 )
364 .unwrap()
365 };
366
367 let name = if name.contains("AmbientColor")
370 || name.contains("ambient_color")
371 {
372 Some("aoTexture")
373 } else if name.contains("DiffuseColor")
374 || name.contains("diffuse_color")
375 || name.contains("base_color_map")
376 || name.contains("texmap_diffuse")
377 {
378 Some("diffuseTexture")
379 } else if name.contains("MetalnessMap")
380 || name.contains("metalness_map")
381 || name.contains("ReflectionFactor")
382 || name.contains("texmap_reflection")
383 || name.contains("texmap_metalness")
384 {
385 Some("metallicTexture")
386 } else if name.contains("RoughnessMap")
387 || name.contains("roughness_map")
388 || name.contains("Shininess")
389 || name.contains("ShininessExponent")
390 || name.contains("texmap_roughness")
391 {
392 Some("roughnessTexture")
393 } else if name.contains("Bump")
394 || name.contains("bump_map")
395 || name.contains("NormalMap")
396 || name.contains("normal_map")
397 || name.contains("texmap_bump")
398 {
399 Some("normalTexture")
400 } else if name.contains("DisplacementColor")
401 || name.contains("displacement_map")
402 {
403 Some("heightTexture")
404 } else if name.contains("EmissiveColor") || name.contains("emit_color_map")
405 {
406 Some("emissionTexture")
407 } else {
408 None
409 };
410
411 if let Some(property_name) = name {
412 material.bind(
413 property_name,
414 MaterialResourceBinding::Texture(MaterialTextureBinding {
415 value: Some(texture),
416 }),
417 );
418 }
419 } else {
420 Log::writeln(
421 MessageKind::Warning,
422 format!(
423 "Unable to find a texture {filename:?} for 3D model {model_path:?} using {model_import_options:?} option!"
424 ),
425 );
426 }
427 }
428 }
429
430 let old_material =
431 map.insert(component_handle, MaterialResource::new_embedded(material));
432 assert!(old_material.is_none());
433 }
434 }
435 Ok(map)
436}
437
438fn create_surfaces(
439 data_set: Vec<FbxSurfaceData>,
440 model: &FbxModel,
441 materials: &MaterialMap,
442) -> Result<Vec<Surface>, FbxError> {
443 let mut surfaces = Vec::new();
444
445 if model.materials.is_empty() {
447 assert_eq!(data_set.len(), 1);
448 let data = data_set.into_iter().next().unwrap();
449 let mut surface_data = data.base_mesh_builder.build();
450 surface_data.blend_shapes_container =
451 make_blend_shapes_container(&surface_data.vertex_buffer, data.blend_shapes);
452 let mut surface = Surface::new(SurfaceResource::new_ok(
453 Uuid::new_v4(),
454 ResourceKind::External,
455 surface_data,
456 ));
457 surface.vertex_weights = data.skin_data;
458 surfaces.push(surface);
459 } else {
460 assert_eq!(data_set.len(), model.materials.len());
461 for (&material_handle, data) in model.materials.iter().zip(data_set.into_iter()) {
462 let mut surface_data = data.base_mesh_builder.build();
463 surface_data.blend_shapes_container =
464 make_blend_shapes_container(&surface_data.vertex_buffer, data.blend_shapes);
465 let mut surface = Surface::new(SurfaceResource::new_ok(
466 Uuid::new_v4(),
467 ResourceKind::External,
468 surface_data,
469 ));
470 surface.vertex_weights = data.skin_data;
471
472 if let Some(material) = materials.get(&material_handle) {
473 surface.set_material(material.clone());
474 } else {
475 err!("No respective material");
476 }
477
478 surfaces.push(surface);
479 }
480 }
481
482 Ok(surfaces)
483}
484
485fn convert_mesh(
486 base: BaseBuilder,
487 fbx_scene: &FbxScene,
488 model: &FbxModel,
489 graph: &mut Graph,
490 materials: &MaterialMap,
491) -> Result<Handle<Mesh>, FbxError> {
492 let geometric_transform = Matrix4::new_translation(&model.geometric_translation)
493 * quat_from_euler(model.geometric_rotation).to_homogeneous()
494 * Matrix4::new_nonuniform_scaling(&model.geometric_scale);
495
496 let mut temp_vertices = Vec::new();
497 let mut triangles = Vec::new();
498
499 let mut face_triangles = Vec::new();
502
503 let mut mesh_surfaces = Vec::new();
504 let mut mesh_blend_shapes = Vec::new();
505
506 for &geom_handle in &model.geoms {
507 let geom = fbx_scene.get(geom_handle).as_mesh_geometry()?;
508 let skin_data = geom.get_skin_data(fbx_scene)?;
509 let blend_shapes = geom.collect_blend_shapes_refs(fbx_scene)?;
510
511 if !mesh_blend_shapes.is_empty() {
512 Log::warn("More than two geoms with blend shapes?");
513 }
514 mesh_blend_shapes = blend_shapes
515 .iter()
516 .map(|bs| BlendShape {
517 weight: bs.deform_percent,
518 name: bs.name.clone(),
519 })
520 .collect();
521
522 let mut data_set = vec![
523 FbxSurfaceData {
524 base_mesh_builder: if geom.deformers.is_empty() {
525 FbxMeshBuilder::Static(RawMeshBuilder::new(1024, 1024))
526 } else {
527 FbxMeshBuilder::Animated(RawMeshBuilder::new(1024, 1024))
528 },
529 blend_shapes: blend_shapes
530 .iter()
531 .map(|bs_channel| {
532 InputBlendShapeData {
533 name: bs_channel.name.clone(),
534 default_weight: bs_channel.deform_percent,
535 positions: Default::default(),
536 normals: Default::default(),
537 tangents: Default::default(),
538 }
539 })
540 .collect(),
541 skin_data: Default::default(),
542 };
543 model.materials.len().max(1)
544 ];
545
546 let mut material_index = 0;
547 let mut n = 0;
548 while n < geom.indices.len() {
549 let origin = n;
550 n += prepare_next_face(
551 &geom.vertices,
552 &geom.indices[origin..],
553 &mut temp_vertices,
554 &mut triangles,
555 &mut face_triangles,
556 );
557 for (triangle, face_triangle) in triangles.iter().zip(face_triangles.iter()) {
558 for (&index, &face_vertex_index) in triangle.iter().zip(face_triangle.iter()) {
559 let polygon_vertex_index = origin + face_vertex_index;
560 let vertex = convert_vertex(
561 geom,
562 &geometric_transform,
563 material_index,
564 index,
565 polygon_vertex_index,
566 &skin_data,
567 )?;
568 let data = data_set.get_mut(vertex.surface_index).unwrap();
569 let weights = vertex.weights;
570 let final_index;
571 let is_unique_vertex = match data.base_mesh_builder {
572 FbxMeshBuilder::Static(ref mut builder) => {
573 final_index = builder.vertex_count();
574 builder.insert(vertex.clone().into())
575 }
576 FbxMeshBuilder::Animated(ref mut builder) => {
577 final_index = builder.vertex_count();
578 builder.insert(vertex.clone().into())
579 }
580 };
581 if is_unique_vertex {
582 if let Some(skin_data) = weights {
583 data.skin_data.push(skin_data);
584 }
585 }
586
587 assert_eq!(blend_shapes.len(), data.blend_shapes.len());
589 for (fbx_blend_shape, blend_shape) in
590 blend_shapes.iter().zip(data.blend_shapes.iter_mut())
591 {
592 let blend_shape_geometry = fbx_scene
593 .get(fbx_blend_shape.geometry)
594 .as_shape_geometry()?;
595
596 if let Some(relative_index) =
599 blend_shape_geometry.indices.get(&(index as i32))
600 {
601 blend_shape.positions.insert(
602 final_index as u32,
603 utils::vec3_f16_from_f32(
604 blend_shape_geometry.vertices[*relative_index as usize],
605 ),
606 );
607 if let Some(normals) = blend_shape_geometry.normals.as_ref() {
608 blend_shape.normals.insert(
609 final_index as u32,
610 utils::vec3_f16_from_f32(normals[*relative_index as usize]),
611 );
612 }
613 if let Some(tangents) = blend_shape_geometry.tangents.as_ref() {
614 blend_shape.normals.insert(
615 final_index as u32,
616 utils::vec3_f16_from_f32(tangents[*relative_index as usize]),
617 );
618 }
619 }
620 }
621 }
622 }
623 if let Some(materials) = geom.materials.as_ref() {
624 if materials.mapping == FbxMapping::ByPolygon {
625 material_index += 1;
626 }
627 }
628 }
629
630 let mut surfaces = create_surfaces(data_set, model, materials)?;
631
632 if geom.tangents.is_none() {
633 for surface in surfaces.iter_mut() {
634 surface.data().data_ref().calculate_tangents().unwrap();
635 }
636 }
637
638 for surface in surfaces {
639 mesh_surfaces.push(surface);
640 }
641 }
642
643 Ok(MeshBuilder::new(base)
644 .with_blend_shapes(mesh_blend_shapes)
645 .with_surfaces(mesh_surfaces)
646 .build(graph))
647}
648
649fn convert_model_to_base(model: &FbxModel) -> BaseBuilder {
650 BaseBuilder::new()
651 .with_inv_bind_pose_transform(model.inv_bind_transform)
652 .with_name(model.name.as_str())
653 .with_local_transform(
654 TransformBuilder::new()
655 .with_local_rotation(quat_from_euler(model.rotation))
656 .with_local_scale(model.scale)
657 .with_local_position(model.translation)
658 .with_post_rotation(quat_from_euler(model.post_rotation))
659 .with_pre_rotation(quat_from_euler(model.pre_rotation))
660 .with_rotation_offset(model.rotation_offset)
661 .with_rotation_pivot(model.rotation_pivot)
662 .with_scaling_offset(model.scaling_offset)
663 .with_scaling_pivot(model.scaling_pivot)
664 .build(),
665 )
666}
667
668fn convert_model(
669 fbx_scene: &FbxScene,
670 model: &FbxModel,
671 graph: &mut Graph,
672 animation: &mut Animation,
673 materials: &MaterialMap,
674) -> Result<Handle<Node>, FbxError> {
675 let base = convert_model_to_base(model);
676
677 let node_handle = if !model.geoms.is_empty() {
679 convert_mesh(base, fbx_scene, model, graph, materials)?.to_base()
680 } else if model.light.is_some() {
681 fbx_scene.get(model.light).as_light()?.convert(base, graph)
682 } else {
683 PivotBuilder::new(base).build(graph).to_base()
684 };
685
686 if !model.animation_curve_nodes.is_empty() {
688 let mut lcl_translation = None;
690 let mut lcl_rotation = None;
691 let mut lcl_scale = None;
692 for &anim_curve_node_handle in model.animation_curve_nodes.iter() {
693 let component = fbx_scene.get(anim_curve_node_handle);
694 if let FbxComponent::AnimationCurveNode(curve_node) = component {
695 if curve_node.actual_type == FbxAnimationCurveNodeType::Rotation {
696 lcl_rotation = Some(curve_node);
697 } else if curve_node.actual_type == FbxAnimationCurveNodeType::Translation {
698 lcl_translation = Some(curve_node);
699 } else if curve_node.actual_type == FbxAnimationCurveNodeType::Scale {
700 lcl_scale = Some(curve_node);
701 }
702 }
703 }
704
705 fn fill_track<F: Fn(f32) -> f32>(
706 track: &mut Track,
707 fbx_scene: &FbxScene,
708 fbx_track: &FbxAnimationCurveNode,
709 default: Vector3<f32>,
710 transform_value: F,
711 ) {
712 let curves = track.data_container_mut().curves_mut();
713
714 if !fbx_track.curves.contains_key("d|X") {
715 curves[0].add_key(CurveKey::new(0.0, default.x, CurveKeyKind::Constant));
716 }
717 if !fbx_track.curves.contains_key("d|Y") {
718 curves[1].add_key(CurveKey::new(0.0, default.y, CurveKeyKind::Constant));
719 }
720 if !fbx_track.curves.contains_key("d|Z") {
721 curves[2].add_key(CurveKey::new(0.0, default.z, CurveKeyKind::Constant));
722 }
723
724 for (id, curve_handle) in fbx_track.curves.iter() {
725 let index = match id.as_str() {
726 "d|X" => Some(0),
727 "d|Y" => Some(1),
728 "d|Z" => Some(2),
729 _ => None,
730 };
731
732 if let Some(index) = index {
733 if let FbxComponent::AnimationCurve(fbx_curve) = fbx_scene.get(*curve_handle) {
734 if fbx_curve.keys.is_empty() {
735 curves[index].add_key(CurveKey::new(
736 0.0,
737 default[index],
738 CurveKeyKind::Constant,
739 ));
740 } else {
741 for pair in fbx_curve.keys.iter() {
742 curves[index].add_key(CurveKey::new(
743 pair.time,
744 transform_value(pair.value),
745 CurveKeyKind::Linear,
746 ))
747 }
748 }
749 }
750 }
751 }
752 }
753
754 fn add_vec3_key(track: &mut Track, value: Vector3<f32>) {
755 let curves = track.data_container_mut().curves_mut();
756 curves[0].add_key(CurveKey::new(0.0, value.x, CurveKeyKind::Constant));
757 curves[1].add_key(CurveKey::new(0.0, value.y, CurveKeyKind::Constant));
758 curves[2].add_key(CurveKey::new(0.0, value.z, CurveKeyKind::Constant));
759 }
760
761 let mut translation_track = Track::new_position();
763 if let Some(lcl_translation) = lcl_translation {
764 fill_track(
765 &mut translation_track,
766 fbx_scene,
767 lcl_translation,
768 model.translation,
769 |v| v,
770 );
771 } else {
772 add_vec3_key(&mut translation_track, model.translation);
773 }
774
775 let mut rotation_track = Track::new_rotation();
776 if let Some(lcl_rotation) = lcl_rotation {
777 fill_track(
778 &mut rotation_track,
779 fbx_scene,
780 lcl_rotation,
781 model.rotation,
782 |v| v.to_radians(),
783 );
784 } else {
785 add_vec3_key(&mut rotation_track, model.rotation);
786 }
787
788 let mut scale_track = Track::new_scale();
789 if let Some(lcl_scale) = lcl_scale {
790 fill_track(&mut scale_track, fbx_scene, lcl_scale, model.scale, |v| v);
791 } else {
792 add_vec3_key(&mut scale_track, model.scale);
793 }
794
795 animation.add_track_with_binding(TrackBinding::new(node_handle), translation_track);
796 animation.add_track_with_binding(TrackBinding::new(node_handle), rotation_track);
797 animation.add_track_with_binding(TrackBinding::new(node_handle), scale_track);
798 }
799
800 animation.fit_length_to_content();
801
802 Ok(node_handle)
803}
804
805async fn convert(
809 fbx_scene: &FbxScene,
810 resource_manager: ResourceManager,
811 scene: &mut Scene,
812 model_path: &Path,
813 model_import_options: &ModelImportOptions,
814) -> Result<(), FbxError> {
815 let root = scene.graph.get_root();
816
817 let mut animation = Animation::default();
818 animation.set_name("Animation");
819
820 let materials = create_materials(
821 fbx_scene,
822 &resource_manager,
823 model_import_options,
824 model_path,
825 )
826 .await?;
827
828 let mut fbx_model_to_node_map = FxHashMap::default();
829 for (component_handle, component) in fbx_scene.pair_iter() {
830 if let FbxComponent::Model(model) = component {
831 let node = convert_model(
832 fbx_scene,
833 model,
834 &mut scene.graph,
835 &mut animation,
836 &materials,
837 )?;
838 scene.graph.link_nodes(node, root);
839 fbx_model_to_node_map.insert(component_handle, node);
840 }
841 }
842
843 if !animation.tracks_data().data_ref().tracks().is_empty() {
845 let mut animations_container = AnimationContainer::new();
846 animations_container.add(animation);
847 AnimationPlayerBuilder::new(BaseBuilder::new().with_name("AnimationPlayer"))
848 .with_animations(animations_container)
849 .build(&mut scene.graph);
850 }
851
852 for (&fbx_model_handle, node_handle) in fbx_model_to_node_map.iter() {
854 if let FbxComponent::Model(fbx_model) = fbx_scene.get(fbx_model_handle) {
855 for fbx_child_handle in fbx_model.children.iter() {
856 if let Some(child_handle) = fbx_model_to_node_map.get(fbx_child_handle) {
857 scene.graph.link_nodes(*child_handle, *node_handle);
858 }
859 }
860 }
861 }
862 scene.graph.update_hierarchical_data();
863
864 for &handle in fbx_model_to_node_map.values() {
867 if let Some(mesh) = scene.graph[handle].cast_mut::<Mesh>() {
868 let mut surface_bones = FxHashSet::default();
869 for surface in mesh.surfaces_mut() {
870 for weight_set in surface.vertex_weights.iter_mut() {
871 for weight in weight_set.iter_mut() {
872 let fbx_model: Handle<FbxComponent> = weight.effector.into();
873 let bone_handle = fbx_model_to_node_map
874 .get(&fbx_model)
875 .ok_or(FbxError::UnableToRemapModelToNode)?;
876 surface_bones.insert(*bone_handle);
877 weight.effector = (*bone_handle).into();
878 }
879 }
880 surface
881 .bones
882 .set_value_silent(surface_bones.iter().copied().collect());
883
884 let data_rc = surface.data();
885 let mut data = data_rc.data_ref();
886 if data.vertex_buffer.vertex_count() as usize == surface.vertex_weights.len() {
887 let mut vertex_buffer_mut = data.vertex_buffer.modify();
888 for (mut view, weight_set) in vertex_buffer_mut
889 .iter_mut()
890 .zip(surface.vertex_weights.iter())
891 {
892 let mut indices = Vector4::default();
893 let mut weights = Vector4::default();
894 for (k, weight) in weight_set.iter().enumerate() {
895 indices[k] = surface
896 .bones
897 .iter()
898 .position(|bone_handle| {
899 *bone_handle == Handle::<Node>::from(weight.effector)
900 })
901 .ok_or(FbxError::UnableToFindBone)?
902 as u8;
903 weights[k] = weight.value;
904 }
905
906 view.write_4_f32(VertexAttributeUsage::BoneWeight, weights)
907 .unwrap();
908 view.write_4_u8(VertexAttributeUsage::BoneIndices, indices)
909 .unwrap();
910 }
911 }
912 }
913 }
914 }
915
916 Ok(())
917}
918
919pub async fn load_to_scene<P: AsRef<Path>>(
923 scene: &mut Scene,
924 resource_manager: ResourceManager,
925 io: &dyn ResourceIo,
926 path: P,
927 model_import_options: &ModelImportOptions,
928) -> Result<(), FbxError> {
929 let start_time = Instant::now();
930
931 Log::writeln(
932 MessageKind::Information,
933 format!("Trying to load {:?}", path.as_ref()),
934 );
935
936 let now = Instant::now();
937 let fbx = FbxDocument::new(path.as_ref(), io).await?;
938 let parsing_time = now.elapsed().as_millis();
939
940 let now = Instant::now();
941 let fbx_scene = FbxScene::new(&fbx)?;
942 let dom_prepare_time = now.elapsed().as_millis();
943
944 let now = Instant::now();
945 convert(
946 &fbx_scene,
947 resource_manager,
948 scene,
949 path.as_ref(),
950 model_import_options,
951 )
952 .await?;
953 let conversion_time = now.elapsed().as_millis();
954
955 Log::writeln(MessageKind::Information,
956 format!("FBX {:?} loaded in {} ms\n\t- Parsing - {} ms\n\t- DOM Prepare - {} ms\n\t- Conversion - {} ms",
957 path.as_ref(), start_time.elapsed().as_millis(), parsing_time, dom_prepare_time, conversion_time));
958
959 let mut hash_set = FxHashSet::<String>::default();
964 for node in scene.graph.linear_iter() {
965 if hash_set.contains(node.name()) {
966 Log::writeln(
967 MessageKind::Error,
968 format!(
969 "A node with existing name {} was found during the load of {} resource! \
970 Do **NOT IGNORE** this message, please fix names in your model, otherwise \
971 engine won't be able to correctly restore data from your resource!",
972 node.name(),
973 path.as_ref().display()
974 ),
975 );
976 } else {
977 hash_set.insert(node.name_owned());
978 }
979 }
980
981 Ok(())
982}