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